Fix all compilation warnings on Linux
[banana.git] / mbox-helper / mbox-helper.c
1 /** Read an mbox
2 */
3
4 #define _GNU_SOURCE
5 #include <unistd.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <ctype.h>
10 #include <locale.h>
11
12 /** Macros
13 */
14 #define LTRIM(pos) while (isspace(*pos)) { pos++; }
15 #define STRTOLOWER(str, ptr) for (ptr = str ; *ptr ; ptr++) { *ptr = tolower(*ptr); }
16
17 /** Boolean
18 */
19 typedef char bool;
20 #define TRUE ((bool)(-1))
21 #define FALSE ((bool)(0))
22
23 /** MBox pointer
24 */
25 typedef struct
26 {
27 FILE *fp; // File pointer
28 long int lastLine; // Offset of the precedent line (-1 if invalid)
29 long int currentLine; // Offset of the current line
30 long int messageId; // Current message Id
31 long int messageBeginning; // Offset of the beginning of the message (FROM_ line)
32
33 char *line; // Line buffer
34 bool isFrom_; // Is the current line a From_ line ?
35 }
36 MBox;
37
38 /** Open a mbox
39 */
40 MBox *openMBox(char *filename)
41 {
42 FILE *fp;
43 MBox *mbox;
44
45 fp = fopen(filename, "r");
46 if (!fp) {
47 return NULL;
48 }
49
50 mbox = (MBox*)malloc(sizeof(MBox));
51 mbox->fp = fp;
52 mbox->lastLine = -1;
53 mbox->currentLine = 0;
54 mbox->messageId = 0;
55 mbox->messageBeginning = 0;
56 mbox->line = NULL;
57 mbox->isFrom_ = FALSE;
58 return mbox;
59 }
60
61 /** Close a mbox
62 */
63 void closeMBox(MBox *mbox)
64 {
65 if (!mbox) {
66 return;
67 }
68 fclose(mbox->fp);
69 if (mbox->line) {
70 free(mbox->line);
71 }
72 free(mbox);
73 }
74
75 /** Read a line in a file
76 */
77 char *readLine(MBox *mbox)
78 {
79 int length;
80 mbox->lastLine = mbox->currentLine;
81 mbox->currentLine = ftell(mbox->fp);
82 mbox->isFrom_ = FALSE;
83
84 if (!mbox->line) {
85 mbox->line = (char*)malloc(1001);
86 }
87 if (!fgets(mbox->line, 1000, mbox->fp)) {
88 mbox->currentLine = -1;
89 return NULL;
90 }
91 length = ftell(mbox->fp) - mbox->currentLine;
92 if (length > 1000) {
93 length = 1000;
94 }
95 if (length) {
96 while (length >= 0 && (isspace(mbox->line[length]) || mbox->line[length] == '\0')) {
97 length--;
98 }
99 mbox->line[length + 1] = '\0';
100 }
101 mbox->isFrom_ = (strstr(mbox->line, "From ") == mbox->line);
102 if (mbox->isFrom_ && mbox->messageBeginning != mbox->currentLine) {
103 mbox->messageBeginning = mbox->currentLine;
104 mbox->messageId++;
105 }
106 return mbox->line;
107 }
108
109 /** Return to the last line
110 */
111 bool lastLine(MBox *mbox)
112 {
113 if (mbox->lastLine != -1) {
114 fseek(mbox->fp, mbox->lastLine, SEEK_SET);
115 mbox->lastLine = -1;
116 readLine(mbox);
117 return TRUE;
118 }
119 return FALSE;
120 }
121
122 bool readFrom_(MBox *mbox)
123 {
124 if (!mbox->isFrom_) {
125 readLine(mbox);
126 }
127 if (!mbox->isFrom_) {
128 return FALSE;
129 }
130 return TRUE;
131 }
132
133 /** Read a message
134 */
135 void readMessage(MBox *mbox, bool display)
136 {
137 if (!readFrom_(mbox)) {
138 return;
139 }
140 while (readLine(mbox)) {
141 if (mbox->isFrom_) {
142 return;
143 }
144 if (display) {
145 if (strstr(mbox->line, ">From ") == mbox->line) {
146 puts(mbox->line + 1);
147 } else {
148 puts(mbox->line);
149 }
150 }
151 }
152 }
153
154 /** Read the headers of a message
155 */
156 void readHeaders(MBox *mbox, char **headers, int hdrsize)
157 {
158 char *current = NULL;
159 char *pos, *ptr;
160 int size, i;
161
162 if (!readFrom_(mbox)) {
163 return;
164 }
165 printf("%d\n%d\n", (int)mbox->messageId, (int)mbox->messageBeginning);
166 while (readLine(mbox)) {
167 if (mbox->isFrom_ || !strlen(mbox->line)) {
168 break;
169 }
170 if (current && strlen(mbox->line) && isspace(*(mbox->line))) {
171 pos = mbox->line;
172 LTRIM(pos);
173 printf(" %s", pos);
174 } else {
175 if (current) {
176 printf("\n");
177 free(current);
178 current = NULL;
179 }
180 pos = strchr(mbox->line, ':');
181 if (!pos || pos == mbox->line) {
182 continue;
183 }
184 size = pos - mbox->line;
185 for (i = 0 ; i < hdrsize ; i++) {
186 if (strlen(headers[i]) == size && strcasestr(mbox->line, headers[i]) == mbox->line) {
187 current = (char*)malloc(size + 1);
188 strcpy(current, headers[i]);
189 current[size] = '\0';
190 }
191 }
192 if (!current && !hdrsize) {
193 current = (char*)malloc(size + 1);
194 strncpy(current, mbox->line, size);
195 current[size] = '\0';
196 STRTOLOWER(current, ptr);
197 }
198 if (current) {
199 puts(current);
200 pos++;
201 LTRIM(pos);
202 printf("%s", pos);
203 }
204 }
205 }
206 if (current) {
207 printf("\n");
208 free(current);
209 current = NULL;
210 }
211 printf("\n");
212 }
213
214 /** Go back to the beginning of the file
215 */
216 void rewindMBox(MBox *mbox)
217 {
218 fseek(mbox->fp, 0, SEEK_SET);
219 mbox->messageId = 0;
220 mbox->messageBeginning = 0;
221 readLine(mbox);
222 }
223
224 /** Go back to the beginning of the message
225 */
226 bool rewindMessage(MBox *mbox)
227 {
228 if (mbox->isFrom_) {
229 return TRUE;
230 }
231 fseek(mbox->fp, mbox->messageBeginning, SEEK_SET);
232 mbox->currentLine = -1;
233 mbox->lastLine = -1;
234 readLine(mbox);
235 return mbox->isFrom_;
236 }
237
238 /** Move to the given offset
239 */
240 bool goToOffset(MBox *mbox, int offset, int index)
241 {
242 fseek(mbox->fp, offset, SEEK_SET);
243 mbox->currentLine = -1;
244 mbox->lastLine = -1;
245 mbox->messageBeginning = offset;
246 mbox->messageId = index;
247 readLine(mbox);
248 if (!mbox->isFrom_) {
249 return FALSE;
250 }
251 return TRUE;
252 }
253
254 /** Move to the given message number
255 */
256 bool goToMessage(MBox *mbox, int index)
257 {
258 if (mbox->messageId > index) {
259 rewindMBox(mbox);
260 } else if(mbox->messageId == index) {
261 rewindMessage(mbox);
262 return TRUE;
263 } else if (!mbox->isFrom_) {
264 while (!feof(mbox->fp) && !mbox->isFrom_) {
265 readLine(mbox);
266 }
267 if (feof(mbox->fp)) {
268 return FALSE;
269 }
270 }
271 while (mbox->messageId < index && !feof(mbox->fp)) {
272 readMessage(mbox, FALSE);
273 }
274 if (mbox->messageId == index) {
275 return TRUE;
276 }
277 return FALSE;
278 }
279
280
281 /** Display the program help
282 */
283 void help(void)
284 {
285 printf("Usage: mbox-helper [action] [options] -f filename [header1 [header2 ...]]\n"
286 "Actions: only the last action given is applied\n"
287 " -c compute the number of messages. If -p is given, process the file starting à the given offset\n"
288 " -d return the headers of the messages given with the -m option. If no header is given in the\n"
289 " command line options, all the headers are returned. The headers are return with the format:\n"
290 " MSG1_ID\\n\n"
291 " MSG1_OFFSET\\n\n"
292 " MSG1_HEADER1_NAME\\n\n"
293 " MSG1_HEADER1_VALUE\\n\n"
294 " MSG1_HEADER2_NAME\\n\n"
295 " MSG2_HEADER2_VALUE\\n\n"
296 " ...\n"
297 " Messages are separated by a blank line\n"
298 " -b return the body of the message given by -m (only 1 message is returned)\n"
299 "Options:\n"
300 " -m begin[:end] id or range of messages to process\n"
301 " -p id:pos indicate that message `id` begins at offset `pos`\n"
302 " -h print this help\n");
303 }
304
305 /** Display an error message
306 * This function display the giver error, then show the program help and exit the program
307 */
308 void error(char *message)
309 {
310 fprintf(stderr, "Invalid parameters: %s\n", message);
311 help();
312 exit(1);
313 }
314
315 /** Main function
316 */
317 int main(int argc, char *argv[])
318 {
319 int c, i = 0;
320 int fmid = -1, lmid = -1;
321 int pmid = 0, pos = 0;
322 char *filename = NULL;
323 char **headers = NULL;
324 char action = 0;
325 int headerNb = 0;
326 char *endptr;
327 MBox *mbox;
328
329 /* getopt variables */
330 extern char *optarg;
331 extern int optind, optopt;
332
333 while ((c = getopt(argc, argv, ":bcdp:hm:f:")) != -1) {
334 switch (c) {
335 case 'f':
336 filename = optarg;
337 break;
338 case 'm':
339 fmid = strtol(optarg, &endptr, 10);
340 if (endptr == optarg) {
341 error("invalid message id");
342 }
343 if (*endptr != ':') {
344 lmid = fmid;
345 } else {
346 lmid = atoi(endptr + 1);
347 }
348 break;
349 case 'p':
350 if ((endptr = strchr(optarg, ':')) != NULL) {
351 pmid = strtol(optarg, &endptr, 10);
352 if (*endptr != ':') {
353 error("invalid position couple given");
354 }
355 pos = atoi(endptr + 1);
356 } else {
357 error("invalid position given");
358 }
359 break;
360 case 'c': case 'd': case 'b':
361 action = c;
362 break;
363 case 'h':
364 help();
365 return 0;
366 case ':':
367 fprintf(stderr, "Missing argument to -%c\n", optopt);
368 break;
369 case '?':
370 fprintf(stderr, "Unrecognized option: -%c\n", optopt);
371 break;
372 }
373 }
374
375 if (!filename) {
376 error("no file defined");
377 }
378
379 setlocale(LC_ALL, "C");
380
381 headerNb = argc - optind;
382 headers = (argv + optind);
383 for (i = 0 ; i < headerNb ; i++) {
384 STRTOLOWER(headers[i], endptr);
385 }
386
387 mbox = openMBox(filename);
388 if (!mbox) {
389 fprintf(stderr, "can't open file '%s'", filename);
390 }
391 if ((fmid >= pmid || fmid == -1) && pos) {
392 if (!goToOffset(mbox, pos, pmid)) {
393 fprintf(stderr, "Offset %d do not match with a message beginning\n", pos);
394 rewindMBox(mbox);
395 }
396 }
397 switch (action) {
398 case 'b':
399 if (fmid == -1) {
400 fprintf(stderr, "you have to define a message number");
401 break;
402 }
403 goToMessage(mbox, fmid);
404 readMessage(mbox, TRUE);
405 break;
406 case 'c':
407 while (!feof(mbox->fp)) {
408 readLine(mbox);
409 }
410 printf("%d\n", (int)(mbox->messageId + 1));
411 break;
412 case 'd':
413 if (fmid == -1) {
414 fprintf(stderr, "you have to define a message number");
415 break;
416 }
417 for (i = fmid ; i <= lmid ; i++) {
418 goToMessage(mbox, i);
419 readHeaders(mbox, headers, headerNb);
420 }
421 break;
422 }
423 closeMBox(mbox);
424
425 return 0;
426 }