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