746f6caf6695b5ae81fd0de703e630330fe63482
1 /********************************************************************************
2 * mbox-helper/mbox-helper.c : read and parse an mbox file
3 * ------------------------
5 * This file is part of the banana distribution
6 * Copyright: See COPYING files that comes with this distribution
7 ********************************************************************************/
21 #define LTRIM(pos) while (isspace(*pos)) { pos++; }
22 #define STRTOLOWER(str, ptr) for (ptr = str ; *ptr ; ptr++) { *ptr = tolower(*ptr); }
28 FILE *fp
; // File pointer
29 long int lastLine
; // Offset of the precedent line (-1 if invalid)
30 long int currentLine
; // Offset of the current line
31 long int messageId
; // Current message Id
32 long int messageBeginning
; // Offset of the beginning of the message (FROM_ line)
34 char *line
; // Line buffer
35 bool isFrom_
; // Is the current line a From_ line ?
40 * @param filename char* Path to the file to open
41 * @return NULL on error, a well initialized MBox structure pointer on success
43 static MBox
*openMBox(char *filename
)
48 fp
= fopen(filename
, "r");
53 mbox
= (MBox
*)malloc(sizeof(MBox
));
56 mbox
->currentLine
= 0;
58 mbox
->messageBeginning
= 0;
60 mbox
->isFrom_
= false;
66 static void closeMBox(MBox
*mbox
)
78 /** Read a line in a file
79 * @param mbox MBox the source mbox
80 * @return the read line
82 * This function do not only read the line, it does minimum parsing stuff and
83 * set the corresponding mbox structure flags
85 static char *readLine(MBox
*mbox
)
88 mbox
->lastLine
= mbox
->currentLine
;
89 mbox
->currentLine
= ftell(mbox
->fp
);
90 mbox
->isFrom_
= false;
93 mbox
->line
= (char*)malloc(1001);
95 if (!fgets(mbox
->line
, 1000, mbox
->fp
)) {
96 mbox
->currentLine
= -1;
99 length
= ftell(mbox
->fp
) - mbox
->currentLine
;
104 while (length
>= 0 && (isspace(mbox
->line
[length
]) || mbox
->line
[length
] == '\0')) {
107 mbox
->line
[length
+ 1] = '\0';
109 mbox
->isFrom_
= (strstr(mbox
->line
, "From ") == mbox
->line
);
110 if (mbox
->isFrom_
&& mbox
->messageBeginning
!= mbox
->currentLine
) {
111 mbox
->messageBeginning
= mbox
->currentLine
;
117 /** Read a From_ line from the mbox
118 * the From_ line MUST be the current or the next one
120 static bool readFrom_(MBox
*mbox
)
122 if (!mbox
->isFrom_
) {
125 return !!mbox
->isFrom_
;
129 * The message is not stored or returned, just skipped.
130 * If display is true, the message is printed on stdio
132 static void readMessage(MBox
*mbox
, bool display
)
134 if (!readFrom_(mbox
)) {
137 while (readLine(mbox
)) {
142 if (strstr(mbox
->line
, ">From ") == mbox
->line
) {
143 puts(mbox
->line
+ 1);
151 /** Read the headers of a message
152 * Read the given headers of the current message of the given mbox
153 * @param mbox MBox source
154 * @param headers char** list of requested headers
155 * @param hdrsize int size of @ref headers
157 * THe headers are printed on stdio
159 static void readHeaders(MBox
*mbox
, char **headers
, int hdrsize
)
161 char *current
= NULL
;
165 if (!readFrom_(mbox
)) {
168 printf("%d\n%d\n", (int)mbox
->messageId
, (int)mbox
->messageBeginning
);
169 while (readLine(mbox
)) {
170 if (mbox
->isFrom_
|| !strlen(mbox
->line
)) {
173 if (current
&& strlen(mbox
->line
) && isspace(*(mbox
->line
))) {
183 pos
= strchr(mbox
->line
, ':');
184 if (!pos
|| pos
== mbox
->line
) {
187 size
= pos
- mbox
->line
;
188 for (i
= 0 ; i
< hdrsize
; i
++) {
189 if ((int)strlen(headers
[i
]) == size
&& strcasestr(mbox
->line
, headers
[i
]) == mbox
->line
) {
190 current
= (char*)malloc(size
+ 1);
191 strcpy(current
, headers
[i
]);
192 current
[size
] = '\0';
195 if (!current
&& !hdrsize
) {
196 current
= (char*)malloc(size
+ 1);
197 strncpy(current
, mbox
->line
, size
);
198 current
[size
] = '\0';
199 STRTOLOWER(current
, ptr
);
217 /** Go back to the beginning of the file
219 static void rewindMBox(MBox
*mbox
)
221 fseek(mbox
->fp
, 0, SEEK_SET
);
223 mbox
->messageBeginning
= 0;
227 /** Go back to the beginning of the current message
228 * @return true if the beginning of a message has been reached
230 static bool rewindMessage(MBox
*mbox
)
235 fseek(mbox
->fp
, mbox
->messageBeginning
, SEEK_SET
);
236 mbox
->currentLine
= -1;
239 return mbox
->isFrom_
;
242 /** Move to the given offset
243 * @param mbox MBox the source mbox
244 * @param offset int offset where to go
245 * @param idx int index of the message corresponding with the offset
246 * @return true if the given offset is the beginning of a message
248 static bool goToOffset(MBox
*mbox
, int offset
, int idx
)
250 fseek(mbox
->fp
, offset
, SEEK_SET
);
251 mbox
->currentLine
= -1;
253 mbox
->messageBeginning
= offset
;
254 mbox
->messageId
= idx
;
256 if (!mbox
->isFrom_
) {
262 /** Move to the given message number
263 * @param mbox MBox the source mbox
264 * @param idx int the index of the message where to go
265 * @return true if the given message has been reached
267 static bool goToMessage(MBox
*mbox
, int idx
)
269 if (mbox
->messageId
> idx
) {
271 } else if(mbox
->messageId
== idx
) {
274 } else if (!mbox
->isFrom_
) {
275 while (!feof(mbox
->fp
) && !mbox
->isFrom_
) {
278 if (feof(mbox
->fp
)) {
282 while (mbox
->messageId
< idx
&& !feof(mbox
->fp
)) {
283 readMessage(mbox
, false);
285 if (mbox
->messageId
== idx
) {
292 /** Display the program usage help
294 static void help(void)
296 printf("Usage: mbox-helper [action] [options] -f filename [header1 [header2 ...]]\n"
297 "Actions: only the last action given is applied\n"
298 " -c compute the number of messages. If -p is given, process the file starting à the given offset\n"
299 " -d return the headers of the messages given with the -m option. If no header is given in the\n"
300 " command line options, all the headers are returned. The headers are return with the format:\n"
303 " MSG1_HEADER1_NAME\\n\n"
304 " MSG1_HEADER1_VALUE\\n\n"
305 " MSG1_HEADER2_NAME\\n\n"
306 " MSG2_HEADER2_VALUE\\n\n"
308 " Messages are separated by a blank line\n"
309 " -b return the body of the message given by -m (only 1 message is returned)\n"
311 " -m begin[:end] id or range of messages to process\n"
312 " -p id:pos indicate that message `id` begins at offset `pos`\n"
313 " -h print this help\n");
316 /** Display an error message
317 * This function display the giver error, then show the program help and exit the program
318 * The memory must be cleared before calling this function
320 static void error(const char *message
)
322 fprintf(stderr
, "Invalid parameters: %s\n", message
);
329 int main(int argc
, char *argv
[])
332 int fmid
= -1, lmid
= -1;
333 int pmid
= 0, pos
= 0;
334 char *filename
= NULL
;
335 char **headers
= NULL
;
341 // Parse command line
342 while ((c
= getopt(argc
, argv
, ":bcdp:hm:f:")) != -1) {
348 fmid
= strtol(optarg
, &endptr
, 10);
349 if (endptr
== optarg
) {
350 error("invalid message id");
352 if (*endptr
!= ':' || !*(endptr
+1)) {
355 lmid
= atoi(endptr
+ 1);
359 if ((endptr
= strchr(optarg
, ':')) != NULL
) {
360 pmid
= strtol(optarg
, &endptr
, 10);
361 if (*endptr
!= ':' || !*(endptr
+1)) {
362 error("invalid position couple given");
364 pos
= atoi(endptr
+ 1);
366 error("invalid position given");
369 case 'c': case 'd': case 'b':
376 fprintf(stderr
, "Missing argument to -%c\n", optopt
);
379 fprintf(stderr
, "Unrecognized option: -%c\n", optopt
);
384 // Check command line arguments consistence
386 error("no file defined");
389 setlocale(LC_ALL
, "C");
391 headerNb
= argc
- optind
;
392 headers
= (argv
+ optind
);
393 for (i
= 0 ; i
< headerNb
; i
++) {
394 STRTOLOWER(headers
[i
], endptr
);
397 mbox
= openMBox(filename
);
399 fprintf(stderr
, "can't open file '%s'", filename
);
402 if ((fmid
>= pmid
|| fmid
== -1) && pos
) {
403 if (!goToOffset(mbox
, pos
, pmid
)) {
404 fprintf(stderr
, "Offset %d do not match with a message beginning\n", pos
);
409 // Do requested stuff
413 fprintf(stderr
, "you have to define a message number");
416 goToMessage(mbox
, fmid
);
417 readMessage(mbox
, true);
420 while (!feof(mbox
->fp
)) {
423 printf("%d\n", (int)(mbox
->messageId
+ 1));
427 fprintf(stderr
, "you have to define a message number");
430 for (i
= fmid
; i
<= lmid
; i
++) {
431 goToMessage(mbox
, i
);
432 readHeaders(mbox
, headers
, headerNb
);