Start of early patch applicator tools for git.
[git/git.git] / mailinfo.c
CommitLineData
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
10static FILE *cmitmsg, *patchfile;
11
12static char line[1000];
13static char name[1000];
14static char email[1000];
15static char subject[1000];
16
17static 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
27static 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
81static void handle_subject(char *line)
82{
83 strcpy(subject, line);
84}
85
86static void add_subject_line(char *line)
87{
88 while (isspace(*line))
89 line++;
90 *--line = ' ';
91 strcat(subject, line);
92}
93
94static 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
122static 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
156static 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 */
178static 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
198 case '~':
199 break;
200 }
201 break;
202 }
203 /* remove ".orig" from the end - common patch behaviour */
204 if (len > 5 && !memcmp(name+len-5, ".orig", 5))
205 len -=5;
206 if (!len)
207 return;
208 printf("filename: %.*s\n", len, name);
209}
210
211static void handle_rest(void)
212{
213 char *sub = cleanup_subject(subject);
214 cleanup_space(name);
215 cleanup_space(email);
216 cleanup_space(sub);
217 printf("Author: %s\nEmail: %s\nSubject: %s\n\n", name, email, sub);
218 FILE *out = cmitmsg;
219
220 do {
221 /* Track filename information from the patch.. */
222 if (!memcmp("---", line, 3)) {
223 out = patchfile;
224 show_filename(line+3);
225 }
226
227 if (!memcmp("+++", line, 3))
228 show_filename(line+3);
229
230 fputs(line, out);
231 } while (fgets(line, sizeof(line), stdin) != NULL);
232
233 if (out == cmitmsg) {
234 fprintf(stderr, "No patch found\n");
235 exit(1);
236 }
237
238 fclose(cmitmsg);
239 fclose(patchfile);
240}
241
242static int eatspace(char *line)
243{
244 int len = strlen(line);
245 while (len > 0 && isspace(line[len-1]))
246 line[--len] = 0;
247 return len;
248}
249
250static void handle_body(void)
251{
252 int has_from = 0;
253
254 /* First line of body can be a From: */
255 while (fgets(line, sizeof(line), stdin) != NULL) {
256 int len = eatspace(line);
257 if (!len)
258 continue;
259 if (!memcmp("From:", line, 5) && isspace(line[5])) {
260 if (!has_from && handle_from(line+6)) {
261 has_from = 1;
262 continue;
263 }
264 }
265 line[len] = '\n';
266 handle_rest();
267 break;
268 }
269}
270
271static void usage(void)
272{
273 fprintf(stderr, "mailinfo msg-file path-file < email\n");
274 exit(1);
275}
276
277int main(int argc, char ** argv)
278{
279 if (argc != 3)
280 usage();
281 cmitmsg = fopen(argv[1], "w");
282 if (!cmitmsg) {
283 perror(argv[1]);
284 exit(1);
285 }
286 patchfile = fopen(argv[2], "w");
287 if (!patchfile) {
288 perror(argv[2]);
289 exit(1);
290 }
291 while (fgets(line, sizeof(line), stdin) != NULL) {
292 int len = eatspace(line);
293 if (!len) {
294 handle_body();
295 break;
296 }
297 check_line(line, len);
298 }
299 return 0;
300}