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