]> diplodocus.org Git - nmh/blobdiff - sbr/m_getfld.c
Oops, fc31cece had a syntax error that gcc ignored, clang doesn't.
[nmh] / sbr / m_getfld.c
index 0d2086785c711938a67f42664fa7de787f35594c..0fe55499941f874e54cfc20c3a803b66ce70d050 100644 (file)
 #include <h/mts.h>
 #include <h/utils.h>
 
 #include <h/mts.h>
 #include <h/utils.h>
 
+/*
+   Purpose
+   =======
+   Reads an Internet message (RFC 5322), or one or more messages
+   stored in a maildrop in mbox (RFC 4155) or MMDF format, from a file
+   stream.  Each call to m_getfld() reads one header field, or a
+   portion of the body, in sequence.
+
+   Inputs
+   ======
+   gstate:  opaque parse state
+   bufsz:  maximum number of characters to load into buf
+   iob:  input file stream
+
+   Outputs
+   =======
+   name:  header field name (array of size NAMESZ=999)
+   buf:  either a header field body or message body
+   bufsz:  number of characters loaded into buf
+   (return value):  message parse state on return from function
+
+   Functions
+   =========
+   void m_getfld_state_destroy (m_getfld_state_t *gstate): destroys
+   the parse state pointed to by the gstate argument.
+
+   m_getfld_state_reset (m_getfld_state_t *gstate): resets the parse
+   state to FLD.
+
+   void m_unknown(FILE *iob):  Determines the message delimiter string
+   for the maildrop.  Called by inc and scan when reading from a
+   maildrop file.
+
+   State variables
+   ===============
+   m_getfld() retains state internally between calls in the
+   m_getfld_state_t variable.  These are used for detecting the end of
+   each message when reading maildrops:
+
+     char **pat_map
+     char *fdelim
+     char *delimend
+     int fdelimlen
+     char *edelim
+     int edelimlen
+     char *msg_delim
+     int msg_style
+
+   Usage
+   =====
+   m_getfld_state_t gstate = 0;
+      ...
+   int state = m_getfld (&gstate, ...);
+      ...
+   m_getfld_state_destroy (&gstate);
+
+   The state is retained internally by gstate.  To reset its state to FLD:
+   m_getfld_state_reset (&gstate);
+*/
+
+/* The following described the old implementation.  The high-level
+   structure hasn't changed, but some of the details have.  I'm
+   leaving this as-is, though, for posterity.
+ */
+
 /* This module has a long and checkered history.  First, it didn't burst
    maildrops correctly because it considered two CTRL-A:s in a row to be
    an inter-message delimiter.  It really is four CTRL-A:s followed by a
 /* This module has a long and checkered history.  First, it didn't burst
    maildrops correctly because it considered two CTRL-A:s in a row to be
    an inter-message delimiter.  It really is four CTRL-A:s followed by a
@@ -25,7 +90,7 @@
    be parsed as well.  Unfortunately the speed issue finally caught up with
    us since this routine is at the very heart of MH.
 
    be parsed as well.  Unfortunately the speed issue finally caught up with
    us since this routine is at the very heart of MH.
 
-   To speed things up considerably, the routine Eom() was made an auxilary
+   To speed things up considerably, the routine Eom() was made an auxiliary
    function called by the macro eom().  Unless we are bursting a maildrop,
    the eom() macro returns FALSE saying we aren't at the end of the
    message.
    function called by the macro eom().  Unless we are bursting a maildrop,
    the eom() macro returns FALSE saying we aren't at the end of the
    message.
    there is data in "name" or "buf").
   */
 
    there is data in "name" or "buf").
   */
 
-/*
-Purpose
-=======
-Reads an Internet message (RFC 5322), or one or more messages stored in a
-maildrop in mbox (RFC 4155) or MMDF format, from a file stream.  Each call
-to m_getfld() reads one header field, or a portion of the body, in sequence.
-
-Inputs
-======
-state:  message parse state
-bufsz:  maximum number of characters to load into buf
-iob:  input file stream
-
-Outputs
-=======
-name:  header field name (array of size NAMESZ=999)
-buf:  either a header field body or message body
-bufsz:  number of characters loaded into buf
-(return value):  message parse state on return from function
-
-Functions
-=========
-void m_unknown(FILE *iob):  Determines the message delimiter string for the
-  maildrop.  Called by inc, scan, and msh when reading from a maildrop file.
-
-void m_eomsbr (int (*action)(int)):  Sets the hook to check for end of
-  message in a maildrop.  Called only by msh.
-
-Those functions save state in the State variables listed below.
-
-State variables
-===============
-m_getfld() retains state internally between calls in some state variables.
-These are used for detecting the end of each message when reading maildrops:
-  unsigned char **pat_map
-  unsigned char *fdelim
-  unsigned char *delimend
-  int fdelimlen
-  unsigned char *edelim
-  int edelimlen
-  char *msg_delim
-  int msg_style
-  int (*eom_action)(int)
-
-Restrictions
-============
-m_getfld() is restricted to operate on one file stream at a time
-because of the retained state (see "State variables" above).  And the
-first call to m_getfld() on that file stream requires that the read
-pointer be at the beginning of the file (ftell() of 0).
-
-Current usage
-=============
-The first call to m_getfld() on a file stream is with a state of FLD.
-Subsequent calls provide the state returned by the previous call.
-Therefore, given the Restrictions above, the state variable could be
-removed from the signature and just retained internally.
-*/
-
 /*
  * static prototypes
  */
 /*
  * static prototypes
  */
-static int m_Eom (int, FILE *);
-static unsigned char *matchc(int, char *, int, char *);
+struct m_getfld_state;
+static int m_Eom (m_getfld_state_t);
+static char *matchc(int, char *, int, char *);
 
 
-#define eom(c,iob)     (s->msg_style != MS_DEFAULT && \
-                        (((c) == *s->msg_delim && m_Eom(c,iob)) ||\
-                         (s->eom_action && (*s->eom_action)(c))))
+#define eom(c,s)       (s->msg_style != MS_DEFAULT && \
+                        ((c) == *s->msg_delim && m_Eom(s)))
 
 /* This replaces the old approach, with its direct access to stdio
  * internals.  It uses one fread() to load a buffer that we manage.
  *
  * MSG_INPUT_SIZE is the size of the buffer.
  * MAX_DELIMITER_SIZE is the maximum size of the delimiter used to
 
 /* This replaces the old approach, with its direct access to stdio
  * internals.  It uses one fread() to load a buffer that we manage.
  *
  * MSG_INPUT_SIZE is the size of the buffer.
  * MAX_DELIMITER_SIZE is the maximum size of the delimiter used to
- * separate messages in a maildrop, such as mbox "\nFrom ".
+ * separate messages in a maildrop, such as mbox "From ".
  *
  * Some of the tests in the test suite assume a MSG_INPUT_SIZE
  *
  * Some of the tests in the test suite assume a MSG_INPUT_SIZE
- * of 8192. */
-#define MSG_INPUT_SIZE BUFSIZ
-#define MAX_DELIMITER_SIZE 32
-
-static struct m_getfld_state {
-    unsigned char msg_buf[2 * MSG_INPUT_SIZE + MAX_DELIMITER_SIZE];
-    unsigned char *readpos;
-    unsigned char *end;  /* One past the last character read in. */
+ * of 8192.
+ */
+#define MSG_INPUT_SIZE NMH_BUFSIZ
+#define MAX_DELIMITER_SIZE 5
+
+struct m_getfld_state {
+    char msg_buf[2 * MSG_INPUT_SIZE + MAX_DELIMITER_SIZE];
+    char *readpos;
+    char *end;  /* One past the last character read in. */
     /* The following support tracking of the read position in the
        input file stream so that callers can interleave m_getfld()
     /* The following support tracking of the read position in the
        input file stream so that callers can interleave m_getfld()
-       calls with ftell() and fseek().  ytes_read replaces the old
+       calls with ftell() and fseek().  bytes_read replaces the old
        m_getfld() msg_count global.  last_caller_pos is stored when
        m_getfld() msg_count global.  last_caller_pos is stored when
-       leaving m_getfld()/m_unkown(), then checked on the next entry.
+       leaving m_getfld()/m_unknown(), then checked on the next entry.
        last_internal_pos is used to remember the position used
        internally by m_getfld() (read_more(), actually). */
     off_t bytes_read;
     off_t total_bytes_read; /* by caller, not necessarily from input file */
     off_t last_caller_pos;
     off_t last_internal_pos;
        last_internal_pos is used to remember the position used
        internally by m_getfld() (read_more(), actually). */
     off_t bytes_read;
     off_t total_bytes_read; /* by caller, not necessarily from input file */
     off_t last_caller_pos;
     off_t last_internal_pos;
+    FILE *iob;
 
 
-    unsigned char **pat_map;
+    char **pat_map;
     int msg_style;
     /*
      * The "full" delimiter string for a packed maildrop consists
      * of a newline followed by the actual delimiter.  E.g., the
      * full string for a Unix maildrop would be: "\n\nFrom ".
     int msg_style;
     /*
      * The "full" delimiter string for a packed maildrop consists
      * of a newline followed by the actual delimiter.  E.g., the
      * full string for a Unix maildrop would be: "\n\nFrom ".
-     * "Fdelim" points to the start of the full string and is used
+     * "fdelim" points to the start of the full string and is used
      * in the BODY case of the main routine to search the buffer for
      * a possible eom.  Msg_delim points to the first character of
      * in the BODY case of the main routine to search the buffer for
      * a possible eom.  Msg_delim points to the first character of
-     * the actual delim. string (i.e., fdelim+1).  Edelim
+     * the actual delim. string (i.e., fdelim+1).  edelim
      * points to the 2nd character of actual delimiter string.  It
      * is used in m_Eom because the first character of the string
      * has been read and matched before m_Eom is called.
      */
     char *msg_delim;
      * points to the 2nd character of actual delimiter string.  It
      * is used in m_Eom because the first character of the string
      * has been read and matched before m_Eom is called.
      */
     char *msg_delim;
-    unsigned char *fdelim;
-    unsigned char *delimend;
+    char *fdelim;
+    char *delimend;
     int fdelimlen;
     int fdelimlen;
-    unsigned char *edelim;
+    char *edelim;
     int edelimlen;
     int edelimlen;
-    int (*eom_action)(int);
-} m;
+    int state;
+    int track_filepos;
+};
+
+static
+void
+m_getfld_state_init (m_getfld_state_t *gstate, FILE *iob) {
+    m_getfld_state_t s;
+
+    NEW(s);
+    *gstate = s;
+    s->readpos = s->end = s->msg_buf;
+    s->bytes_read = s->total_bytes_read = 0;
+    s->last_caller_pos = s->last_internal_pos = 0;
+    s->iob = iob;
+    s->pat_map = NULL;
+    s->msg_style = MS_DEFAULT;
+    s->msg_delim = "";
+    s->fdelim = s->delimend = s->edelim = NULL;
+    s->fdelimlen = s->edelimlen = 0;
+    s->state = FLD;
+    s->track_filepos = 0;
+}
+
+/* scan() needs to force a state an initial state of FLD for each message. */
+void
+m_getfld_state_reset (m_getfld_state_t *gstate) {
+    if (*gstate) {
+       (*gstate)->state = FLD;
+    }
+}
+
+/* If the caller interleaves ftell*()/fseek*() calls with m_getfld()
+   calls, m_getfld() must keep track of the file position.  The caller
+   must use this function to inform m_getfld(). */
+void
+m_getfld_track_filepos (m_getfld_state_t *gstate, FILE *iob) {
+    if (! *gstate) {
+       m_getfld_state_init (gstate, iob);
+    }
+
+    (*gstate)->track_filepos = 1;
+}
+
+void m_getfld_state_destroy (m_getfld_state_t *gstate) {
+    m_getfld_state_t s = *gstate;
+
+    if (s) {
+       if (s->fdelim) {
+           free (s->fdelim-1);
+           free (s->pat_map);
+       }
+       free (s);
+       *gstate = 0;
+    }
+}
 
 /*
   Summary of file and message input buffer positions:
 
 /*
   Summary of file and message input buffer positions:
@@ -284,176 +346,185 @@ static struct m_getfld_state {
 
 
 static void
 
 
 static void
-enter_getfld (FILE *iob, struct m_getfld_state *m) {
-    off_t pos = ftello (iob);
-
-    /* Rely on Restriction that the first call to m_getfld (), etc.,
-       is with the read position for the file stream set to 0. */
-    if (pos == 0) {
-       /* A new file stream, so reset the buffer state. */
-       m->readpos = m->end = m->msg_buf;
-       m->total_bytes_read = 0;
-       m->last_caller_pos = m->last_internal_pos = ftello (iob);
-       m->pat_map = NULL;
-       m->fdelim = m->delimend = m->edelim = NULL;
-       m->msg_style = MS_DEFAULT;
-       m->msg_delim = "";
-       m->fdelimlen = m->edelimlen = 0;
-       m->eom_action = NULL;
-    } else {
-       off_t pos_movement = pos - m->last_caller_pos; /* Can be < 0. */
+enter_getfld (m_getfld_state_t *gstate, FILE *iob) {
+    m_getfld_state_t s;
+    off_t pos;
+    off_t pos_movement;
 
 
-       if (pos_movement == 0) {
-           pos = m->last_internal_pos;
-       } else {
-           /* The current file stream position differs from the last one, so
-              caller must have called ftell/o().  Adjust accordingly. */
-           if (m->readpos + pos_movement >= m->msg_buf  &&
-               m->readpos + pos_movement < m->end) {
-               /* We can shift readpos and remain within the bounds of
-                  msg_buf. */
-               m->readpos += pos_movement;
-               m->total_bytes_read += pos_movement;
-               pos = m->last_internal_pos;
-           } else {
-               size_t num_read;
-
-               /* This seek skips past an integral number of chunks of
-                  size MSG_INPUT_SIZE. */
-               fseeko (iob, pos / MSG_INPUT_SIZE * MSG_INPUT_SIZE, SEEK_SET);
-               num_read = fread (m->msg_buf, 1, MSG_INPUT_SIZE, iob);
-               m->readpos = m->msg_buf  +  pos % MSG_INPUT_SIZE;
-               m->end = m->msg_buf + num_read;
-               m->total_bytes_read = pos;
-           }
-       }
+    if (! *gstate) {
+       m_getfld_state_init (gstate, iob);
+    }
+    s = *gstate;
+    s->bytes_read = 0;
+
+    /* This is ugly and no longer necessary, but is retained just in
+       case it's needed again.  The parser used to open the input file
+       multiple times, so we had to always use the FILE * that's
+       passed to m_getfld().  Now the parser inits a new
+       m_getfld_state for each file.  See comment below about the
+       readpos shift code being currently unused. */
+    s->iob = iob;
+
+    if (!s->track_filepos)
+        return;
+
+    pos = ftello(iob);
+    if (pos == 0 && s->last_internal_pos == 0)
+        return;
+
+    if (s->last_internal_pos == 0) {
+        s->total_bytes_read = pos;
+        return;
+    }
 
 
-       fseeko (iob, pos, SEEK_SET);
+    pos_movement = pos - s->last_caller_pos; /* Can be < 0. */
+    if (pos_movement == 0) {
+        pos = s->last_internal_pos;
+    } else {
+        /* The current file stream position differs from the
+           last one, so caller must have called ftell/o().
+           Or, this is the first call and the file position
+           was not at 0. */
+
+        if (s->readpos + pos_movement >= s->msg_buf  &&
+            s->readpos + pos_movement < s->end) {
+            /* This is currently unused.  It could be used by
+               parse_mime() if it was changed to use a global
+               m_getfld_state. */
+            /* We can shift readpos and remain within the
+               bounds of msg_buf. */
+            s->readpos += pos_movement;
+            s->total_bytes_read += pos_movement;
+            pos = s->last_internal_pos;
+        } else {
+            size_t num_read;
+
+            /* This seek skips past an integral number of
+               chunks of size MSG_INPUT_SIZE. */
+            fseeko (iob, pos/MSG_INPUT_SIZE * MSG_INPUT_SIZE, SEEK_SET);
+            num_read = fread (s->msg_buf, 1, MSG_INPUT_SIZE, iob);
+            s->readpos = s->msg_buf  +  pos % MSG_INPUT_SIZE;
+            s->end = s->msg_buf + num_read;
+            s->total_bytes_read = pos;
+        }
     }
 
     }
 
-    m->bytes_read = 0;
+    fseeko (iob, pos, SEEK_SET);
 }
 
 static void
 }
 
 static void
-leave_getfld (struct m_getfld_state *m, FILE *iob) {
-    /* Save the internal file position that we use for the input buffer. */
-    m->last_internal_pos = ftello (iob);
-
-    /* Set file stream position so that callers can use ftell(). */
-    m->total_bytes_read += m->bytes_read;
-    fseeko (iob, m->total_bytes_read, SEEK_SET);
-    m->last_caller_pos = ftello (iob);
+leave_getfld (m_getfld_state_t s) {
+    s->total_bytes_read += s->bytes_read;
+
+    if (s->track_filepos) {
+       /* Save the internal file position that we use for the input buffer. */
+       s->last_internal_pos = ftello (s->iob);
+
+       /* Set file stream position so that callers can use ftell(). */
+       fseeko (s->iob, s->total_bytes_read, SEEK_SET);
+       s->last_caller_pos = ftello (s->iob);
+    }
 }
 
 static size_t
 }
 
 static size_t
-read_more (struct m_getfld_state *s, FILE *iob) {
+read_more (m_getfld_state_t s) {
     /* Retain at least edelimlen characters that have already been
        read so that we can back up to them in m_Eom(). */
     ssize_t retain = s->edelimlen;
     size_t num_read;
 
     if (retain < s->end - s->readpos) retain = s->end - s->readpos;
     /* Retain at least edelimlen characters that have already been
        read so that we can back up to them in m_Eom(). */
     ssize_t retain = s->edelimlen;
     size_t num_read;
 
     if (retain < s->end - s->readpos) retain = s->end - s->readpos;
-    /* assert (retain <= s->readpos - s->msg_buf <= sizeof msg_buf); */
+    assert (retain <= s->readpos - s->msg_buf);
 
     /* Move what we want to retain at end of the buffer to the beginning. */
     memmove (s->msg_buf, s->readpos - retain, retain);
 
     s->readpos = s->msg_buf + retain;
 
     /* Move what we want to retain at end of the buffer to the beginning. */
     memmove (s->msg_buf, s->readpos - retain, retain);
 
     s->readpos = s->msg_buf + retain;
-    num_read = fread (s->readpos, 1, MSG_INPUT_SIZE, iob);
+    num_read = fread (s->readpos, 1, MSG_INPUT_SIZE, s->iob);
     s->end = s->readpos + num_read;
 
     return num_read;
 }
 
     s->end = s->readpos + num_read;
 
     return num_read;
 }
 
+/* The return values of the following functions are a bit
+   subtle.  They can return 0x00 - 0xff as a valid character,
+   but EOF is typically 0xffffffff. */
 static int
 static int
-Getc (FILE *iob) {
-    struct m_getfld_state *s = &m;
-
-    if (s->end - s->readpos < 1) {
-       if (read_more (&m, iob) == 0) {
-           /* Pretend that we read a character.  That's what stdio does. */
-           ++s->readpos;
-           return EOF;
-       }
-    }
+Getc (m_getfld_state_t s) {
+    if ((s->end - s->readpos < 1 && read_more (s) == 0) ||
+        s->readpos >= s->end)
+        return EOF;
 
 
-    ++s->bytes_read;
-    return s->readpos < s->end  ?  *s->readpos++  :  EOF;
+    s->bytes_read++;
+    return (unsigned char)*s->readpos++;
 }
 
 static int
 }
 
 static int
-Peek (FILE *iob) {
-    struct m_getfld_state *s = &m;
-
-    int next_char = Getc (iob);
-    --s->readpos;
-    --s->bytes_read;
-
-    return next_char;
+Peek (m_getfld_state_t s) {
+    if (s->end - s->readpos < 1  &&  read_more (s) == 0) {
+        return EOF;
+    } else {
+        return s->readpos < s->end  ?  (unsigned char) *s->readpos  :  EOF;
+    }
 }
 
 static int
 }
 
 static int
-Ungetc (int c, FILE *iob) {
-    struct m_getfld_state *s = &m;
-    NMH_UNUSED (iob);
-
+Ungetc (int c, m_getfld_state_t s) {
     if (s->readpos == s->msg_buf) {
        return EOF;
     } else {
        --s->bytes_read;
     if (s->readpos == s->msg_buf) {
        return EOF;
     } else {
        --s->bytes_read;
-       return *--s->readpos = c;
+       return *--s->readpos = (unsigned char) c;
     }
 }
 
 
 int
     }
 }
 
 
 int
-m_getfld (int state, unsigned char name[NAMESZ], unsigned char *buf,
-          int *bufsz, FILE *iob)
+m_getfld (m_getfld_state_t *gstate, char name[NAMESZ], char *buf, int *bufsz,
+          FILE *iob)
 {
 {
-    struct m_getfld_state *s = &m;
-    register unsigned char *cp;
+    m_getfld_state_t s;
+    register char *cp;
     register int max, n, c;
 
     register int max, n, c;
 
-    enter_getfld (iob, &m);
+    enter_getfld (gstate, iob);
+    s = *gstate;
 
 
-    if ((c = Getc(iob)) < 0) {
+    if ((c = Getc(s)) == EOF) {
        *bufsz = *buf = 0;
        *bufsz = *buf = 0;
-       leave_getfld (&m, iob);
-       return FILEEOF;
+       leave_getfld (s);
+       return s->state = FILEEOF;
     }
     }
-    if (eom (c, iob)) {
-       if (! s->eom_action) {
-           /* flush null messages */
-           while ((c = Getc(iob)) >= 0 && eom (c, iob))
-               ;
-
-           if (c >= 0)
-               Ungetc(c, iob);
-       }
+    if (eom (c, s)) {
+       /* flush null messages */
+       while ((c = Getc(s)) != EOF && eom (c, s))
+           ;
+
+       if (c != EOF)
+           Ungetc(c, s);
        *bufsz = *buf = 0;
        *bufsz = *buf = 0;
-       leave_getfld (&m, iob);
-       return FILEEOF;
+       leave_getfld (s);
+       return s->state = FILEEOF;
     }
 
     }
 
-    switch (state) {
+    switch (s->state) {
        case FLD:
            if (c == '\n' || c == '-') {
                /* we hit the header/body separator */
        case FLD:
            if (c == '\n' || c == '-') {
                /* we hit the header/body separator */
-               while (c != '\n' && (c = Getc(iob)) >= 0) continue;
-
-               if (c < 0 || (c = Getc(iob)) < 0 || eom (c, iob)) {
-                   if (! s->eom_action) {
-                       /* flush null messages */
-                       while ((c = Getc(iob)) >= 0 && eom (c, iob))
-                           ;
-                       if (c >= 0)
-                           Ungetc(c, iob);
-                   }
+               while (c != '\n' && (c = Getc(s)) != EOF)
+                    ;
+
+               if (c == EOF || (c = Getc(s)) == EOF || eom (c, s)) {
+                   /* flush null messages */
+                   while ((c = Getc(s)) != EOF && eom (c, s))
+                       ;
+                   if (c != EOF)
+                       Ungetc(c, s);
                    *bufsz = *buf = 0;
                    *bufsz = *buf = 0;
-                   leave_getfld (&m, iob);
-                   return FILEEOF;
+                   leave_getfld (s);
+                   return s->state = FILEEOF;
                }
                }
-               state = BODY;
+               s->state = BODY;
                goto body;
            }
            /*
                goto body;
            }
            /*
@@ -469,7 +540,7 @@ m_getfld (int state, unsigned char name[NAMESZ], unsigned char *buf,
               account for that. */
            for (n = 1;
                 c != ':'  &&  c != '\n'  &&  c != EOF  &&  n < max;
               account for that. */
            for (n = 1;
                 c != ':'  &&  c != '\n'  &&  c != EOF  &&  n < max;
-                ++n, c = Getc (iob)) {
+                ++n, c = Getc (s)) {
                *cp++ = c;
            }
 
                *cp++ = c;
            }
 
@@ -477,11 +548,11 @@ m_getfld (int state, unsigned char name[NAMESZ], unsigned char *buf,
               the ':' or the first folded whitespace. */
            {
                int next_char;
               the ':' or the first folded whitespace. */
            {
                int next_char;
-               if (c == EOF  ||  (next_char = Peek (iob)) == EOF) {
+               if (c == EOF  ||  (next_char = Peek (s)) == EOF) {
                    *bufsz = *cp = *buf = 0;
                    advise (NULL, "eof encountered in field \"%s\"", name);
                    *bufsz = *cp = *buf = 0;
                    advise (NULL, "eof encountered in field \"%s\"", name);
-                   leave_getfld (&m, iob);
-                   return FMTERR;
+                   leave_getfld (s);
+                   return s->state = FMTERR;
                }
            }
 
                }
            }
 
@@ -506,7 +577,7 @@ m_getfld (int state, unsigned char name[NAMESZ], unsigned char *buf,
                    /* No, it can't.  Oh well, guess we'll blow up. */
                    *bufsz = *cp = *buf = 0;
                    advise (NULL, "eol encountered in field \"%s\"", name);
                    /* No, it can't.  Oh well, guess we'll blow up. */
                    *bufsz = *cp = *buf = 0;
                    advise (NULL, "eol encountered in field \"%s\"", name);
-                   state = FMTERR;
+                   s->state = FMTERR;
                    break;
                }
                memcpy (buf, name, n - 1);
                    break;
                }
                memcpy (buf, name, n - 1);
@@ -516,8 +587,8 @@ m_getfld (int state, unsigned char name[NAMESZ], unsigned char *buf,
                   (and n) include that, but it was not put into the
                   name array in the for loop above.  So subtract 1. */
                *bufsz = --s->bytes_read;  /* == n - 1 */
                   (and n) include that, but it was not put into the
                   name array in the for loop above.  So subtract 1. */
                *bufsz = --s->bytes_read;  /* == n - 1 */
-               leave_getfld (&m, iob);
-               return BODY;
+               leave_getfld (s);
+               return s->state = BODY;
            } else if (max <= n) {
                /* By design, the loop above discards the last character
                    it had read.  It's in c, use it. */
            } else if (max <= n) {
                /* By design, the loop above discards the last character
                    it had read.  It's in c, use it. */
@@ -525,12 +596,12 @@ m_getfld (int state, unsigned char name[NAMESZ], unsigned char *buf,
                *bufsz = *cp = *buf = 0;
                advise (NULL, "field name \"%s\" exceeds %d bytes", name,
                        NAMESZ - 2);
                *bufsz = *cp = *buf = 0;
                advise (NULL, "field name \"%s\" exceeds %d bytes", name,
                        NAMESZ - 2);
-               state = LENERR;
+               s->state = LENERR;
                break;
            }
 
            /* Trim any trailing spaces from the end of name. */
                break;
            }
 
            /* Trim any trailing spaces from the end of name. */
-           while (isspace (*--cp) && cp >= name) continue;
+           while (isspace ((unsigned char) *--cp) && cp >= name) continue;
            *++cp = 0;
            /* readpos points to the first character of the field body. */
            /* fall through */
            *++cp = 0;
            /* readpos points to the first character of the field body. */
            /* fall through */
@@ -548,20 +619,29 @@ m_getfld (int state, unsigned char name[NAMESZ], unsigned char *buf,
            n = 0;
            for (finished = 0; ! finished; ) {
                while (c != '\n'  &&  c != EOF  &&  n++ < max) {
            n = 0;
            for (finished = 0; ! finished; ) {
                while (c != '\n'  &&  c != EOF  &&  n++ < max) {
-                   *cp++ = c = Getc (iob);
+                   if ((c = Getc (s)) != EOF) { *cp++ = c; }
                }
 
                }
 
-               if (c != EOF) c = Peek (iob);
+               if (c != EOF) c = Peek (s);
                if (max < n) {
                if (max < n) {
-                   /* the dest buffer is full */
-                   state = FLDPLUS;
+                   /* The dest buffer is full.  Need to back the read
+                      pointer up by one because when m_getfld() is
+                      reentered, it will read a character.  Then
+                      we'll jump right to the FLDPLUS handling code,
+                      which will not store that character, but
+                      instead move on to the next one. */
+                   if (s->readpos > s->msg_buf) {
+                       --s->readpos;
+                       --s->bytes_read;
+                   }
+                   s->state = FLDPLUS;
                    finished = 1;
                } else if (c != ' '  &&  c != '\t') {
                    /* The next character is not folded whitespace, so
                       prepare to move on to the next field.  It's OK
                       if c is EOF, it will be handled on the next
                       call to m_getfld (). */
                    finished = 1;
                } else if (c != ' '  &&  c != '\t') {
                    /* The next character is not folded whitespace, so
                       prepare to move on to the next field.  It's OK
                       if c is EOF, it will be handled on the next
                       call to m_getfld (). */
-                   state = FLD;
+                   s->state = FLD;
                    finished = 1;
                } else {
                    /* Folded header field, continues on the next line. */
                    finished = 1;
                } else {
                    /* Folded header field, continues on the next line. */
@@ -577,10 +657,10 @@ m_getfld (int state, unsigned char name[NAMESZ], unsigned char *buf,
             * get the message body up to bufsz characters or the
             * end of the message.
             */
             * get the message body up to bufsz characters or the
             * end of the message.
             */
-           unsigned char *bp;
+           char *bp;
 
            max = *bufsz-1;
 
            max = *bufsz-1;
-           /* Back up and store the current position and update cnt. */
+           /* Back up and store the current position. */
            bp = --s->readpos;
            c = s->end - s->readpos < max  ?  s->end - s->readpos  :  max;
            if (s->msg_style != MS_DEFAULT && c > 1) {
            bp = --s->readpos;
            c = s->end - s->readpos < max  ?  s->end - s->readpos  :  max;
            if (s->msg_style != MS_DEFAULT && c > 1) {
@@ -596,7 +676,7 @@ m_getfld (int state, unsigned char name[NAMESZ], unsigned char *buf,
                 * algorithms vs. brute force.)  Since I (currently)
                 * run MH on a vax, we use the matchc instruction. --vj
                 */
                 * algorithms vs. brute force.)  Since I (currently)
                 * run MH on a vax, we use the matchc instruction. --vj
                 */
-               unsigned char *ep;
+               char *ep;
 
                if ((ep = matchc( s->fdelimlen, s->fdelim, c, bp )))
                    c = ep - bp + 1;
 
                if ((ep = matchc( s->fdelimlen, s->fdelim, c, bp )))
                    c = ep - bp + 1;
@@ -614,10 +694,10 @@ m_getfld (int state, unsigned char name[NAMESZ], unsigned char *buf,
                     * ends with one of the characters in the pattern
                     * (excluding the first and last), we do only one test.
                     */
                     * ends with one of the characters in the pattern
                     * (excluding the first and last), we do only one test.
                     */
-                   unsigned char *sp;
+                   char *sp;
 
                    ep = bp + c - 1;
 
                    ep = bp + c - 1;
-                   if ((sp = s->pat_map[*ep])) {
+                   if ((sp = s->pat_map[(unsigned char) *ep])) {
                        do {
                            /* This if() is true unless (a) the buffer is too
                             * small to contain this delimiter prefix, or
                        do {
                            /* This if() is true unless (a) the buffer is too
                             * small to contain this delimiter prefix, or
@@ -668,26 +748,29 @@ m_getfld (int state, unsigned char name[NAMESZ], unsigned char *buf,
         }
 
        default:
         }
 
        default:
-           adios (NULL, "m_getfld() called with bogus state of %d", state);
+           adios (NULL, "m_getfld() called with bogus state of %d", s->state);
     }
 
     *cp = 0;
     }
 
     *cp = 0;
-    leave_getfld (&m, iob);
+    leave_getfld (s);
 
 
-    return state;
+    return s->state;
 }
 
 
 void
 }
 
 
 void
-m_unknown(FILE *iob)
+m_unknown(m_getfld_state_t *gstate, FILE *iob)
 {
 {
-    struct m_getfld_state *s = &m;
+    m_getfld_state_t s;
     register int c;
     char text[MAX_DELIMITER_SIZE];
     register int c;
     char text[MAX_DELIMITER_SIZE];
+    char from[] = "From ";
     register char *cp;
     register char *delimstr;
     register char *cp;
     register char *delimstr;
+    unsigned int i;
 
 
-    enter_getfld (iob, &m);
+    enter_getfld (gstate, iob);
+    s = *gstate;
 
 /*
  * Figure out what the message delimitter string is for this
 
 /*
  * Figure out what the message delimitter string is for this
@@ -704,37 +787,37 @@ m_unknown(FILE *iob)
 
     s->msg_style = MS_UNKNOWN;
 
 
     s->msg_style = MS_UNKNOWN;
 
-    for (c = 0, cp = text; c < 5; ++c, ++cp) {
-       if ((*cp = Getc (iob)) == EOF) {
+    for (i = 0, cp = text; i < sizeof text; ++i, ++cp) {
+       if ((c = Getc (s)) == EOF) {
+           *cp = '\0';
            break;
            break;
+       } else {
+           *cp = c;
        }
     }
 
        }
     }
 
-    if (c == 5  &&  strncmp (text, "From ", 5) == 0) {
+    if (i == sizeof from-1  &&  strncmp (text, "From ", sizeof from-1) == 0) {
        s->msg_style = MS_MBOX;
        delimstr = "\nFrom ";
        s->msg_style = MS_MBOX;
        delimstr = "\nFrom ";
-       while ((c = Getc (iob)) != '\n' && c >= 0) continue;
-       /* m_unknown is only called on maildrop files, and they are only
-          read using m_getfld ().  The caller musn't try to read from
-          the stream directly because the file position indicator was
-          not advanced based on bytes_read, but instead on whatever
-          was read into the message buffer. */
+       while ((c = Getc(s)) != EOF && c != '\n')
+            ;
     } else {
        /* not a Unix style maildrop */
        s->readpos -= s->bytes_read;
     } else {
        /* not a Unix style maildrop */
        s->readpos -= s->bytes_read;
+       s->bytes_read = 0;
        delimstr = mmdlm2;
        s->msg_style = MS_MMDF;
     }
     c = strlen (delimstr);
        delimstr = mmdlm2;
        s->msg_style = MS_MMDF;
     }
     c = strlen (delimstr);
-    s->fdelim = (unsigned char *) mh_xmalloc((size_t) (c + 3));
+    s->fdelim = mh_xmalloc (c + 3);
     *s->fdelim++ = '\0';
     *s->fdelim = '\n';
     *s->fdelim++ = '\0';
     *s->fdelim = '\n';
-    s->msg_delim = (char *)s->fdelim+1;
-    s->edelim = (unsigned char *)s->msg_delim+1;
+    s->msg_delim = s->fdelim+1;
+    s->edelim = s->msg_delim+1;
     s->fdelimlen = c + 1;
     s->edelimlen = c - 1; /* == strlen (delimstr) */
     strcpy (s->msg_delim, delimstr);
     s->fdelimlen = c + 1;
     s->edelimlen = c - 1; /* == strlen (delimstr) */
     strcpy (s->msg_delim, delimstr);
-    s->delimend = (unsigned char *)s->msg_delim + s->edelimlen;
+    s->delimend = s->msg_delim + s->edelimlen;
     if (s->edelimlen <= 1)
        adios (NULL, "maildrop delimiter must be at least 2 bytes");
     /*
     if (s->edelimlen <= 1)
        adios (NULL, "maildrop delimiter must be at least 2 bytes");
     /*
@@ -743,39 +826,20 @@ m_unknown(FILE *iob)
      * separator) or the last char (since the matchc would have found it
      * if it was a real delim).
      */
      * separator) or the last char (since the matchc would have found it
      * if it was a real delim).
      */
-    s->pat_map = (unsigned char **) calloc (256, sizeof(unsigned char *));
+    s->pat_map = (char **) mh_xcalloc (256, sizeof(char *));
 
 
-    for (cp = (char *) s->fdelim + 1; cp < (char *) s->delimend; cp++ )
-       s->pat_map[(unsigned char)*cp] = (unsigned char *) cp;
+    for (cp = s->fdelim + 1; cp < s->delimend; cp++ )
+       s->pat_map[(unsigned char)*cp] = cp;
 
     if (s->msg_style == MS_MMDF) {
        /* flush extra msg hdrs */
 
     if (s->msg_style == MS_MMDF) {
        /* flush extra msg hdrs */
-       while ((c = Getc(iob)) >= 0 && eom (c, iob))
+       while ((c = Getc(s)) != EOF && eom (c, s))
            ;
            ;
-       if (c >= 0)
-           Ungetc(c, iob);
+       if (c != EOF)
+           Ungetc(c, s);
     }
 
     }
 
-    leave_getfld (&m, iob);
-}
-
-
-void
-m_eomsbr (int (*action)(int))
-{
-    struct m_getfld_state *s = &m;
-
-    if ((s->eom_action = action)) {
-       s->msg_style = MS_MSH;
-       *s->msg_delim = 0;
-       s->fdelimlen = 1;
-       s->delimend = s->fdelim;
-    } else {
-       s->msg_style = MS_MMDF;
-       s->msg_delim = (char *)s->fdelim + 1;
-       s->fdelimlen = strlen((char *)s->fdelim);
-       s->delimend = (unsigned char *)(s->msg_delim + s->edelimlen);
-    }
+    leave_getfld (s);
 }
 
 
 }
 
 
@@ -784,16 +848,20 @@ m_eomsbr (int (*action)(int))
  */
 
 static int
  */
 
 static int
-m_Eom (int c, FILE *iob)
+m_Eom (m_getfld_state_t s)
 {
 {
-    struct m_getfld_state *s = &m;
     register int i;
     char text[MAX_DELIMITER_SIZE];
     char *cp;
 
     for (i = 0, cp = text; i < s->edelimlen; ++i, ++cp) {
     register int i;
     char text[MAX_DELIMITER_SIZE];
     char *cp;
 
     for (i = 0, cp = text; i < s->edelimlen; ++i, ++cp) {
-       if ((*cp = Getc (iob)) == EOF) {
+       int c2;
+
+       if ((c2 = Getc (s)) == EOF) {
+           *cp = '\0';
            break;
            break;
+       } else {
+           *cp = c2;
        }
     }
 
        }
     }
 
@@ -809,20 +877,21 @@ m_Eom (int c, FILE *iob)
           Note that on input, a character had already been read
           with Getc().  It will be unget by m_getfld () on return. */
        s->readpos -= s->bytes_read - 1;
           Note that on input, a character had already been read
           with Getc().  It will be unget by m_getfld () on return. */
        s->readpos -= s->bytes_read - 1;
+       s->bytes_read = 1;
        return 0;
     }
 
     if (s->msg_style == MS_MBOX) {
        return 0;
     }
 
     if (s->msg_style == MS_MBOX) {
-       while ((c = Getc (iob)) != '\n')
-           if (c < 0)
-               break;
+       int c;
+       while ((c = Getc(s)) != EOF && c != '\n')
+            ;
     }
 
     return 1;
 }
 
 
     }
 
     return 1;
 }
 
 
-static unsigned char *
+static char *
 matchc(int patln, char *pat, int strln, char *str)
 {
        register char *es = str + strln - patln;
 matchc(int patln, char *pat, int strln, char *str)
 {
        register char *es = str + strln - patln;
@@ -841,6 +910,6 @@ matchc(int patln, char *pat, int strln, char *str)
                while (pp < ep && *sp++ == *pp)
                        pp++;
                if (pp >= ep)
                while (pp < ep && *sp++ == *pp)
                        pp++;
                if (pp >= ep)
-                       return ((unsigned char *)--str);
+                       return --str;
        }
 }
        }
 }