c1dcac130530174ec5335d2c752d76403ad1d3ad
[git.git] / mailinfo.c
1 /*
2  * Another stupid program, this one parsing the headers of an
3  * email to figure out authorship and subject
4  */
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <ctype.h>
9
10 static FILE *cmitmsg, *patchfile, *filelist;
11
12 static char line[1000];
13 static char name[1000];
14 static char email[1000];
15 static char subject[1000];
16
17 static char *sanity_check(char *name, char *email)
18 {
19         int len = strlen(name);
20         if (len < 3 || len > 60)
21                 return email;
22         if (strchr(name, '@') || strchr(name, '<') || strchr(name, '>'))
23                 return email;
24         return name;
25 }
26
27 static int handle_from(char *line)
28 {
29         char *at = strchr(line, '@');
30         char *dst;
31
32         if (!at)
33                 return 0;
34
35         /*
36          * If we already have one email, don't take any confusing lines
37          */
38         if (*email && strchr(at+1, '@'))
39                 return 0;
40
41         while (at > line) {
42                 char c = at[-1];
43                 if (isspace(c) || c == '<')
44                         break;
45                 at--;
46         }
47         dst = email;
48         for (;;) {
49                 unsigned char c = *at;
50                 if (!c || c == '>' || isspace(c))
51                         break;
52                 *at++ = ' ';
53                 *dst++ = c;
54         }
55         *dst++ = 0;
56
57         at = line + strlen(line);
58         while (at > line) {
59                 unsigned char c = *--at;
60                 if (isalnum(c))
61                         break;
62                 *at = 0;
63         }
64
65         at = line;
66         for (;;) {
67                 unsigned char c = *at;
68                 if (!c)
69                         break;
70                 if (isalnum(c))
71                         break;
72                 at++;
73         }
74
75         at = sanity_check(at, email);
76         
77         strcpy(name, at);
78         return 1;
79 }
80
81 static void handle_subject(char *line)
82 {
83         strcpy(subject, line);
84 }
85
86 static void add_subject_line(char *line)
87 {
88         while (isspace(*line))
89                 line++;
90         *--line = ' ';
91         strcat(subject, line);
92 }
93
94 static void check_line(char *line, int len)
95 {
96         static int cont = -1;
97         if (!memcmp(line, "From:", 5) && isspace(line[5])) {
98                 handle_from(line+6);
99                 cont = 0;
100                 return;
101         }
102         if (!memcmp(line, "Subject:", 8) && isspace(line[8])) {
103                 handle_subject(line+9);
104                 cont = 1;
105                 return;
106         }
107         if (isspace(*line)) {
108                 switch (cont) {
109                 case 0:
110                         fprintf(stderr, "I don't do 'From:' line continuations\n");
111                         break;
112                 case 1:
113                         add_subject_line(line);
114                         return;
115                 default:
116                         break;
117                 }
118         }
119         cont = -1;
120 }
121
122 static char * cleanup_subject(char *subject)
123 {
124         for (;;) {
125                 char *p;
126                 int len, remove;
127                 switch (*subject) {
128                 case 'r': case 'R':
129                         if (!memcmp("e:", subject+1, 2)) {
130                                 subject +=3;
131                                 continue;
132                         }
133                         break;
134                 case ' ': case '\t': case ':':
135                         subject++;
136                         continue;
137
138                 case '[':
139                         p = strchr(subject, ']');
140                         if (!p) {
141                                 subject++;
142                                 continue;
143                         }
144                         len = strlen(p);
145                         remove = p - subject;
146                         if (remove <= len *2) {
147                                 subject = p+1;
148                                 continue;
149                         }       
150                         break;
151                 }
152                 return subject;
153         }
154 }                       
155
156 static void cleanup_space(char *buf)
157 {
158         unsigned char c;
159         while ((c = *buf) != 0) {
160                 buf++;
161                 if (isspace(c)) {
162                         buf[-1] = ' ';
163                         c = *buf;
164                         while (isspace(c)) {
165                                 int len = strlen(buf);
166                                 memmove(buf, buf+1, len);
167                                 c = *buf;
168                         }
169                 }
170         }
171 }
172
173 /*
174  * Hacky hacky. This depends not only on -p1, but on
175  * filenames not having some special characters in them,
176  * like tilde.
177  */
178 static void show_filename(char *line)
179 {
180         int len;
181         char *name = strchr(line, '/');
182
183         if (!name || !isspace(*line))
184                 return;
185         name++;
186         len = 0;
187         for (;;) {
188                 unsigned char c = name[len];
189                 switch (c) {
190                 default:
191                         len++;
192                         continue;
193
194                 case 0: case ' ':
195                 case '\t': case '\n':
196                         break;
197
198                 /* patch tends to special-case these things.. */
199                 case '~':
200                         break;
201                 }
202                 break;
203         }
204         /* remove ".orig" from the end - common patch behaviour */
205         if (len > 5 && !memcmp(name+len-5, ".orig", 5))
206                 len -=5;
207         if (!len)
208                 return;
209         fprintf(filelist, "%.*s\n", len, name);
210 }
211
212 static void handle_rest(void)
213 {
214         char *sub = cleanup_subject(subject);
215         cleanup_space(name);
216         cleanup_space(email);
217         cleanup_space(sub);
218         printf("Author: %s\nEmail: %s\nSubject: %s\n\n", name, email, sub);
219         FILE *out = cmitmsg;
220
221         do {
222                 /* Track filename information from the patch.. */
223                 if (!memcmp("---", line, 3)) {
224                         out = patchfile;
225                         show_filename(line+3);
226                 }
227
228                 if (!memcmp("+++", line, 3))
229                         show_filename(line+3);
230
231                 fputs(line, out);
232         } while (fgets(line, sizeof(line), stdin) != NULL);
233
234         if (out == cmitmsg) {
235                 fprintf(stderr, "No patch found\n");
236                 exit(1);
237         }
238
239         fclose(cmitmsg);
240         fclose(patchfile);
241 }
242
243 static int eatspace(char *line)
244 {
245         int len = strlen(line);
246         while (len > 0 && isspace(line[len-1]))
247                 line[--len] = 0;
248         return len;
249 }
250
251 static void handle_body(void)
252 {
253         int has_from = 0;
254
255         /* First line of body can be a From: */
256         while (fgets(line, sizeof(line), stdin) != NULL) {
257                 int len = eatspace(line);
258                 if (!len)
259                         continue;
260                 if (!memcmp("From:", line, 5) && isspace(line[5])) {
261                         if (!has_from && handle_from(line+6)) {
262                                 has_from = 1;
263                                 continue;
264                         }
265                 }
266                 line[len] = '\n';
267                 handle_rest();
268                 break;
269         }
270 }
271
272 static void usage(void)
273 {
274         fprintf(stderr, "mailinfo msg-file path-file filelist-file < email\n");
275         exit(1);
276 }
277
278 int main(int argc, char ** argv)
279 {
280         if (argc != 4)
281                 usage();
282         cmitmsg = fopen(argv[1], "w");
283         if (!cmitmsg) {
284                 perror(argv[1]);
285                 exit(1);
286         }
287         patchfile = fopen(argv[2], "w");
288         if (!patchfile) {
289                 perror(argv[2]);
290                 exit(1);
291         }
292         filelist = fopen(argv[3], "w");
293         if (!filelist) {
294                 perror(argv[3]);
295                 exit(1);
296         }
297         while (fgets(line, sizeof(line), stdin) != NULL) {
298                 int len = eatspace(line);
299                 if (!len) {
300                         handle_body();
301                         break;
302                 }
303                 check_line(line, len);
304         }
305         return 0;
306 }