3 * m_getfld.c -- read/parse a message
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.
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
22 c == '\001' && peekc (iob) == '\001'
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.
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
33 The next thing to do is to read the mts.conf file and initialize
34 delimiter[] and delimlen accordingly...
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.
45 while (eom (c = Getc (iob), iob))
48 loop caused m_getfld() to return FMTERR. So, that logic was changed to
49 check for (*eom_action) and act accordingly.
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().
57 ------------------------
58 (Written by Van Jacobson for the mh6 m_getfld, January, 1986):
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.
74 If you hack on this and slow it down, I, my children and my
75 children's children will curse you.
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".)
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.
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.
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.
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.
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").
140 Reads an Internet message (RFC 5322), or one or more messages stored in a
141 maildrop in mbox (RFC 4155) or MMDF format, from a file stream. Each call
142 to m_getfld() reads one header field, or a portion of the body, in sequence.
146 state: message parse state
147 bufsz: maximum number of characters to load into buf
148 iob: input file stream
152 name: header field name (array of size NAMESZ=999)
153 buf: either a header field body or message body
154 (return value): message parse state on return from function
155 (global) int msg_count: number of characters loaded into buf
157 Functions (part of Inputs, really)
159 void m_unknown(FILE *iob): Determines the message delimiter string for the
160 maildrop. Called by inc, scan, and msh when reading from a maildrop file.
162 void m_eomsbr (int (*action)(int)): Sets the hook to check for end of
163 message in a maildrop. Called only by msh.
165 Those functions save state in the State variables listed below.
170 FLD // Field returned
171 FLDPLUS // Field returned with more to come
172 FLDEOF // Field returned ending at eom
173 BODY // Body returned with more to come
174 BODYEOF // Body returned ending at eom
175 FILEEOF // Reached end of input file
176 FMTERR // Message Format error
177 LENERR // Name too long error from getfld
179 msg_style is maildrop style, one of:
180 MS_UNKNOWN // type not known yet
181 MS_DEFAULT // default (one msg per file)
182 MS_MBOX // Unix-style "from" lines
183 MS_MMDF // string mmdlm2
186 State variables (part of Outputs)
188 m_getfld() retains state internally between calls in some state variables.
190 These two variables are global, but only used internally by m_getfld.c:
194 These are used for the end-of-message matcher when reading maildrops:
195 static unsigned char **pat_map
196 static unsigned char *fdelim
197 static unsigned char *delimend
199 static unsigned char *edelim
204 m_getfld() is restricted to operate on one file stream at a time because of
205 the retained state (see "State variables" above).
209 The first call to m_getfld() on a file stream is with a state of FLD.
210 Subsequent calls provide the state returned by the previous call.
212 Along the way, I thought of these possible interface changes that we
213 might want to consider before rototilling the internals:
215 1) To remove globals that don't need to be:
216 Change msg_style and msg_delim to be file static.
218 2) To remove a global:
219 Change bufsz to be in-out instead of in, and therefore int * instead of
220 int, and use that instead of global msg_count. There are only 3 call
221 sites that use msg_count so it wouldn't take much effort to remove use of
222 it. Of course, all call sites would have to change to provide an int *
223 instead of an int. Some now pass constants.
225 3) To remove the state argument from the signature:
226 Given the Current usage and Restriction above, the state variable could
227 be removed from the signature and just retained internally.
229 4) To remove the Restriction above:
230 One approach would be for m_getfld() to retain multiple copies of that
231 state, one per iob that it sees. Another approach would be for the
232 caller to store it in an opaque struct, the address of which is passed
233 through the interface.
239 static int m_Eom (int, FILE *);
240 static unsigned char *matchc(int, char *, int, char *);
241 static unsigned char *locc(int, unsigned char *, unsigned char);
243 #define Getc(iob) getc(iob)
244 #define eom(c,iob) (msg_style != MS_DEFAULT && \
245 (((c) == *msg_delim && m_Eom(c,iob)) ||\
246 (eom_action && (*eom_action)(c))))
248 static unsigned char **pat_map;
251 * defined in sbr/m_msgdef.c = 0
252 * This is a disgusting hack for "inc" so it can know how many
253 * characters were stuffed in the buffer on the last call
254 * (see comments in uip/scansbr.c).
256 extern int msg_count;
259 * defined in sbr/m_msgdef.c = MS_DEFAULT
261 extern int msg_style;
264 * The "full" delimiter string for a packed maildrop consists
265 * of a newline followed by the actual delimiter. E.g., the
266 * full string for a Unix maildrop would be: "\n\nFrom ".
267 * "Fdelim" points to the start of the full string and is used
268 * in the BODY case of the main routine to search the buffer for
269 * a possible eom. Msg_delim points to the first character of
270 * the actual delim. string (i.e., fdelim+1). Edelim
271 * points to the 2nd character of actual delimiter string. It
272 * is used in m_Eom because the first character of the string
273 * has been read and matched before m_Eom is called.
275 extern char *msg_delim; /* defined in sbr/m_msgdef.c = "" */
276 static unsigned char *fdelim;
277 static unsigned char *delimend;
278 static int fdelimlen;
279 static unsigned char *edelim;
280 static int edelimlen;
282 static int (*eom_action)(int) = NULL;
285 # define _ptr _p /* Gag */
286 # define _cnt _r /* Retch */
287 # define _filbuf __srget /* Puke */
288 # define DEFINED__FILBUF_TO_SOMETHING_SPECIFIC
290 # if defined __CYGWIN__
291 /* Cygwin's stdio.h does not declare __srget(). */
293 # endif /* __CYGWIN__ */
296 #ifndef DEFINED__FILBUF_TO_SOMETHING_SPECIFIC
297 extern int _filbuf(FILE*);
302 m_getfld (int state, unsigned char name[NAMESZ], unsigned char *buf,
303 int bufsz, FILE *iob)
305 register unsigned char *bp, *cp, *ep, *sp;
306 register int cnt, c, i, j;
308 if ((c = Getc(iob)) < 0) {
315 /* flush null messages */
316 while ((c = Getc(iob)) >= 0 && eom (c, iob))
330 if (c == '\n' || c == '-') {
331 /* we hit the header/body separator */
332 while (c != '\n' && (c = Getc(iob)) >= 0)
335 if (c < 0 || (c = Getc(iob)) < 0 || eom (c, iob)) {
337 /* flush null messages */
338 while ((c = Getc(iob)) >= 0 && eom (c, iob))
351 * get the name of this component. take characters up
352 * to a ':', a newline or NAMESZ-1 characters, whichever
359 bp = sp = (unsigned char *) iob->_IO_read_ptr - 1;
360 j = (cnt = ((long) iob->_IO_read_end -
361 (long) iob->_IO_read_ptr) + 1) < i ? cnt : i;
362 #elif defined(__DragonFly__)
363 bp = sp = (unsigned char *) ((struct __FILE_public *)iob)->_p - 1;
364 j = (cnt = ((struct __FILE_public *)iob)->_r+1) < i ? cnt : i;
366 bp = sp = (unsigned char *) iob->_ptr - 1;
367 j = (cnt = iob->_cnt+1) < i ? cnt : i;
369 while (--j >= 0 && (c = *bp++) != ':' && c != '\n')
373 if ((cnt -= j) <= 0) {
375 iob->_IO_read_ptr = iob->_IO_read_end;
376 if (__underflow(iob) == EOF) {
377 #elif defined(__DragonFly__)
378 if (__srget(iob) == EOF) {
380 if (_filbuf(iob) == EOF) {
383 advise (NULL, "eof encountered in field \"%s\"", name);
387 iob->_IO_read_ptr++; /* NOT automatic in __underflow()! */
391 iob->_IO_read_ptr = bp + 1;
392 #elif defined(__DragonFly__)
393 ((struct __FILE_public *)iob)->_p = bp + 1;
394 ((struct __FILE_public *)iob)->_r = cnt - 1;
404 * something went wrong. possibilities are:
405 * . hit a newline (error)
406 * . got more than namesz chars. (error)
407 * . hit the end of the buffer. (loop)
410 /* We hit the end of the line without seeing ':' to
411 * terminate the field name. This is usually (always?)
412 * spam. But, blowing up is lame, especially when
413 * scan(1)ing a folder with such messages. Pretend such
414 * lines are the first of the body (at least mutt also
415 * handles it this way). */
417 /* See if buf can hold this line, since we were assuming
418 * we had a buffer of NAMESZ, not bufsz. */
419 /* + 1 for the newline */
421 /* No, it can't. Oh well, guess we'll blow up. */
423 advise (NULL, "eol encountered in field \"%s\"", name);
427 memcpy (buf, name, j - 1);
430 /* mhparse.c:get_content wants to find the position of the
431 * body start, but it thinks there's a blank line between
432 * the header and the body (naturally!), so seek back so
433 * that things line up even though we don't have that
434 * blank line in this case. Simpler parsers (e.g. mhl)
435 * get extra newlines, but that should be harmless enough,
436 * right? This is a corrupt message anyway. */
437 fseek (iob, ftell (iob) - 2, SEEK_SET);
442 advise (NULL, "field name \"%s\" exceeds %d bytes", name, NAMESZ - 2);
448 while (isspace (*--cp) && cp >= name)
455 * get (more of) the text of a field. take
456 * characters up to the end of this field (newline
457 * followed by non-blank) or bufsz-1 characters.
459 cp = buf; i = bufsz-1;
462 cnt = (long) iob->_IO_read_end - (long) iob->_IO_read_ptr;
463 bp = (unsigned char *) --iob->_IO_read_ptr;
464 #elif defined(__DragonFly__)
465 cnt = ((struct __FILE_public *)iob)->_r++;
466 bp = (unsigned char *) --((struct __FILE_public *)iob)->_p;
469 bp = (unsigned char *) --iob->_ptr;
471 c = cnt < i ? cnt : i;
472 while ((ep = locc( c, bp, '\n' ))) {
474 * if we hit the end of this field, return.
476 if ((j = *++ep) != ' ' && j != '\t') {
478 j = ep - (unsigned char *) iob->_IO_read_ptr;
479 memcpy (cp, iob->_IO_read_ptr, j);
480 iob->_IO_read_ptr = ep;
481 #elif defined(__DragonFly__)
482 j = ep - (unsigned char *) ((struct __FILE_public *)iob)->_p;
483 memcpy (cp, ((struct __FILE_public *)iob)->_p, j);
484 ((struct __FILE_public *)iob)->_p = ep;
485 ((struct __FILE_public *)iob)->_r -= j;
487 j = ep - (unsigned char *) iob->_ptr;
488 memcpy (cp, iob->_ptr, j);
500 * end of input or dest buffer - copy what we've found.
503 c += bp - (unsigned char *) iob->_IO_read_ptr;
504 memcpy( cp, iob->_IO_read_ptr, c);
505 #elif defined(__DragonFly__)
506 c += bp - (unsigned char *) ((struct __FILE_public *)iob)->_p;
507 memcpy( cp, ((struct __FILE_public *)iob)->_p, c);
509 c += bp - (unsigned char *) iob->_ptr;
510 memcpy( cp, iob->_ptr, c);
515 /* the dest buffer is full */
517 iob->_IO_read_ptr += c;
518 #elif defined(__DragonFly__)
519 ((struct __FILE_public *)iob)->_r -= c;
520 ((struct __FILE_public *)iob)->_p += c;
529 * There's one character left in the input buffer.
530 * Copy it & fill the buffer. If the last char
531 * was a newline and the next char is not whitespace,
532 * this is the end of the field. Otherwise loop.
536 *cp++ = j = *(iob->_IO_read_ptr + c);
537 iob->_IO_read_ptr = iob->_IO_read_end;
538 c = __underflow(iob);
539 iob->_IO_read_ptr++; /* NOT automatic! */
540 #elif defined(__DragonFly__)
541 *cp++ =j = *(((struct __FILE_public *)iob)->_p + c);
544 *cp++ = j = *(iob->_ptr + c);
548 ((j == '\0' || j == '\n') && c != ' ' && c != '\t')) {
552 #elif defined(__DragonFly__)
553 --((struct __FILE_public *)iob)->_p;
554 ++((struct __FILE_public *)iob)->_r;
569 * get the message body up to bufsz characters or the
570 * end of the message. Sleazy hack: if bufsz is negative
571 * we assume that we were called to copy directly into
572 * the output buffer and we don't add an eos.
574 i = (bufsz < 0) ? -bufsz : bufsz-1;
576 bp = (unsigned char *) --iob->_IO_read_ptr;
577 cnt = (long) iob->_IO_read_end - (long) iob->_IO_read_ptr;
578 #elif defined(__DragonFly__)
579 bp = (unsigned char *) --((struct __FILE_public *)iob)->_p;
580 cnt = ++((struct __FILE_public *)iob)->_r;
582 bp = (unsigned char *) --iob->_ptr;
585 c = (cnt < i ? cnt : i);
586 if (msg_style != MS_DEFAULT && c > 1) {
588 * packed maildrop - only take up to the (possible)
589 * start of the next message. This "matchc" should
590 * probably be a Boyer-Moore matcher for non-vaxen,
591 * particularly since we have the alignment table
592 * all built for the end-of-buffer test (next).
593 * But our vax timings indicate that the "matchc"
594 * instruction is 50% faster than a carefully coded
595 * B.M. matcher for most strings. (So much for elegant
596 * algorithms vs. brute force.) Since I (currently)
597 * run MH on a vax, we use the matchc instruction. --vj
599 if ((ep = matchc( fdelimlen, fdelim, c, bp )))
603 * There's no delim in the buffer but there may be
604 * a partial one at the end. If so, we want to leave
605 * it so the "eom" check on the next call picks it up.
606 * Use a modified Boyer-Moore matcher to make this
607 * check relatively cheap. The first "if" figures
608 * out what position in the pattern matches the last
609 * character in the buffer. The inner "while" matches
610 * the pattern against the buffer, backwards starting
611 * at that position. Note that unless the buffer
612 * ends with one of the characters in the pattern
613 * (excluding the first and last), we do only one test.
616 if ((sp = pat_map[*ep])) {
618 /* This if() is true unless (a) the buffer is too
619 * small to contain this delimiter prefix, or
620 * (b) it contains exactly enough chars for the
622 * For case (a) obviously we aren't going to match.
623 * For case (b), if the buffer really contained exactly
624 * a delim prefix, then the m_eom call at entry
625 * should have found it. Thus it's not a delim
626 * and we know we won't get a match.
628 if (((sp - fdelim) + 2) <= c) {
630 /* Unfortunately although fdelim has a preceding NUL
631 * we can't use this as a sentinel in case the buffer
632 * contains a NUL in exactly the wrong place (this
633 * would cause us to run off the front of fdelim).
635 while (*--ep == *--cp)
639 /* we matched the entire delim prefix,
640 * so only take the buffer up to there.
641 * we know ep >= bp -- check above prevents underrun
647 /* try matching one less char of delim string */
649 } while (--sp > fdelim);
653 memcpy( buf, bp, c );
655 iob->_IO_read_ptr += c;
656 #elif defined(__DragonFly__)
657 ((struct __FILE_public *)iob)->_r -= c;
658 ((struct __FILE_public *)iob)->_p += c;
671 adios (NULL, "m_getfld() called with bogus state of %d", state);
675 msg_count = cp - buf;
687 register char *delimstr;
690 * Figure out what the message delimitter string is for this
691 * maildrop. (This used to be part of m_Eom but I didn't like
692 * the idea of an "if" statement that could only succeed on the
693 * first call to m_Eom getting executed on each call, i.e., at
694 * every newline in the message).
696 * If the first line of the maildrop is a Unix "From " line, we
697 * say the style is MBOX and eat the rest of the line. Otherwise
698 * we say the style is MMDF and look for the delimiter string
699 * specified when nmh was built (or from the mts.conf file).
702 msg_style = MS_UNKNOWN;
705 if (fread (text, sizeof(*text), 5, iob) == 5
706 && strncmp (text, "From ", 5) == 0) {
708 delimstr = "\nFrom ";
709 while ((c = getc (iob)) != '\n' && c >= 0)
712 /* not a Unix style maildrop */
713 fseek (iob, pos, SEEK_SET);
714 if (mmdlm2 == NULL || *mmdlm2 == 0)
715 mmdlm2 = "\001\001\001\001\n";
719 c = strlen (delimstr);
720 fdelim = (unsigned char *) mh_xmalloc((size_t) (c + 3));
723 msg_delim = (char *)fdelim+1;
724 edelim = (unsigned char *)msg_delim+1;
727 strcpy (msg_delim, delimstr);
728 delimend = (unsigned char *)msg_delim + edelimlen;
730 adios (NULL, "maildrop delimiter must be at least 2 bytes");
732 * build a Boyer-Moore end-position map for the matcher in m_getfld.
733 * N.B. - we don't match just the first char (since it's the newline
734 * separator) or the last char (since the matchc would have found it
735 * if it was a real delim).
737 pat_map = (unsigned char **) calloc (256, sizeof(unsigned char *));
739 for (cp = (char *) fdelim + 1; cp < (char *) delimend; cp++ )
740 pat_map[(unsigned char)*cp] = (unsigned char *) cp;
742 if (msg_style == MS_MMDF) {
743 /* flush extra msg hdrs */
744 while ((c = Getc(iob)) >= 0 && eom (c, iob))
753 m_eomsbr (int (*action)(int))
755 if ((eom_action = action)) {
762 msg_delim = (char *)fdelim + 1;
763 fdelimlen = strlen((char *)fdelim);
764 delimend = (unsigned char *)(msg_delim + edelimlen);
770 * test for msg delimiter string
774 m_Eom (int c, FILE *iob)
776 register long pos = 0L;
781 if ((i = fread (text, sizeof *text, edelimlen, iob)) != edelimlen
782 || strncmp (text, (char *)edelim, edelimlen)) {
783 if (i == 0 && msg_style == MS_MBOX)
784 /* the final newline in the (brain damaged) unix-format
785 * maildrop is part of the delimitter - delete it.
790 fseek (iob, pos, SEEK_SET);
793 fseek (iob, (long)(pos-1), SEEK_SET);
794 getc (iob); /* should be OK */
798 if (msg_style == MS_MBOX) {
799 while ((c = getc (iob)) != '\n')
808 static unsigned char *
809 matchc(int patln, char *pat, int strln, char *str)
811 register char *es = str + strln - patln;
814 register char *ep = pat + patln;
815 register char pc = *pat++;
824 while (pp < ep && *sp++ == *pp)
827 return ((unsigned char *)--str);
833 * Locate character "term" in the next "cnt" characters of "src".
834 * If found, return its address, otherwise return 0.
837 static unsigned char *
838 locc(int cnt, unsigned char *src, unsigned char term)
840 while (*src++ != term && --cnt > 0);
842 return (cnt > 0 ? --src : (unsigned char *)0);