]>
Commit | Line | Data |
---|---|---|
2744b234 LT |
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 | ||
853916ff | 10 | static FILE *cmitmsg, *patchfile, *filelist; |
2744b234 LT |
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 | ||
853916ff | 198 | /* patch tends to special-case these things.. */ |
2744b234 LT |
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; | |
853916ff | 209 | fprintf(filelist, "%.*s\n", len, name); |
2744b234 LT |
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 | { | |
853916ff | 274 | fprintf(stderr, "mailinfo msg-file path-file filelist-file < email\n"); |
2744b234 LT |
275 | exit(1); |
276 | } | |
277 | ||
278 | int main(int argc, char ** argv) | |
279 | { | |
853916ff | 280 | if (argc != 4) |
2744b234 LT |
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 | } | |
853916ff LT |
292 | filelist = fopen(argv[3], "w"); |
293 | if (!filelist) { | |
294 | perror(argv[3]); | |
295 | exit(1); | |
296 | } | |
2744b234 LT |
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 | } |