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