File Coverage

C.xs
Criterion Covered Total %
statement 0 394 0.0
branch 0 288 0.0
condition n/a
subroutine n/a
pod n/a
total 0 682 0.0


line stmt bran cond sub pod time code
1              
2             #include "EXTERN.h"
3             #include "perl.h"
4             #include "XSUB.h"
5             #include
6              
7             #define TRACE_INTERNAL 7 /* synchronize this with the %trace_levels */
8             #define NO_TRACE 6 /* in Mail::Reporter */
9             #define TRACE_ERRORS 5
10             #define TRACE_WARNINGS 4
11             #define TRACE_PROGRESS 3
12             #define TRACE_NOTICES 2
13             #define TRACE_DEBUG 1
14              
15             /* [3.007] Although the rfc5322 spec says max 1000, we do permit a larger
16             * header-line for "robustness"
17             */
18              
19             #ifndef DFLT_LINE
20             #define DFLT_LINE 1024
21             #endif
22              
23             #ifndef NULL
24             #define NULL
25             #endif
26              
27             #ifndef EOL
28             #define EOL '\0'
29             #endif
30              
31             #ifndef CR
32             #define CR '\015'
33             #endif
34              
35             #ifndef LF
36             #define LF '\012'
37             #endif
38              
39             #define MAX_FOLD 512
40             #define FOLDSTART " "
41             #define COPYSIZE 4096
42              
43             typedef struct separator
44             { char * line;
45             int length;
46             struct separator * next;
47             } Separator;
48              
49             typedef struct
50             { char * filename;
51             FILE * file;
52             Separator * separators;
53              
54             int trace;
55             int dosmode;
56              
57             int strip_gt;
58             int keep_line; /* unget line */
59              
60             char * line;
61             int line_buf_size;
62             long line_start;
63             } Mailbox;
64              
65             static Mailbox ** boxes = NULL;
66             static int nr_boxes = 0;
67              
68             /*
69             * new_mailbox
70             */
71              
72 0           Mailbox * new_mailbox(char *filename)
73             { Mailbox * box;
74              
75 0           New(0, box, 1, Mailbox);
76 0           box->keep_line = 0;
77 0           box->strip_gt = 0;
78 0           box->dosmode = 1; /* will be set to 0 if not true */
79 0           box->separators = NULL;
80              
81             /* Copy the filename. */
82 0           New(0, box->filename, strlen(filename)+1, char);
83 0           strcpy(box->filename, filename);
84              
85 0           New(0, box->line, DFLT_LINE, char);
86 0           box->line_buf_size = DFLT_LINE;
87 0           return box;
88             }
89              
90             /*
91             * take_box_slot
92             */
93              
94 0           static int take_box_slot(Mailbox *new)
95             { int boxnr;
96              
97 0 0         if(boxes==NULL)
98 0           { nr_boxes = 10;
99 0           Newz(0, boxes, nr_boxes, Mailbox *);
100 0           boxnr = 0;
101             }
102             else
103 0 0         { for(boxnr = 0; boxnr < nr_boxes; boxnr++)
104 0 0         if(boxes[boxnr]==NULL) break;
105              
106 0 0         if(boxnr >= nr_boxes)
107             { /* Add 10 more slots for MailBoxes. */
108             int i;
109 0           Renew(boxes, nr_boxes + 10, Mailbox *);
110              
111 0 0         for(i=0; i<10; i++)
112 0           boxes[nr_boxes++] = NULL;
113             }
114             }
115              
116             /*fprintf(stderr, "Occupy slot %d\n", boxnr);*/
117 0           boxes[boxnr] = new;
118 0           return boxnr;
119             }
120              
121             /*
122             * free_box_slot
123             */
124              
125 0           static void free_box_slot(int boxnr)
126             {
127 0 0         if(boxnr >= 0 && boxnr < nr_boxes) /* bit careful */
    0          
128 0           boxes[boxnr] = NULL;
129 0           }
130              
131             /*
132             * get_box
133             */
134              
135 0           static Mailbox *get_box(int boxnr)
136             {
137 0 0         if(boxnr < 0 || boxnr >= nr_boxes) return NULL;
    0          
138 0           return boxes[boxnr];
139             }
140              
141             /*
142             * get_one_line
143             */
144              
145 0           static char * get_one_line(Mailbox *box)
146             {
147 0 0         if(box->keep_line)
148 0           { box->keep_line = 0;
149 0           return box->line;
150             }
151              
152 0           box->line_start = (long)ftell(box->file);
153 0           int bufsize = box->line_buf_size;
154 0           int bytes = 0;
155              
156             while(1)
157 0 0         { if(!fgets(box->line + bytes, bufsize - bytes, box->file))
158 0           break;
159              
160 0           bytes = strlen(box->line);
161 0 0         if(bytes < bufsize-1 || box->line[bufsize-1]=='\n')
    0          
162             break;
163              
164             /* Extend header line buffer to contain more than DFLT_SIZE
165             * chars. Rfc5322 restricts size to 998 octets, but larger
166             * headers are encountered in the wild.
167             */
168 0           bufsize = box->line_buf_size *= 2;
169 0           Renew(box->line, bufsize, char);
170             }
171              
172 0 0         if(!bytes)
173 0           return NULL;
174              
175              
176 0 0         if(box->dosmode)
177 0           { int len = strlen(box->line);
178 0 0         if(len >= 2 && box->line[len-2]==CR)
    0          
179 0           { box->line[len-2] = '\n'; /* Remove CR's before LF's */
180 0           box->line[len-1] = EOL;
181             }
182             else
183 0 0         if(len==0 || box->line[len-1]!='\n') /* Last line on Win* may lack */
    0          
184 0           { box->line[len] = '\n'; /* newline. Add it silently */
185 0           box->line[len+1] = EOL;
186             }
187 0           else box->dosmode = 0; /* Apparently not dosmode at all*/
188             }
189              
190 0           return box->line;
191             }
192              
193             /*
194             * file_position
195             * Give the file-position of the line to be processed.
196             */
197              
198 0           static long file_position(Mailbox *box)
199 0 0         { return box->keep_line ? box->line_start : (long)ftell(box->file);
200             }
201              
202             /*
203             * goto_position
204             * Jump to a different place in the file.
205             */
206              
207 0           static int goto_position(Mailbox *box, long where)
208 0           { box->keep_line = 0;
209 0           return fseek(box->file, where, 0);
210             }
211              
212             /*
213             * read_header_line
214             */
215              
216 0           static int read_header_line(Mailbox *box, SV **field, SV **content)
217             {
218             char * line;
219             char * reader;
220             int length, field_error;
221              
222 0           line = get_one_line(box);
223 0 0         if(line==NULL) return 0; /* end of file. */
224 0 0         if(line[0]=='\n') return 0; /* normal end of header. */
225              
226             /*
227             * Read the header's field.
228             */
229              
230 0 0         for(reader = line; *reader!=':' && *reader!='\n'; reader++)
    0          
231             ;
232              
233 0 0         if(*reader=='\n')
234 0           { fprintf(stderr, "Unexpected end of header (C parser):\n %s", line);
235 0           box->keep_line = 1;
236 0           return 0;
237             }
238              
239 0           field_error = 0;
240 0 0         for(length=reader-line-1; length >= 0 && isspace(line[length]); --length)
    0          
241 0           field_error++;
242              
243 0 0         if(field_error && box->trace <= TRACE_WARNINGS)
    0          
244 0           { fprintf(stderr, "Blanks stripped after header-fieldname:\n %s",line);
245             }
246              
247 0           *field = newSVpvn(line, length+1);
248              
249             /*
250             * Now read the content.
251             */
252              
253             /* skip leading blanks. */
254 0 0         for(++reader; isspace(*reader); ++reader)
255             ;
256 0           *content = newSVpv(reader, 0);
257              
258             /*
259             * Add folded lines.
260             */
261              
262             while(1)
263 0           { line = get_one_line(box);
264 0 0         if(line==NULL) break;
265              
266 0 0         if( !isspace(line[0]) || line[0]=='\n')
    0          
267 0           { box->keep_line = 1;
268 0           break;
269             }
270 0           sv_catpv(*content, line);
271             }
272              
273 0           return 1;
274             }
275              
276             /*
277             * is_good_end
278             * Look if the predicted size of the message may be real. Real means
279             * that after the given location is end-of-file, or some blank lines
280             * and then the active separator.
281             *
282             * This function returns whether this seems the right end.
283             */
284              
285 0           static int is_good_end(Mailbox *box, long where)
286             { char *line;
287             int found;
288             Separator *sep;
289             long old_location;
290              
291 0           sep = box->separators;
292 0 0         if(sep==NULL) return 1; /* no seps, than we have to trust it. */
293              
294 0           old_location = file_position(box);
295 0 0         if(where >= 0)
296 0 0         { if(goto_position(box, where)!=0)
297             { /* File too short. */
298 0           goto_position(box, old_location);
299 0           return 0; /* Impossible seek. */
300             }
301 0           box->keep_line = 0; /* carefully destroy unget-line. */
302             }
303              
304 0           line = get_one_line(box); /* find first non-empty line. */
305 0 0         while(line!=NULL && line[0]=='\n' && line[1]==EOL)
    0          
    0          
306 0           line = get_one_line(box);
307              
308 0 0         found = (line==NULL || strncmp(line, sep->line, sep->length)==0);
    0          
309              
310 0           goto_position(box, old_location);
311 0           return found;
312             }
313              
314             /*
315             * skip_empty_lines
316             */
317              
318 0           static void skip_empty_lines(Mailbox *box)
319             { char * line;
320              
321             while(1)
322 0           { line = get_one_line(box);
323              
324 0 0         if(line==NULL)
325 0           break;
326              
327 0 0         if(line[0]!='\n')
328 0           { box->keep_line = 1;
329 0           break;
330             }
331             }
332 0           }
333              
334             /*
335             * read_stripped_lines
336             * In dosmode, each line must be stripped from the \r, and
337             * when we have the From-line seperator, /^>+From / must be stripped
338             * from one >.
339             *
340             * Reading from a Windows file will translate \r\n into \n. But it
341             * is hard to find-out if this is the case. However, the Content-Length
342             * field count these line-seps both. That's why the ftell() is asked
343             * to provide the real location.
344             */
345              
346 0           static int is_separator(Separator *sep, char *line)
347             {
348 0 0         if(strncmp(sep->line, line, sep->length)!=0) return 0;
349              
350 0 0         if(strcmp(sep->line, "From ") !=0) return 1;
351              
352             /* From separators shall contain a year in the line */
353 0 0         while(*line)
354 0 0         { if( (line[0]=='1' || line[0]=='2')
    0          
355 0 0         && isdigit(line[1]) && isdigit(line[2]) && isdigit(line[3])
    0          
    0          
356 0           ) return 1;
357              
358 0           line++;
359             }
360              
361 0           return 0;
362             }
363              
364 0           static char **read_stripped_lines(Mailbox *box,
365             int expect_chars, int expect_lines,
366             int *nr_chars, int *nr_lines)
367 0           { char ** lines = NULL;
368             int max_lines;
369 0           long start = file_position(box);
370 0           int last_blank = 0;
371             long last_position;
372              
373 0           last_position = start;
374 0 0         max_lines = expect_lines >= 0 ? (expect_lines+10) : 1000;
375              
376             /*fprintf(stderr, "maxlines %ld\n", (long)max_lines);*/
377 0           New(0, lines, max_lines, char *);
378 0           *nr_lines = 0;
379 0           *nr_chars = 0;
380              
381             while(1)
382 0           { char *line;
383             char *linecopy;
384             Separator *sep;
385             int length;
386              
387 0 0         if(*nr_lines == expect_lines && is_good_end(box, -1))
    0          
388 0           break;
389              
390 0 0         if(file_position(box)-start == expect_chars && is_good_end(box,-1))
    0          
391 0           break;
392              
393 0           line = get_one_line(box);
394 0 0         if(line==NULL) /* remove empty line before eof.*/
395 0 0         { if(last_blank && box->separators)
    0          
396 0           { Safefree( lines[ --(*nr_lines) ] );
397 0           (*nr_chars)--;
398 0           goto_position(box, last_position);
399 0           last_blank = 0;
400             }
401 0           break;
402             }
403              
404             /*
405             * Check for separator
406             */
407              
408 0           sep = box->separators;
409 0 0         while(sep != NULL && !is_separator(sep, line))
    0          
410 0           sep = sep->next;
411              
412 0 0         if(sep!=NULL)
413             { /* Separator found */
414 0           box->keep_line = 1; /* keep separator line to read later. */
415              
416 0 0         if(last_blank) /* Remove blank line before separator. */
417 0           { Safefree( lines[ --(*nr_lines) ] );
418 0           (*nr_chars)--;
419 0           goto_position(box, last_position);
420 0           last_blank = 0;
421             }
422              
423 0           break;
424             }
425              
426             /*
427             * >>>>From becomes >>>From
428             */
429              
430 0 0         if(box->strip_gt && line[0]=='>')
    0          
431 0           { char *reader = line;
432 0 0         while(*reader == '>') reader++;
433 0 0         if(strncmp(reader, "From ", 5)==0)
434 0           line++;
435             }
436              
437             /*
438             * Store line
439             */
440              
441 0 0         if(*nr_lines >= max_lines)
442 0           { max_lines = max_lines + max_lines/2;
443 0           lines = Renew(lines, max_lines, char *);
444             }
445              
446 0           length = strlen(line);
447 0           last_blank = length==1;
448 0           last_position = box->line_start;
449              
450 0           New(0, linecopy, length+1, char);
451 0           strcpy(linecopy, line);
452              
453 0           lines[*nr_lines] = linecopy;
454              
455 0           (*nr_lines)++;
456 0           *nr_chars += length;
457             }
458              
459 0           return lines;
460             }
461              
462             /*
463             * scan_stripped_lines
464             * Like read_stripped_lines, but then without allocation memory.
465             */
466              
467 0           static int scan_stripped_lines(Mailbox *box,
468             int expect_chars, int expect_lines,
469             int *nr_chars, int *nr_lines)
470 0           { long start = file_position(box);
471             long last_position;
472 0           int last_blank = 0;
473              
474 0           *nr_lines = 0;
475 0           *nr_chars = 0;
476 0           last_position = start;
477              
478             while(1)
479 0           { char *line;
480             Separator *sep;
481             int length;
482              
483 0 0         if(*nr_lines == expect_lines && is_good_end(box, -1))
    0          
484 0           break;
485              
486 0 0         if(file_position(box)-start == expect_chars && is_good_end(box,-1))
    0          
487 0           break;
488              
489 0           line = get_one_line(box);
490 0 0         if(line==NULL)
491             { /* remove empty line before eof if separator.*/
492 0 0         if(last_blank && box->separators)
    0          
493 0           { (*nr_lines)--;
494 0           (*nr_chars)--;
495 0           goto_position(box, last_position);
496 0           last_blank = 0;
497             }
498 0           break;
499             }
500              
501             /*
502             * Check for separator
503             */
504              
505 0           sep = box->separators;
506 0 0         while(sep != NULL && !is_separator(sep, line))
    0          
507 0           sep = sep->next;
508              
509 0 0         if(sep!=NULL)
510             { /* Separator found */
511 0           box->keep_line = 1; /* keep separator line to read later */
512 0 0         if(last_blank) /* remove empty line before separator */
513 0           { (*nr_lines)--;
514 0           (*nr_chars)--;
515 0           goto_position(box, last_position);
516 0           last_blank = 0;
517             }
518 0           break;
519             }
520              
521             /*
522             * >>>>From becomes >>>From
523             */
524              
525 0 0         if(box->strip_gt && line[0]=='>')
    0          
526 0           { char *reader = line;
527 0 0         while(*reader == '>') reader++;
528 0 0         if(strncmp(reader, "From ", 5)==0)
529 0           line++;
530             }
531              
532             /*
533             * Count
534             */
535              
536 0           (*nr_lines)++;
537 0           length = strlen(line);
538 0           *nr_chars += length;
539 0           last_blank = length==1;
540 0           last_position = box->line_start;
541             }
542              
543             /**hier**/
544             /*fprintf(stderr, "Scanning done\n");*/
545 0           return 1;
546             }
547              
548             /*
549             * take_scalar
550             * Take a block of file-data into one scalar, as efficient as possible.
551             */
552              
553 0           static SV* take_scalar(Mailbox *box, long begin, long end)
554             {
555             char buffer[COPYSIZE];
556 0           size_t tocopy = end - begin;
557 0           size_t bytes = 1;
558 0           SV *result = newSVpv("", 0);
559              
560             /* pre-grow the scalar, so Perl doesn't need to re-alloc */
561 0 0         SvGROW(result, tocopy);
    0          
562              
563 0           goto_position(box, begin);
564 0 0         while(tocopy > 0 && bytes > 0)
    0          
565 0           { int take = tocopy < COPYSIZE ? tocopy : COPYSIZE;
566 0           bytes = fread(buffer, take, 1, box->file);
567 0           sv_catpvn(result, buffer, bytes);
568 0           tocopy -= bytes;
569             }
570              
571 0           return result;
572             }
573              
574             /***
575             *** HERE XS STARTS
576             ***/
577              
578             MODULE = Mail::Box::Parser::C PACKAGE = Mail::Box::Parser::C PREFIX = MBPC_
579              
580             PROTOTYPES: ENABLE
581              
582             #
583             # open_filename
584             #
585              
586             int
587             MBPC_open_filename(char *name, char *mode, int trace)
588              
589             PREINIT:
590             Mailbox * box;
591             int boxnr;
592             FILE * file;
593              
594             CODE:
595              
596             /* Open the file. */
597 0           file = fopen(name, mode);
598 0 0         if(file==NULL)
599             { /*fprintf(stderr, "Unable to open file %s for %s.\n", name, mode);*/
600 0           XSRETURN_UNDEF;
601             }
602              
603 0           box = new_mailbox(name);
604 0           box->file = file;
605              
606 0           boxnr = take_box_slot(box);
607              
608             /*fprintf(stderr, "Open is done.\n");*/
609 0 0         RETVAL = boxnr;
610              
611             OUTPUT:
612             RETVAL
613              
614             #
615             # open_filehandle
616             #
617              
618             int
619             MBPC_open_filehandle(FILE *fh, char *name, int trace)
620              
621             PREINIT:
622             Mailbox * box;
623             int boxnr;
624              
625             CODE:
626 0           box = new_mailbox(name);
627 0           box->file = fh;
628              
629 0           boxnr = take_box_slot(box);
630              
631             /*fprintf(stderr, "Open with filehande is done.\n");*/
632 0 0         RETVAL = boxnr;
633              
634             OUTPUT:
635             RETVAL
636              
637              
638             #
639             # close_file
640             #
641              
642             void
643             MBPC_close_file(int boxnr)
644              
645             PREINIT:
646             Mailbox * box;
647             Separator * sep;
648              
649             CODE:
650 0           box = get_box(boxnr);
651 0 0         if(box==NULL) return;
652              
653 0           free_box_slot(boxnr);
654              
655 0 0         if(box->file != NULL)
656 0           { fclose(box->file);
657 0           box->file = NULL;
658             }
659              
660 0           sep = box->separators;
661 0 0         while(sep!=NULL)
662 0           { Separator * next = sep->next;
663 0           Safefree(sep->line);
664 0           Safefree(sep);
665 0           sep = next;
666             }
667              
668 0           Safefree(box->filename);
669 0           Safefree(box);
670              
671              
672             #
673             # push_separator
674             #
675              
676             void
677             MBPC_push_separator(int boxnr, char *line_start)
678              
679             PREINIT:
680             Mailbox *box;
681             Separator *sep;
682              
683             PPCODE:
684 0           box = get_box(boxnr);
685 0 0         if(box==NULL) return;
686              
687             /*fprintf(stderr, "separator %s\n", line_start);*/
688 0           New(0, sep, 1, Separator);
689 0           sep->length = strlen(line_start);
690              
691             /*fprintf(stderr, "separator %ld\n", (long)sep->length+1);*/
692 0           New(0, sep->line, sep->length+1, char);
693 0           strcpy(sep->line, line_start);
694              
695 0           sep->next = box->separators;
696 0           box->separators = sep;
697              
698 0 0         if(strncmp(sep->line, "From ", sep->length)==0)
699 0           box->strip_gt++;
700              
701              
702             #
703             # pop_separator
704             #
705              
706             SV *
707             MBPC_pop_separator(int boxnr)
708              
709             PREINIT:
710             Mailbox *box;
711             Separator *old;
712              
713             CODE:
714 0           box = get_box(boxnr);
715 0 0         if(box==NULL) XSRETURN_UNDEF;
716              
717 0           old = box->separators;
718 0 0         if(old==NULL) XSRETURN_UNDEF;
719              
720 0 0         if(strncmp(old->line, "From ", old->length)==0)
721 0           box->strip_gt--;
722              
723             /*fprintf(stderr, "pop sep %s\n", old->line);*/
724 0           box->separators = old->next;
725 0           RETVAL = newSVpv(old->line, old->length);
726              
727 0           Safefree(old->line);
728 0           Safefree(old);
729              
730             OUTPUT:
731             RETVAL
732              
733              
734             #
735             # get_position
736             #
737              
738             long
739             MBPC_get_position(int boxnr)
740              
741             PREINIT:
742             Mailbox *box;
743              
744             CODE:
745 0           box = get_box(boxnr);
746 0 0         if(box==NULL) RETVAL = 0;
747 0           else RETVAL = file_position(box);
748              
749             OUTPUT:
750             RETVAL
751              
752             #
753             # set_position
754             #
755              
756             int
757             MBPC_set_position(int boxnr, long where)
758              
759             PREINIT:
760             Mailbox *box;
761              
762             CODE:
763 0           box = get_box(boxnr);
764              
765 0 0         if(box==NULL) RETVAL = 0;
766 0           else RETVAL = goto_position(box, where)==0;
767              
768             OUTPUT:
769             RETVAL
770              
771             #
772             # read_header
773             # Returns (begin, end, list-of-fields)
774             # Where
775             # begin and end represent file-locations before resp after the header
776             # each field is a ref to an array with a name/content pair, representing
777             # one line.
778             #
779              
780             void
781             MBPC_read_header(int boxnr)
782              
783             PREINIT:
784             Mailbox * box;
785             SV * name;
786             SV * content;
787             SV * end;
788              
789             PPCODE:
790 0           box = get_box(boxnr);
791 0 0         if(box==NULL || box->file==NULL) return;
    0          
792              
793 0 0         XPUSHs(sv_2mortal(newSViv((IV)file_position(box))));
794 0 0         XPUSHs(end = sv_newmortal());
795              
796 0 0         while(read_header_line(box, &name, &content))
797 0           { AV * field = newAV();
798 0           av_push(field, name); /* av_push does not increase refcount */
799 0           av_push(field, content);
800 0 0         XPUSHs(sv_2mortal(newRV_noinc((SV *)field)));
801             }
802            
803             /*fprintf(stderr, "Header has been read\n");*/
804 0           sv_setiv(end, (IV)file_position(box));
805              
806             #
807             # in_dosmode
808             #
809              
810             int
811             MBPC_in_dosmode(int boxnr)
812              
813             PREINIT:
814             Mailbox *box;
815              
816             CODE:
817 0           box = get_box(boxnr);
818 0 0         if(box==NULL)
819 0           XSRETURN_UNDEF;
820              
821 0 0         RETVAL = box->dosmode;
822              
823             OUTPUT:
824             RETVAL
825              
826              
827             #
828             # read_separator
829             # Return a line with the last defined separator. Empty lines before this
830             # are permitted, but no other lines.
831             #
832              
833             void
834             MBPC_read_separator(int boxnr)
835              
836             PREINIT:
837             Mailbox *box;
838             Separator *sep;
839             char *line;
840              
841             PPCODE:
842 0           box = get_box(boxnr);
843 0 0         if(box==NULL)
844 0           XSRETURN_EMPTY;
845              
846 0           sep = box->separators; /* Never success when there is no sep */
847 0 0         if(sep==NULL)
848 0           XSRETURN_EMPTY;
849              
850 0           line = get_one_line(box); /* Get first real line. */
851 0 0         while(line!=NULL && line[0]=='\n' && line[1]==EOL)
    0          
    0          
852 0           line = get_one_line(box);
853              
854 0 0         if(line==NULL) /* EOF reached. */
855 0           XSRETURN_EMPTY;
856              
857 0 0         if(strncmp(sep->line, line, sep->length)!=0)
858 0           { box->keep_line = 1;
859 0           return;
860             }
861              
862 0 0         EXTEND(SP, 2);
863 0           PUSHs(sv_2mortal(newSViv(box->line_start)));
864 0           PUSHs(sv_2mortal(newSVpv(line, strlen(line))));
865              
866              
867             #
868             # body_as_string
869             # Read the whole body into one scalar, and return it.
870             # When lines need a post-processing, we read line-by-line. Otherwise
871             # we can read the block as a whole.
872             #
873              
874             void
875             MBPC_body_as_string(int boxnr, int expect_chars, int expect_lines)
876              
877             PREINIT:
878             Mailbox *box;
879             SV *result;
880             char **lines;
881 0           int nr_lines = 0;
882 0           int nr_chars = 0;
883             int line_nr;
884             long begin;
885              
886             PPCODE:
887 0           box = get_box(boxnr);
888 0 0         if(box==NULL)
889 0           XSRETURN_EMPTY;
890              
891 0           begin = file_position(box);
892              
893 0 0         if(!box->dosmode && !box->strip_gt && expect_chars >=0)
    0          
    0          
894             {
895 0           long end = begin + expect_chars;
896              
897 0 0         if(is_good_end(box, end))
898 0 0         { EXTEND(SP, 3);
899 0           PUSHs(sv_2mortal(newSViv(begin)));
900 0           PUSHs(sv_2mortal(newSViv(file_position(box))));
901 0           PUSHs(sv_2mortal(take_scalar(box, begin, end)));
902 0           XSRETURN(3);
903             }
904             }
905              
906 0           lines = read_stripped_lines(box, expect_chars, expect_lines,
907             &nr_chars, &nr_lines);
908              
909 0 0         if(lines==NULL)
910 0           XSRETURN_EMPTY;
911              
912             /* Join the strings. */
913 0           result = newSVpv("",0);
914 0 0         SvGROW(result, (unsigned int)nr_chars);
    0          
915              
916 0 0         for(line_nr=0; line_nr
917 0           { sv_catpv(result, lines[line_nr]);
918 0           Safefree(lines[line_nr]);
919             }
920              
921 0           skip_empty_lines(box);
922 0           Safefree(lines);
923              
924 0 0         EXTEND(SP, 3);
925 0           PUSHs(sv_2mortal(newSViv(begin)));
926 0           PUSHs(sv_2mortal(newSViv(file_position(box))));
927 0           PUSHs(sv_2mortal(result));
928              
929              
930             #
931             # body_as_list
932             # Read the whole body into a list of scalars.
933             #
934              
935             void
936             MBPC_body_as_list(int boxnr, int expect_chars, int expect_lines)
937              
938             PREINIT:
939             Mailbox *box;
940             char **lines;
941 0           int nr_lines = 0;
942 0           int nr_chars = 0;
943             int line_nr;
944             long begin;
945             AV * results;
946              
947             PPCODE:
948 0           box = get_box(boxnr);
949 0 0         if(box==NULL)
950 0           XSRETURN_EMPTY;
951              
952 0           begin = file_position(box);
953 0           lines = read_stripped_lines(box, expect_chars, expect_lines,
954             &nr_chars, &nr_lines);
955              
956 0 0         if(lines==NULL) return;
957              
958 0 0         XPUSHs(sv_2mortal(newSViv(begin)));
959 0 0         XPUSHs(sv_2mortal(newSViv(file_position(box))));
960              
961             /* Allocating the lines for real. */
962              
963 0           results = (AV *)sv_2mortal((SV *)newAV());
964 0           av_extend(results, nr_lines);
965              
966 0 0         for(line_nr=0; line_nr
967 0           { char *line = lines[line_nr];
968 0           av_push(results, newSVpv(line, 0));
969 0           Safefree(line);
970             }
971              
972 0 0         XPUSHs(sv_2mortal(newRV((SV *)results)));
973              
974 0           skip_empty_lines(box);
975 0           Safefree(lines);
976              
977              
978             #
979             # body_as_file
980             # Read the whole body into a file.
981             #
982              
983             void
984             MBPC_body_as_file(int boxnr, FILE *out, int expect_chars, int expect_lines)
985              
986             PREINIT:
987             Mailbox *box;
988             char **lines;
989 0           int nr_lines=0;
990 0           int nr_chars=0;
991             int line_nr;
992             long begin;
993              
994             PPCODE:
995 0           box = get_box(boxnr);
996 0 0         if(box==NULL)
997 0           XSRETURN_EMPTY;
998              
999 0           begin = file_position(box);
1000 0           lines = read_stripped_lines(box, expect_chars, expect_lines,
1001             &nr_chars, &nr_lines);
1002              
1003 0 0         if(lines==NULL)
1004 0           XSRETURN_EMPTY;
1005              
1006 0 0         EXTEND(SP, 3);
1007 0           PUSHs(sv_2mortal(newSViv((IV)begin)));
1008 0           PUSHs(sv_2mortal(newSViv((IV)file_position(box))));
1009 0           PUSHs(sv_2mortal(newSViv((IV)nr_lines)));
1010              
1011             /* write the lines to file. */
1012              
1013 0 0         for(line_nr=0; line_nr
1014 0           { fprintf(out, "%s", lines[line_nr]);
1015 0           Safefree(lines[line_nr]);
1016             }
1017              
1018 0           skip_empty_lines(box);
1019 0           Safefree(lines);
1020              
1021              
1022             #
1023             # body_delayed
1024             # Skip the whole body, only counting chars and lines.
1025             #
1026              
1027             void
1028             MBPC_body_delayed(int boxnr, int expect_chars, int expect_lines)
1029              
1030             PREINIT:
1031             Mailbox *box;
1032 0           int nr_lines = 0;
1033 0           int nr_chars = 0;
1034             long begin;
1035              
1036             PPCODE:
1037 0           box = get_box(boxnr);
1038 0 0         if(box==NULL)
1039 0           XSRETURN_EMPTY;
1040              
1041 0           begin = file_position(box);
1042              
1043 0 0         if(expect_chars >=0)
1044             {
1045 0           long end = begin + expect_chars;
1046 0 0         if(is_good_end(box, end))
1047             { /* Accept new end */
1048 0           goto_position(box, end);
1049              
1050 0 0         EXTEND(SP, 4);
1051 0           PUSHs(sv_2mortal(newSViv((IV)begin)));
1052 0           PUSHs(sv_2mortal(newSViv((IV)end)));
1053 0           PUSHs(sv_2mortal(newSViv((IV)expect_chars)));
1054 0           PUSHs(sv_2mortal(newSViv((IV)expect_lines)));
1055 0           skip_empty_lines(box);
1056 0           XSRETURN(4);
1057             }
1058             }
1059              
1060 0 0         if(scan_stripped_lines(box, expect_chars, expect_lines,
1061             &nr_chars, &nr_lines))
1062 0 0         { EXTEND(SP, 4);
1063 0           PUSHs(sv_2mortal(newSViv((IV)begin)));
1064 0           PUSHs(sv_2mortal(newSViv((IV)file_position(box))));
1065 0           PUSHs(sv_2mortal(newSViv((IV)nr_chars)));
1066 0           PUSHs(sv_2mortal(newSViv((IV)nr_lines)));
1067 0           skip_empty_lines(box);
1068             }
1069              
1070             #
1071             # get_filehandle
1072             #
1073              
1074             FILE *
1075             MBPC_get_filehandle(int boxnr)
1076              
1077             PREINIT:
1078             Mailbox * box;
1079              
1080             CODE:
1081 0           box = get_box(boxnr);
1082 0 0         if(box==NULL) XSRETURN_UNDEF;
1083              
1084 0           RETVAL = box->file;
1085              
1086             OUTPUT:
1087             RETVAL
1088