]> diplodocus.org Git - nmh/blob - sbr/m_getfld.c
More cleaned and conversion to the new parameter API.
[nmh] / 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 /*
15 Purpose
16 =======
17 Reads an Internet message (RFC 5322), or one or more messages
18 stored in a maildrop in mbox (RFC 4155) or MMDF format, from a file
19 stream. Each call to m_getfld() reads one header field, or a
20 portion of the body, in sequence.
21
22 Inputs
23 ======
24 gstate: opaque parse state
25 bufsz: maximum number of characters to load into buf
26 iob: input file stream
27
28 Outputs
29 =======
30 name: header field name (array of size NAMESZ=999)
31 buf: either a header field body or message body
32 bufsz: number of characters loaded into buf
33 (return value): message parse state on return from function
34
35 Functions
36 =========
37 void m_getfld_state_destroy (m_getfld_state_t *gstate): destroys
38 the parse state pointed to by the gstate argument.
39
40 m_getfld_state_reset (m_getfld_state_t *gstate): resets the parse
41 state to FLD.
42
43 void m_unknown(FILE *iob): Determines the message delimiter string
44 for the maildrop. Called by inc, scan, and msh when reading from a
45 maildrop file.
46
47 void m_eomsbr (int (*action)(int)): Sets the hook to check for end
48 of message in a maildrop. Called only by msh.
49
50 State variables
51 ===============
52 m_getfld() retains state internally between calls in the
53 m_getfld_state_t variable. These are used for detecting the end of
54 each message when reading maildrops:
55
56 char **pat_map
57 char *fdelim
58 char *delimend
59 int fdelimlen
60 char *edelim
61 int edelimlen
62 char *msg_delim
63 int msg_style
64 int (*eom_action)(int)
65
66 Usage
67 =====
68 m_getfld_state_t gstate = 0;
69 ...
70 int state = m_getfld (&gstate, ...);
71 ...
72 m_getfld_state_destroy (&gstate);
73
74 The state is retained internally by gstate. To reset its state to FLD:
75 m_getfld_state_reset (&gstate);
76 */
77
78 /* The following described the old implementation. The high-level
79 structure hasn't changed, but some of the details have. I'm
80 leaving this as-is, though, for posterity.
81 */
82
83 /* This module has a long and checkered history. First, it didn't burst
84 maildrops correctly because it considered two CTRL-A:s in a row to be
85 an inter-message delimiter. It really is four CTRL-A:s followed by a
86 newline. Unfortunately, MMDF will convert this delimiter *inside* a
87 message to a CTRL-B followed by three CTRL-A:s and a newline. This
88 caused the old version of m_getfld() to declare eom prematurely. The
89 fix was a lot slower than
90
91 c == '\001' && peekc (iob) == '\001'
92
93 but it worked, and to increase generality, MBOX style maildrops could
94 be parsed as well. Unfortunately the speed issue finally caught up with
95 us since this routine is at the very heart of MH.
96
97 To speed things up considerably, the routine Eom() was made an auxilary
98 function called by the macro eom(). Unless we are bursting a maildrop,
99 the eom() macro returns FALSE saying we aren't at the end of the
100 message.
101
102 The next thing to do is to read the mts.conf file and initialize
103 delimiter[] and delimlen accordingly...
104
105 After mhl was made a built-in in msh, m_getfld() worked just fine
106 (using m_unknown() at startup). Until one day: a message which was
107 the result of a bursting was shown. Then, since the burst boundaries
108 aren't CTRL-A:s, m_getfld() would blinding plunge on past the boundary.
109 Very sad. The solution: introduce m_eomsbr(). This hook gets called
110 after the end of each line (since testing for eom involves an fseek()).
111 This worked fine, until one day: a message with no body portion arrived.
112 Then the
113
114 while (eom (c = Getc (iob), iob))
115 continue;
116
117 loop caused m_getfld() to return FMTERR. So, that logic was changed to
118 check for (*eom_action) and act accordingly.
119
120 This worked fine, until one day: someone didn't use four CTRL:A's as
121 their delimiters. So, the bullet got bit and we read mts.h and
122 continue to struggle on. It's not that bad though, since the only time
123 the code gets executed is when inc (or msh) calls it, and both of these
124 have already called mts_init().
125
126 ------------------------
127 (Written by Van Jacobson for the mh6 m_getfld, January, 1986):
128
129 This routine was accounting for 60% of the cpu time used by most mh
130 programs. I spent a bit of time tuning and it now accounts for <10%
131 of the time used. Like any heavily tuned routine, it's a bit
132 complex and you want to be sure you understand everything that it's
133 doing before you start hacking on it. Let me try to emphasize
134 that: every line in this atrocity depends on every other line,
135 sometimes in subtle ways. You should understand it all, in detail,
136 before trying to change any part. If you do change it, test the
137 result thoroughly (I use a hand-constructed test file that exercises
138 all the ways a header name, header body, header continuation,
139 header-body separator, body line and body eom can align themselves
140 with respect to a buffer boundary). "Minor" bugs in this routine
141 result in garbaged or lost mail.
142
143 If you hack on this and slow it down, I, my children and my
144 children's children will curse you.
145
146 This routine gets used on three different types of files: normal,
147 single msg files, "packed" unix or mmdf mailboxs (when used by inc)
148 and packed, directoried bulletin board files (when used by msh).
149 The biggest impact of different file types is in "eom" testing. The
150 code has been carefully organized to test for eom at appropriate
151 times and at no other times (since the check is quite expensive).
152 I have tried to arrange things so that the eom check need only be
153 done on entry to this routine. Since an eom can only occur after a
154 newline, this is easy to manage for header fields. For the msg
155 body, we try to efficiently search the input buffer to see if
156 contains the eom delimiter. If it does, we take up to the
157 delimiter, otherwise we take everything in the buffer. (The change
158 to the body eom/copy processing produced the most noticeable
159 performance difference, particularly for "inc" and "show".)
160
161 There are three qualitatively different things this routine busts
162 out of a message: field names, field text and msg bodies. Field
163 names are typically short (~8 char) and the loop that extracts them
164 might terminate on a colon, newline or max width. I considered
165 using a Vax "scanc" to locate the end of the field followed by a
166 "bcopy" but the routine call overhead on a Vax is too large for this
167 to work on short names. If Berkeley ever makes "inline" part of the
168 C optimiser (so things like "scanc" turn into inline instructions) a
169 change here would be worthwhile.
170
171 Field text is typically 60 - 100 characters so there's (barely)
172 a win in doing a routine call to something that does a "locc"
173 followed by a "bmove". About 30% of the fields have continuations
174 (usually the 822 "received:" lines) and each continuation generates
175 another routine call. "Inline" would be a big win here, as well.
176
177 Messages, as of this writing, seem to come in two flavors: small
178 (~1K) and long (>2K). Most messages have 400 - 600 bytes of headers
179 so message bodies average at least a few hundred characters.
180 Assuming your system uses reasonably sized stdio buffers (1K or
181 more), this routine should be able to remove the body in large
182 (>500 byte) chunks. The makes the cost of a call to "bcopy"
183 small but there is a premium on checking for the eom in packed
184 maildrops. The eom pattern is always a simple string so we can
185 construct an efficient pattern matcher for it (e.g., a Vax "matchc"
186 instruction). Some thought went into recognizing the start of
187 an eom that has been split across two buffers.
188
189 This routine wants to deal with large chunks of data so, rather
190 than "getc" into a local buffer, it uses stdio's buffer. If
191 you try to use it on a non-buffered file, you'll get what you
192 deserve. This routine "knows" that struct FILEs have a _ptr
193 and a _cnt to describe the current state of the buffer and
194 it knows that _filbuf ignores the _ptr & _cnt and simply fills
195 the buffer. If stdio on your system doesn't work this way, you
196 may have to make small changes in this routine.
197
198 This routine also "knows" that an EOF indication on a stream is
199 "sticky" (i.e., you will keep getting EOF until you reposition the
200 stream). If your system doesn't work this way it is broken and you
201 should complain to the vendor. As a consequence of the sticky
202 EOF, this routine will never return any kind of EOF status when
203 there is data in "name" or "buf").
204 */
205
206 /*
207 * static prototypes
208 */
209 struct m_getfld_state;
210 static int m_Eom (m_getfld_state_t, int);
211 static char *matchc(int, char *, int, char *);
212
213 #define eom(c,s) (s->msg_style != MS_DEFAULT && \
214 (((c) == *s->msg_delim && m_Eom(s,c)) || \
215 (s->eom_action && (*s->eom_action)(c))))
216
217 /* This replaces the old approach, with its direct access to stdio
218 * internals. It uses one fread() to load a buffer that we manage.
219 *
220 * MSG_INPUT_SIZE is the size of the buffer.
221 * MAX_DELIMITER_SIZE is the maximum size of the delimiter used to
222 * separate messages in a maildrop, such as mbox "From ".
223 *
224 * Some of the tests in the test suite assume a MSG_INPUT_SIZE
225 * of 4096.
226 */
227 #define MSG_INPUT_SIZE 4096
228 #define MAX_DELIMITER_SIZE 5
229
230 struct m_getfld_state {
231 char msg_buf[2 * MSG_INPUT_SIZE + MAX_DELIMITER_SIZE];
232 char *readpos;
233 char *end; /* One past the last character read in. */
234 /* The following support tracking of the read position in the
235 input file stream so that callers can interleave m_getfld()
236 calls with ftell() and fseek(). ytes_read replaces the old
237 m_getfld() msg_count global. last_caller_pos is stored when
238 leaving m_getfld()/m_unknown(), then checked on the next entry.
239 last_internal_pos is used to remember the position used
240 internally by m_getfld() (read_more(), actually). */
241 off_t bytes_read;
242 off_t total_bytes_read; /* by caller, not necessarily from input file */
243 off_t last_caller_pos;
244 off_t last_internal_pos;
245 FILE *iob;
246
247 char **pat_map;
248 int msg_style;
249 /*
250 * The "full" delimiter string for a packed maildrop consists
251 * of a newline followed by the actual delimiter. E.g., the
252 * full string for a Unix maildrop would be: "\n\nFrom ".
253 * "Fdelim" points to the start of the full string and is used
254 * in the BODY case of the main routine to search the buffer for
255 * a possible eom. Msg_delim points to the first character of
256 * the actual delim. string (i.e., fdelim+1). Edelim
257 * points to the 2nd character of actual delimiter string. It
258 * is used in m_Eom because the first character of the string
259 * has been read and matched before m_Eom is called.
260 */
261 char *msg_delim;
262 char *fdelim;
263 char *delimend;
264 int fdelimlen;
265 char *edelim;
266 int edelimlen;
267 int (*eom_action)(int);
268 int state;
269 int track_filepos;
270 };
271
272 static
273 void
274 m_getfld_state_init (m_getfld_state_t *gstate, FILE *iob) {
275 m_getfld_state_t s;
276
277 s = *gstate = (m_getfld_state_t) mh_xmalloc(sizeof (struct m_getfld_state));
278 s->readpos = s->end = s->msg_buf;
279 s->bytes_read = s->total_bytes_read = 0;
280 s->last_caller_pos = s->last_internal_pos = 0;
281 s->iob = iob;
282 s->pat_map = NULL;
283 s->msg_style = MS_DEFAULT;
284 s->msg_delim = "";
285 s->fdelim = s->delimend = s->edelim = NULL;
286 s->fdelimlen = s->edelimlen = 0;
287 s->eom_action = NULL;
288 s->state = FLD;
289 s->track_filepos = 0;
290 }
291
292 /* scan() needs to force a state an initial state of FLD for each message. */
293 void
294 m_getfld_state_reset (m_getfld_state_t *gstate) {
295 if (*gstate) {
296 (*gstate)->state = FLD;
297 }
298 }
299
300 /* If the caller interleaves ftell*()/fseek*() calls with m_getfld()
301 calls, m_getfld() must keep track of the file position. The caller
302 must use this function to inform m_getfld(). */
303 void
304 m_getfld_track_filepos (m_getfld_state_t *gstate, FILE *iob) {
305 if (! *gstate) {
306 m_getfld_state_init (gstate, iob);
307 }
308
309 (*gstate)->track_filepos = 1;
310 }
311
312 void m_getfld_state_destroy (m_getfld_state_t *gstate) {
313 m_getfld_state_t s = *gstate;
314
315 if (s) {
316 if (s->fdelim) {
317 free (s->fdelim-1);
318 free (s->pat_map);
319 }
320 free (s);
321 *gstate = 0;
322 }
323 }
324
325 /*
326 Summary of file and message input buffer positions:
327
328 input file -------------------------------------------EOF
329 | |
330 last_caller_pos last_internal_pos
331
332
333 msg_buf --------------------EOF
334 | | |
335 msg_buf readpos end
336
337 |<>|=retained characters, difference
338 between last_internal_pos and
339 first readpos value after reading
340 in new chunk in read_more()
341
342 When returning from m_getfld()/m_unknown():
343 1) Save the internal file position in last_internal_pos. That's the
344 m_getfld() position reference in the input file.
345 2) Set file stream position so that callers can use ftell().
346
347 When entering m_getfld()/m_unknown():
348 Check to see if the call had changed the file position. If so,
349 adjust the internal position reference accordingly. If not, restore
350 the internal file position from last_internal_pos.
351 */
352
353
354 static void
355 enter_getfld (m_getfld_state_t *gstate, FILE *iob) {
356 m_getfld_state_t s;
357 off_t pos = ftello (iob);
358
359 if (! *gstate) {
360 m_getfld_state_init (gstate, iob);
361 }
362 s = *gstate;
363 s->bytes_read = 0;
364
365 /* This is ugly and no longer necessary, but is retained just in
366 case it's needed again. The parser used to open the input file
367 multiple times, so we had to always use the FILE * that's
368 passed to m_getfld(). Now the parser inits a new
369 m_getfld_state for each file. See comment below about the
370 readpos shift code being currently unused. */
371 s->iob = iob;
372
373 if (s->track_filepos && (pos != 0 || s->last_internal_pos != 0)) {
374 if (s->last_internal_pos == 0) {
375 s->total_bytes_read = pos;
376 } else {
377 off_t pos_movement = pos - s->last_caller_pos; /* Can be < 0. */
378
379 if (pos_movement == 0) {
380 pos = s->last_internal_pos;
381 } else {
382 /* The current file stream position differs from the
383 last one, so caller must have called ftell/o().
384 Or, this is the first call and the file position
385 was not at 0. */
386
387 if (s->readpos + pos_movement >= s->msg_buf &&
388 s->readpos + pos_movement < s->end) {
389 /* This is currently unused. It could be used by
390 parse_mime() if it was changed to use a global
391 m_getfld_state. */
392 /* We can shift readpos and remain within the
393 bounds of msg_buf. */
394 s->readpos += pos_movement;
395 s->total_bytes_read += pos_movement;
396 pos = s->last_internal_pos;
397 } else {
398 size_t num_read;
399
400 /* This seek skips past an integral number of
401 chunks of size MSG_INPUT_SIZE. */
402 fseeko (iob, pos/MSG_INPUT_SIZE * MSG_INPUT_SIZE, SEEK_SET);
403 num_read = fread (s->msg_buf, 1, MSG_INPUT_SIZE, iob);
404 s->readpos = s->msg_buf + pos % MSG_INPUT_SIZE;
405 s->end = s->msg_buf + num_read;
406 s->total_bytes_read = pos;
407 }
408 }
409
410 fseeko (iob, pos, SEEK_SET);
411 }
412 }
413 }
414
415 static void
416 leave_getfld (m_getfld_state_t s) {
417 s->total_bytes_read += s->bytes_read;
418
419 if (s->track_filepos) {
420 /* Save the internal file position that we use for the input buffer. */
421 s->last_internal_pos = ftello (s->iob);
422
423 /* Set file stream position so that callers can use ftell(). */
424 fseeko (s->iob, s->total_bytes_read, SEEK_SET);
425 s->last_caller_pos = ftello (s->iob);
426 }
427 }
428
429 static size_t
430 read_more (m_getfld_state_t s) {
431 /* Retain at least edelimlen characters that have already been
432 read so that we can back up to them in m_Eom(). */
433 ssize_t retain = s->edelimlen;
434 size_t num_read;
435
436 if (retain < s->end - s->readpos) retain = s->end - s->readpos;
437 assert (retain <= s->readpos - s->msg_buf);
438
439 /* Move what we want to retain at end of the buffer to the beginning. */
440 memmove (s->msg_buf, s->readpos - retain, retain);
441
442 s->readpos = s->msg_buf + retain;
443 num_read = fread (s->readpos, 1, MSG_INPUT_SIZE, s->iob);
444 s->end = s->readpos + num_read;
445
446 return num_read;
447 }
448
449 /* The return values of the following functions are a bit
450 subtle. They can return 0x00 - 0xff as a valid character,
451 but EOF is typically 0xffffffff. */
452 static int
453 Getc (m_getfld_state_t s) {
454 if (s->end - s->readpos < 1) {
455 if (read_more (s) == 0) {
456 /* Pretend that we read a character. That's what stdio does. */
457 ++s->readpos;
458 return EOF;
459 }
460 }
461
462 ++s->bytes_read;
463 return s->readpos < s->end ? (unsigned char) *s->readpos++ : EOF;
464 }
465
466 static int
467 Peek (m_getfld_state_t s) {
468 if (s->end - s->readpos < 1) {
469 if (read_more (s) == 0) {
470 /* Pretend that we read a character. That's what stdio does. */
471 ++s->readpos;
472 return EOF;
473 }
474 }
475
476 return s->readpos < s->end ? (unsigned char) *s->readpos : EOF;
477 }
478
479 static int
480 Ungetc (int c, m_getfld_state_t s) {
481 if (s->readpos == s->msg_buf) {
482 return EOF;
483 } else {
484 --s->bytes_read;
485 return *--s->readpos = (unsigned char) c;
486 }
487 }
488
489
490 int
491 m_getfld (m_getfld_state_t *gstate, char name[NAMESZ], char *buf, int *bufsz,
492 FILE *iob)
493 {
494 m_getfld_state_t s;
495 register char *cp;
496 register int max, n, c;
497
498 enter_getfld (gstate, iob);
499 s = *gstate;
500
501 if ((c = Getc(s)) < 0) {
502 *bufsz = *buf = 0;
503 leave_getfld (s);
504 return s->state = FILEEOF;
505 }
506 if (eom (c, s)) {
507 if (! s->eom_action) {
508 /* flush null messages */
509 while ((c = Getc(s)) >= 0 && eom (c, s))
510 ;
511
512 if (c >= 0)
513 Ungetc(c, s);
514 }
515 *bufsz = *buf = 0;
516 leave_getfld (s);
517 return s->state = FILEEOF;
518 }
519
520 switch (s->state) {
521 case FLD:
522 if (c == '\n' || c == '-') {
523 /* we hit the header/body separator */
524 while (c != '\n' && (c = Getc(s)) >= 0) continue;
525
526 if (c < 0 || (c = Getc(s)) < 0 || eom (c, s)) {
527 if (! s->eom_action) {
528 /* flush null messages */
529 while ((c = Getc(s)) >= 0 && eom (c, s))
530 ;
531 if (c >= 0)
532 Ungetc(c, s);
533 }
534 *bufsz = *buf = 0;
535 leave_getfld (s);
536 return s->state = FILEEOF;
537 }
538 s->state = BODY;
539 goto body;
540 }
541 /*
542 * get the name of this component. take characters up
543 * to a ':', a newline or NAMESZ-1 characters, whichever
544 * comes first.
545 */
546 cp = name;
547 max = NAMESZ - 1;
548 /* Get the field name. The first time through the loop,
549 this copies out the first character, which was loaded
550 into c prior to loop entry. Initialize n to 1 to
551 account for that. */
552 for (n = 1;
553 c != ':' && c != '\n' && c != EOF && n < max;
554 ++n, c = Getc (s)) {
555 *cp++ = c;
556 }
557
558 /* Check for next character, which is either the space after
559 the ':' or the first folded whitespace. */
560 {
561 int next_char;
562 if (c == EOF || (next_char = Peek (s)) == EOF) {
563 *bufsz = *cp = *buf = 0;
564 advise (NULL, "eof encountered in field \"%s\"", name);
565 leave_getfld (s);
566 return s->state = FMTERR;
567 }
568 }
569
570 /* If c isn't ':' here, something went wrong. Possibilities are:
571 * . hit a newline (error)
572 * . got more than namesz chars. (error)
573 */
574 if (c == ':') {
575 /* Finished header name, fall through to FLDPLUS below. */
576 } else if (c == '\n') {
577 /* We hit the end of the line without seeing ':' to
578 * terminate the field name. This is usually (always?)
579 * spam. But, blowing up is lame, especially when
580 * scan(1)ing a folder with such messages. Pretend such
581 * lines are the first of the body (at least mutt also
582 * handles it this way). */
583
584 /* See if buf can hold this line, since we were assuming
585 * we had a buffer of NAMESZ, not bufsz. */
586 /* + 1 for the newline */
587 if (*bufsz < n + 1) {
588 /* No, it can't. Oh well, guess we'll blow up. */
589 *bufsz = *cp = *buf = 0;
590 advise (NULL, "eol encountered in field \"%s\"", name);
591 s->state = FMTERR;
592 break;
593 }
594 memcpy (buf, name, n - 1);
595 buf[n - 1] = '\n';
596 buf[n] = '\0';
597 /* The last character read was '\n'. s->bytes_read
598 (and n) include that, but it was not put into the
599 name array in the for loop above. So subtract 1. */
600 *bufsz = --s->bytes_read; /* == n - 1 */
601 leave_getfld (s);
602 return s->state = BODY;
603 } else if (max <= n) {
604 /* By design, the loop above discards the last character
605 it had read. It's in c, use it. */
606 *cp++ = c;
607 *bufsz = *cp = *buf = 0;
608 advise (NULL, "field name \"%s\" exceeds %d bytes", name,
609 NAMESZ - 2);
610 s->state = LENERR;
611 break;
612 }
613
614 /* Trim any trailing spaces from the end of name. */
615 while (isspace ((unsigned char) *--cp) && cp >= name) continue;
616 *++cp = 0;
617 /* readpos points to the first character of the field body. */
618 /* fall through */
619
620 case FLDPLUS: {
621 /*
622 * get (more of) the text of a field. Take
623 * characters up to the end of this field (newline
624 * followed by non-blank) or bufsz-1 characters.
625 */
626 int finished;
627
628 cp = buf;
629 max = *bufsz-1;
630 n = 0;
631 for (finished = 0; ! finished; ) {
632 while (c != '\n' && c != EOF && n++ < max) {
633 *cp++ = c = Getc (s);
634 }
635
636 if (c != EOF) c = Peek (s);
637 if (max < n) {
638 /* The dest buffer is full. Need to back the read
639 pointer up by one because when m_getfld() is
640 reentered, it will read a character. Then
641 we'll jump right to the FLDPLUS handling code,
642 which will not store that character, but
643 instead move on to the next one. */
644 if (s->readpos > s->msg_buf) {
645 --s->readpos;
646 --s->bytes_read;
647 }
648 s->state = FLDPLUS;
649 finished = 1;
650 } else if (c != ' ' && c != '\t') {
651 /* The next character is not folded whitespace, so
652 prepare to move on to the next field. It's OK
653 if c is EOF, it will be handled on the next
654 call to m_getfld (). */
655 s->state = FLD;
656 finished = 1;
657 } else {
658 /* Folded header field, continues on the next line. */
659 }
660 }
661 *bufsz = s->bytes_read;
662 break;
663 }
664
665 body:
666 case BODY: {
667 /*
668 * get the message body up to bufsz characters or the
669 * end of the message.
670 */
671 char *bp;
672
673 max = *bufsz-1;
674 /* Back up and store the current position. */
675 bp = --s->readpos;
676 c = s->end - s->readpos < max ? s->end - s->readpos : max;
677 if (s->msg_style != MS_DEFAULT && c > 1) {
678 /*
679 * packed maildrop - only take up to the (possible)
680 * start of the next message. This "matchc" should
681 * probably be a Boyer-Moore matcher for non-vaxen,
682 * particularly since we have the alignment table
683 * all built for the end-of-buffer test (next).
684 * But our vax timings indicate that the "matchc"
685 * instruction is 50% faster than a carefully coded
686 * B.M. matcher for most strings. (So much for elegant
687 * algorithms vs. brute force.) Since I (currently)
688 * run MH on a vax, we use the matchc instruction. --vj
689 */
690 char *ep;
691
692 if ((ep = matchc( s->fdelimlen, s->fdelim, c, bp )))
693 c = ep - bp + 1;
694 else {
695 /*
696 * There's no delim in the buffer but there may be
697 * a partial one at the end. If so, we want to leave
698 * it so the "eom" check on the next call picks it up.
699 * Use a modified Boyer-Moore matcher to make this
700 * check relatively cheap. The first "if" figures
701 * out what position in the pattern matches the last
702 * character in the buffer. The inner "while" matches
703 * the pattern against the buffer, backwards starting
704 * at that position. Note that unless the buffer
705 * ends with one of the characters in the pattern
706 * (excluding the first and last), we do only one test.
707 */
708 char *sp;
709
710 ep = bp + c - 1;
711 if ((sp = s->pat_map[(unsigned char) *ep])) {
712 do {
713 /* This if() is true unless (a) the buffer is too
714 * small to contain this delimiter prefix, or
715 * (b) it contains exactly enough chars for the
716 * delimiter prefix.
717 * For case (a) obviously we aren't going to match.
718 * For case (b), if the buffer really contained exactly
719 * a delim prefix, then the m_eom call at entry
720 * should have found it. Thus it's not a delim
721 * and we know we won't get a match.
722 */
723 if (((sp - s->fdelim) + 2) <= c) {
724 cp = sp;
725 /* Unfortunately although fdelim has a preceding NUL
726 * we can't use this as a sentinel in case the buffer
727 * contains a NUL in exactly the wrong place (this
728 * would cause us to run off the front of fdelim).
729 */
730 while (*--ep == *--cp)
731 if (cp < s->fdelim)
732 break;
733 if (cp < s->fdelim) {
734 /* we matched the entire delim prefix,
735 * so only take the buffer up to there.
736 * we know ep >= bp -- check above prevents underrun
737 */
738 c = (ep - bp) + 2;
739 break;
740 }
741 }
742 /* try matching one less char of delim string */
743 ep = bp + c - 1;
744 } while (--sp > s->fdelim);
745 }
746 }
747 }
748 memcpy( buf, bp, c );
749 /* Advance the current position to reflect the copy out.
750 c is less than or equal to the number of bytes remaining
751 in the read buffer, so will not overrun it. */
752 s->readpos += c;
753 cp = buf + c;
754 /* Subtract 1 from c because the first character was read by
755 Getc(), and therefore already accounted for in s->bytes_read. */
756 s->bytes_read += c - 1;
757 *bufsz = s->bytes_read;
758 break;
759 }
760
761 default:
762 adios (NULL, "m_getfld() called with bogus state of %d", s->state);
763 }
764
765 *cp = 0;
766 leave_getfld (s);
767
768 return s->state;
769 }
770
771
772 void
773 m_unknown(m_getfld_state_t *gstate, FILE *iob)
774 {
775 m_getfld_state_t s;
776 register int c;
777 char text[MAX_DELIMITER_SIZE];
778 char from[] = "From ";
779 register char *cp;
780 register char *delimstr;
781 unsigned int i;
782
783 enter_getfld (gstate, iob);
784 s = *gstate;
785
786 /*
787 * Figure out what the message delimitter string is for this
788 * maildrop. (This used to be part of m_Eom but I didn't like
789 * the idea of an "if" statement that could only succeed on the
790 * first call to m_Eom getting executed on each call, i.e., at
791 * every newline in the message).
792 *
793 * If the first line of the maildrop is a Unix "From " line, we
794 * say the style is MBOX and eat the rest of the line. Otherwise
795 * we say the style is MMDF and look for the delimiter string
796 * specified when nmh was built (or from the mts.conf file).
797 */
798
799 s->msg_style = MS_UNKNOWN;
800
801 for (i = 0, cp = text; i < sizeof text; ++i, ++cp) {
802 if ((*cp = Getc (s)) == EOF) {
803 break;
804 }
805 }
806
807 if (i == sizeof from-1 && strncmp (text, "From ", sizeof from-1) == 0) {
808 s->msg_style = MS_MBOX;
809 delimstr = "\nFrom ";
810 while ((c = Getc (s)) != '\n' && c >= 0) continue;
811 } else {
812 /* not a Unix style maildrop */
813 s->readpos -= s->bytes_read;
814 s->bytes_read = 0;
815 delimstr = mmdlm2;
816 s->msg_style = MS_MMDF;
817 }
818 c = strlen (delimstr);
819 s->fdelim = mh_xmalloc (c + 3);
820 *s->fdelim++ = '\0';
821 *s->fdelim = '\n';
822 s->msg_delim = s->fdelim+1;
823 s->edelim = s->msg_delim+1;
824 s->fdelimlen = c + 1;
825 s->edelimlen = c - 1; /* == strlen (delimstr) */
826 strcpy (s->msg_delim, delimstr);
827 s->delimend = s->msg_delim + s->edelimlen;
828 if (s->edelimlen <= 1)
829 adios (NULL, "maildrop delimiter must be at least 2 bytes");
830 /*
831 * build a Boyer-Moore end-position map for the matcher in m_getfld.
832 * N.B. - we don't match just the first char (since it's the newline
833 * separator) or the last char (since the matchc would have found it
834 * if it was a real delim).
835 */
836 s->pat_map = (char **) calloc (256, sizeof(char *));
837
838 for (cp = s->fdelim + 1; cp < s->delimend; cp++ )
839 s->pat_map[(unsigned char)*cp] = cp;
840
841 if (s->msg_style == MS_MMDF) {
842 /* flush extra msg hdrs */
843 while ((c = Getc(s)) >= 0 && eom (c, s))
844 ;
845 if (c >= 0)
846 Ungetc(c, s);
847 }
848
849 leave_getfld (s);
850 }
851
852
853 void
854 m_eomsbr (m_getfld_state_t s, int (*action)(int))
855 {
856 if ((s->eom_action = action)) {
857 s->msg_style = MS_MSH;
858 *s->msg_delim = 0;
859 s->fdelimlen = 1;
860 s->delimend = s->fdelim;
861 } else {
862 s->msg_style = MS_MMDF;
863 s->msg_delim = s->fdelim + 1;
864 s->fdelimlen = strlen (s->fdelim);
865 s->delimend = s->msg_delim + s->edelimlen;
866 }
867 }
868
869
870 /*
871 * test for msg delimiter string
872 */
873
874 static int
875 m_Eom (m_getfld_state_t s, int c)
876 {
877 register int i;
878 char text[MAX_DELIMITER_SIZE];
879 char *cp;
880
881 for (i = 0, cp = text; i < s->edelimlen; ++i, ++cp) {
882 if ((*cp = Getc (s)) == EOF) {
883 break;
884 }
885 }
886
887 if (i != s->edelimlen ||
888 strncmp (text, (char *)s->edelim, s->edelimlen)) {
889 if (i == 0 && s->msg_style == MS_MBOX)
890 /* the final newline in the (brain damaged) unix-format
891 * maildrop is part of the delimitter - delete it.
892 */
893 return 1;
894
895 /* Did not find delimiter, so restore the read position.
896 Note that on input, a character had already been read
897 with Getc(). It will be unget by m_getfld () on return. */
898 s->readpos -= s->bytes_read - 1;
899 s->bytes_read = 1;
900 return 0;
901 }
902
903 if (s->msg_style == MS_MBOX) {
904 while ((c = Getc (s)) != '\n')
905 if (c < 0)
906 break;
907 }
908
909 return 1;
910 }
911
912
913 static char *
914 matchc(int patln, char *pat, int strln, char *str)
915 {
916 register char *es = str + strln - patln;
917 register char *sp;
918 register char *pp;
919 register char *ep = pat + patln;
920 register char pc = *pat++;
921
922 for(;;) {
923 while (pc != *str++)
924 if (str > es)
925 return 0;
926 if (str > es+1)
927 return 0;
928 sp = str; pp = pat;
929 while (pp < ep && *sp++ == *pp)
930 pp++;
931 if (pp >= ep)
932 return --str;
933 }
934 }