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