]> diplodocus.org Git - nmh/blob - sbr/m_getfld.c
Just reworded the bit about '%s' being safe not to quote (it's only safe not to
[nmh] / 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 <zotnet/mts/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
734 sp = str; pp = pat;
735 while (pp < ep && *sp++ == *pp)
736 pp++;
737 if (pp >= ep)
738 return ((unsigned char *)--str);
739 }
740 }
741
742
743 /*
744 * Locate character "term" in the next "cnt" characters of "src".
745 * If found, return its address, otherwise return 0.
746 */
747
748 static unsigned char *
749 locc(int cnt, unsigned char *src, unsigned char term)
750 {
751 while (*src++ != term && --cnt > 0);
752
753 return (cnt > 0 ? --src : (unsigned char *)0);
754 }
755