Resolve the circular dependency of libmh on libmts on libmh. The
[mmh] / sbr / m_getfld.c
1
2 /*
3  * m_getfld.c -- read/parse a message
4  *
5  * $Id$
6  */
7
8 #include <h/mh.h>
9 #include <h/mts.h>
10
11 /* This module has a long and checkered history.  First, it didn't burst
12    maildrops correctly because it considered two CTRL-A:s in a row to be
13    an inter-message delimiter.  It really is four CTRL-A:s followed by a
14    newline.  Unfortunately, MMDF will convert this delimiter *inside* a
15    message to a CTRL-B followed by three CTRL-A:s and a newline.  This
16    caused the old version of m_getfld() to declare eom prematurely.  The
17    fix was a lot slower than
18
19                 c == '\001' && peekc (iob) == '\001'
20
21    but it worked, and to increase generality, MBOX style maildrops could
22    be parsed as well.  Unfortunately the speed issue finally caught up with
23    us since this routine is at the very heart of MH.
24
25    To speed things up considerably, the routine Eom() was made an auxilary
26    function called by the macro eom().  Unless we are bursting a maildrop,
27    the eom() macro returns FALSE saying we aren't at the end of the
28    message.
29
30    The next thing to do is to read the mts.conf file and initialize
31    delimiter[] and delimlen accordingly...
32
33    After mhl was made a built-in in msh, m_getfld() worked just fine
34    (using m_unknown() at startup).  Until one day: a message which was
35    the result of a bursting was shown. Then, since the burst boundaries
36    aren't CTRL-A:s, m_getfld() would blinding plunge on past the boundary.
37    Very sad.  The solution: introduce m_eomsbr().  This hook gets called
38    after the end of each line (since testing for eom involves an fseek()).
39    This worked fine, until one day: a message with no body portion arrived.
40    Then the
41
42                    while (eom (c = Getc (iob), iob))
43                         continue;
44
45    loop caused m_getfld() to return FMTERR.  So, that logic was changed to
46    check for (*eom_action) and act accordingly.
47
48    This worked fine, until one day: someone didn't use four CTRL:A's as
49    their delimiters.  So, the bullet got bit and we read mts.h and
50    continue to struggle on.  It's not that bad though, since the only time
51    the code gets executed is when inc (or msh) calls it, and both of these
52    have already called mts_init().
53
54    ------------------------
55    (Written by Van Jacobson for the mh6 m_getfld, January, 1986):
56
57    This routine was accounting for 60% of the cpu time used by most mh
58    programs.  I spent a bit of time tuning and it now accounts for <10%
59    of the time used.  Like any heavily tuned routine, it's a bit
60    complex and you want to be sure you understand everything that it's
61    doing before you start hacking on it.  Let me try to emphasize
62    that:  every line in this atrocity depends on every other line,
63    sometimes in subtle ways.  You should understand it all, in detail,
64    before trying to change any part.  If you do change it, test the
65    result thoroughly (I use a hand-constructed test file that exercises
66    all the ways a header name, header body, header continuation,
67    header-body separator, body line and body eom can align themselves
68    with respect to a buffer boundary).  "Minor" bugs in this routine
69    result in garbaged or lost mail.
70
71    If you hack on this and slow it down, I, my children and my
72    children's children will curse you.
73
74    This routine gets used on three different types of files: normal,
75    single msg files, "packed" unix or mmdf mailboxs (when used by inc)
76    and packed, directoried bulletin board files (when used by msh).
77    The biggest impact of different file types is in "eom" testing.  The
78    code has been carefully organized to test for eom at appropriate
79    times and at no other times (since the check is quite expensive).
80    I have tried to arrange things so that the eom check need only be
81    done on entry to this routine.  Since an eom can only occur after a
82    newline, this is easy to manage for header fields.  For the msg
83    body, we try to efficiently search the input buffer to see if
84    contains the eom delimiter.  If it does, we take up to the
85    delimiter, otherwise we take everything in the buffer.  (The change
86    to the body eom/copy processing produced the most noticeable
87    performance difference, particularly for "inc" and "show".)
88
89    There are three qualitatively different things this routine busts
90    out of a message: field names, field text and msg bodies.  Field
91    names are typically short (~8 char) and the loop that extracts them
92    might terminate on a colon, newline or max width.  I considered
93    using a Vax "scanc" to locate the end of the field followed by a
94    "bcopy" but the routine call overhead on a Vax is too large for this
95    to work on short names.  If Berkeley ever makes "inline" part of the
96    C optimiser (so things like "scanc" turn into inline instructions) a
97    change here would be worthwhile.
98
99    Field text is typically 60 - 100 characters so there's (barely)
100    a win in doing a routine call to something that does a "locc"
101    followed by a "bmove".  About 30% of the fields have continuations
102    (usually the 822 "received:" lines) and each continuation generates
103    another routine call.  "Inline" would be a big win here, as well.
104
105    Messages, as of this writing, seem to come in two flavors: small
106    (~1K) and long (>2K).  Most messages have 400 - 600 bytes of headers
107    so message bodies average at least a few hundred characters.
108    Assuming your system uses reasonably sized stdio buffers (1K or
109    more), this routine should be able to remove the body in large
110    (>500 byte) chunks.  The makes the cost of a call to "bcopy"
111    small but there is a premium on checking for the eom in packed
112    maildrops.  The eom pattern is always a simple string so we can
113    construct an efficient pattern matcher for it (e.g., a Vax "matchc"
114    instruction).  Some thought went into recognizing the start of
115    an eom that has been split across two buffers.
116
117    This routine wants to deal with large chunks of data so, rather
118    than "getc" into a local buffer, it uses stdio's buffer.  If
119    you try to use it on a non-buffered file, you'll get what you
120    deserve.  This routine "knows" that struct FILEs have a _ptr
121    and a _cnt to describe the current state of the buffer and
122    it knows that _filbuf ignores the _ptr & _cnt and simply fills
123    the buffer.  If stdio on your system doesn't work this way, you
124    may have to make small changes in this routine.
125    
126    This routine also "knows" that an EOF indication on a stream is
127    "sticky" (i.e., you will keep getting EOF until you reposition the
128    stream).  If your system doesn't work this way it is broken and you
129    should complain to the vendor.  As a consequence of the sticky
130    EOF, this routine will never return any kind of EOF status when
131    there is data in "name" or "buf").
132   */
133
134
135 /*
136  * static prototypes
137  */
138 static int m_Eom (int, FILE *);
139 static unsigned char *matchc(int, char *, int, char *);
140 static unsigned char *locc(int, unsigned char *, unsigned char);
141
142 #define Getc(iob)       getc(iob)
143 #define eom(c,iob)      (msg_style != MS_DEFAULT && \
144                          (((c) == *msg_delim && m_Eom(c,iob)) ||\
145                           (eom_action && (*eom_action)(c))))
146
147 static unsigned char **pat_map;
148
149 /*
150  * defined in sbr/m_msgdef.c = 0
151  * This is a disgusting hack for "inc" so it can know how many
152  * characters were stuffed in the buffer on the last call
153  * (see comments in uip/scansbr.c).
154  */
155 extern int msg_count;
156
157 /*
158  * defined in sbr/m_msgdef.c = MS_DEFAULT
159  */
160 extern int msg_style;
161
162 /*
163  * The "full" delimiter string for a packed maildrop consists
164  * of a newline followed by the actual delimiter.  E.g., the
165  * full string for a Unix maildrop would be: "\n\nFrom ".
166  * "Fdelim" points to the start of the full string and is used
167  * in the BODY case of the main routine to search the buffer for
168  * a possible eom.  Msg_delim points to the first character of
169  * the actual delim. string (i.e., fdelim+1).  Edelim
170  * points to the 2nd character of actual delimiter string.  It
171  * is used in m_Eom because the first character of the string
172  * has been read and matched before m_Eom is called.
173  */
174 extern char *msg_delim;         /* defined in sbr/m_msgdef.c = "" */
175 static unsigned char *fdelim;
176 static unsigned char *delimend;
177 static int fdelimlen;
178 static unsigned char *edelim;
179 static int edelimlen;
180
181 static int (*eom_action)() = NULL;
182
183 #ifdef _FSTDIO
184 # define _ptr    _p             /* Gag   */
185 # define _cnt    _r             /* Retch */
186 # define _filbuf __srget        /* Puke  */
187 # define DEFINED__FILBUF_TO_SOMETHING_SPECIFIC
188 #endif
189
190 #ifdef SCO_5_STDIO
191 # define _ptr  __ptr
192 # define _cnt  __cnt
193 # define _base __base
194 # define _filbuf(fp)  ((fp)->__cnt = 0, __filbuf(fp))
195 # define DEFINED__FILBUF_TO_SOMETHING_SPECIFIC
196 #endif
197
198 #ifndef DEFINED__FILBUF_TO_SOMETHING_SPECIFIC
199 extern int  _filbuf(FILE*);
200 #endif
201
202
203 int
204 m_getfld (int state, unsigned char *name, unsigned char *buf,
205           int bufsz, FILE *iob)
206 {
207     register unsigned char  *bp, *cp, *ep, *sp;
208     register int cnt, c, i, j;
209
210     if ((c = Getc(iob)) < 0) {
211         msg_count = 0;
212         *buf = 0;
213         return FILEEOF;
214     }
215     if (eom (c, iob)) {
216         if (! eom_action) {
217             /* flush null messages */
218             while ((c = Getc(iob)) >= 0 && eom (c, iob))
219                 ;
220             if (c >= 0)
221                 ungetc(c, iob);
222         }
223         msg_count = 0;
224         *buf = 0;
225         return FILEEOF;
226     }
227
228     switch (state) {
229         case FLDEOF: 
230         case BODYEOF: 
231         case FLD: 
232             if (c == '\n' || c == '-') {
233                 /* we hit the header/body separator */
234                 while (c != '\n' && (c = Getc(iob)) >= 0)
235                     ;
236
237                 if (c < 0 || (c = Getc(iob)) < 0 || eom (c, iob)) {
238                     if (! eom_action) {
239                         /* flush null messages */
240                         while ((c = Getc(iob)) >= 0 && eom (c, iob))
241                             ;
242                         if (c >= 0)
243                             ungetc(c, iob);
244                     }
245                     msg_count = 0;
246                     *buf = 0;
247                     return FILEEOF;
248                 }
249                 state = BODY;
250                 goto body;
251             }
252             /*
253              * get the name of this component.  take characters up
254              * to a ':', a newline or NAMESZ-1 characters, whichever
255              * comes first.  
256              */
257             cp = name;
258             i = NAMESZ - 1;
259             for (;;) {
260 #ifdef LINUX_STDIO
261                 bp = sp = (unsigned char *) iob->_IO_read_ptr - 1;
262                 j = (cnt = ((long) iob->_IO_read_end -
263                         (long) iob->_IO_read_ptr)  + 1) < i ? cnt : i;
264 #else
265                 bp = sp = (unsigned char *) iob->_ptr - 1;
266                 j = (cnt = iob->_cnt+1) < i ? cnt : i;
267 #endif
268                 while (--j >= 0 && (c = *bp++) != ':' && c != '\n')
269                     *cp++ = c;
270
271                 j = bp - sp;
272                 if ((cnt -= j) <= 0) {
273 #ifdef LINUX_STDIO
274                     iob->_IO_read_ptr = iob->_IO_read_end;
275                     if (__underflow(iob) == EOF) {
276 #else
277                     if (_filbuf(iob) == EOF) {
278 #endif
279                         *cp = *buf = 0;
280                         advise (NULL, "eof encountered in field \"%s\"", name);
281                         return FMTERR;
282                     }
283 #ifdef LINUX_STDIO
284                 iob->_IO_read_ptr++; /* NOT automatic in __underflow()! */
285 #endif
286                 } else {
287 #ifdef LINUX_STDIO
288                     iob->_IO_read_ptr = bp + 1;
289 #else
290                     iob->_ptr = bp + 1;
291                     iob->_cnt = cnt - 1;
292 #endif
293                 }
294                 if (c == ':')
295                     break;
296
297                 /*
298                  * something went wrong.  possibilities are:
299                  *  . hit a newline (error)
300                  *  . got more than namesz chars. (error)
301                  *  . hit the end of the buffer. (loop)
302                  */
303                 if (c == '\n') {
304                     *cp = *buf = 0;
305                     advise (NULL, "eol encountered in field \"%s\"", name);
306                     state = FMTERR;
307                     goto finish;
308                 }
309                 if ((i -= j) <= 0) {
310                     *cp = *buf = 0;
311                     advise (NULL, "field name \"%s\" exceeds %d bytes", name, NAMESZ - 1);
312                     state = LENERR;
313                     goto finish;
314                 }
315             }
316
317             while (isspace (*--cp) && cp >= name)
318                 ;
319             *++cp = 0;
320             /* fall through */
321
322         case FLDPLUS: 
323             /*
324              * get (more of) the text of a field.  take
325              * characters up to the end of this field (newline
326              * followed by non-blank) or bufsz-1 characters.
327              */
328             cp = buf; i = bufsz-1;
329             for (;;) {
330 #ifdef LINUX_STDIO
331                 cnt = (long) iob->_IO_read_end - (long) iob->_IO_read_ptr;
332                 bp = (unsigned char *) --iob->_IO_read_ptr;
333 #else
334                 cnt = iob->_cnt++;
335                 bp = (unsigned char *) --iob->_ptr;
336 #endif
337                 c = cnt < i ? cnt : i;
338                 while ((ep = locc( c, bp, '\n' ))) {
339                     /*
340                      * if we hit the end of this field, return.
341                      */
342                     if ((j = *++ep) != ' ' && j != '\t') {
343 #ifdef LINUX_STDIO
344                         j = ep - (unsigned char *) iob->_IO_read_ptr;
345                         memcpy (cp, iob->_IO_read_ptr, j);
346                         iob->_IO_read_ptr = ep;
347 #else
348                         j = ep - (unsigned char *) iob->_ptr;
349                         memcpy (cp, iob->_ptr, j);
350                         iob->_ptr = ep;
351                         iob->_cnt -= j;
352 #endif
353                         cp += j;
354                         state = FLD;
355                         goto finish;
356                     }
357                     c -= ep - bp;
358                     bp = ep;
359                 }
360                 /*
361                  * end of input or dest buffer - copy what we've found.
362                  */
363 #ifdef LINUX_STDIO
364                 c += bp - (unsigned char *) iob->_IO_read_ptr;
365                 memcpy( cp, iob->_IO_read_ptr, c);
366 #else
367                 c += bp - (unsigned char *) iob->_ptr;
368                 memcpy( cp, iob->_ptr, c);
369 #endif
370                 i -= c;
371                 cp += c;
372                 if (i <= 0) {
373                     /* the dest buffer is full */
374 #ifdef LINUX_STDIO
375                     iob->_IO_read_ptr += c;
376 #else
377                     iob->_cnt -= c;
378                     iob->_ptr += c;
379 #endif
380                     state = FLDPLUS;
381                     break;
382                 }
383                 /* 
384                  * There's one character left in the input buffer.
385                  * Copy it & fill the buffer.  If the last char
386                  * was a newline and the next char is not whitespace,
387                  * this is the end of the field.  Otherwise loop.
388                  */
389                 --i;
390 #ifdef LINUX_STDIO
391                 *cp++ = j = *(iob->_IO_read_ptr + c);
392                 iob->_IO_read_ptr = iob->_IO_read_end;
393                 c = __underflow(iob);
394                 iob->_IO_read_ptr++;    /* NOT automatic! */
395 #else
396                 *cp++ = j = *(iob->_ptr + c);
397                 c = _filbuf(iob);
398 #endif
399                 if (c == EOF || 
400                   ((j == '\0' || j == '\n') && c != ' ' && c != '\t')) {
401                     if (c != EOF) {
402 #ifdef LINUX_STDIO
403                         --iob->_IO_read_ptr;
404 #else
405                         --iob->_ptr;
406                         ++iob->_cnt;
407 #endif
408                     }
409                     state = FLD;
410                     break;
411                 }
412             }
413             break;
414
415         case BODY: 
416         body:
417             /*
418              * get the message body up to bufsz characters or the
419              * end of the message.  Sleazy hack: if bufsz is negative
420              * we assume that we were called to copy directly into
421              * the output buffer and we don't add an eos.
422              */
423             i = (bufsz < 0) ? -bufsz : bufsz-1;
424 #ifdef LINUX_STDIO
425             bp = (unsigned char *) --iob->_IO_read_ptr;
426             cnt = (long) iob->_IO_read_end - (long) iob->_IO_read_ptr;
427 #else
428             bp = (unsigned char *) --iob->_ptr;
429             cnt = ++iob->_cnt;
430 #endif
431             c = (cnt < i ? cnt : i);
432             if (msg_style != MS_DEFAULT && c > 1) {
433                 /*
434                  * packed maildrop - only take up to the (possible)
435                  * start of the next message.  This "matchc" should
436                  * probably be a Boyer-Moore matcher for non-vaxen,
437                  * particularly since we have the alignment table
438                  * all built for the end-of-buffer test (next).
439                  * But our vax timings indicate that the "matchc"
440                  * instruction is 50% faster than a carefully coded
441                  * B.M. matcher for most strings.  (So much for elegant
442                  * algorithms vs. brute force.)  Since I (currently)
443                  * run MH on a vax, we use the matchc instruction. --vj
444                  */
445                 if ((ep = matchc( fdelimlen, fdelim, c, bp )))
446                     c = ep - bp + 1;
447                 else {
448                     /*
449                      * There's no delim in the buffer but there may be
450                      * a partial one at the end.  If so, we want to leave
451                      * it so the "eom" check on the next call picks it up.
452                      * Use a modified Boyer-Moore matcher to make this
453                      * check relatively cheap.  The first "if" figures
454                      * out what position in the pattern matches the last
455                      * character in the buffer.  The inner "while" matches
456                      * the pattern against the buffer, backwards starting
457                      * at that position.  Note that unless the buffer
458                      * ends with one of the characters in the pattern
459                      * (excluding the first and last), we do only one test.
460                      */
461                     ep = bp + c - 1;
462                     if ((sp = pat_map[*ep])) {
463                         do {
464                             cp = sp;
465                             while (*--ep == *--cp)
466                             ;
467                             if (cp < fdelim) {
468                                 if (ep >= bp)
469                                     /*
470                                      * ep < bp means that all the buffer
471                                      * contains is a prefix of delim.
472                                      * If this prefix is really a delim, the
473                                      * m_eom call at entry should have found
474                                      * it.  Thus it's not a delim and we can
475                                      * take all of it.
476                                      */
477                                     c = (ep - bp) + 2;
478                             break;
479                         }
480                             /* try matching one less char of delim string */
481                             ep = bp + c - 1;
482                         } while (--sp > fdelim);
483                     }
484                 }
485             }
486             memcpy( buf, bp, c );
487 #ifdef LINUX_STDIO
488             iob->_IO_read_ptr += c;
489 #else
490             iob->_cnt -= c;
491             iob->_ptr += c;
492 #endif
493             if (bufsz < 0) {
494                 msg_count = c;
495                 return (state);
496             }
497             cp = buf + c;
498             break;
499
500         default: 
501             adios (NULL, "m_getfld() called with bogus state of %d", state);
502     }
503 finish:
504     *cp = 0;
505     msg_count = cp - buf;
506     return (state);
507 }
508
509
510 #ifdef RPATHS
511 static char unixbuf[BUFSIZ] = "";
512 #endif /* RPATHS */
513
514 void
515 m_unknown(FILE *iob)
516 {
517     register int c;
518     register long pos;
519     char text[10];
520     register char *cp;
521     register char *delimstr;
522
523 /*
524  * Figure out what the message delimitter string is for this
525  * maildrop.  (This used to be part of m_Eom but I didn't like
526  * the idea of an "if" statement that could only succeed on the
527  * first call to m_Eom getting executed on each call, i.e., at
528  * every newline in the message).
529  *
530  * If the first line of the maildrop is a Unix "From " line, we
531  * say the style is MBOX and eat the rest of the line.  Otherwise
532  * we say the style is MMDF and look for the delimiter string
533  * specified when nmh was built (or from the mts.conf file).
534  */
535
536     msg_style = MS_UNKNOWN;
537
538     pos = ftell (iob);
539     if (fread (text, sizeof(*text), 5, iob) == 5
540             && strncmp (text, "From ", 5) == 0) {
541         msg_style = MS_MBOX;
542         delimstr = "\nFrom ";
543 #ifndef RPATHS
544         while ((c = getc (iob)) != '\n' && c >= 0)
545             ;
546 #else /* RPATHS */
547         cp = unixbuf;
548         while ((c = getc (iob)) != '\n' && cp - unixbuf < BUFSIZ - 1)
549             *cp++ = c;
550         *cp = 0;
551 #endif /* RPATHS */
552     } else {
553         /* not a Unix style maildrop */
554         fseek (iob, pos, SEEK_SET);
555         if (mmdlm2 == NULL || *mmdlm2 == 0)
556             mmdlm2 = "\001\001\001\001\n";
557         delimstr = mmdlm2;
558         msg_style = MS_MMDF;
559     }
560     c = strlen (delimstr);
561     fdelim = (unsigned char *) malloc((size_t) (c + 3));
562     *fdelim++ = '\0';
563     *fdelim = '\n';
564     msg_delim = (char *)fdelim+1;
565     edelim = (unsigned char *)msg_delim+1;
566     fdelimlen = c + 1;
567     edelimlen = c - 1;
568     strcpy (msg_delim, delimstr);
569     delimend = (unsigned char *)msg_delim + edelimlen;
570     if (edelimlen <= 1)
571         adios (NULL, "maildrop delimiter must be at least 2 bytes");
572     /*
573      * build a Boyer-Moore end-position map for the matcher in m_getfld.
574      * N.B. - we don't match just the first char (since it's the newline
575      * separator) or the last char (since the matchc would have found it
576      * if it was a real delim).
577      */
578     pat_map = (unsigned char **) calloc (256, sizeof(unsigned char *));
579
580     for (cp = (char *) fdelim + 1; cp < (char *) delimend; cp++ )
581         pat_map[(unsigned char)*cp] = (unsigned char *) cp;
582
583     if (msg_style == MS_MMDF) {
584         /* flush extra msg hdrs */
585         while ((c = Getc(iob)) >= 0 && eom (c, iob))
586             ;
587         if (c >= 0)
588             ungetc(c, iob);
589     }
590 }
591
592
593 void
594 m_eomsbr (int (*action)())
595 {
596     if ((eom_action = action)) {
597         msg_style = MS_MSH;
598         *msg_delim = 0;
599         fdelimlen = 1;
600         delimend = fdelim;
601     } else {
602         msg_style = MS_MMDF;
603         msg_delim = (char *)fdelim + 1;
604         fdelimlen = strlen((char *)fdelim);
605         delimend = (unsigned char *)(msg_delim + edelimlen);
606     }
607 }
608
609
610 /*
611  * test for msg delimiter string
612  */
613
614 static int
615 m_Eom (int c, FILE *iob)
616 {
617     register long pos = 0L;
618     register int i;
619     char text[10];
620 #ifdef RPATHS
621     register char *cp;
622 #endif /* RPATHS */
623
624     pos = ftell (iob);
625     if ((i = fread (text, sizeof *text, edelimlen, iob)) != edelimlen
626             || strncmp (text, (char *)edelim, edelimlen)) {
627         if (i == 0 && msg_style == MS_MBOX)
628             /* the final newline in the (brain damaged) unix-format
629              * maildrop is part of the delimitter - delete it.
630              */
631             return 1;
632
633 #if 0
634         fseek (iob, pos, SEEK_SET);
635 #endif
636
637         fseek (iob, (long)(pos-1), SEEK_SET);
638         getc (iob);             /* should be OK */
639         return 0;
640     }
641
642     if (msg_style == MS_MBOX) {
643 #ifndef RPATHS
644         while ((c = getc (iob)) != '\n')
645             if (c < 0)
646                 break;
647 #else /* RPATHS */
648         cp = unixbuf;
649         while ((c = getc (iob)) != '\n' && c >= 0 && cp - unixbuf < BUFSIZ - 1)
650             *cp++ = c;
651         *cp = 0;
652 #endif /* RPATHS */
653     }
654
655     return 1;
656 }
657
658
659 #ifdef RPATHS
660 /*
661  * Return the Return-Path and Delivery-Date
662  * header information.
663  *
664  * Currently, I'm assuming that the "From " line
665  * takes one of the following forms.
666  *
667  * From sender date remote from host   (for UUCP delivery)
668  * From sender@host  date              (for sendmail delivery)
669  */
670
671 int
672 get_returnpath (char *rp, int rplen, char *dd, int ddlen)
673 {
674     char *ap, *bp, *cp, *dp;
675
676     ap = unixbuf;
677     if (!(bp = cp = strchr(ap, ' ')))
678         return 0;
679
680     /*
681      * Check for "remote from" in envelope to see
682      * if this message uses UUCP style addressing
683      */
684     while ((cp = strchr(++cp, 'r'))) {
685         if (strncmp (cp, "remote from", 11) == 0) {
686             cp = strrchr (cp, ' ');
687             break;
688         }
689     }
690
691     /*
692      * Get the Return-Path information from
693      * the "From " envelope.
694      */
695     if (cp) {
696         /* return path for UUCP style addressing */
697         dp = strchr (++cp, '\n');
698         snprintf (rp, rplen, "%.*s!%.*s\n", dp - cp, cp, bp - ap, ap);
699     } else {
700         /* return path for standard domain addressing */
701         snprintf (rp, rplen, "%.*s\n", bp - ap, ap);
702     }
703
704     /*
705      * advance over the spaces to get to
706      * delivery date on envelope
707      */
708     while (*bp == ' ')
709         bp++;
710
711     /* Now get delivery date from envelope */
712     snprintf (dd, ddlen, "%.*s\n", 24, bp);
713
714     unixbuf[0] = 0;
715     return 1;
716 }
717 #endif /* RPATHS */
718
719
720 static unsigned char *
721 matchc(int patln, char *pat, int strln, char *str)
722 {
723         register char *es = str + strln - patln;
724         register char *sp;
725         register char *pp;
726         register char *ep = pat + patln;
727         register char pc = *pat++;
728
729         for(;;) {
730                 while (pc != *str++)
731                         if (str > es)
732                                 return 0;
733                 if (str > es+1)
734                         return 0;
735                 sp = str; pp = pat;
736                 while (pp < ep && *sp++ == *pp)
737                         pp++;
738                 if (pp >= ep) 
739                         return ((unsigned char *)--str);
740         }
741 }
742
743
744 /*
745  * Locate character "term" in the next "cnt" characters of "src".
746  * If found, return its address, otherwise return 0.
747  */
748
749 static unsigned char *
750 locc(int cnt, unsigned char *src, unsigned char term)
751 {
752     while (*src++ != term && --cnt > 0);
753
754     return (cnt > 0 ? --src : (unsigned char *)0);
755 }
756