Avoid doing the "filelist" thing, since "git-apply" picks up the files automatically
[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 date[1000];
14 static char name[1000];
15 static char email[1000];
16 static char subject[1000];
17
18 static char *sanity_check(char *name, char *email)
19 {
20         int len = strlen(name);
21         if (len < 3 || len > 60)
22                 return email;
23         if (strchr(name, '@') || strchr(name, '<') || strchr(name, '>'))
24                 return email;
25         return name;
26 }
27
28 static int handle_from(char *line)
29 {
30         char *at = strchr(line, '@');
31         char *dst;
32
33         if (!at)
34                 return 0;
35
36         /*
37          * If we already have one email, don't take any confusing lines
38          */
39         if (*email && strchr(at+1, '@'))
40                 return 0;
41
42         while (at > line) {
43                 char c = at[-1];
44                 if (isspace(c) || c == '<')
45                         break;
46                 at--;
47         }
48         dst = email;
49         for (;;) {
50                 unsigned char c = *at;
51                 if (!c || c == '>' || isspace(c))
52                         break;
53                 *at++ = ' ';
54                 *dst++ = c;
55         }
56         *dst++ = 0;
57
58         at = line + strlen(line);
59         while (at > line) {
60                 unsigned char c = *--at;
61                 if (isalnum(c))
62                         break;
63                 *at = 0;
64         }
65
66         at = line;
67         for (;;) {
68                 unsigned char c = *at;
69                 if (!c)
70                         break;
71                 if (isalnum(c))
72                         break;
73                 at++;
74         }
75
76         at = sanity_check(at, email);
77         
78         strcpy(name, at);
79         return 1;
80 }
81
82 static void handle_date(char *line)
83 {
84         strcpy(date, line);
85 }
86
87 static void handle_subject(char *line)
88 {
89         strcpy(subject, line);
90 }
91
92 static void add_subject_line(char *line)
93 {
94         while (isspace(*line))
95                 line++;
96         *--line = ' ';
97         strcat(subject, line);
98 }
99
100 static void check_line(char *line, int len)
101 {
102         static int cont = -1;
103         if (!memcmp(line, "From:", 5) && isspace(line[5])) {
104                 handle_from(line+6);
105                 cont = 0;
106                 return;
107         }
108         if (!memcmp(line, "Date:", 5) && isspace(line[5])) {
109                 handle_date(line+6);
110                 cont = 0;
111                 return;
112         }
113         if (!memcmp(line, "Subject:", 8) && isspace(line[8])) {
114                 handle_subject(line+9);
115                 cont = 1;
116                 return;
117         }
118         if (isspace(*line)) {
119                 switch (cont) {
120                 case 0:
121                         fprintf(stderr, "I don't do 'Date:' or 'From:' line continuations\n");
122                         break;
123                 case 1:
124                         add_subject_line(line);
125                         return;
126                 default:
127                         break;
128                 }
129         }
130         cont = -1;
131 }
132
133 static char * cleanup_subject(char *subject)
134 {
135         for (;;) {
136                 char *p;
137                 int len, remove;
138                 switch (*subject) {
139                 case 'r': case 'R':
140                         if (!memcmp("e:", subject+1, 2)) {
141                                 subject +=3;
142                                 continue;
143                         }
144                         break;
145                 case ' ': case '\t': case ':':
146                         subject++;
147                         continue;
148
149                 case '[':
150                         p = strchr(subject, ']');
151                         if (!p) {
152                                 subject++;
153                                 continue;
154                         }
155                         len = strlen(p);
156                         remove = p - subject;
157                         if (remove <= len *2) {
158                                 subject = p+1;
159                                 continue;
160                         }       
161                         break;
162                 }
163                 return subject;
164         }
165 }                       
166
167 static void cleanup_space(char *buf)
168 {
169         unsigned char c;
170         while ((c = *buf) != 0) {
171                 buf++;
172                 if (isspace(c)) {
173                         buf[-1] = ' ';
174                         c = *buf;
175                         while (isspace(c)) {
176                                 int len = strlen(buf);
177                                 memmove(buf, buf+1, len);
178                                 c = *buf;
179                         }
180                 }
181         }
182 }
183
184 static void handle_rest(void)
185 {
186         char *sub = cleanup_subject(subject);
187         cleanup_space(name);
188         cleanup_space(date);
189         cleanup_space(email);
190         cleanup_space(sub);
191         printf("Author: %s\nEmail: %s\nSubject: %s\nDate: %s\n\n", name, email, sub, date);
192         FILE *out = cmitmsg;
193
194         do {
195                 if (!memcmp("diff -", line, 6) ||
196                     !memcmp("---", line, 3))
197                         out = patchfile;
198
199                 fputs(line, out);
200         } while (fgets(line, sizeof(line), stdin) != NULL);
201
202         if (out == cmitmsg) {
203                 fprintf(stderr, "No patch found\n");
204                 exit(1);
205         }
206
207         fclose(cmitmsg);
208         fclose(patchfile);
209 }
210
211 static int eatspace(char *line)
212 {
213         int len = strlen(line);
214         while (len > 0 && isspace(line[len-1]))
215                 line[--len] = 0;
216         return len;
217 }
218
219 static void handle_body(void)
220 {
221         int has_from = 0;
222
223         /* First line of body can be a From: */
224         while (fgets(line, sizeof(line), stdin) != NULL) {
225                 int len = eatspace(line);
226                 if (!len)
227                         continue;
228                 if (!memcmp("From:", line, 5) && isspace(line[5])) {
229                         if (!has_from && handle_from(line+6)) {
230                                 has_from = 1;
231                                 continue;
232                         }
233                 }
234                 line[len] = '\n';
235                 handle_rest();
236                 break;
237         }
238 }
239
240 static void usage(void)
241 {
242         fprintf(stderr, "mailinfo msg-file path-file < email\n");
243         exit(1);
244 }
245
246 int main(int argc, char ** argv)
247 {
248         if (argc != 3)
249                 usage();
250         cmitmsg = fopen(argv[1], "w");
251         if (!cmitmsg) {
252                 perror(argv[1]);
253                 exit(1);
254         }
255         patchfile = fopen(argv[2], "w");
256         if (!patchfile) {
257                 perror(argv[2]);
258                 exit(1);
259         }
260         while (fgets(line, sizeof(line), stdin) != NULL) {
261                 int len = eatspace(line);
262                 if (!len) {
263                         handle_body();
264                         break;
265                 }
266                 check_line(line, len);
267         }
268         return 0;
269 }