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