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;
11
12static int keep_subject = 0;
13static char line[1000];
14static char date[1000];
15static char name[1000];
16static char email[1000];
17static char subject[1000];
18
19static 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
29static 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
83static void handle_date(char *line)
84{
85 strcpy(date, line);
86}
87
88static void handle_subject(char *line)
89{
90 strcpy(subject, line);
91}
92
93static 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
103static 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
139static 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
156static 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
184static 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
192static 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
221static 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
242static void usage(void)
243{
244 fprintf(stderr, "mailinfo msg-file patch-file < email\n");
245 exit(1);
246}
247
248static const char mailinfo_usage[] =
249"git-mailinfo [-k] msg patch <mail >info";
250int 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}