Rearranged whitespace (and comments) in all the code!
[mmh] / sbr / mf.c
1 /*
2  * mf.c -- mail filter subroutines
3  *
4  * This code is Copyright (c) 2002, by the authors of nmh.  See the
5  * COPYRIGHT file in the root directory of the nmh distribution for
6  * complete copyright information.
7  */
8
9 #include <h/mf.h>
10 #include <ctype.h>
11 #include <stdio.h>
12 #include <h/utils.h>
13
14 /*
15  * static prototypes
16  */
17 static char *getcpy (char *);
18 static void compress (char *, unsigned char *);
19 static int isat (char *);
20 static int parse_address (void);
21 static int phrase (char *);
22 static int route_addr (char *);
23 static int local_part (char *);
24 static int domain (char *);
25 static int route (char *);
26 static int my_lex (char *);
27
28
29 static char *
30 getcpy (char *s)
31 {
32         register char *p;
33
34         if (!s) {
35 /* causes compiles to blow up because the symbol _cleanup is undefined
36  * where did this ever come from? */
37                 /* _cleanup(); */
38                 abort();
39                 for(;;)
40                         pause();
41         }
42         p = mh_xmalloc ((size_t) (strlen (s) + 2));
43         strcpy (p, s);
44         return p;
45 }
46
47
48 int
49 isfrom(char *string)
50 {
51         return (strncmp (string, "From ", 5) == 0
52                 || strncmp (string, ">From ", 6) == 0);
53 }
54
55
56 int
57 lequal (unsigned char *a, unsigned char *b)
58 {
59         for (; *a; a++, b++)
60                 if (*b == 0)
61                         return FALSE;
62                 else {
63                         char c1 = islower (*a) ? toupper (*a) : *a;
64                         char c2 = islower (*b) ? toupper (*b) : *b;
65                         if (c1 != c2)
66                                 return FALSE;
67                 }
68
69         return (*b == 0);
70 }
71
72
73 /*
74  * seekadrx() is tricky.  We want to cover both UUCP-style and ARPA-style
75  * addresses, so for each list of addresses we see if we can find some
76  * character to give us a hint.
77  */
78
79
80 #define CHKADR 0  /* undertermined address style */
81 #define UNIXDR 1  /* UNIX-style address */
82 #define ARPADR 2  /* ARPAnet-style address */
83
84
85 static char *punctuators = ";<>.()[]";
86 static char *vp = NULL;
87 static char *tp = NULL;
88
89 static struct adrx adrxs1;
90
91
92 struct adrx *
93 seekadrx (char *addrs)
94 {
95         static int state = CHKADR;
96         register char *cp;
97         register struct adrx *adrxp;
98
99         if (state == CHKADR)
100                 for (state = UNIXDR, cp = addrs; *cp; cp++)
101                         if (strchr(punctuators, *cp)) {
102                                 state = ARPADR;
103                                 break;
104                         }
105
106         switch (state) {
107                 case UNIXDR:
108                         adrxp = uucpadrx (addrs);
109                         break;
110
111                 case ARPADR:
112                 default:
113                         adrxp = getadrx (addrs);
114                         break;
115         }
116
117         if (adrxp == NULL)
118                 state = CHKADR;
119
120         return adrxp;
121 }
122
123
124 /*
125  * uucpadrx() implements a partial UUCP-style address parser.  It's based
126  * on the UUCP notion that addresses are separated by spaces or commas.
127  */
128
129
130 struct adrx *
131 uucpadrx (char *addrs)
132 {
133         register unsigned char *cp, *wp, *xp, *yp;
134         register char *zp;
135         register struct adrx *adrxp = &adrxs1;
136
137         if (vp == NULL) {
138                 vp = tp = getcpy (addrs);
139                 compress (addrs, vp);
140         } else if (tp == NULL) {
141                 free (vp);
142                 vp = NULL;
143                 return NULL;
144         }
145
146         for (cp = tp; isspace (*cp); cp++)
147                 continue;
148         if (*cp == 0) {
149                 free (vp);
150                 vp = tp = NULL;
151                 return NULL;
152         }
153
154         if ((wp = strchr(cp, ',')) == NULL) {
155                 if ((wp = strchr(cp, ' ')) != NULL) {
156                         xp = wp;
157                         while (isspace (*xp))
158                                 xp++;
159                         if (*xp != 0 && isat (--xp)) {
160                                 yp = xp + 4;
161                                 while (isspace (*yp))
162                                         yp++;
163                                 if (*yp != 0) {
164                                         if ((zp = strchr(yp, ' ')) != NULL)
165                                                 *zp = 0, tp = ++zp;
166                                         else
167                                                 tp = NULL;
168                                 } else
169                                         *wp = 0, tp = ++wp;
170                         } else
171                                 *wp = 0, tp = ++wp;
172                 } else
173                         tp = NULL;
174         } else
175                 *wp = 0, tp = ++wp;
176
177         if (adrxp->text)
178                 free (adrxp->text);
179         adrxp->text = getcpy (cp);
180         adrxp->mbox = cp;
181         adrxp->host = adrxp->path = NULL;
182         if ((wp = strrchr(cp, '@')) != NULL) {
183                 *wp++ = 0;
184                 adrxp->host = *wp ? wp : NULL;
185         } else
186                 for (wp = cp + strlen (cp) - 4; wp >= cp; wp--)
187                         if (isat (wp)) {
188                                 *wp++ = 0;
189                                 adrxp->host = wp + 3;
190                         }
191
192         adrxp->pers = adrxp->grp = adrxp->note = adrxp->err = NULL;
193         adrxp->ingrp = 0;
194
195         return adrxp;
196 }
197
198
199 static void
200 compress (char *fp, unsigned char *tp)
201 {
202         register char c;
203         register unsigned char *cp;
204
205         for (c = ' ', cp = tp; (*tp = *fp++) != 0;)
206                 if (isspace (*tp)) {
207                         if (c != ' ')
208                                 *tp++ = c = ' ';
209                 } else
210                         c = *tp++;
211
212         if (c == ' ' && cp < tp)
213                 *--tp = 0;
214 }
215
216
217 static int
218 isat (char *p)
219 {
220         return (strncmp (p, " AT ", 4)
221                 && strncmp (p, " At ", 4)
222                 && strncmp (p, " aT ", 4)
223                 && strncmp (p, " at ", 4) ? FALSE : TRUE);
224 }
225
226
227 /*
228  *
229  * getadrx() implements a partial 822-style address parser.  The parser
230  * is neither complete nor correct.  It does however recognize nearly all
231  * of the 822 address syntax.  In addition it handles the majority of the
232  * 733 syntax as well.  Most problems arise from trying to accomodate both.
233  *
234  * In terms of 822, the route-specification in
235  *
236  *     "<" [route] local-part "@" domain ">"
237  *
238  * is parsed and returned unchanged.  Multiple at-signs are compressed
239  * via source-routing.  Recursive groups are not allowed as per the
240  * standard.
241  *
242  * In terms of 733, " at " is recognized as equivalent to "@".
243  *
244  * In terms of both the parser will not complain about missing hosts.
245  *
246  * -----
247  *
248  * We should not allow addresses like
249  *
250  *     Marshall T. Rose <MRose@UCI>
251  *
252  * but should insist on
253  *
254  *     "Marshall T. Rose" <MRose@UCI>
255  *
256  * Unfortunately, a lot of mailers stupidly let people get away with this.
257  *
258  * -----
259  *
260  * We should not allow addresses like
261  *
262  *     <MRose@UCI>
263  *
264  * but should insist on
265  *
266  *     MRose@UCI
267  *
268  * Unfortunately, a lot of mailers stupidly let people's UAs get away with
269  * this.
270  *
271  * -----
272  *
273  * We should not allow addresses like
274  *
275  *     @UCI:MRose@UCI-750a
276  *
277  * but should insist on
278  *
279  *     Marshall Rose <@UCI:MRose@UCI-750a>
280  *
281  * Unfortunately, a lot of mailers stupidly do this.
282  *
283  */
284
285 #define QUOTE  '\\'
286
287 #define LX_END   0
288 #define LX_ERR   1
289 #define LX_ATOM  2
290 #define LX_QSTR  3
291 #define LX_DLIT  4
292 #define LX_SEMI  5
293 #define LX_COMA  6
294 #define LX_LBRK  7
295 #define LX_RBRK  8
296 #define LX_COLN  9
297 #define LX_DOT  10
298 #define LX_AT   11
299
300 struct specials {
301         char lx_chr;
302         int  lx_val;
303 };
304
305 static struct specials special[] = {
306         { ';',   LX_SEMI },
307         { ',',   LX_COMA },
308         { '<',   LX_LBRK },
309         { '>',   LX_RBRK },
310         { ':',   LX_COLN },
311         { '.',   LX_DOT },
312         { '@',   LX_AT },
313         { '(',   LX_ERR },
314         { ')',   LX_ERR },
315         { QUOTE, LX_ERR },
316         { '"',   LX_ERR },
317         { '[',   LX_ERR },
318         { ']',   LX_ERR },
319         { 0,     0 }
320 };
321
322 static int glevel = 0;
323 static int ingrp = 0;
324 static int last_lex = LX_END;
325
326 static char *dp = NULL;
327 static unsigned char *cp = NULL;
328 static unsigned char *ap = NULL;
329 static char *pers = NULL;
330 static char *mbox = NULL;
331 static char *host = NULL;
332 static char *path = NULL;
333 static char *grp = NULL;
334 static char *note = NULL;
335 static char err[BUFSIZ];
336 static char adr[BUFSIZ];
337
338 static struct adrx  adrxs2;
339
340
341 struct adrx *
342 getadrx (char *addrs)
343 {
344         register char *bp;
345         register struct adrx *adrxp = &adrxs2;
346
347         if (pers)
348                 free (pers);
349         if (mbox)
350                 free (mbox);
351         if (host)
352                 free (host);
353         if (path)
354                 free (path);
355         if (grp)
356                 free (grp);
357         if (note)
358                 free (note);
359         pers = mbox = host = path = grp = note = NULL;
360         err[0] = 0;
361
362         if (dp == NULL) {
363                 dp = cp = getcpy (addrs ? addrs : "");
364                 glevel = 0;
365         } else if (cp == NULL) {
366                 free (dp);
367                 dp = NULL;
368                 return NULL;
369         }
370
371         switch (parse_address ()) {
372                 case DONE:
373                         free (dp);
374                         dp = cp = NULL;
375                         return NULL;
376
377                 case OK:
378                         switch (last_lex) {
379                                 case LX_COMA:
380                                 case LX_END:
381                                         break;
382
383                                 default:  /* catch trailing comments */
384                                         bp = cp;
385                                         my_lex (adr);
386                                         cp = bp;
387                                         break;
388                         }
389                         break;
390
391                 default:
392                         break;
393                 }
394
395         if (err[0])
396                 for (;;) {
397                         switch (last_lex) {
398                                 case LX_COMA:
399                                 case LX_END:
400                                         break;
401
402                                 default:
403                                         my_lex (adr);
404                                         continue;
405                         }
406                         break;
407                 }
408         while (isspace (*ap))
409                 ap++;
410         if (cp)
411                 sprintf (adr, "%.*s", (int)(cp - ap), ap);
412         else
413                 strcpy (adr, ap);
414         bp = adr + strlen (adr) - 1;
415         if (*bp == ',' || *bp == ';' || *bp == '\n')
416                 *bp = 0;
417
418         adrxp->text = adr;
419         adrxp->pers = pers;
420         adrxp->mbox = mbox;
421         adrxp->host = host;
422         adrxp->path = path;
423         adrxp->grp = grp;
424         adrxp->ingrp = ingrp;
425         adrxp->note = note;
426         adrxp->err = err[0] ? err : NULL;
427
428         return adrxp;
429 }
430
431
432 static int
433 parse_address (void)
434 {
435         char buffer[BUFSIZ];
436
437 again: ;
438         ap = cp;
439         switch (my_lex (buffer)) {
440                 case LX_ATOM:
441                 case LX_QSTR:
442                         pers = getcpy (buffer);
443                         break;
444
445                 case LX_SEMI:
446                         if (glevel-- <= 0) {
447                                 strcpy (err, "extraneous semi-colon");
448                                 return NOTOK;
449                         }
450                 case LX_COMA:
451                         if (note) {
452                                 free (note);
453                                 note = NULL;
454                         }
455                         goto again;
456
457                 case LX_END:
458                         return DONE;
459
460                 case LX_LBRK:  /* sigh (2) */
461                         goto get_addr;
462
463                 case LX_AT:  /* sigh (3) */
464                         cp = ap;
465                         if (route_addr (buffer) == NOTOK)
466                                 return NOTOK;
467                         return OK;  /* why be choosy? */
468
469                 default:
470                         sprintf (err, "illegal address construct (%s)", buffer);
471                         return NOTOK;
472         }
473
474         switch (my_lex (buffer)) {
475                 case LX_ATOM:
476                 case LX_QSTR:
477                         pers = add (buffer, add (" ", pers));
478         more_phrase: ;  /* sigh (1) */
479                         if (phrase (buffer) == NOTOK)
480                                 return NOTOK;
481
482                         switch (last_lex) {
483                                 case LX_LBRK:
484                         get_addr: ;
485                                         if (route_addr (buffer) == NOTOK)
486                                                 return NOTOK;
487                                         if (last_lex == LX_RBRK)
488                                                 return OK;
489                                         sprintf (err, "missing right-bracket (%s)", buffer);
490                                         return NOTOK;
491
492                                 case LX_COLN:
493                         get_group: ;
494                                         if (glevel++ > 0) {
495                                                 sprintf (err, "nested groups not allowed (%s)", pers);
496                                                 return NOTOK;
497                                         }
498                                         grp = add (": ", pers);
499                                         pers = NULL;
500                                         {
501                                                 char   *pp = cp;
502
503                                                 for (;;)
504                                                         switch (my_lex (buffer)) {
505                                                                 case LX_SEMI:
506                                                                 case LX_END: /* tsk, tsk */
507                                                                         glevel--;
508                                                                         return OK;
509
510                                                                 case LX_COMA:
511                                                                         continue;
512
513                                                                 default:
514                                                                         cp = pp;
515                                                                         return parse_address ();
516                                                         }
517                                         }
518
519                                 case LX_DOT:  /* sigh (1) */
520                                         pers = add (".", pers);
521                                         goto more_phrase;
522
523                                 default:
524                                         sprintf (err, "no mailbox in address, only a phrase (%s%s)", pers, buffer);
525                                         return NOTOK;
526                         }
527
528                 case LX_LBRK:
529                         goto get_addr;
530
531                 case LX_COLN:
532                         goto get_group;
533
534                 case LX_DOT:
535                         mbox = add (buffer, pers);
536                         pers = NULL;
537                         if (route_addr (buffer) == NOTOK)
538                                 return NOTOK;
539                         goto check_end;
540
541                 case LX_AT:
542                         ingrp = glevel;
543                         mbox = pers;
544                         pers = NULL;
545                         if (domain (buffer) == NOTOK)
546                                 return NOTOK;
547         check_end: ;
548                         switch (last_lex) {
549                                 case LX_SEMI:
550                                         if (glevel-- <= 0) {
551                                                 strcpy (err, "extraneous semi-colon");
552                                                 return NOTOK;
553                                         }
554                                 case LX_COMA:
555                                 case LX_END:
556                                         return OK;
557
558                                 default:
559                                         sprintf (err, "junk after local@domain (%s)", buffer);
560                                         return NOTOK;
561                         }
562
563                 case LX_SEMI:  /* no host */
564                 case LX_COMA:
565                 case LX_END:
566                         ingrp = glevel;
567                         if (last_lex == LX_SEMI && glevel-- <= 0) {
568                                 strcpy (err, "extraneous semi-colon");
569                                 return NOTOK;
570                         }
571                         mbox = pers;
572                         pers = NULL;
573                         return OK;
574
575                 default:
576                         sprintf (err, "missing mailbox (%s)", buffer);
577                         return NOTOK;
578         }
579 }
580
581
582 static int
583 phrase (char *buffer)
584 {
585         for (;;)
586                 switch (my_lex (buffer)) {
587                         case LX_ATOM:
588                         case LX_QSTR:
589                                 pers = add (buffer, add (" ", pers));
590                                 continue;
591
592                         default:
593                                 return OK;
594                 }
595 }
596
597
598 static int
599 route_addr (char *buffer)
600 {
601         register char *pp = cp;
602
603         if (my_lex (buffer) == LX_AT) {
604                 if (route (buffer) == NOTOK)
605                         return NOTOK;
606         }
607         else
608                 cp = pp;
609
610         if (local_part (buffer) == NOTOK)
611                 return NOTOK;
612
613         switch (last_lex) {
614                 case LX_AT:
615                         return domain (buffer);
616
617                 case LX_SEMI:  /* if in group */
618                 case LX_RBRK:  /* no host */
619                 case LX_COMA:
620                 case LX_END:
621                         return OK;
622
623                 default:
624                         sprintf (err, "no at-sign after local-part (%s)", buffer);
625                         return NOTOK;
626         }
627 }
628
629
630 static int
631 local_part (char *buffer)
632 {
633         ingrp = glevel;
634
635         for (;;) {
636                 switch (my_lex (buffer)) {
637                         case LX_ATOM:
638                         case LX_QSTR:
639                                 mbox = add (buffer, mbox);
640                                 break;
641
642                         default:
643                                 sprintf (err, "no mailbox in local-part (%s)", buffer);
644                                 return NOTOK;
645                 }
646
647                 switch (my_lex (buffer)) {
648                         case LX_DOT:
649                                 mbox = add (buffer, mbox);
650                                 continue;
651
652                         default:
653                                 return OK;
654                 }
655         }
656 }
657
658
659 static int
660 domain (char *buffer)
661 {
662         for (;;) {
663                 switch (my_lex (buffer)) {
664                         case LX_ATOM:
665                         case LX_DLIT:
666                                 host = add (buffer, host);
667                                 break;
668
669                         default:
670                                 sprintf (err, "no sub-domain in domain-part of address (%s)", buffer);
671                                 return NOTOK;
672                 }
673
674                 switch (my_lex (buffer)) {
675                         case LX_DOT:
676                                 host = add (buffer, host);
677                                 continue;
678
679                         case LX_AT:  /* sigh (0) */
680                                 mbox = add (host, add ("%", mbox));
681                                 free (host);
682                                 host = NULL;
683                                 continue;
684
685                         default:
686                                 return OK;
687                 }
688         }
689 }
690
691
692 static int
693 route (char *buffer)
694 {
695         path = getcpy ("@");
696
697         for (;;) {
698                 switch (my_lex (buffer)) {
699                         case LX_ATOM:
700                         case LX_DLIT:
701                                 path = add (buffer, path);
702                                 break;
703
704                         default:
705                                 sprintf (err, "no sub-domain in domain-part of address (%s)", buffer);
706                                 return NOTOK;
707                 }
708                 switch (my_lex (buffer)) {
709                         case LX_COMA:
710                                 path = add (buffer, path);
711                                 for (;;) {
712                                         switch (my_lex (buffer)) {
713                                                 case LX_COMA:
714                                                         continue;
715
716                                                 case LX_AT:
717                                                         path = add (buffer, path);
718                                                         break;
719
720                                                 default:
721                                                         sprintf (err, "no at-sign found for next domain in route (%s)",
722                                                                          buffer);
723                                         }
724                                         break;
725                                 }
726                                 continue;
727
728                         case LX_AT:  /* XXX */
729                         case LX_DOT:
730                                 path = add (buffer, path);
731                                 continue;
732
733                         case LX_COLN:
734                                 path = add (buffer, path);
735                                 return OK;
736
737                         default:
738                                 sprintf (err, "no colon found to terminate route (%s)", buffer);
739                                 return NOTOK;
740                 }
741         }
742 }
743
744
745 static int
746 my_lex (char *buffer)
747 {
748         /* buffer should be at least BUFSIZ bytes long */
749         int i, gotat = 0;
750         register unsigned char c;
751         register char *bp;
752
753 /* Add C to the buffer bp. After use of this macro *bp is guaranteed to be within the buffer. */
754 #define ADDCHR(C) do { *bp++ = (C); if ((bp - buffer) == (BUFSIZ-1)) goto my_lex_buffull; } while (0)
755
756         bp = buffer;
757         *bp = 0;
758         if (!cp)
759                 return (last_lex = LX_END);
760
761         gotat = isat (cp);
762         c = *cp++;
763         while (isspace (c))
764                 c = *cp++;
765         if (c == 0) {
766                 cp = NULL;
767                 return (last_lex = LX_END);
768         }
769
770         if (c == '(') {
771                 ADDCHR(c);
772                 for (i = 0;;)
773                         switch (c = *cp++) {
774                                 case 0:
775                                         cp = NULL;
776                                         return (last_lex = LX_ERR);
777                                 case QUOTE:
778                                         ADDCHR(c);
779                                         if ((c = *cp++) == 0) {
780                                                 cp = NULL;
781                                                 return (last_lex = LX_ERR);
782                                         }
783                                         ADDCHR(c);
784                                         continue;
785                                 case '(':
786                                         i++;
787                                 default:
788                                         ADDCHR(c);
789                                         continue;
790                                 case ')':
791                                         ADDCHR(c);
792                                         if (--i < 0) {
793                                                 *bp = 0;
794                                                 note = note ? add (buffer, add (" ", note))
795                                                         : getcpy (buffer);
796                                                 return my_lex (buffer);
797                                         }
798                         }
799         }
800
801         if (c == '"') {
802                 ADDCHR(c);
803                 for (;;)
804                         switch (c = *cp++) {
805                                 case 0:
806                                         cp = NULL;
807                                         return (last_lex = LX_ERR);
808                                 case QUOTE:
809                                         ADDCHR(c);
810                                         if ((c = *cp++) == 0) {
811                                                 cp = NULL;
812                                                 return (last_lex = LX_ERR);
813                                         }
814                                 default:
815                                         ADDCHR(c);
816                                         continue;
817                                 case '"':
818                                         ADDCHR(c);
819                                         *bp = 0;
820                                         return (last_lex = LX_QSTR);
821                         }
822         }
823
824         if (c == '[') {
825                 ADDCHR(c);
826                 for (;;)
827                         switch (c = *cp++) {
828                                 case 0:
829                                         cp = NULL;
830                                         return (last_lex = LX_ERR);
831                                 case QUOTE:
832                                         ADDCHR(c);
833                                         if ((c = *cp++) == 0) {
834                                                 cp = NULL;
835                                                 return (last_lex = LX_ERR);
836                                         }
837                                 default:
838                                         ADDCHR(c);
839                                         continue;
840                                 case ']':
841                                         ADDCHR(c);
842                                         *bp = 0;
843                                         return (last_lex = LX_DLIT);
844                         }
845         }
846
847         ADDCHR(c);
848         *bp = 0;
849         for (i = 0; special[i].lx_chr != 0; i++)
850                 if (c == special[i].lx_chr)
851                         return (last_lex = special[i].lx_val);
852
853         if (iscntrl (c))
854                 return (last_lex = LX_ERR);
855
856         for (;;) {
857                 if ((c = *cp++) == 0)
858                         break;
859                 for (i = 0; special[i].lx_chr != 0; i++)
860                         if (c == special[i].lx_chr)
861                                 goto got_atom;
862                 if (iscntrl (c) || isspace (c))
863                         break;
864                 ADDCHR(c);
865         }
866 got_atom: ;
867         if (c == 0)
868                 cp = NULL;
869         else
870                 cp--;
871         *bp = 0;
872         last_lex = !gotat || cp == NULL || strchr(cp, '<') != NULL
873                 ? LX_ATOM : LX_AT;
874         return last_lex;
875
876  my_lex_buffull:
877         /* Out of buffer space. *bp is the last byte in the buffer */
878         *bp = 0;
879         return (last_lex = LX_ERR);
880 }
881
882
883 char *
884 legal_person (char *p)
885 {
886         int i;
887         register char *cp;
888         static char buffer[BUFSIZ];
889
890         if (*p == '"')
891                 return p;
892         for (cp = p; *cp; cp++)
893                 for (i = 0; special[i].lx_chr; i++)
894                         if (*cp == special[i].lx_chr) {
895                                 sprintf (buffer, "\"%s\"", p);
896                                 return buffer;
897                         }
898
899         return p;
900 }
901
902
903 int
904 mfgets (FILE *in, char **bp)
905 {
906         int i;
907         register char *cp, *dp, *ep;
908         static int len = 0;
909         static char *pp = NULL;
910
911         if (pp == NULL)
912                 pp = mh_xmalloc ((size_t) (len = BUFSIZ));
913
914         for (ep = (cp = pp) + len - 2;;) {
915                 switch (i = getc (in)) {
916                         case EOF:
917                 eol:            ;
918                                 if (cp != pp) {
919                                         *cp = 0;
920                                         *bp = pp;
921                                         return OK;
922                                 }
923                 eoh:            ;
924                                 *bp = NULL;
925                                 free (pp);
926                                 pp = NULL;
927                                 return DONE;
928
929                         case 0:
930                                 continue;
931
932                         case '\n':
933                                 if (cp == pp)  /* end of headers, gobble it */
934                                         goto eoh;
935                                 switch (i = getc (in)) {
936                                         default:  /* end of line */
937                                         case '\n':  /* end of headers, save for next call */
938                                                 ungetc (i, in);
939                                                 goto eol;
940
941                                         case ' ':  /* continue headers */
942                                         case '\t':
943                                                 *cp++ = '\n';
944                                                 break;
945                                 }  /* fall into default case */
946
947                         default:
948                                 *cp++ = i;
949                                 break;
950                 }
951                 if (cp >= ep) {
952                         dp = mh_xrealloc (pp, (size_t) (len += BUFSIZ));
953                         cp += dp - pp, ep = (pp = cp) + len - 2;
954                 }
955         }
956 }