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
10static FILE *cmitmsg, *patchfile, *filelist;
11
12static char line[1000];
13static char date[1000];
14static char name[1000];
15static char email[1000];
16static char subject[1000];
17
18static 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
28static 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
82static void handle_date(char *line)
83{
84 strcpy(date, line);
85}
86
87static void handle_subject(char *line)
88{
89 strcpy(subject, line);
90}
91
92static void add_subject_line(char *line)
93{
94 while (isspace(*line))
95 line++;
96 *--line = ' ';
97 strcat(subject, line);
98}
99
100static 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
133static 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
167static 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/*
185 * Hacky hacky. This depends not only on -p1, but on
186 * filenames not having some special characters in them,
187 * like tilde.
188 */
189static void show_filename(char *line)
190{
191 int len;
192 char *name = strchr(line, '/');
193
194 if (!name || !isspace(*line))
195 return;
196 name++;
197 len = 0;
198 for (;;) {
199 unsigned char c = name[len];
200 switch (c) {
201 default:
202 len++;
203 continue;
204
205 case 0: case ' ':
206 case '\t': case '\n':
207 break;
208
209 /* patch tends to special-case these things.. */
210 case '~':
211 break;
212 }
213 break;
214 }
215 /* remove ".orig" from the end - common patch behaviour */
216 if (len > 5 && !memcmp(name+len-5, ".orig", 5))
217 len -=5;
218 if (!len)
219 return;
220 fprintf(filelist, "%.*s\n", len, name);
221}
222
223static void handle_rest(void)
224{
225 char *sub = cleanup_subject(subject);
226 cleanup_space(name);
227 cleanup_space(date);
228 cleanup_space(email);
229 cleanup_space(sub);
230 printf("Author: %s\nEmail: %s\nSubject: %s\nDate: %s\n\n", name, email, sub, date);
231 FILE *out = cmitmsg;
232
233 do {
234 /* Track filename information from the patch.. */
235 if (!memcmp("---", line, 3)) {
236 out = patchfile;
237 show_filename(line+3);
238 }
239
240 if (!memcmp("+++", line, 3))
241 show_filename(line+3);
242
243 fputs(line, out);
244 } while (fgets(line, sizeof(line), stdin) != NULL);
245
246 if (out == cmitmsg) {
247 fprintf(stderr, "No patch found\n");
248 exit(1);
249 }
250
251 fclose(cmitmsg);
252 fclose(patchfile);
253}
254
255static int eatspace(char *line)
256{
257 int len = strlen(line);
258 while (len > 0 && isspace(line[len-1]))
259 line[--len] = 0;
260 return len;
261}
262
263static void handle_body(void)
264{
265 int has_from = 0;
266
267 /* First line of body can be a From: */
268 while (fgets(line, sizeof(line), stdin) != NULL) {
269 int len = eatspace(line);
270 if (!len)
271 continue;
272 if (!memcmp("From:", line, 5) && isspace(line[5])) {
273 if (!has_from && handle_from(line+6)) {
274 has_from = 1;
275 continue;
276 }
277 }
278 line[len] = '\n';
279 handle_rest();
280 break;
281 }
282}
283
284static void usage(void)
285{
286 fprintf(stderr, "mailinfo msg-file path-file filelist-file < email\n");
287 exit(1);
288}
289
290int main(int argc, char ** argv)
291{
292 if (argc != 4)
293 usage();
294 cmitmsg = fopen(argv[1], "w");
295 if (!cmitmsg) {
296 perror(argv[1]);
297 exit(1);
298 }
299 patchfile = fopen(argv[2], "w");
300 if (!patchfile) {
301 perror(argv[2]);
302 exit(1);
303 }
304 filelist = fopen(argv[3], "w");
305 if (!filelist) {
306 perror(argv[3]);
307 exit(1);
308 }
309 while (fgets(line, sizeof(line), stdin) != NULL) {
310 int len = eatspace(line);
311 if (!len) {
312 handle_body();
313 break;
314 }
315 check_line(line, len);
316 }
317 return 0;
318}