Changed type of name argument to m_getfld() from unsigned char *
[mmh] / sbr / m_getfld.c
index 8697f20..1d5d764 100644 (file)
@@ -2,11 +2,14 @@
 /*
  * m_getfld.c -- read/parse a message
  *
- * $Id$
+ * This code is Copyright (c) 2002, by the authors of nmh.  See the
+ * COPYRIGHT file in the root directory of the nmh distribution for
+ * complete copyright information.
  */
 
 #include <h/mh.h>
-#include <zotnet/mts/mts.h>
+#include <h/mts.h>
+#include <h/utils.h>
 
 /* 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
    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
+(return value):  message parse state on return from function
+(global) int msg_count:  number of characters loaded into buf
+
+Functions (part of Inputs, really)
+=========
+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.
+
+Definitions
+===========
+state is one of:
+  FLD      // Field returned
+  FLDPLUS  // Field returned with more to come
+  FLDEOF   // Field returned ending at eom
+  BODY     // Body  returned with more to come
+  BODYEOF  // Body  returned ending at eom
+  FILEEOF  // Reached end of input file
+  FMTERR   // Message Format error
+  LENERR   // Name too long error from getfld
+
+msg_style is maildrop style, one of:
+  MS_UNKNOWN // type not known yet
+  MS_DEFAULT // default (one msg per file)
+  MS_MBOX    // Unix-style "from" lines
+  MS_MMDF    // string mmdlm2
+  MS_MSH     // whacko msh
+
+State variables (part of Outputs)
+===============
+m_getfld() retains state internally between calls in some state variables.
+
+These two variables are global, but only used internally by m_getfld.c:
+int msg_style
+char *msg_delim
+
+These are used for the end-of-message matcher when reading maildrops:
+static unsigned char **pat_map
+static unsigned char *fdelim
+static unsigned char *delimend
+static int fdelimlen
+static unsigned char *edelim
+static int edelimlen
+
+Restriction
+===========
+m_getfld() is restricted to operate on one file stream at a time because of
+the retained state (see "State variables" above).
+
+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.
+
+Along the way, I thought of these possible interface changes that we
+might want to consider before rototilling the internals:
+
+1) To remove globals that don't need to be:
+   Change msg_style and msg_delim to be file static.
+
+2) To remove a global:
+   Change bufsz to be in-out instead of in, and therefore int * instead of
+   int, and use that instead of global msg_count.  There are only 3 call
+   sites that use msg_count so it wouldn't take much effort to remove use of
+   it.  Of course, all call sites would have to change to provide an int *
+   instead of an int.  Some now pass constants.
+
+3) To remove the state argument from the signature:
+   Given the Current usage and Restriction above, the state variable could
+   be removed from the signature and just retained internally.
+
+4) To remove the Restriction above:
+   One approach would be for m_getfld() to retain multiple copies of that
+   state, one per iob that it sees.  Another approach would be for the
+   caller to store it in an opaque struct, the address of which is passed
+   through the interface.
+*/
 
 /*
  * static prototypes
@@ -178,24 +279,27 @@ static int fdelimlen;
 static unsigned char *edelim;
 static int edelimlen;
 
-static int (*eom_action)() = NULL;
+static int (*eom_action)(int) = NULL;
 
 #ifdef _FSTDIO
 # define _ptr    _p            /* Gag   */
 # define _cnt    _r            /* Retch */
 # define _filbuf __srget       /* Puke  */
+# define DEFINED__FILBUF_TO_SOMETHING_SPECIFIC
+
+# if defined __CYGWIN__
+  /* Cygwin's stdio.h does not declare __srget(). */
+  int __srget(FILE *);
+# endif /* __CYGWIN__ */
 #endif
 
-#ifdef SCO_5_STDIO
-# define _ptr  __ptr
-# define _cnt  __cnt
-# define _base __base
-# define _filbuf(fp)  ((fp)->__cnt = 0, __filbuf(fp))
+#ifndef DEFINED__FILBUF_TO_SOMETHING_SPECIFIC
+extern int  _filbuf(FILE*);
 #endif
 
 
 int
-m_getfld (int state, unsigned char *name, unsigned char *buf,
+m_getfld (int state, unsigned char name[NAMESZ], unsigned char *buf,
           int bufsz, FILE *iob)
 {
     register unsigned char  *bp, *cp, *ep, *sp;
@@ -255,11 +359,14 @@ m_getfld (int state, unsigned char *name, unsigned char *buf,
                bp = sp = (unsigned char *) iob->_IO_read_ptr - 1;
                j = (cnt = ((long) iob->_IO_read_end -
                        (long) iob->_IO_read_ptr)  + 1) < i ? cnt : i;
+#elif defined(__DragonFly__)
+               bp = sp = (unsigned char *) ((struct __FILE_public *)iob)->_p - 1;
+               j = (cnt = ((struct __FILE_public *)iob)->_r+1) < i ? cnt : i;
 #else
                bp = sp = (unsigned char *) iob->_ptr - 1;
                j = (cnt = iob->_cnt+1) < i ? cnt : i;
 #endif
-               while ((c = *bp++) != ':' && c != '\n' && --j >= 0)
+               while (--j >= 0 && (c = *bp++) != ':' && c != '\n')
                    *cp++ = c;
 
                j = bp - sp;
@@ -267,6 +374,8 @@ m_getfld (int state, unsigned char *name, unsigned char *buf,
 #ifdef LINUX_STDIO
                    iob->_IO_read_ptr = iob->_IO_read_end;
                    if (__underflow(iob) == EOF) {
+#elif defined(__DragonFly__)
+                   if (__srget(iob) == EOF) {
 #else
                    if (_filbuf(iob) == EOF) {
 #endif
@@ -280,6 +389,9 @@ m_getfld (int state, unsigned char *name, unsigned char *buf,
                } else {
 #ifdef LINUX_STDIO
                    iob->_IO_read_ptr = bp + 1;
+#elif defined(__DragonFly__)
+                   ((struct __FILE_public *)iob)->_p = bp + 1;
+                   ((struct __FILE_public *)iob)->_r = cnt - 1;
 #else
                    iob->_ptr = bp + 1;
                    iob->_cnt = cnt - 1;
@@ -295,14 +407,39 @@ m_getfld (int state, unsigned char *name, unsigned char *buf,
                 *  . hit the end of the buffer. (loop)
                 */
                if (c == '\n') {
-                   *cp = *buf = 0;
-                   advise (NULL, "eol encountered in field \"%s\"", name);
-                   state = FMTERR;
-                   goto finish;
+                   /* We hit the end of the line without seeing ':' to
+                    * terminate the field name.  This is usually (always?)
+                    * spam.  But, blowing up is lame, especially when
+                    * scan(1)ing a folder with such messages.  Pretend such
+                    * lines are the first of the body (at least mutt also
+                    * handles it this way). */
+
+                   /* See if buf can hold this line, since we were assuming
+                    * we had a buffer of NAMESZ, not bufsz. */
+                   /* + 1 for the newline */
+                   if (bufsz < j + 1) {
+                       /* No, it can't.  Oh well, guess we'll blow up. */
+                       *cp = *buf = 0;
+                       advise (NULL, "eol encountered in field \"%s\"", name);
+                       state = FMTERR;
+                       goto finish;
+                   }
+                   memcpy (buf, name, j - 1);
+                   buf[j - 1] = '\n';
+                   buf[j] = '\0';
+                   /* mhparse.c:get_content wants to find the position of the
+                    * body start, but it thinks there's a blank line between
+                    * the header and the body (naturally!), so seek back so
+                    * that things line up even though we don't have that
+                    * blank line in this case.  Simpler parsers (e.g. mhl)
+                    * get extra newlines, but that should be harmless enough,
+                    * right?  This is a corrupt message anyway. */
+                   fseek (iob, ftell (iob) - 2, SEEK_SET);
+                   return BODY;
                }
                if ((i -= j) <= 0) {
                    *cp = *buf = 0;
-                   advise (NULL, "field name \"%s\" exceeds %d bytes", name, NAMESZ - 1);
+                   advise (NULL, "field name \"%s\" exceeds %d bytes", name, NAMESZ - 2);
                    state = LENERR;
                    goto finish;
                }
@@ -324,6 +461,9 @@ m_getfld (int state, unsigned char *name, unsigned char *buf,
 #ifdef LINUX_STDIO
                cnt = (long) iob->_IO_read_end - (long) iob->_IO_read_ptr;
                bp = (unsigned char *) --iob->_IO_read_ptr;
+#elif defined(__DragonFly__)
+               cnt = ((struct __FILE_public *)iob)->_r++;
+               bp = (unsigned char *) --((struct __FILE_public *)iob)->_p;
 #else
                cnt = iob->_cnt++;
                bp = (unsigned char *) --iob->_ptr;
@@ -338,6 +478,11 @@ m_getfld (int state, unsigned char *name, unsigned char *buf,
                        j = ep - (unsigned char *) iob->_IO_read_ptr;
                        memcpy (cp, iob->_IO_read_ptr, j);
                        iob->_IO_read_ptr = ep;
+#elif defined(__DragonFly__)
+                       j = ep - (unsigned char *) ((struct __FILE_public *)iob)->_p;
+                       memcpy (cp, ((struct __FILE_public *)iob)->_p, j);
+                       ((struct __FILE_public *)iob)->_p = ep;
+                       ((struct __FILE_public *)iob)->_r -= j;
 #else
                        j = ep - (unsigned char *) iob->_ptr;
                        memcpy (cp, iob->_ptr, j);
@@ -357,6 +502,9 @@ m_getfld (int state, unsigned char *name, unsigned char *buf,
 #ifdef LINUX_STDIO
                c += bp - (unsigned char *) iob->_IO_read_ptr;
                memcpy( cp, iob->_IO_read_ptr, c);
+#elif defined(__DragonFly__)
+               c += bp - (unsigned char *) ((struct __FILE_public *)iob)->_p;
+               memcpy( cp, ((struct __FILE_public *)iob)->_p, c);
 #else
                c += bp - (unsigned char *) iob->_ptr;
                memcpy( cp, iob->_ptr, c);
@@ -367,6 +515,9 @@ m_getfld (int state, unsigned char *name, unsigned char *buf,
                    /* the dest buffer is full */
 #ifdef LINUX_STDIO
                    iob->_IO_read_ptr += c;
+#elif defined(__DragonFly__)
+                   ((struct __FILE_public *)iob)->_r -= c;
+                   ((struct __FILE_public *)iob)->_p += c;
 #else
                    iob->_cnt -= c;
                    iob->_ptr += c;
@@ -386,14 +537,21 @@ m_getfld (int state, unsigned char *name, unsigned char *buf,
                iob->_IO_read_ptr = iob->_IO_read_end;
                c = __underflow(iob);
                iob->_IO_read_ptr++;    /* NOT automatic! */
+#elif defined(__DragonFly__)
+               *cp++ =j = *(((struct __FILE_public *)iob)->_p + c);
+               c = __srget(iob);
 #else
                *cp++ = j = *(iob->_ptr + c);
                c = _filbuf(iob);
 #endif
-               if ((j == '\0' || j == '\n') && c != ' ' && c != '\t') {
+                if (c == EOF || 
+                 ((j == '\0' || j == '\n') && c != ' ' && c != '\t')) {
                    if (c != EOF) {
 #ifdef LINUX_STDIO
                        --iob->_IO_read_ptr;
+#elif defined(__DragonFly__)
+                       --((struct __FILE_public *)iob)->_p;
+                       ++((struct __FILE_public *)iob)->_r;
 #else
                        --iob->_ptr;
                        ++iob->_cnt;
@@ -417,6 +575,9 @@ m_getfld (int state, unsigned char *name, unsigned char *buf,
 #ifdef LINUX_STDIO
            bp = (unsigned char *) --iob->_IO_read_ptr;
            cnt = (long) iob->_IO_read_end - (long) iob->_IO_read_ptr;
+#elif defined(__DragonFly__)
+           bp = (unsigned char *) --((struct __FILE_public *)iob)->_p;
+           cnt = ++((struct __FILE_public *)iob)->_r;
 #else
            bp = (unsigned char *) --iob->_ptr;
            cnt = ++iob->_cnt;
@@ -454,22 +615,35 @@ m_getfld (int state, unsigned char *name, unsigned char *buf,
                    ep = bp + c - 1;
                    if ((sp = pat_map[*ep])) {
                        do {
-                           cp = sp;
-                           while (*--ep == *--cp)
-                           ;
-                           if (cp < fdelim) {
-                               if (ep >= bp)
-                                   /*
-                                    * ep < bp means that all the buffer
-                                    * contains is a prefix of delim.
-                                    * If this prefix is really a delim, the
-                                    * m_eom call at entry should have found
-                                    * it.  Thus it's not a delim and we can
-                                    * take all of it.
+                           /* This if() is true unless (a) the buffer is too
+                            * small to contain this delimiter prefix, or
+                            * (b) it contains exactly enough chars for the
+                            * delimiter prefix.
+                            * For case (a) obviously we aren't going to match.
+                            * For case (b), if the buffer really contained exactly
+                            * a delim prefix, then the m_eom call at entry
+                            * should have found it.  Thus it's not a delim
+                            * and we know we won't get a match.
+                            */
+                           if (((sp - fdelim) + 2) <= c) {
+                               cp = sp;
+                               /* Unfortunately although fdelim has a preceding NUL
+                                * we can't use this as a sentinel in case the buffer
+                                * contains a NUL in exactly the wrong place (this
+                                * would cause us to run off the front of fdelim).
+                                */
+                               while (*--ep == *--cp)
+                                   if (cp < fdelim)
+                                       break;
+                               if (cp < fdelim) {
+                                   /* we matched the entire delim prefix,
+                                    * so only take the buffer up to there.
+                                    * we know ep >= bp -- check above prevents underrun
                                     */
                                    c = (ep - bp) + 2;
-                           break;
-                       }
+                                   break;
+                               }
+                           }
                            /* try matching one less char of delim string */
                            ep = bp + c - 1;
                        } while (--sp > fdelim);
@@ -479,6 +653,9 @@ m_getfld (int state, unsigned char *name, unsigned char *buf,
            memcpy( buf, bp, c );
 #ifdef LINUX_STDIO
            iob->_IO_read_ptr += c;
+#elif defined(__DragonFly__)
+           ((struct __FILE_public *)iob)->_r -= c;
+           ((struct __FILE_public *)iob)->_p += c;
 #else
            iob->_cnt -= c;
            iob->_ptr += c;
@@ -500,10 +677,6 @@ finish:
 }
 
 
-#ifdef RPATHS
-static char unixbuf[BUFSIZ] = "";
-#endif /* RPATHS */
-
 void
 m_unknown(FILE *iob)
 {
@@ -533,15 +706,8 @@ m_unknown(FILE *iob)
            && strncmp (text, "From ", 5) == 0) {
        msg_style = MS_MBOX;
        delimstr = "\nFrom ";
-#ifndef        RPATHS
        while ((c = getc (iob)) != '\n' && c >= 0)
            ;
-#else /* RPATHS */
-       cp = unixbuf;
-       while ((c = getc (iob)) != '\n')
-           *cp++ = c;
-       *cp = 0;
-#endif /* RPATHS */
     } else {
        /* not a Unix style maildrop */
        fseek (iob, pos, SEEK_SET);
@@ -551,7 +717,7 @@ m_unknown(FILE *iob)
        msg_style = MS_MMDF;
     }
     c = strlen (delimstr);
-    fdelim = (unsigned char *) malloc((size_t) (c + 3));
+    fdelim = (unsigned char *) mh_xmalloc((size_t) (c + 3));
     *fdelim++ = '\0';
     *fdelim = '\n';
     msg_delim = (char *)fdelim+1;
@@ -571,7 +737,7 @@ m_unknown(FILE *iob)
     pat_map = (unsigned char **) calloc (256, sizeof(unsigned char *));
 
     for (cp = (char *) fdelim + 1; cp < (char *) delimend; cp++ )
-       pat_map[*cp] = (unsigned char *) cp;
+       pat_map[(unsigned char)*cp] = (unsigned char *) cp;
 
     if (msg_style == MS_MMDF) {
        /* flush extra msg hdrs */
@@ -584,7 +750,7 @@ m_unknown(FILE *iob)
 
 
 void
-m_eomsbr (int (*action)())
+m_eomsbr (int (*action)(int))
 {
     if ((eom_action = action)) {
        msg_style = MS_MSH;
@@ -610,9 +776,6 @@ m_Eom (int c, FILE *iob)
     register long pos = 0L;
     register int i;
     char text[10];
-#ifdef RPATHS
-    register char *cp;
-#endif /* RPATHS */
 
     pos = ftell (iob);
     if ((i = fread (text, sizeof *text, edelimlen, iob)) != edelimlen
@@ -633,81 +796,13 @@ m_Eom (int c, FILE *iob)
     }
 
     if (msg_style == MS_MBOX) {
-#ifndef RPATHS
        while ((c = getc (iob)) != '\n')
            if (c < 0)
                break;
-#else /* RPATHS */
-       cp = unixbuf;
-       while ((c = getc (iob)) != '\n' && c >= 0)
-           *cp++ = c;
-       *cp = 0;
-#endif /* RPATHS */
-    }
-
-    return 1;
-}
-
-
-#ifdef RPATHS
-/*
- * Return the Return-Path and Delivery-Date
- * header information.
- *
- * Currently, I'm assuming that the "From " line
- * takes one of the following forms.
- *
- * From sender date remote from host   (for UUCP delivery)
- * From sender@host  date              (for sendmail delivery)
- */
-
-int
-get_returnpath (char *rp, int rplen, char *dd, int ddlen)
-{
-    char *ap, *bp, *cp, *dp;
-
-    ap = unixbuf;
-    if (!(bp = cp = strchr(ap, ' ')))
-       return 0;
-
-    /*
-     * Check for "remote from" in envelope to see
-     * if this message uses UUCP style addressing
-     */
-    while ((cp = strchr(++cp, 'r'))) {
-       if (strncmp (cp, "remote from", 11) == 0) {
-           cp = strrchr (cp, ' ');
-           break;
-       }
     }
 
-    /*
-     * Get the Return-Path information from
-     * the "From " envelope.
-     */
-    if (cp) {
-       /* return path for UUCP style addressing */
-       dp = strchr (++cp, '\n');
-       snprintf (rp, rplen, "%.*s!%.*s\n", dp - cp, cp, bp - ap, ap);
-    } else {
-       /* return path for standard domain addressing */
-       snprintf (rp, rplen, "%.*s\n", bp - ap, ap);
-    }
-
-    /*
-     * advance over the spaces to get to
-     * delivery date on envelope
-     */
-    while (*bp == ' ')
-       bp++;
-
-    /* Now get delivery date from envelope */
-    snprintf (dd, ddlen, "%.*s\n", 24, bp);
-
-    unixbuf[0] = 0;
     return 1;
 }
-#endif /* RPATHS */
 
 
 static unsigned char *
@@ -723,7 +818,8 @@ matchc(int patln, char *pat, int strln, char *str)
                while (pc != *str++)
                        if (str > es)
                                return 0;
-
+               if (str > es+1)
+                       return 0;
                sp = str; pp = pat;
                while (pp < ep && *sp++ == *pp)
                        pp++;