Start of early patch applicator tools for git.
[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;
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                 case '~':
199                         break;
200                 }
201                 break;
202         }
203         /* remove ".orig" from the end - common patch behaviour */
204         if (len > 5 && !memcmp(name+len-5, ".orig", 5))
205                 len -=5;
206         if (!len)
207                 return;
208         printf("filename: %.*s\n", len, name);
209 }
210
211 static void handle_rest(void)
212 {
213         char *sub = cleanup_subject(subject);
214         cleanup_space(name);
215         cleanup_space(email);
216         cleanup_space(sub);
217         printf("Author: %s\nEmail: %s\nSubject: %s\n\n", name, email, sub);
218         FILE *out = cmitmsg;
219
220         do {
221                 /* Track filename information from the patch.. */
222                 if (!memcmp("---", line, 3)) {
223                         out = patchfile;
224                         show_filename(line+3);
225                 }
226
227                 if (!memcmp("+++", line, 3))
228                         show_filename(line+3);
229
230                 fputs(line, out);
231         } while (fgets(line, sizeof(line), stdin) != NULL);
232
233         if (out == cmitmsg) {
234                 fprintf(stderr, "No patch found\n");
235                 exit(1);
236         }
237
238         fclose(cmitmsg);
239         fclose(patchfile);
240 }
241
242 static int eatspace(char *line)
243 {
244         int len = strlen(line);
245         while (len > 0 && isspace(line[len-1]))
246                 line[--len] = 0;
247         return len;
248 }
249
250 static void handle_body(void)
251 {
252         int has_from = 0;
253
254         /* First line of body can be a From: */
255         while (fgets(line, sizeof(line), stdin) != NULL) {
256                 int len = eatspace(line);
257                 if (!len)
258                         continue;
259                 if (!memcmp("From:", line, 5) && isspace(line[5])) {
260                         if (!has_from && handle_from(line+6)) {
261                                 has_from = 1;
262                                 continue;
263                         }
264                 }
265                 line[len] = '\n';
266                 handle_rest();
267                 break;
268         }
269 }
270
271 static void usage(void)
272 {
273         fprintf(stderr, "mailinfo msg-file path-file < email\n");
274         exit(1);
275 }
276
277 int main(int argc, char ** argv)
278 {
279         if (argc != 3)
280                 usage();
281         cmitmsg = fopen(argv[1], "w");
282         if (!cmitmsg) {
283                 perror(argv[1]);
284                 exit(1);
285         }
286         patchfile = fopen(argv[2], "w");
287         if (!patchfile) {
288                 perror(argv[2]);
289                 exit(1);
290         }
291         while (fgets(line, sizeof(line), stdin) != NULL) {
292                 int len = eatspace(line);
293                 if (!len) {
294                         handle_body();
295                         break;
296                 }
297                 check_line(line, len);
298         }
299         return 0;
300 }