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