-static void
-enter_getfld (FILE *iob, struct m_getfld_buffer *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_file_pos = m->last_internal_pos = ftello (iob);
- } else {
- /* If the current file stream position differs from the last one,
- then caller must have called ftell(), so adjust. */
- if (pos != m->last_file_pos) {
- size_t num_read;
-
- /* Opportunity for optimization here: don't reread if the
- new position had already been read into the buffer,
- just move m->readpos to it. */
- fseeko (iob, 0, SEEK_SET);
- do {
- num_read = fread (m->msg_buf, 1, MSG_INPUT_SIZE, iob);
- pos -= num_read;
- } while (pos > 0);
- pos += num_read;
- /* assert (ftello (iob) == pos); */
- m->readpos = m->msg_buf + pos;
- m->end = m->msg_buf + num_read;
- m->last_internal_pos = m->last_file_pos = pos;
- m->total_bytes_read = pos;
- } else {
- /* Restore the file position that we use for the input buffer. */
- pos = m->last_internal_pos;
+ /* One of the MS_* macros tracking the type of iob's content and
+ * thus if it's a single email, or several with delimeters. Default
+ * is MS_DEFAULT. */
+ int msg_style;
+
+ /* The message delimeter if iob has multiple emails, else NULL. For
+ * MS_MBOX it's the string that separates two emails, "\nFrom ",
+ * i.e. the terminating blank line of the previous email, and the
+ * starting From_ line of the next, but for MS_MMDF it's
+ * "\001\001\001\001\n" that may start or terminate an email. */
+ char *msg_delim;
+ /* The last non-NUL char of msg_delim. */
+ char *delimend;
+ /* When searching for msg_delim after an email, it's only of
+ * interest at the start of the line, i.e. when preceded by a
+ * linefeed. fdelim points to msg_delim[-1] that contains '\n' so
+ * it can be used as the needle. */
+ char *fdelim;
+ /* strlen(fdelim). */
+ int fdelimlen;
+ /* The second char of msg_delim. Used when the first char has
+ * already been matched to test the rest. */
+ char *edelim;
+ /* strlen(edelim). */
+ int edelimlen;
+ /* The relationship between all of these pointers and lengths for
+ * the two possible msg_delim values.
+ *
+ * "\0\n\nFrom \0" 9 "\0\n\001\001\001\001\n\0" 8
+ * | || | | | | |
+ * | || s->delimend | | | s->delimend
+ * | || | | |
+ * | |s->edelim s->edelimlen=5 | | s->edelim s->edelimlen=4
+ * | | | |
+ * | s->msg_delim | s->msg_delim
+ * | |
+ * s->fdelim s->fdelimlen=7 s->fdelim s->fdelimlen=6
+ */
+
+ /* Maps all the bytes of msg_delim, apart from the last two,
+ * including the NUL, onto the last position in msg_delim where they
+ * occur. Bytes not present are NULL. */
+ char **pat_map;
+
+ /* The parser's current state. Also returned to the caller, amongst
+ * other possible values, to indicate the token consumed. One of
+ * FLD, FLDPLUS, BODY, or FILEEOF. */
+ int state;
+};
+
+m_getfld_state_t m_getfld_state_init(FILE *iob)
+{
+ m_getfld_state_t s;
+
+ NEW(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;
+
+ return s;
+}
+
+/* scan() needs to force 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) {
+ *gstate = m_getfld_state_init(iob);
+ }
+
+ (*gstate)->track_filepos = 1;
+}
+
+/* m_getfld_track_filepos() with the existing iob. */
+void m_getfld_track_filepos2(m_getfld_state_t *gstate)
+{
+ if (!*gstate)
+ adios(NULL, "m_getfld_track_filepos2 without gstate");
+
+ m_getfld_track_filepos(gstate, (*gstate)->iob);
+}
+
+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);