]> diplodocus.org Git - nmh/commitdiff
Merge commit '8206fbf', due to my screwup of committing it on a detached
authorKen Hornstein <kenh@pobox.com>
Tue, 2 Dec 2014 18:12:45 +0000 (13:12 -0500)
committerKen Hornstein <kenh@pobox.com>
Tue, 2 Dec 2014 18:12:45 +0000 (13:12 -0500)
HEAD after a git-bisect.

50 files changed:
Makefile.am
config/version.sh
docs/contrib/build_nmh
docs/pending-release-notes
etc/mhn.defaults.sh
h/fmt_compile.h
h/fmt_scan.h
h/mh.h
h/mhparse.h
h/mime.h
h/utils.h
man/mh-alias.man
man/mh-format.man
man/mhfixmsg.man
man/mhstore.man
man/repl.man
sbr/addrsbr.c
sbr/charstring.c
sbr/fmt_compile.c
sbr/fmt_new.c
sbr/fmt_scan.c
sbr/m_getfld.c
sbr/utils.c
test/format/test-mymbox
test/mhbuild/test-attach
test/mhfixmsg/test-mhfixmsg
test/mhstore/test-mhstore
test/repl/test-repl
uip/ap.c
uip/burst.c
uip/dropsbr.c
uip/fmtdump.c
uip/fmttest.c
uip/install-mh.c
uip/mhbuildsbr.c
uip/mhfixmsg.c
uip/mhfree.c
uip/mhlist.c
uip/mhlsbr.c
uip/mhn.c
uip/mhoutsbr.c
uip/mhparse.c
uip/mhshow.c
uip/mhshowsbr.c
uip/mhstore.c
uip/mhstoresbr.c
uip/picksbr.c
uip/repl.c
uip/scansbr.c
uip/sortm.c

index 7ffcf9b..1cc0875 100644 (file)
@@ -119,7 +119,7 @@ clean-local:
 ##
 ## Stuff that should be cleaned via "make maintainer-clean"
 ##
-MAINTAINERCLEANFILES = cscope.files cscope.out cscope.in.out cscope.po.out *.plist
+MAINTAINERCLEANFILES = atags cscope.files cscope.out cscope.in.out cscope.po.out *.plist
 
 ##
 ## And our own superclean, to get everything left by maintainer-clean.
@@ -478,8 +478,8 @@ etc_gen_ctype_checked_LDADD = $(POSTLINK)
 ## Hard-code the config/version.c target, instead of using $@, so this
 ## rule works for the distcheck target with Solaris (System V) make.
 ## distcheck uses VPATH, causes that make to prepend the VPATH to $@.
-config/version.c: Makefile
-       sh $(srcdir)/config/version.sh $(VERSION) > ./config/version.c
+config/version.c: Makefile $(srcdir)/config/version.sh
+       env srcdir="$(srcdir)" sh $(srcdir)/config/version.sh $(VERSION) > ./config/version.c
 
 sbr/sigmsg.h: $(srcdir)/sbr/sigmsg.awk $(SIGNAL_H)
        $(AWK) -f $(srcdir)/sbr/sigmsg.awk $(SIGNAL_H) > $@
index 1ff7eed..bf95447 100755 (executable)
@@ -38,5 +38,12 @@ done
 
 IFS=" "
 
-echo "char *version_str = \"nmh-$VERSION [compiled on $HOSTNAME at `date`]\";"
+if [ -d "${srcdir}/.git" ] ; then
+    branch=`(git branch | grep '^\*' | tr -d '* ') || true`
+fi
+if [ "${branch}" -a "${branch}" != "master" ] ; then
+    echo "char *version_str = \"nmh-$VERSION [branch ${branch}] [compiled on $HOSTNAME at `date`]\";"
+else
+    echo "char *version_str = \"nmh-$VERSION [compiled on $HOSTNAME at `date`]\";"
+fi
 echo "char *version_num = \"nmh-$VERSION\";"
index ba421bf..499fade 100755 (executable)
@@ -131,7 +131,7 @@ if install-mh -check >/dev/null 2>&1; then
 
   config_prefix=`cd $mhbin/.. && pwd`
 
-  mtsconf=`dirname "$mhbin"`/etc/mts.conf
+  mtsconf=`mhparam etcdir`/mts.conf
   if [ -f "$mtsconf" ]; then
     mts_entry=`grep '^mts:' "$mtsconf"`
     if [ "$mts_entry" ]; then
index 07c073a..fe12e69 100644 (file)
@@ -24,6 +24,10 @@ NEW FEATURES
   obsolete support programs that should be removed.
 - When building from source, configure will derive ${prefix} from an existing
   nmh installation if it finds one in your $PATH.
+- Added getmymbox and getmyaddr mh-format(5) function escapes.
+- The -changecur and -nochangecur switches have been added to mhfixmsg(1).
+- mhfixmsg now removes an extraneous trailing semicolon from header
+  parameter lists.
 
 -----------------
 OBSOLETE FEATURES
index 298dae6..d9d35b0 100755 (executable)
@@ -46,6 +46,7 @@ expand | sed -e 's/^   //' -e 's/  *$//'" >> $TMP
 fi
 
 echo "mhstore-store-text: %m%P.txt" >> $TMP
+echo "mhstore-store-text/html: %m%P.html" >> $TMP
 echo "mhstore-store-text/richtext: %m%P.rt" >> $TMP
 echo "mhstore-store-video/mpeg: %m%P.mpg" >> $TMP
 echo "mhstore-store-application/PostScript: %m%P.ps" >> $TMP
@@ -314,10 +315,17 @@ fi
 
 # This entry is used to retrieve external-body types that use a "url"
 # access-type.
-PGM=`$SEARCHPROG "$SEARCHPATH" curl`
-if [ ! -z "$PGM" ]; then
-       echo "nmh-access-url: $PGM -L" >> $TMP
-fi
+case "`uname`" in
+  FreeBSD)
+       echo "nmh-access-url: fetch -o -" >> $TMP
+       ;;
+  *)
+       PGM=`$SEARCHPROG "$SEARCHPATH" curl`
+       if [ ! -z "$PGM" ]; then
+               echo "nmh-access-url: $PGM -L" >> $TMP
+       fi
+       ;;
+esac
 
 # output a sorted version of the file
 sort < $TMP
index b0c12c0..9e94b3c 100644 (file)
@@ -73,7 +73,7 @@
 #define FT_LV_HOSTTYPE  63      /* set "value" to addr host type       */
 #define FT_LV_INGRPF    64      /* set "value" to addr in-group flag   */
 #define FT_LS_UNQUOTE   65      /* remove RFC 2822 quotes from "str"   */
-#define FT_LV_NOHOSTF   66     /* set "value" to addr no-host flag */
+#define FT_LV_NOHOSTF   66      /* set "value" to addr no-host flag */
 
 /* Date Coercion */
 #define FT_LOCALDATE    67      /* Coerce date to local timezone */
 #define FT_FORMATADDR   71      /* let external routine format addr    */
 #define FT_CONCATADDR   72      /* formataddr w/out duplicate removal  */
 #define FT_MYMBOX       73      /* do "mymbox" test on comp            */
+#define FT_GETMYMBOX    74      /* return "mymbox" mailbox string      */
+#define FT_GETMYADDR    75      /* return "mymbox" addr string         */
 
 /* conditionals & control flow (must be last) */
-#define FT_SAVESTR      74      /* save current str reg               */
-#define FT_DONE         75      /* stop formatting                    */
-#define FT_PAUSE        76      /* pause                              */
-#define FT_NOP          77      /* nop                                */
-#define FT_GOTO         78      /* (relative) goto                    */
-#define FT_IF_S_NULL    79      /* test if "str" null                 */
-#define FT_IF_S         80      /* test if "str" non-null             */
-#define FT_IF_V_EQ      81      /* test if "value" = literal          */
-#define FT_IF_V_NE      82      /* test if "value" != literal         */
-#define FT_IF_V_GT      83      /* test if "value" > literal          */
-#define FT_IF_MATCH     84      /* test if "str" contains literal     */
-#define FT_IF_AMATCH    85      /* test if "str" starts with literal  */
-#define FT_S_NULL       86      /* V = 1 if "str" null                */
-#define FT_S_NONNULL    87      /* V = 1 if "str" non-null            */
-#define FT_V_EQ         88      /* V = 1 if "value" = literal         */
-#define FT_V_NE         89      /* V = 1 if "value" != literal        */
-#define FT_V_GT         90      /* V = 1 if "value" > literal         */
-#define FT_V_MATCH      91      /* V = 1 if "str" contains literal    */
-#define FT_V_AMATCH     92      /* V = 1 if "str" starts with literal */
+#define FT_SAVESTR      76      /* save current str reg               */
+#define FT_DONE         77      /* stop formatting                    */
+#define FT_PAUSE        78      /* pause                              */
+#define FT_NOP          79      /* nop                                */
+#define FT_GOTO         80      /* (relative) goto                    */
+#define FT_IF_S_NULL    81      /* test if "str" null                 */
+#define FT_IF_S         82      /* test if "str" non-null             */
+#define FT_IF_V_EQ      83      /* test if "value" = literal          */
+#define FT_IF_V_NE      84      /* test if "value" != literal         */
+#define FT_IF_V_GT      85      /* test if "value" > literal          */
+#define FT_IF_MATCH     86      /* test if "str" contains literal     */
+#define FT_IF_AMATCH    87      /* test if "str" starts with literal  */
+#define FT_S_NULL       88      /* V = 1 if "str" null                */
+#define FT_S_NONNULL    89      /* V = 1 if "str" non-null            */
+#define FT_V_EQ         90      /* V = 1 if "value" = literal         */
+#define FT_V_NE         91      /* V = 1 if "value" != literal        */
+#define FT_V_GT         92      /* V = 1 if "value" > literal         */
+#define FT_V_MATCH      93      /* V = 1 if "str" contains literal    */
+#define FT_V_AMATCH     94      /* V = 1 if "str" starts with literal */
 
 #define IF_FUNCS FT_S_NULL      /* start of "if" functions */
index 2bcc265..5f659d4 100644 (file)
@@ -114,6 +114,12 @@ struct fmt_callbacks {
 
 char *new_fs (char *form, char *format, char *default_fs);
 
+/*
+ * Free memory allocated by new_fs().  It allocates to a static so
+ * no argument is necessary.
+ */
+void free_fs ();
+
 /*
  * Compile a format string into a set of format instructions.  Arguments are:
  *
diff --git a/h/mh.h b/h/mh.h
index f1568a4..b8c60ca 100644 (file)
--- a/h/mh.h
+++ b/h/mh.h
@@ -1,4 +1,3 @@
-
 /*
  * mh.h -- main header file for all of nmh
  */
@@ -61,6 +60,7 @@ void charstring_push_back (charstring_t, const char);
 /* Append possibly multi-byte character(s): */
 void charstring_push_back_chars (charstring_t, const char [], size_t, size_t);
 void charstring_append (charstring_t, const charstring_t);
+void charstring_append_cstring (charstring_t, const char []);
 void charstring_clear (charstring_t);
 /* Don't store return value of charstring_buffer() and use later after
    intervening push_back's; use charstring_buffer_copy() instead. */
index b9e7b90..371e671 100644 (file)
@@ -205,6 +205,7 @@ struct text {
 #define        MULTI_ALTERNATE 0x02
 #define        MULTI_DIGEST    0x03
 #define        MULTI_PARALLEL  0x04
+#define        MULTI_RELATED   0x05
 
 /* Structure for subparts of a multipart content */
 struct part {
@@ -528,3 +529,13 @@ int show_content_aux(CT ct, int alternate, char *cp, char *cracked,
                     struct format *fmt);
 
 extern int checksw;    /* Add Content-MD5 field */
+
+/*
+ * mhstore
+ * Put it here because it uses the CT typedef.
+ */
+typedef struct mhstoreinfo *mhstoreinfo_t;
+mhstoreinfo_t mhstoreinfo_create(CT *, char *, const char *, int, int);
+int mhstoreinfo_files_not_clobbered(const mhstoreinfo_t);
+void mhstoreinfo_free(mhstoreinfo_t);
+void store_all_messages (mhstoreinfo_t);
index 92d6462..9fcaf5d 100644 (file)
--- a/h/mime.h
+++ b/h/mime.h
@@ -12,7 +12,9 @@
 #define        DESCR_FIELD     "Content-Description"
 #define        DISPO_FIELD     "Content-Disposition"
 #define        MD5_FIELD       "Content-MD5"
-#define        ATTACH_FIELD    "Attach"
+#define PSEUDOHEADER_PREFIX            "Nmh-"
+#define        ATTACH_FIELD                    PSEUDOHEADER_PREFIX "Attach"
+#define        ATTACH_FIELD_ALT                "Attach"
 
 #define        isatom(c)   (isascii((unsigned char) c) \
                      && !isspace ((unsigned char) c) \
index 92c940d..cd69678 100644 (file)
--- a/h/utils.h
+++ b/h/utils.h
@@ -5,6 +5,7 @@
 
 void *mh_xmalloc(size_t);
 void *mh_xrealloc(void *, size_t);
+void *mh_xcalloc(size_t, size_t);
 char *pwd(void);
 char *add(const char *, char *);
 char *addlist(char *, const char *);
index 7ebbf0f..9a7776e 100644 (file)
@@ -1,4 +1,4 @@
-.TH MH-ALIAS %manext5% "Apr 18, 2014" "%nmhversion%"
+.TH MH-ALIAS %manext5% "November 15, 2014" "%nmhversion%"
 .\"
 .\" %nmhwarning%
 .\"
@@ -105,8 +105,8 @@ An approximation of the way aliases are resolved at posting time is:
 Build a list of all addresses from the message to be delivered,
 eliminating duplicate addresses.
 .IP 2)
-If this draft originated on the local host, then for those addresses in
-the message that have no host specified, perform alias resolution.
+For those addresses in the message that have no host specified,
+perform alias resolution.
 .IP 3)
 For each line in the alias file, compare \*(lqalias\*(rq against all of
 the existing addresses.  If a match, remove the matched \*(lqalias\*(rq
index 7ac0bc0..1e2f41a 100644 (file)
@@ -1,4 +1,4 @@
-.TH MH-FORMAT %manext5% "November 4, 2012" "%nmhversion%"
+.TH MH-FORMAT %manext5% "October 27, 2014" "%nmhversion%"
 .\"
 .\" %nmhwarning%
 .\"
@@ -366,7 +366,7 @@ mon date    integer month of the year
 month  date    string  month of the year (abbrev.)
 lmonth date    string  month of the year
 year   date    integer year (may be > 100)
-zone   date    integer timezone in hours
+zone   date    integer timezone in minutes
 tzone  date    string  timezone string
 szone  date    integer timezone explicit?
                        (1=explicit,0=implicit,\-1=unknown)
@@ -395,7 +395,11 @@ addr       addr    string  mbox@host or host!mbox rendering*
 pers   addr    string  the personal name*
 note   addr    string  commentary text*
 mbox   addr    string  the local mailbox*
-mymbox addr    integer List has the user's address? (0 or 1)
+mymbox addr    integer list has the user's address? (0 or 1)
+getmymbox      addr    string  the user's (first) address,
+                       with personal name
+getmyaddr      addr    string  the user's (first) address,
+                       without personal name
 host   addr    string  the host domain*
 nohost addr    integer no host was present (0 or 1)*
 type   addr    integer host type* (0=local,1=network,
index 81d069b..3bb8e84 100644 (file)
@@ -1,4 +1,4 @@
-.TH MHFIXMSG %manext1% "August 20, 2014" "%nmhversion%"
+.TH MHFIXMSG %manext1% "November 30, 2014" "%nmhversion%"
 .\"
 .\" %nmhwarning%
 .\"
@@ -28,6 +28,7 @@ mhfixmsg \- rewrite MIME messages with various transformations
 .RB [ \-rmmproc
 .IR program ]
 .RB [ \-normmproc ]
+.RB [ \-changecur " | " \-nochangecur ]
 .RB [ \-verbose " | " \-noverbose ]
 .RB [ \-version ]
 .RB [ \-help ]
@@ -148,12 +149,28 @@ and other
 .B nmh
 programs that parse MIME messages.
 .PP
+.B mhfixmsg
+applies one transformation unconditionally:  it removes an extraneous
+trailing semicolon from the parameter lists of MIME header fields.
+.PP
 The
 .B \-verbose
 switch directs
 .B mhfixmsg
 to output informational message for each transformation applied.
 .PP
+The return status of
+.B mhfixmsg
+is 0 if all of the requested transformations are performed, or
+non-zero otherwise.
+.RB ( mhfixmsg
+will not decode to binary content, but a request to do so is
+not considered a failure, and is noted with
+.BR \-verbose .)
+If a problem is detected with any one of multiple messages such that
+the return status is non-zero, then none of the messages will be
+modified.
+.PP
 The
 .B \-file
 .I file
@@ -261,12 +278,19 @@ call
 .B mhfixmsg
 immediately after a successful invocation of
 .BR inc .
-For example, with bash:
+One approach could be based on:
 .PP
 .RS 5
-alias inc='inc && mhfixmsg'
+msgs=`inc -format '%(msg)'`  &&  [ -n "$msgs" ]  &&  scan $msgs  &&  \
+mhfixmsg -nochangecur $msgs
 .RE
 .PP
+Another approach would rely on adding a sequence to Unseen-Sequence,
+which
+.B inc
+sets with the newly incorporated messages.  Those could then be
+supplied to
+.BR mhfixmsg .
 .SS "Integration with procmail"
 By way of example, here is an excerpt from a procmailrc file
 that filters messages through
@@ -346,12 +370,15 @@ is checked.
 .RB ` \-noreplacetextplain '
 .RB ` \-fixboundary '
 .RB ` \-fixcte '
+.RB ` \-changecur '
 .RB ` \-noverbose '
 .fi
 .SH CONTEXT
 If a folder is given, it will become the current folder.  The last
-message selected from a folder will become the current message.  If
+message selected from a folder will become the current message, unless
 the
+.B \-nochangecur
+switch is enabled.  If the
 .B \-file
 switch or an absolute pathname is used, the context will not be
 modified.
index fe50d25..fbdd370 100644 (file)
@@ -141,7 +141,6 @@ as a Content-MD5 header field), then
 will attempt to
 verify the integrity of the content.
 .SS "Storing the Contents"
-The
 .B mhstore
 will store the contents of the named messages in
 \*(lqnative\*(rq (decoded) format.  Two things must be determined:
index 72325e5..208676e 100644 (file)
@@ -1,4 +1,4 @@
-.TH REPL %manext1% "April 18, 2014" "%nmhversion%"
+.TH REPL %manext1% "November 4, 2014" "%nmhversion%"
 .\"
 .\" %nmhwarning%
 .\"
@@ -9,7 +9,7 @@ repl \- reply to a message
 .na
 .B repl
 .RI [ +folder ]
-.RI [ msgs ]
+.RI [ msg ]
 .RB [ \-annotate " | " \-noannotate ]
 .RB [ \-group " | " \-nogroup ]
 .RB [ \-cc
index 3028847..23a7cb4 100644 (file)
@@ -11,6 +11,7 @@
 #include <h/addrsbr.h>
 #include <h/mf.h>
 #include <h/mts.h>
+#include <h/utils.h>
 
 /* High level parsing of addresses:
 
@@ -138,7 +139,7 @@ getm (char *str, char *dfhost, int dftype, char *eresult, size_t eresultsize)
        dftype = LOCALHOST;
     }
 
-    mp = (struct mailname *) calloc ((size_t) 1, sizeof(*mp));
+    mp = (struct mailname *) mh_xcalloc ((size_t) 1, sizeof(*mp));
     if (mp == NULL) {
        if (eresult) {
            strncpy (eresult, "insufficient memory to represent address",
index d007e7b..d94961a 100644 (file)
@@ -102,13 +102,25 @@ charstring_append (charstring_t dest, const charstring_t src) {
     const size_t num = src->cur - src->buffer;
 
     if (num > 0) {
-        charstring_reserve (dest, num + (dest->cur - dest->buffer));
+        charstring_reserve (dest, dest->cur - dest->buffer + num);
         memcpy (dest->cur, src->buffer, num);
         dest->cur += num;
         dest->chars += src->chars;
     }
 }
 
+void
+charstring_append_cstring (charstring_t dest, const char src[]) {
+    const size_t num = strlen (src);
+
+    if (num > 0) {
+        charstring_reserve (dest, dest->cur - dest->buffer + num);
+        memcpy (dest->cur, src, num);  /* Exclude src's trailing newline. */
+        dest->cur += num;
+        dest->chars += num;
+    }
+}
+
 void
 charstring_clear (charstring_t s) {
     s->cur = s->buffer;
@@ -134,14 +146,12 @@ charstring_buffer (const charstring_t s) {
 
 char *
 charstring_buffer_copy (const charstring_t s) {
-    char *copy = strdup (charstring_buffer (s));
+    char *copy = mh_xmalloc (s->cur - s->buffer + 1);
 
-    if (copy) {
-        return copy;
-    } else {
-        advise ("strdup", "unable to copy charstring buffer");
-        return NULL;
-    }
+    /* Use charstring_buffer() to null terminate the buffer. */
+    memcpy (copy, charstring_buffer (s), s->cur - s->buffer + 1);
+
+    return copy;
 }
 
 size_t
index 62b27ea..dd43dc8 100644 (file)
@@ -223,6 +223,8 @@ static struct ftable functable[] = {
      { "friendly",   TF_COMP,  FT_LS_FRIENDLY, FT_PARSEADDR,   TFL_PUTS },
 
      { "mymbox",     TF_COMP,  FT_LV_COMPFLAG, FT_MYMBOX,      TFL_PUTN },
+     { "getmymbox",  TF_COMP,  FT_STR,         FT_GETMYMBOX,   0 },
+     { "getmyaddr",  TF_COMP,  FT_LS_ADDR,     FT_GETMYADDR,   TFL_PUTS },
 
      { "unquote",    TF_EXPR,  FT_LS_UNQUOTE,  0,              TFL_PUTS },
 
@@ -284,7 +286,7 @@ static struct colormap colortable[] = {
 
 /* Add new component to the hash table */
 #define NEWCOMP(cm,name) do { \
-       cm = ((struct comp *) calloc(1, sizeof (struct comp)));\
+       cm = ((struct comp *) mh_xcalloc (1, sizeof (struct comp)));\
        cm->c_name = getcpy(name);\
        cm->c_refcount++;\
        ncomp++;\
@@ -406,7 +408,7 @@ fmt_compile(char *fstring, struct format **fmt, int reset_comptable)
      */
     i = strlen(fstring)/2 + 1;
                if (i==1) i++;
-    next_fp = formatvec = (struct format *)calloc ((size_t) i,
+    next_fp = formatvec = (struct format *)mh_xcalloc ((size_t) i,
                                                   sizeof(struct format));
     if (next_fp == NULL)
        adios (NULL, "unable to allocate format storage");
@@ -562,14 +564,19 @@ do_name(char *sp, int preprocess)
        if (cm->c_type & CT_ADDR) {
            CERROR("component used as both date and address");
        }
-       cm->c_tws = (struct tws *)
-           calloc((size_t) 1, sizeof(*cm->c_tws));
+       if (cm->c_tws) {
+           memset (cm->c_tws, 0, sizeof *cm->c_tws);
+       } else {
+           cm->c_tws = mh_xcalloc (1, sizeof *cm->c_tws);
+       }
        fp->f_type = preprocess;
        PUTCOMP(sp);
        cm->c_type |= CT_DATE;
        break;
 
     case FT_MYMBOX:
+    case FT_GETMYMBOX:
+    case FT_GETMYADDR:
        if (!primed) {
            ismymbox ((struct mailname *) 0);
            primed++;
index 44a18b3..9ee8956 100644 (file)
@@ -58,6 +58,13 @@ new_fs (char *form, char *format, char *default_fs)
 }
 
 
+void
+free_fs (){
+    free (formats);
+    formats = 0;
+}
+
+
 /*
  * Expand escapes in format strings
  */
index 08405da..5732af9 100644 (file)
@@ -832,7 +832,7 @@ fmt_scan (struct format *format, charstring_t scanlp, int width, int *dat,
                    value = -1;
            break;
        case FT_LV_DST:
-           value = fmt->f_comp->c_tws->tw_flags & TW_DST;
+           value = fmt->f_comp->c_tws->tw_flags & TW_DST ? 1 : 0;
            break;
        case FT_LS_822DATE:
            str = dasctime (fmt->f_comp->c_tws , TW_ZONE);
@@ -1062,6 +1062,8 @@ fmt_scan (struct format *format, charstring_t scanlp, int width, int *dat,
            break;
 
        case FT_MYMBOX:
+       case FT_GETMYMBOX:
+       case FT_GETMYADDR:
            /*
             * if there's no component, we say true.  Otherwise we
             * say "true" only if we can parse the address and it
@@ -1073,24 +1075,46 @@ fmt_scan (struct format *format, charstring_t scanlp, int width, int *dat,
            if ((sp = comp->c_text) && (sp = getname(sp)) &&
                (mn = getm (sp, NULL, 0, NULL, 0))) {
                comp->c_mn = mn;
-               if (ismymbox(mn))
+               if (ismymbox(mn)) {
                    comp->c_flags |= CF_TRUE;
-               else
+                   /* Set str for use with FT_GETMYMBOX.  With
+                      FT_GETMYADDR, comp->c_mn will be run through
+                      FT_LS_ADDR, which will strip off any pers
+                      name. */
+                   str = mn->m_text;
+               } else {
                    comp->c_flags &= ~CF_TRUE;
+               }
                while ((sp = getname(sp)))
                    if ((comp->c_flags & CF_TRUE) == 0 &&
                        (mn = getm (sp, NULL, 0, NULL, 0)))
-                       if (ismymbox(mn))
+                       if (ismymbox(mn)) {
                            comp->c_flags |= CF_TRUE;
+                           /* Set str and comp->c_text for use with
+                              FT_GETMYMBOX.  With FT_GETMYADDR,
+                              comp->c_mn will be run through
+                              FT_LS_ADDR, which will strip off any
+                              pers name. */
+                           free (comp->c_text);
+                           comp->c_text = str = strdup (mn->m_text);
+                           comp->c_mn = mn;
+                       }
+               comp->c_flags |= CF_PARSED;
            } else {
                while (getname(""))             /* XXX */
                    ;
                if (comp->c_text == 0)
                    comp->c_flags |= CF_TRUE;
-               else
+               else {
                    comp->c_flags &= ~CF_TRUE;
+               }
                comp->c_mn = &fmt_mnull;
            }
+           if ((comp->c_flags & CF_TRUE) == 0  &&
+               (fmt->f_type == FT_GETMYMBOX || fmt->f_type == FT_GETMYADDR)) {
+               /* Fool FT_LS_ADDR into not producing an address. */
+               comp->c_mn = &fmt_mnull; comp->c_text = NULL;
+           }
            break;
        }
 
index 902e993..b096054 100644 (file)
@@ -825,7 +825,7 @@ m_unknown(m_getfld_state_t *gstate, FILE *iob)
      * separator) or the last char (since the matchc would have found it
      * if it was a real delim).
      */
-    s->pat_map = (char **) calloc (256, sizeof(char *));
+    s->pat_map = (char **) mh_xcalloc (256, sizeof(char *));
 
     for (cp = s->fdelim + 1; cp < s->delimend; cp++ )
        s->pat_map[(unsigned char)*cp] = cp;
index 9dece99..c6f75e0 100644 (file)
@@ -64,6 +64,24 @@ mh_xrealloc(void *ptr, size_t size)
     return memory;
 }
 
+/*
+ * Safely call calloc
+ */
+void *
+mh_xcalloc(size_t nmemb, size_t size)
+{
+    void *memory;
+
+    if (nmemb == 0  ||  size == 0)
+        adios(NULL, "Tried to calloc 0 bytes");
+
+    if ((memory = calloc(nmemb, size))) {
+        return memory;
+    } else {
+        adios(NULL, "calloc failed");
+    }
+}
+
 /*
  * Return the present working directory, if the current directory does not
  * exist, or is too long, make / the pwd.
index 753f92e..42f584c 100755 (executable)
@@ -31,9 +31,9 @@ myname="Random User <random@user.something.com>"
 
 #### Remove existing Local-Mailbox: profile component, if any.  Then
 #### add one.
-grep -v 'Local-Mailbox: ' ${MH} > ${MH}.new
-mv -f ${MH}.new ${MH}
-echo "Local-Mailbox: ${myname}" >> ${MH}
+grep -v 'Local-Mailbox: ' "$MH" > "$MH".new
+mv -f "$MH".new "$MH"
+echo "Local-Mailbox: ${myname}" >> "$MH"
 
 run_test "echo \
          `run_prog ${MH_LIBEXEC_DIR}/ap -format '%(mymbox{text})' "${myname}"`" \
@@ -50,4 +50,42 @@ run_test "echo \
          `run_prog ${MH_LIBEXEC_DIR}/ap -format '%(mymbox{text})' "${myname}"`" \
          1 "Local-Mailbox with Alternate-Mailbox test"
 
+# check getmymbox, without match
+run_test 'fmttest -message -format %(getmymbox{from}) first' ''
+
+# check getmyaddr, without match
+run_test 'fmttest -message -format %(getmyaddr{from}) first' ''
+
+grep -v 'Alternate-Mailboxes: ' "$MH" > "$MH".new
+mv -f "$MH".new "$MH"
+cat >>"$MH" <<EOF
+Alternate-Mailboxes: test1@example.com
+EOF
+
+# check getmymbox, with match
+run_test 'fmttest -message -format %(getmymbox{from}) first' \
+         'Test1 <test1@example.com>'
+
+# check getmyaddr, with match
+run_test 'fmttest -message -format %(getmyaddr{from}) first' \
+         'test1@example.com'
+
+# check getmymbox and getmyaddr, with match of other than first address in
+#   component
+cat >`mhpath new` <<'EOF'
+From: Test11 <test11@example.com>
+Cc: Test0 <test0@example.com>, Test3 <test3@example.com>,
+    Test1 <test1@example.com>, Test2 <test2@example.com>
+To: Some User <user@example.com>
+Date: Fri, 29 Sep 2006 00:00:00
+Message-Id: 11@test.nmh
+Subject: Testing message 11
+
+This is message number 11
+EOF
+run_test 'fmttest -message -format %(getmymbox{cc}) last' \
+         'Test1 <test1@example.com>'
+run_test 'fmttest -message -format %(getmyaddr{cc}) last' \
+         'test1@example.com'
+
 exit $failed
index 11e0551..3e20158 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 ######################################################
 #
-# Test the use of the Attach: header
+# Test the use of the Nmh-Attach: header
 #
 ######################################################
 
@@ -29,7 +29,7 @@ cat > "$draft" <<EOF
 To: Mr Test <mrtest@example.com>
 cc:
 Fcc: +outbox
-Attach: ${srcdir}/test/mhbuild/tiny.jpg
+Nmh-Attach: ${srcdir}/test/mhbuild/tiny.jpg
 ------
 EOF
 
@@ -59,8 +59,8 @@ cat > "$draft" <<EOF
 To: Mr Test <mrtest@example.com>
 cc:
 Fcc: +outbox
-Attach: ${srcdir}/test/mhbuild/tiny.jpg
-Attach: ${srcdir}/test/mhbuild/nulls
+Nmh-Attach: ${srcdir}/test/mhbuild/tiny.jpg
+Nmh-Attach: ${srcdir}/test/mhbuild/nulls
 ------
 This is a test
 EOF
@@ -108,7 +108,7 @@ cat > "$draft" <<EOF
 To: Mr Test <mrtest@example.com>
 cc:
 Fcc: +outbox
-Attach:
+Nmh-Attach:
 ------
 This is a test of an empty attach header.
 EOF
@@ -243,7 +243,7 @@ check "$draft" "$expected"
 
 #
 # Two parts, one attachment; make sure mixing of multiparts at different
-# levels works
+# levels works.  This also tests Attach: instead of Nmh-Attach:.
 #
 
 cat > "$draft" <<EOF
index cac5fb8..cc69aea 100755 (executable)
@@ -57,6 +57,7 @@ Usage: mhfixmsg [+folder] [msgs] [switches]
   -outfile file
   -rmmproc program
   -normmproc
+  -[no]changecur
   -[no]verbose
   -version
   -help
@@ -465,8 +466,7 @@ This is additional content before the first subpart of the multipart.
 
 ------- =_aaaaaaaaaa0
 Content-Type: multipart/related;
-    type="multipart/alternative";
-    boundary="subpart__1.1"
+    type="text/plain"; boundary="subpart__1.1"
 
 --subpart__1.1
 Content-Type: text/plain; charset="iso-8859-1"
@@ -515,8 +515,7 @@ This is additional content before the first subpart of the multipart.
 
 ------- =_aaaaaaaaaa0
 Content-Type: multipart/related;
-    type="multipart/alternative";
-    boundary="subpart__1.1"
+    type="text/plain"; boundary="subpart__1.1"
 
 --subpart__1.1
 Content-Type: text/plain; charset="iso-8859-1"
@@ -793,24 +792,16 @@ run_test 'mhfixmsg last -outfile '"$actual"' -verbose' \
 check "$expected" "$actual"
 
 
-# check that text/plain part is added to lone text/html in multipart/related
-prepare_space >"$expected" <<EOF
+# check that text/plain part is not added to text/html in multipart/related
+# when it shouldn't be
+cat >`mhpath new` <<EOF
 MIME-Version: 1.0
 Date: Tue, 26 Feb 2013 18:07:20 -0600
 Subject: multipart/related, not /alternative
 Content-Type: multipart/related;
-        boundary="----=_Part_90310_101292502.1"
+        type="text/html"; boundary="----=_Part_90310_101292502.1"
 
 ------=_Part_90310_101292502.1
-Content-Type: multipart/alternative; boundary="----=_nmh-multipart1"
-
-------=_nmh-multipart1
-Content-Type: text/plain; charset="us-ascii"
-Content-Transfer-Encoding: 7bit
-
-This is the real content.
-
-------=_nmh-multipart1
 Content-Type: text/html; charset="us-ascii"
 Content-Transfer-Encoding: 7bit
 
@@ -823,8 +814,6 @@ Content-Transfer-Encoding: 7bit
   </body>
 </html>
 
-------=_nmh-multipart1--
-
 ------=_Part_90310_101292502.1
 Content-Type: text/plain; charset="us-ascii"
 Content-Transfer-Encoding: 7bit
@@ -834,32 +823,7 @@ Your email client does not support HTML messages
 ------=_Part_90310_101292502.1--
 EOF
 
-cat >`mhpath new` <<EOF
-MIME-Version: 1.0
-Date: Tue, 26 Feb 2013 18:07:20 -0600
-Subject: multipart/related, not /alternative
-Content-Type: multipart/related;
-        boundary="----=_Part_90310_101292502.1"
-
-------=_Part_90310_101292502.1
-Content-Type: text/html; charset="us-ascii"
-Content-Transfer-Encoding: 7bit
-
-<html><head>
-    <meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
-    <title>HTML Content</title>
-  </head>
-  <body>
-    This is the real content.
-  </body>
-</html>
-------=_Part_90310_101292502.1
-Content-Type: text/plain; charset="us-ascii"
-Content-Transfer-Encoding: 7bit
-
-Your email client does not support HTML messages
-------=_Part_90310_101292502.1--
-EOF
+cp -p `mhpath last` "$expected"
 
 if [ $can_reformat_texthtml -eq 1 ]; then
   #### lynx inserts multiple blank lines, so squeeze them.
@@ -1065,6 +1029,58 @@ run_prog mhfixmsg last -replacetextplain -noreplacetextplain -outfile "$actual"
 check "$expected" "$actual"
 
 
+# check removal of extraneous trailing semicolon from header parameter list
+cat >"$expected" <<EOF
+To: recipient@example.com
+Subject: test
+From: sender@example.com
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa0"
+
+------- =_aaaaaaaaaa0
+Content-Type: text/plain; charset="us-ascii"
+
+test
+
+------- =_aaaaaaaaaa0
+Content-Type: text/plain; charset="us-ascii";
+ name="DATE"
+Content-Description: check folded headers, with and without trailing semicolon
+Content-Disposition: attachment;
+ filename="DATE"
+
+14 April 2014
+
+------- =_aaaaaaaaaa0--
+EOF
+
+cat >`mhpath new` <<EOF
+To: recipient@example.com
+Subject: test
+From: sender@example.com
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa0"
+
+------- =_aaaaaaaaaa0
+Content-Type: text/plain; charset="us-ascii";
+
+test
+
+------- =_aaaaaaaaaa0
+Content-Type: text/plain; charset="us-ascii";
+ name="DATE"
+Content-Description: check folded headers, with and without trailing semicolon
+Content-Disposition: attachment;
+ filename="DATE";
+
+14 April 2014
+
+------- =_aaaaaaaaaa0--
+EOF
+run_test 'mhfixmsg last -outfile '"$actual" ''
+check "$expected" "$actual"
+
+
 # check rmmproc
 cat >"$MH_TEST_DIR/Mail/rmmproc" <<'EOF'
 mv "$1" "$1.backup"
index 06dc474..fdf950a 100755 (executable)
@@ -212,7 +212,11 @@ check "$expected" 11.2.txt 'keep first'
 # check -nocheck, the default
 run_test 'mhstore last -part 2 -check -nocheck' \
          'storing message 11 part 2 as file 11.2.txt'
-check "$expected" 11.2.txt
+check "$expected" 11.2.txt 'keep first'
+
+# check -outfile - with -file
+run_prog mhstore -noverbose -file `mhpath 11` -part 2 -outfile - >"$actual" 2>&1
+check "$expected" "$actual"
 
 # check reassembly of message/partial messages (RFC 1521, 7.3.2)
 msgfile=`mhpath new`
index 63d99dd..e4856d0 100755 (executable)
@@ -29,8 +29,7 @@ Usage: repl: [+folder] [msg] [switches]
   switches are:
   -[no]group
   -[no]annotate
-  -cc all|to|cc|me
-  -nocc type
+  -[no]cc all|to|cc|me
   -draftfolder +folder
   -draftmessage msg
   -nodraftfolder
index b231a0e..b124c30 100644 (file)
--- a/uip/ap.c
+++ b/uip/ap.c
@@ -11,6 +11,7 @@
 #include <h/addrsbr.h>
 #include <h/fmt_scan.h>
 #include <h/mts.h>
+#include <h/utils.h>
 
 #define        NADDRS  100
 
@@ -154,7 +155,7 @@ process (char *arg, int length)
 
     (q = &pq)->pq_next = NULL;
     while ((cp = getname (arg))) {
-       if ((p = (struct pqpair *) calloc ((size_t) 1, sizeof(*p))) == NULL)
+       if ((p = (struct pqpair *) mh_xcalloc ((size_t) 1, sizeof(*p))) == NULL)
            adios (NULL, "unable to allocate pqpair memory");
        if ((mp = getm (cp, NULL, 0, error, sizeof(error))) == NULL) {
            p->pq_text = getcpy (cp);
index 71b226c..5375f8a 100644 (file)
@@ -165,7 +165,7 @@ main (int argc, char **argv)
     seq_setprev (mp);  /* set the previous-sequence */
 
     smsgs = (struct smsg *)
-       calloc ((size_t) (MAXFOLDER + 2), sizeof(*smsgs));
+       mh_xcalloc ((size_t) (MAXFOLDER + 2), sizeof(*smsgs));
     if (smsgs == NULL)
        adios (NULL, "unable to allocate burst storage");
 
index 66edece..f6d67a0 100644 (file)
@@ -164,7 +164,7 @@ mbx_read (FILE *fp, long pos, struct drop **drops, int noisy)
     char buffer[BUFSIZ];
     register struct drop *cp, *dp, *ep, *pp;
 
-    pp = (struct drop *) calloc ((size_t) (len = MAXFOLDER), sizeof(*dp));
+    pp = (struct drop *) mh_xcalloc ((size_t) (len = MAXFOLDER), sizeof(*dp));
     if (pp == NULL) {
        if (noisy)
            admonish (NULL, "unable to allocate drop storage");
@@ -504,7 +504,7 @@ map_read (char *file, long pos, struct drop **drops, int noisy)
     }
 
     msgp = mp->d_id;
-    dp = (struct drop *) calloc ((size_t) (msgp + 1), sizeof(*dp));
+    dp = (struct drop *) mh_xcalloc ((size_t) (msgp + 1), sizeof(*dp));
     if (dp == NULL) {
        close (md);
        return 0;
index 86dbfef..463c7ed 100644 (file)
@@ -210,6 +210,8 @@ dumpone(struct format *fmt)
        case FT_LS_FRIENDLY:
        case FT_PARSEADDR:
        case FT_MYMBOX:
+       case FT_GETMYMBOX:
+       case FT_GETMYADDR:
                printf(", c_name ");
                litputs(fmt->f_comp->c_name);
                if (fmt->f_comp->c_type)
@@ -413,6 +415,8 @@ f_typestr(int t)
        case FT_FORMATADDR: return("FORMATADDR");
        case FT_CONCATADDR: return("CONCATADDR");
        case FT_MYMBOX: return("MYMBOX");
+       case FT_GETMYMBOX: return("GETMYMBOX");
+       case FT_GETMYADDR: return("GETMYADDR");
        case FT_SAVESTR: return("SAVESTR");
 #ifdef FT_PAUSE
        case FT_PAUSE: return ("PAUSE");
index 90df1e9..a7742bd 100644 (file)
@@ -420,7 +420,7 @@ process_addresses(struct format *fmt, struct msgs_array *addrs,
     for (i = 0; i < addrs->size; i++) {
        (q = &pq)->pq_next = NULL;
        while ((cp = getname(addrs->msgs[i]))) {
-           if ((p = (struct pqpair *) calloc ((size_t) 1, sizeof(*p))) == NULL)
+           if ((p = (struct pqpair *) mh_xcalloc ((size_t) 1, sizeof(*p))) == NULL)
                adios (NULL, "unable to allocate pqpair memory");
            if ((mp = getm(cp, NULL, 0, error, sizeof(error))) == NULL) {
                p->pq_text = getcpy(cp);
@@ -846,6 +846,8 @@ dumpone(struct format *fmt)
        case FT_LS_FRIENDLY:
        case FT_PARSEADDR:
        case FT_MYMBOX:
+       case FT_GETMYMBOX:
+       case FT_GETMYADDR:
                printf(", c_name ");
                litputs(fmt->f_comp->c_name);
                if (fmt->f_comp->c_type)
@@ -1086,6 +1088,8 @@ f_typestr(int t)
        case FT_FORMATADDR: return("FORMATADDR");
        case FT_CONCATADDR: return("CONCATADDR");
        case FT_MYMBOX: return("MYMBOX");
+       case FT_GETMYMBOX: return("GETMYMBOX");
+       case FT_GETMYADDR: return("GETMYADDR");
 #ifdef FT_ADDTOSEQ
        case FT_ADDTOSEQ: return("ADDTOSEQ");
 #endif
@@ -1334,8 +1338,8 @@ mlistfree(void)
 {
     struct mailname *mp, *mp2;
 
-    for (mp = mq.m_next; mp; mp = mp2->m_next) {
-       mp2 = mp;
+    for (mp = mq.m_next; mp; mp = mp2) {
+       mp2 = mp->m_next;
        mnfree(mp);
     }
 }
index 2240f87..ce8e294 100644 (file)
@@ -208,6 +208,13 @@ query:
      */
     if ((out = fopen (defpath, "w")) == NULL)
        adios (defpath, "unable to write");
+    /*
+     * The main purpose of this first line is to fool file(1).
+     * Without it, if the first line of the profile is Path:,
+     * file 5.19 reports its type as message/news.  With it,
+     * it reports the type as text/plain.
+     */
+    fprintf (out, "MH-Profile-Version: 1.0\n");
     for (np = m_defs; np; np = np->n_next) {
        if (!np->n_context)
            fprintf (out, "%s: %s\n", np->n_name, np->n_field);
index 68c555d..e046886 100644 (file)
@@ -145,7 +145,7 @@ build_mime (char *infile, int autobuild, int dist, int directives,
     /*
      * Allocate space for primary (outside) content
      */
-    if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
+    if ((ct = (CT) mh_xcalloc (1, sizeof(*ct))) == NULL)
        adios (NULL, "out of memory");
 
     /*
@@ -205,7 +205,8 @@ build_mime (char *infile, int autobuild, int dist, int directives,
             * header; in that case, add it to our attach list
             */
 
-           if (strcasecmp(ATTACH_FIELD, np) == 0) {
+           if (strcasecmp(ATTACH_FIELD, np) == 0  ||
+               strcasecmp(ATTACH_FIELD_ALT, np) == 0) {
                struct attach_list *entry;
                char *s = vp, *e = vp + strlen(vp) - 1;
                free(np);
@@ -295,7 +296,7 @@ finish_field:
     ct->c_type = CT_MULTIPART;
     ct->c_subtype = MULTI_MIXED;
 
-    if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
+    if ((m = (struct multipart *) mh_xcalloc (1, sizeof(*m))) == NULL)
        adios (NULL, "out of memory");
     ct->c_ctparams = (void *) m;
     pp = &m->mp_parts;
@@ -315,7 +316,7 @@ finish_field:
        if (!p)
            continue;
 
-       if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
+       if ((part = (struct part *) mh_xcalloc (1, sizeof(*part))) == NULL)
            adios (NULL, "out of memory");
        *pp = part;
        pp = &part->mp_next;
@@ -336,7 +337,7 @@ finish_field:
            adios("reading", "Unable to open %s for", at_entry->filename);
        }
 
-       if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
+       if ((p = (CT) mh_xcalloc (1, sizeof(*p))) == NULL)
            adios(NULL, "out of memory");
 
        init_decoded_content(p, infile);
@@ -349,7 +350,7 @@ finish_field:
 
        setup_attach_content(p, at_entry->filename);
 
-       if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
+       if ((part = (struct part *) mh_xcalloc (1, sizeof(*part))) == NULL)
            adios (NULL, "out of memory");
        *pp = part;
        pp = &part->mp_next;
@@ -370,7 +371,7 @@ finish_field:
        struct part *part;
        struct text *t;
 
-       if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
+       if ((p = (CT) mh_xcalloc (1, sizeof(*p))) == NULL)
            adios(NULL, "out of memory");
 
        init_decoded_content(p, infile);
@@ -390,13 +391,13 @@ finish_field:
        p->c_begin = ftell(in);
        p->c_end = ftell(in);
 
-       if ((t = (struct text *) calloc (1, sizeof (*t))) == NULL)
+       if ((t = (struct text *) mh_xcalloc (1, sizeof (*t))) == NULL)
            adios (NULL, "out of memory");
 
        t->tx_charset = CHARSET_SPECIFIED;
        p->c_ctparams = t;
 
-       if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
+       if ((part = (struct part *) mh_xcalloc (1, sizeof(*part))) == NULL)
            adios (NULL, "out of memory");
        *pp = part;
        part->mp_part = p;
@@ -544,7 +545,7 @@ user_content (FILE *in, char *buf, CT *ctp, const char *infilename)
     }
 
     /* allocate basic Content structure */
-    if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
+    if ((ct = (CT) mh_xcalloc (1, sizeof(*ct))) == NULL)
        adios (NULL, "out of memory");
     *ctp = ct;
 
@@ -776,7 +777,7 @@ use_forw:
             * reference, we need to create another Content structure
             * for the message/external-body to wrap it in.
             */
-           if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
+           if ((ct = (CT) mh_xcalloc (1, sizeof(*ct))) == NULL)
                adios (NULL, "out of memory");
            init_decoded_content(ct, infilename);
            *ctp = ct;
@@ -785,7 +786,7 @@ use_forw:
            ct->c_type = CT_MESSAGE;
            ct->c_subtype = MESSAGE_EXTERNAL;
 
-           if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
+           if ((e = (struct exbody *) mh_xcalloc (1, sizeof(*e))) == NULL)
                adios (NULL, "out of memory");
            ct->c_ctparams = (void *) e;
 
@@ -895,7 +896,7 @@ use_forw:
            ct->c_type = CT_MULTIPART;
            ct->c_subtype = MULTI_DIGEST;
 
-           if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
+           if ((m = (struct multipart *) mh_xcalloc (1, sizeof(*m))) == NULL)
                adios (NULL, "out of memory");
            ct->c_ctparams = (void *) m;
            pp = &m->mp_parts;
@@ -906,7 +907,7 @@ use_forw:
                    CT p;
                    CE pe;
 
-                   if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
+                   if ((p = (CT) mh_xcalloc (1, sizeof(*p))) == NULL)
                        adios (NULL, "out of memory");
                    init_decoded_content (p, infilename);
                    pe = &p->c_cefile;
@@ -920,7 +921,7 @@ use_forw:
                    if (listsw && stat (pe->ce_file, &st) != NOTOK)
                        p->c_end = (long) st.st_size;
 
-                   if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
+                   if ((part = (struct part *) mh_xcalloc (1, sizeof(*part))) == NULL)
                        adios (NULL, "out of memory");
                    *pp = part;
                    pp = &part->mp_next;
@@ -981,7 +982,7 @@ use_forw:
        ct->c_type = CT_MULTIPART;
        ct->c_subtype = vrsn;
 
-       if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
+       if ((m = (struct multipart *) mh_xcalloc (1, sizeof(*m))) == NULL)
            adios (NULL, "out of memory");
        ct->c_ctparams = (void *) m;
 
@@ -998,7 +999,7 @@ use_forw:
            if (!p)
                continue;
 
-           if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
+           if ((part = (struct part *) mh_xcalloc (1, sizeof(*part))) == NULL)
                adios (NULL, "out of memory");
            *pp = part;
            pp = &part->mp_next;
index 4d5083c..8acd200 100644 (file)
@@ -30,6 +30,8 @@
     X("outfile file", 0, OUTFILESW) \
     X("rmmproc program", 0, RPROCSW) \
     X("normmproc", 0, NRPRCSW) \
+    X("changecur", 0, CHGSW) \
+    X("nochangecur", 0, NCHGSW) \
     X("verbose", 0, VERBSW) \
     X("noverbose", 0, NVERBSW) \
     X("version", 0, VERSIONSW) \
@@ -53,6 +55,9 @@ int debugsw; /* Needed by mhparse.c. */
 extern int skip_mp_cte_check;                 /* flag to InitMultiPart */
 extern int suppress_bogus_mp_content_warning; /* flag to InitMultiPart */
 extern int bogus_mp_content;                  /* flag from InitMultiPart */
+/* flags to/from parse_header_attrs */
+extern int suppress_extraneous_trailing_semicolon_warning;
+extern int extraneous_trailing_semicolon;
 
 /* mhoutsbr.c */
 int output_message (CT, char *);
@@ -83,6 +88,8 @@ static int replace_boundary (CT, char *, char *);
 static int fix_multipart_cte (CT, int *);
 static int set_ce (CT, int);
 static int ensure_text_plain (CT *, CT, int *, int);
+static int find_textplain_sibling (CT, int, int *);
+static int insert_new_text_plain_part (CT, int, CT);
 static CT build_text_plain_part (CT);
 static CT divide_part (CT);
 static void copy_ctinfo (CI, CI);
@@ -97,6 +104,7 @@ static int decode_text_parts (CT, int, int *);
 static int content_encoding (CT, const char **);
 static int strip_crs (CT, int *);
 static int convert_charsets (CT, char *, int *);
+static int fix_always (CT, int *);
 static int write_content (CT, char *, char *, int, int);
 static int remove_file (char *);
 static void report (char *, char *, char *, char *, ...);
@@ -114,6 +122,7 @@ main (int argc, char **argv) {
     CT *ctp;
     FILE *fp;
     int using_stdin = 0;
+    int chgflag = 1;
     int status = OK;
     fix_transformations fx;
     fx.reformat = fx.fixcte = fx.fixboundary = 1;
@@ -212,6 +221,12 @@ main (int argc, char **argv) {
             case NRPRCSW:
                 rmmproc = NULL;
                 continue;
+            case CHGSW:
+                chgflag = 1;
+                continue;
+            case NCHGSW:
+                chgflag = 0;
+                continue;
             case VERBSW:
                 verbosw = 1;
                 continue;
@@ -247,6 +262,7 @@ main (int argc, char **argv) {
     }
 
     suppress_bogus_mp_content_warning = skip_mp_cte_check = 1;
+    suppress_extraneous_trailing_semicolon_warning = 1;
 
     if (! context_find ("path"))
         free (path ("./", TFOLDER));
@@ -285,7 +301,7 @@ main (int argc, char **argv) {
             }
         }
 
-        if (! (cts = (CT *) calloc ((size_t) 2, sizeof *cts))) {
+        if (! (cts = (CT *) mh_xcalloc ((size_t) 2, sizeof *cts))) {
             adios (NULL, "out of memory");
         }
         ctp = cts;
@@ -320,7 +336,8 @@ main (int argc, char **argv) {
                 done (1);
         seq_setprev (mp);       /* set the previous-sequence */
 
-        if (! (cts = (CT *) calloc ((size_t) (mp->numsel + 1), sizeof *cts))) {
+        if (! (cts =
+               (CT *) mh_xcalloc ((size_t) (mp->numsel + 1), sizeof *cts))) {
             adios (NULL, "out of memory");
         }
         ctp = cts;
@@ -334,7 +351,9 @@ main (int argc, char **argv) {
             }
         }
 
-        seq_setcur (mp, mp->hghsel);      /* update current message */
+        if (chgflag) {
+            seq_setcur (mp, mp->hghsel);  /* update current message */
+        }
         seq_save (mp);                    /* synchronize sequences  */
         context_replace (pfolder, folder);/* update current folder  */
         context_save ();                  /* save the context file  */
@@ -391,6 +410,7 @@ mhfixmsgsbr (CT *ctp, const fix_transformations *fx, char *outfile) {
     }
 
     reverse_alternative_parts (*ctp);
+    status = fix_always (*ctp, &message_mods);
     if (status == OK  &&  fx->fixboundary) {
         status = fix_boundary (ctp, &message_mods);
     }
@@ -408,7 +428,7 @@ mhfixmsgsbr (CT *ctp, const fix_transformations *fx, char *outfile) {
         status = convert_charsets (*ctp, fx->textcharset, &message_mods);
     }
 
-    if (! (*ctp)->c_umask) {
+    if (status == OK  &&  ! (*ctp)->c_umask) {
         /* Set the umask for the contents file.  This currently
            isn't used but just in case it is in the future. */
         struct stat st;
@@ -462,7 +482,7 @@ fix_boundary (CT *ct, int *message_mods) {
     struct multipart *mp;
     int status = OK;
 
-    if (bogus_mp_content) {
+    if (ct  &&  (*ct)->c_type == CT_MULTIPART  &&  bogus_mp_content) {
         mp = (struct multipart *) (*ct)->c_ctparams;
 
         /*
@@ -497,6 +517,7 @@ fix_boundary (CT *ct, int *message_mods) {
                                         "fix multipart boundary");
                             }
                         } else {
+                            *ct = NULL;
                             advise (NULL, "unable to parse fixed part");
                             status = NOTOK;
                         }
@@ -829,69 +850,72 @@ ensure_text_plain (CT *ct, CT parent, int *message_mods, int replacetextplain) {
 
     switch ((*ct)->c_type) {
     case CT_TEXT: {
-        int has_text_plain = 0;
-
         /* Nothing to do for text/plain. */
         if ((*ct)->c_subtype == TEXT_PLAIN) { return OK; }
 
         if (parent  &&  parent->c_type == CT_MULTIPART  &&
             parent->c_subtype == MULTI_ALTERNATE) {
-            struct multipart *mp = (struct multipart *) parent->c_ctparams;
-            struct part *part, *prev;
             int new_subpart_number = 1;
+            int has_text_plain =
+                find_textplain_sibling (parent, replacetextplain,
+                                        &new_subpart_number);
 
-            /* See if there is a sibling text/plain. */
-            for (prev = part = mp->mp_parts; part; part = part->mp_next) {
-                ++new_subpart_number;
-                if (part->mp_part->c_type == CT_TEXT  &&
-                    part->mp_part->c_subtype == TEXT_PLAIN) {
-                    if (replacetextplain) {
-                        struct part *old_part;
-                        if (part == mp->mp_parts) {
-                            old_part = mp->mp_parts;
-                            mp->mp_parts = part->mp_next;
-                        } else {
-                            old_part = prev->mp_next;
-                            prev->mp_next = part->mp_next;
-                        }
-                        if (verbosw) {
-                            report (NULL, parent->c_partno, parent->c_file,
-                                    "remove text/plain part %s",
-                                    old_part->mp_part->c_partno);
-                        }
-                        free_content (old_part->mp_part);
-                        free (old_part);
-                    } else {
-                        has_text_plain = 1;
+            if (! has_text_plain) {
+                /* Parent is a multipart/alternative.  Insert a new
+                   text/plain subpart. */
+                const int inserted =
+                    insert_new_text_plain_part (*ct, new_subpart_number,
+                                                parent);
+                if (inserted) {
+                    ++*message_mods;
+                    if (verbosw) {
+                        report (NULL, parent->c_partno, parent->c_file,
+                                "insert text/plain part");
                     }
-                    break;
+                } else {
+                    status = NOTOK;
                 }
-                prev = part;
+            }
+        } else if (parent  &&  parent->c_type == CT_MULTIPART  &&
+            parent->c_subtype == MULTI_RELATED) {
+            char *type_subtype =
+                concat ((*ct)->c_ctinfo.ci_type, "/",
+                        (*ct)->c_ctinfo.ci_subtype, NULL);
+            const char *parent_type =
+                get_param (parent->c_ctinfo.ci_first_pm, "type", '?', 1);
+            int new_subpart_number = 1;
+            int has_text_plain = 0;
+
+            /* Have to do string comparison on the subtype because we
+               don't enumerate all of them in c_subtype values.
+               parent_type will be NULL if the multipart/related part
+               doesn't have a type parameter.  The type parameter must
+               be specified according to RFC 2387 Sec. 3.1 but not all
+               messages comply. */
+            if (parent_type  &&  strcasecmp (type_subtype, parent_type) == 0) {
+                /* The type of this part matches the root type of the
+                   parent multipart/related.  Look to see if there's
+                   text/plain sibling. */
+                has_text_plain =
+                    find_textplain_sibling (parent, replacetextplain,
+                                            &new_subpart_number);
             }
 
+            free (type_subtype);
+
             if (! has_text_plain) {
                 /* Parent is a multipart/alternative.  Insert a new
                    text/plain subpart. */
-                struct part *new_part = mh_xmalloc (sizeof *new_part);
-
-                if ((new_part->mp_part = build_text_plain_part (*ct))) {
-                    char buffer[16];
-                    snprintf (buffer, sizeof buffer, "%d", new_subpart_number);
-
-                    new_part->mp_next = mp->mp_parts;
-                    mp->mp_parts = new_part;
-                    new_part->mp_part->c_partno =
-                        concat (parent->c_partno ? parent->c_partno : "1", ".",
-                                buffer, NULL);
-
+                const int inserted =
+                    insert_new_text_plain_part (*ct, new_subpart_number,
+                                                parent);
+                if (inserted) {
                     ++*message_mods;
                     if (verbosw) {
                         report (NULL, parent->c_partno, parent->c_file,
                                 "insert text/plain part");
                     }
                 } else {
-                    free_content (new_part->mp_part);
-                    free (new_part);
                     status = NOTOK;
                 }
             }
@@ -946,9 +970,8 @@ ensure_text_plain (CT *ct, CT parent, int *message_mods, int replacetextplain) {
 
     case CT_MESSAGE:
         if ((*ct)->c_subtype == MESSAGE_EXTERNAL) {
-            struct exbody *e;
+            struct exbody *e = (struct exbody *) (*ct)->c_ctparams;
 
-            e = (struct exbody *) (*ct)->c_ctparams;
             status = ensure_text_plain (&e->eb_content, *ct, message_mods,
                                         replacetextplain);
         }
@@ -959,6 +982,71 @@ ensure_text_plain (CT *ct, CT parent, int *message_mods, int replacetextplain) {
 }
 
 
+/* See if there is a sibling text/plain. */
+static int
+find_textplain_sibling (CT parent, int replacetextplain,
+                        int *new_subpart_number) {
+    struct multipart *mp = (struct multipart *) parent->c_ctparams;
+    struct part *part, *prev;
+    int has_text_plain = 0;
+
+    for (prev = part = mp->mp_parts; part; part = part->mp_next) {
+        ++*new_subpart_number;
+        if (part->mp_part->c_type == CT_TEXT  &&
+            part->mp_part->c_subtype == TEXT_PLAIN) {
+            if (replacetextplain) {
+                struct part *old_part;
+                if (part == mp->mp_parts) {
+                    old_part = mp->mp_parts;
+                    mp->mp_parts = part->mp_next;
+                } else {
+                    old_part = prev->mp_next;
+                    prev->mp_next = part->mp_next;
+                }
+                if (verbosw) {
+                    report (NULL, parent->c_partno, parent->c_file,
+                            "remove text/plain part %s",
+                            old_part->mp_part->c_partno);
+                }
+                free_content (old_part->mp_part);
+                free (old_part);
+            } else {
+                has_text_plain = 1;
+            }
+            break;
+        }
+        prev = part;
+    }
+
+    return has_text_plain;
+}
+
+
+static int
+insert_new_text_plain_part (CT ct, int new_subpart_number, CT parent) {
+    struct multipart *mp = (struct multipart *) parent->c_ctparams;
+    struct part *new_part = mh_xmalloc (sizeof *new_part);
+
+    if ((new_part->mp_part = build_text_plain_part (ct))) {
+        char buffer[16];
+        snprintf (buffer, sizeof buffer, "%d", new_subpart_number);
+
+        new_part->mp_next = mp->mp_parts;
+        mp->mp_parts = new_part;
+        new_part->mp_part->c_partno =
+            concat (parent->c_partno ? parent->c_partno : "1", ".",
+                    buffer, NULL);
+
+        return 1;
+    } else {
+        free_content (new_part->mp_part);
+        free (new_part);
+
+        return 0;
+    }
+}
+
+
 static CT
 build_text_plain_part (CT encoded_part) {
     CT tp_part = divide_part (encoded_part);
@@ -985,7 +1073,7 @@ build_text_plain_part (CT encoded_part) {
     }
 
     free_content (tp_part);
-    (void) m_unlink (tmp_plain_file);
+    if (tmp_plain_file) { (void) m_unlink (tmp_plain_file); }
     free (tmp_plain_file);
 
     return NULL;
@@ -996,7 +1084,7 @@ static CT
 divide_part (CT ct) {
     CT new_part;
 
-    if ((new_part = (CT) calloc (1, sizeof *new_part)) == NULL)
+    if ((new_part = (CT) mh_xcalloc (1, sizeof *new_part)) == NULL)
         adios (NULL, "out of memory");
 
     /* Just copy over what is needed for decoding.  c_vrsn and
@@ -1130,8 +1218,10 @@ reformat_part (CT ct, char *file, char *type, char *subtype, int c_type) {
 /* Identifies 7bit or 8bit content based on charset. */
 static int
 charset_encoding (CT ct) {
-    int encoding =
-        strcasecmp (content_charset (ct), "US-ASCII")  ?  CE_8BIT  :  CE_7BIT;
+    char *ct_charset = content_charset (ct);
+    int encoding = strcasecmp (ct_charset, "US-ASCII")  ?  CE_8BIT  :  CE_7BIT;
+
+    free (ct_charset);
 
     return encoding;
 }
@@ -1148,11 +1238,11 @@ build_multipart_alt (CT first_alt, CT new_part, int type, int subtype) {
     struct multipart *m;
     const struct str2init *ctinit;
 
-    if ((ct = (CT) calloc (1, sizeof *ct)) == NULL)
+    if ((ct = (CT) mh_xcalloc (1, sizeof *ct)) == NULL)
         adios (NULL, "out of memory");
 
     /* Set up the multipart/alternative part.  These fields of *ct were
-       initialized to 0 by calloc():
+       initialized to 0 by mh_xcalloc():
        c_fp, c_unlink, c_begin, c_end,
        c_vrsn, c_ctline, c_celine,
        c_id, c_descr, c_dispo, c_partno,
@@ -1249,20 +1339,20 @@ build_multipart_alt (CT first_alt, CT new_part, int type, int subtype) {
     }
 
     add_param(&ct->c_ctinfo.ci_first_pm, &ct->c_ctinfo.ci_last_pm,
-             "boundary", boundary, 0);
+              "boundary", boundary, 0);
 
     p = (struct part *) mh_xmalloc (sizeof *p);
     p->mp_next = (struct part *) mh_xmalloc (sizeof *p->mp_next);
     p->mp_next->mp_next = NULL;
     p->mp_next->mp_part = first_alt;
 
-    if ((m = (struct multipart *) calloc (1, sizeof (struct multipart))) ==
+    if ((m = (struct multipart *) mh_xcalloc (1, sizeof (struct multipart))) ==
         NULL)
         adios (NULL, "out of memory");
     m->mp_start = concat (boundary, "\n", NULL);
     m->mp_stop = concat (boundary, "--\n", NULL);
     m->mp_parts = p;
-    ct->c_ctparams = (void *) m;
+    ct->c_ctparams = m;
 
     free (boundary);
 
@@ -1482,9 +1572,8 @@ decode_text_parts (CT ct, int encoding, int *message_mods) {
 
     case CT_MESSAGE:
         if (ct->c_subtype == MESSAGE_EXTERNAL) {
-            struct exbody *e;
+            struct exbody *e = (struct exbody *) ct->c_ctparams;
 
-            e = (struct exbody *) ct->c_ctparams;
             status = decode_text_parts (e->eb_content, encoding, message_mods);
         }
         break;
@@ -1701,6 +1790,8 @@ strip_crs (CT ct, int *message_mods) {
         }
     }
 
+    free (charset);
+
     return status;
 }
 
@@ -1715,14 +1806,18 @@ convert_charsets (CT ct, char *dest_charset, int *message_mods) {
             status = convert_charset (ct, dest_charset, message_mods);
             if (status == OK) {
                 if (verbosw) {
+                    char *ct_charset = content_charset (ct);
+
                     report (NULL, ct->c_partno, ct->c_file,
-                            "convert %s to %s",
-                            content_charset(ct), dest_charset);
+                            "convert %s to %s", ct_charset, dest_charset);
+                    free (ct_charset);
                 }
             } else {
+                char *ct_charset = content_charset (ct);
+
                 report ("iconv", ct->c_partno, ct->c_file,
-                        "failed to convert %s to %s",
-                        content_charset(ct), dest_charset);
+                        "failed to convert %s to %s", ct_charset, dest_charset);
+                free (ct_charset);
             }
         }
         break;
@@ -1742,9 +1837,8 @@ convert_charsets (CT ct, char *dest_charset, int *message_mods) {
 
     case CT_MESSAGE:
         if (ct->c_subtype == MESSAGE_EXTERNAL) {
-            struct exbody *e;
+            struct exbody *e = (struct exbody *) ct->c_ctparams;
 
-            e = (struct exbody *) ct->c_ctparams;
             status =
                 convert_charsets (e->eb_content, dest_charset, message_mods);
         }
@@ -1758,6 +1852,73 @@ convert_charsets (CT ct, char *dest_charset, int *message_mods) {
 }
 
 
+/*
+ * Fix various problems that aren't handled elsewhere.  These
+ * are fixed unconditionally:  there are no switches to disable
+ * them.  (Currently, "problems" is just one:  an extraneous
+ * semicolon at the end of a header parameter list.)
+ */
+static int
+fix_always (CT ct, int *message_mods) {
+    int status = OK;
+
+    switch (ct->c_type) {
+    case CT_MULTIPART: {
+        struct multipart *m = (struct multipart *) ct->c_ctparams;
+        struct part *part;
+
+        for (part = m->mp_parts; status == OK  &&  part; part = part->mp_next) {
+            status = fix_always (part->mp_part, message_mods);
+        }
+        break;
+    }
+
+    case CT_MESSAGE:
+        if (ct->c_subtype == MESSAGE_EXTERNAL) {
+            struct exbody *e = (struct exbody *) ct->c_ctparams;
+
+            status = fix_always (e->eb_content, message_mods);
+        }
+        break;
+
+    default: {
+        HF hf;
+
+        for (hf = ct->c_first_hf; hf; hf = hf->next) {
+            size_t len = strlen (hf->value);
+
+            /* whitespace following a trailing ';' will be nuked as well */
+            if (hf->value[len - 1] == '\n')
+                while (isspace((unsigned char)(hf->value[len - 2]))) len--;
+
+            if (hf->value[len - 2] == ';') {
+                /* Remove trailing ';' from parameter value. */
+                hf->value[len - 2] = '\n';
+                hf->value[len - 1] = '\0';
+
+                /* Also, if Content-Type parameter, remove trailing ';'
+                   from ct->c_ctline.  This probably isn't necessary
+                   but can't hurt. */
+                if (strcasecmp(hf->name, "Content-Type") == 0 && ct->c_ctline) {
+                    size_t l = strlen(ct->c_ctline) - 1;
+                    while (isspace((unsigned char)(ct->c_ctline[l])) || ct->c_ctline[l] == ';')
+                        ct->c_ctline[l--] = '\0';
+                }
+
+                ++*message_mods;
+                if (verbosw) {
+                    report (NULL, ct->c_partno, ct->c_file,
+                            "remove trailing ; from %s parameter value",
+                            hf->name);
+                }
+            }
+        }
+    }}
+
+    return status;
+}
+
+
 static int
 write_content (CT ct, char *input_filename, char *outfile, int modify_inplace,
                int message_mods) {
index f2726d0..89829be 100644 (file)
@@ -307,9 +307,10 @@ freects_done (int status)
 {
     CT *ctp;
 
-    if ((ctp = cts))
-       for (; *ctp; ctp++)
-           free_content (*ctp);
+    for (ctp = cts; ctp && *ctp; ctp++)
+       free_content (*ctp);
+
+    free (cts);
 
     exit (status);
 }
index f09c147..91b0984 100644 (file)
@@ -248,7 +248,7 @@ do_cache:
      * check if message is coming from file
      */
     if (file) {
-       if (!(cts = (CT *) calloc ((size_t) 2, sizeof(*cts))))
+       if (!(cts = (CT *) mh_xcalloc ((size_t) 2, sizeof(*cts))))
            adios (NULL, "out of memory");
        ctp = cts;
 
@@ -281,7 +281,7 @@ do_cache:
                done (1);
        seq_setprev (mp);       /* set the previous-sequence */
 
-       if (!(cts = (CT *) calloc ((size_t) (mp->numsel + 1), sizeof(*cts))))
+       if (!(cts = (CT *) mh_xcalloc ((size_t) (mp->numsel + 1), sizeof(*cts))))
            adios (NULL, "out of memory");
        ctp = cts;
 
index 8d8fea1..28d4894 100644 (file)
@@ -778,7 +778,7 @@ evalvar (struct mcomp *c1)
            return 1;
        }
 
-       args = (struct arglist *) calloc((size_t) 1, sizeof(struct arglist));
+       args = (struct arglist *) mh_xcalloc ((size_t) 1, sizeof(struct arglist));
 
        if (arglist_tail)
            arglist_tail->a_next = args;
@@ -1184,7 +1184,7 @@ mcomp_format (struct mcomp *c1, struct mcomp *c2)
 
     (q = &pq)->pq_next = NULL;
     while ((cp = getname (ap))) {
-       if ((p = (struct pqpair *) calloc ((size_t) 1, sizeof(*p))) == NULL)
+       if ((p = (struct pqpair *) mh_xcalloc ((size_t) 1, sizeof(*p))) == NULL)
            adios (NULL, "unable to allocate pqpair memory");
        else {
            if ((mp = getm (cp, NULL, 0, error, sizeof(error))) == NULL) {
@@ -1240,7 +1240,7 @@ add_queue (struct mcomp **head, struct mcomp **tail, char *name, char *text, int
 {
     struct mcomp *c1;
 
-    if ((c1 = (struct mcomp *) calloc ((size_t) 1, sizeof(*c1))) == NULL)
+    if ((c1 = (struct mcomp *) mh_xcalloc ((size_t) 1, sizeof(*c1))) == NULL)
        adios (NULL, "unable to allocate comp memory");
     else {
        c1->c_flags = flags & ~INIT;
index 7363688..39d8693 100644 (file)
--- a/uip/mhn.c
+++ b/uip/mhn.c
@@ -119,12 +119,6 @@ int part_ok (CT, int);
 int type_ok (CT, int);
 void flush_errors (void);
 
-/* mhstoresbr.c */
-typedef struct mhstoreinfo *mhstoreinfo_t;
-mhstoreinfo_t mhstoreinfo_create(CT *, char *, const char *, int, int);
-void mhstoreinfo_free(mhstoreinfo_t);
-void store_all_messages (mhstoreinfo_t);
-
 /* mhcachesbr.c */
 void cache_all_messages (CT *);
 
@@ -446,7 +440,7 @@ do_cache:
      * check if message is coming from file
      */
     if (file) {
-       if (!(cts = (CT *) calloc ((size_t) 2, sizeof(*cts))))
+       if (!(cts = (CT *) mh_xcalloc ((size_t) 2, sizeof(*cts))))
            adios (NULL, "out of memory");
        ctp = cts;
 
@@ -479,7 +473,7 @@ do_cache:
                done (1);
        seq_setprev (mp);       /* set the previous-sequence */
 
-       if (!(cts = (CT *) calloc ((size_t) (mp->numsel + 1), sizeof(*cts))))
+       if (!(cts = (CT *) mh_xcalloc ((size_t) (mp->numsel + 1), sizeof(*cts))))
            adios (NULL, "out of memory");
        ctp = cts;
 
index ca1acf8..0c0a297 100644 (file)
@@ -407,6 +407,7 @@ three_print:
        putc ('\n', out);
 
     (*ct->c_ceclosefnx) (ct);
+    free (bufp);
     return OK;
 }
 
index 548a950..96af58e 100644 (file)
@@ -34,10 +34,14 @@ int checksw = 0;    /* check Content-MD5 field */
  * 1) Instruct parser not to detect invalid Content-Transfer-Encoding
  *    in a multipart.
  * 2) Suppress the warning about bogus multipart content, and report it.
+ * 3) Suppress the warning about extraneous trailing ';' in header parameter
+ *    lists, and report it.
  */
 int skip_mp_cte_check;
 int suppress_bogus_mp_content_warning;
 int bogus_mp_content;
+int suppress_extraneous_trailing_semicolon_warning;
+int extraneous_trailing_semicolon;
 
 /*
  * Structures for TEXT messages
@@ -59,6 +63,7 @@ struct k2v SubMultiPart[] = {
     { "alternative", MULTI_ALTERNATE },
     { "digest",      MULTI_DIGEST },
     { "parallel",    MULTI_PARALLEL },
+    { "related",     MULTI_RELATED },
     { NULL,          MULTI_UNKNOWN }    /* this one must be last! */
 };
 
@@ -283,7 +288,7 @@ get_content (FILE *in, char *file, int toplevel)
     m_getfld_state_t gstate = 0;
 
     /* allocate the content structure */
-    if (!(ct = (CT) calloc (1, sizeof(*ct))))
+    if (!(ct = (CT) mh_xcalloc (1, sizeof(*ct))))
        adios (NULL, "out of memory");
 
     ct->c_fp = in;
@@ -1018,7 +1023,7 @@ InitText (CT ct)
     ct->c_subtype = kv->kv_value;
 
     /* allocate text character set structure */
-    if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
+    if ((t = (struct text *) mh_xcalloc (1, sizeof(*t))) == NULL)
        adios (NULL, "out of memory");
     ct->c_ctparams = (void *) t;
 
@@ -1126,7 +1131,7 @@ InitMultiPart (CT ct)
     }
 
     /* allocate primary structure for multipart info */
-    if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
+    if ((m = (struct multipart *) mh_xcalloc (1, sizeof(*m))) == NULL)
        adios (NULL, "out of memory");
     ct->c_ctparams = (void *) m;
 
@@ -1171,7 +1176,7 @@ InitMultiPart (CT ct)
            if (strcmp (bufp + 2, m->mp_start))
                continue;
 next_part:
-           if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
+           if ((part = (struct part *) mh_xcalloc (1, sizeof(*part))) == NULL)
                adios (NULL, "out of memory");
            *next = part;
            next = &part->mp_next;
@@ -1179,7 +1184,6 @@ next_part:
            if (!(p = get_content (fp, ct->c_file,
                        ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
                free(bufp);
-               fclose (ct->c_fp);
                ct->c_fp = NULL;
                return NOTOK;
            }
@@ -1354,7 +1358,7 @@ InitMessage (CT ct)
                PM pm;
                struct partial *p;
 
-               if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
+               if ((p = (struct partial *) mh_xcalloc (1, sizeof(*p))) == NULL)
                    adios (NULL, "out of memory");
                ct->c_ctparams = (void *) p;
 
@@ -1403,7 +1407,7 @@ invalid_param:
                CT p;
                FILE *fp;
 
-               if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
+               if ((e = (struct exbody *) mh_xcalloc (1, sizeof(*e))) == NULL)
                    adios (NULL, "out of memory");
                ct->c_ctparams = (void *) e;
 
@@ -2127,6 +2131,7 @@ ready_to_go:
       fclose (ct->c_fp);
       ct->c_fp = NULL;
     }
+    free (bufp);
     return fileno (ce->ce_fp);
 
 clean_up:
@@ -2135,6 +2140,7 @@ clean_up:
       fclose (ct->c_fp);
       ct->c_fp = NULL;
     }
+    free (bufp);
     return NOTOK;
 }
 
@@ -3148,6 +3154,8 @@ ct_subtype_str (int type, int subtype) {
             return "digest";
         case MULTI_PARALLEL:
             return "parallel";
+        case MULTI_RELATED:
+            return "related";
         default:
             return "unknown_multipart_subtype";
         }
@@ -3272,10 +3280,13 @@ parse_header_attrs (const char *filename, const char *fieldname,
         }
 
        if (*cp == 0) {
-           advise (NULL,
-                   "extraneous trailing ';' in message %s's %s: "
-                    "parameter list",
-                   filename, fieldname);
+           if (! suppress_extraneous_trailing_semicolon_warning) {
+               advise (NULL,
+                       "extraneous trailing ';' in message %s's %s: "
+                       "parameter list",
+                       filename, fieldname);
+           }
+           extraneous_trailing_semicolon = 1;
            return DONE;
        }
 
@@ -3550,6 +3561,7 @@ bad_quote:
                                "%s's %s: field\n%*s(parameter %s)", sp->index,
                                filename, fieldname, strlen(invo_name) + 2, "",
                                nameptr);
+                       free (nameptr);
                        return NOTOK;
                    }
                    if (sp2->index < sp->index &&
@@ -3565,6 +3577,7 @@ bad_quote:
                           "param in message %s's %s: field\n%*s(parameter %s)",
                           filename, fieldname, strlen(invo_name) + 2, "",
                           nameptr);
+                   free (nameptr);
                    return NOTOK;
                }
            }
@@ -3640,21 +3653,16 @@ bad_quote:
 }
 
 /*
- * Return the charset for a particular content type.  Return pointer is
- * only valid until the next call to content_charset().
+ * Return the charset for a particular content type.
  */
 
 char *
 content_charset (CT ct) {
-    static char *ret_charset = NULL;
-
-    if (ret_charset != NULL) {
-       free(ret_charset);
-    }
+    char *ret_charset = NULL;
 
     ret_charset = get_param(ct->c_ctinfo.ci_first_pm, "charset", '?', 0);
 
-    return ret_charset ? ret_charset : "US-ASCII";
+    return ret_charset ? ret_charset : getcpy ("US-ASCII");
 }
 
 
index 98cab9d..0227d0c 100644 (file)
@@ -314,7 +314,7 @@ do_cache:
      * check if message is coming from file
      */
     if (file) {
-       if (!(cts = (CT *) calloc ((size_t) 2, sizeof(*cts))))
+       if (!(cts = (CT *) mh_xcalloc ((size_t) 2, sizeof(*cts))))
            adios (NULL, "out of memory");
        ctp = cts;
 
@@ -358,7 +358,7 @@ do_cache:
        seq_setprev (mp);       /* set the Previous-Sequence */
        seq_setunseen (mp, 1);  /* unset the Unseen-Sequence */
 
-       if (!(cts = (CT *) calloc ((size_t) (mp->numsel + 1), sizeof(*cts))))
+       if (!(cts = (CT *) mh_xcalloc ((size_t) (mp->numsel + 1), sizeof(*cts))))
            adios (NULL, "out of memory");
        ctp = cts;
 
index 6236352..986ffb9 100644 (file)
@@ -354,10 +354,12 @@ show_content_aux (CT ct, int alternate, char *cp, char *cracked, struct format *
             if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
                 return NOTOK;
         } else {
+            char *charset = content_charset (ct);
             admonish (NULL, "unable to convert character set%s%s to %s",
                       ct->c_partno  ?  " of part "  :  "",
                       ct->c_partno  ?  ct->c_partno  :  "",
-                      content_charset (ct));
+                      charset);
+            free (charset);
         }
     }
 
@@ -567,7 +569,7 @@ show_multi_internal (CT ct, int alternate, int concatsw, int textonly,
     int        alternating, nowalternate, result;
     struct multipart *m = (struct multipart *) ct->c_ctparams;
     struct part *part;
-    int any_part_ok;
+    int request_matched;
     CT p;
 
     alternating = 0;
@@ -578,13 +580,13 @@ show_multi_internal (CT ct, int alternate, int concatsw, int textonly,
        alternating  = 1;
     }
 
-/*
- * alternate   -> we are a part inside an multipart/alternative
- * alternating -> we are a multipart/alternative
- */
+    /*
+     * alternate   -> we are a part inside an multipart/alternative
+     * alternating -> we are a multipart/alternative
+     */
 
     result = alternate ? NOTOK : OK;
-    any_part_ok = 0;
+    request_matched = 0;
 
     for (part = m->mp_parts; part; part = part->mp_next) {
        p = part->mp_part;
@@ -592,7 +594,7 @@ show_multi_internal (CT ct, int alternate, int concatsw, int textonly,
        if (part_ok (p, 1) && type_ok (p, 1)) {
            int inneresult;
 
-           any_part_ok = 1;
+           request_matched = 1;
 
            inneresult = show_switch (p, nowalternate, concatsw, textonly,
                                      inlineonly, fmt);
@@ -621,7 +623,7 @@ show_multi_internal (CT ct, int alternate, int concatsw, int textonly,
        }
     }
 
-    if (alternating && !part && any_part_ok) {
+    if (alternating && !part && request_matched) {
        if (!alternate)
            content_error (NULL, ct, "don't know how to display any of the contents");
        result = NOTOK;
@@ -629,7 +631,9 @@ show_multi_internal (CT ct, int alternate, int concatsw, int textonly,
     }
 
 out:
-    return result;
+    /* if no parts matched what was requested, there can't have been
+     * any display errors, so we report OK. */
+    return request_matched ? result : OK;
 }
 
 
@@ -1037,6 +1041,7 @@ convert_charset (CT ct, char *dest_charset, int *message_mods) {
         if ((conv_desc = iconv_open (dest_charset, src_charset)) ==
             (iconv_t) -1) {
             advise (NULL, "Can't convert %s to %s", src_charset, dest_charset);
+            free (src_charset);
             return NOTOK;
         }
 
@@ -1177,6 +1182,7 @@ iconv_start:
 #endif /* ! HAVE_ICONV */
     }
 
+    free (src_charset);
     return status;
 }
 
@@ -1201,9 +1207,8 @@ convert_content_charset (CT ct, char **file) {
         } else {
             status = NOTOK;
         }
-
-        free (charset);
     }
+    free (charset);
 #else  /* ! HAVE_ICONV */
     NMH_UNUSED (ct);
     NMH_UNUSED (file);
index 72079a8..17dcc01 100644 (file)
@@ -51,12 +51,6 @@ extern int wcachesw;
 extern char *cache_public;
 extern char *cache_private;
 
-/* mhstoresbr.c */
-typedef struct mhstoreinfo *mhstoreinfo_t;
-mhstoreinfo_t mhstoreinfo_create(CT *, char *, const char *, int, int);
-int mhstoreinfo_files_not_clobbered(const mhstoreinfo_t);
-void mhstoreinfo_free(mhstoreinfo_t);
-
 /* mhmisc.c */
 extern int npart;
 extern int ntype;
@@ -75,9 +69,6 @@ int part_ok (CT, int);
 int type_ok (CT, int);
 void flush_errors (void);
 
-/* mhstoresbr.c */
-void store_all_messages (mhstoreinfo_t);
-
 /* mhfree.c */
 extern CT *cts;
 void freects_done (int) NORETURN;
@@ -269,14 +260,14 @@ do_cache:
      * check if message is coming from file
      */
     if (file) {
-       if (!(cts = (CT *) calloc ((size_t) 2, sizeof(*cts))))
+       if (!(cts = (CT *) mh_xcalloc ((size_t) 2, sizeof(*cts))))
            adios (NULL, "out of memory");
        ctp = cts;
 
        if ((ct = parse_mime (file))) {
            *ctp++ = ct;
            if (outfile) {
-               ct->c_storage = outfile;
+               ct->c_storage = add (outfile, NULL);
            }
         }
     } else {
@@ -306,7 +297,7 @@ do_cache:
                done (1);
        seq_setprev (mp);       /* set the previous-sequence */
 
-       if (!(cts = (CT *) calloc ((size_t) (mp->numsel + 1), sizeof(*cts))))
+       if (!(cts = (CT *) mh_xcalloc ((size_t) (mp->numsel + 1), sizeof(*cts))))
            adios (NULL, "out of memory");
        ctp = cts;
 
index 3ca4c7c..ae835fb 100644 (file)
@@ -41,8 +41,6 @@ struct mhstoreinfo {
     enum clobber_policy_t clobber_policy;  /* -clobber selection */
 };
 
-typedef struct mhstoreinfo *mhstoreinfo_t;
-
 mhstoreinfo_t
 mhstoreinfo_create (CT *ct, char *pwd, const char *csw, int asw, int vsw) {
     mhstoreinfo_t info = mh_xmalloc (sizeof *info);
@@ -61,6 +59,7 @@ mhstoreinfo_create (CT *ct, char *pwd, const char *csw, int asw, int vsw) {
 void
 mhstoreinfo_free (mhstoreinfo_t info) {
     free (info->cwd);
+    free (info->dir);
     free (info);
 }
 
@@ -82,11 +81,6 @@ int part_ok (CT, int);
 int type_ok (CT, int);
 void flush_errors (void);
 
-/*
- * prototypes
- */
-void store_all_messages (mhstoreinfo_t);
-
 /*
  * static prototypes
  */
@@ -298,10 +292,9 @@ store_multi (CT ct, mhstoreinfo_t info)
                /* Support mhstore -outfile.  The MIME parser doesn't
                   load c_storage, so we know that p->c_storage is
                   NULL here. */
-               p->c_storage = ct->c_storage;
+               p->c_storage = add (ct->c_storage, NULL);
            }
            result = store_switch (p, info);
-           p->c_storage = NULL;
 
            if (result == OK && ct->c_subtype == MULTI_ALTERNATE)
                break;
@@ -352,7 +345,7 @@ store_partial (CT ct, mhstoreinfo_t info)
        return NOTOK;
     }
 
-    if ((base = (CT *) calloc ((size_t) (i + 1), sizeof(*base))) == NULL)
+    if ((base = (CT *) mh_xcalloc ((size_t) (i + 1), sizeof(*base))) == NULL)
        adios (NULL, "out of memory");
 
     ctq = base;
@@ -465,10 +458,9 @@ store_external (CT ct, mhstoreinfo_t info)
     if (ct->c_storage) {
        /* Support mhstore -outfile.  The MIME parser doesn't load
           c_storage, so we know that p->c_storage is NULL here. */
-       p->c_storage = ct->c_storage;
+       p->c_storage = add (ct->c_storage, NULL);
     }
     result = store_switch (p, info);
-    p->c_storage = NULL;
 
     p->c_partno = NULL;
     return result;
@@ -625,7 +617,7 @@ store_content (CT ct, CT p, mhstoreinfo_t info)
        }
     } else {
        /* The output filename was explicitly specified, so use it. */
-       if ((ct->c_storage = clobber_check (add (ct->c_storage, NULL), info)) ==
+       if ((ct->c_storage = clobber_check (ct->c_storage, info)) ==
            NULL) {
            return NOTOK;
        }
@@ -1240,6 +1232,10 @@ clobber_check (char *original_file, mhstoreinfo_t info) {
   char *cwd = NULL;
   int check_again;
 
+  if (! strcmp (original_file, "-")) {
+      return original_file;
+  }
+
   if (info->clobber_policy == NMH_CLOBBER_ASK) {
     /* Save cwd for possible use in loop below. */
     char *slash;
@@ -1250,7 +1246,9 @@ clobber_check (char *original_file, mhstoreinfo_t info) {
     if (slash) {
       *slash = '\0';
     } else {
-      /* original_file wasn't a full path, which shouldn't happen. */
+      /* original_file isn't a full path, which should only happen if
+         it is -. */
+      free (cwd);
       cwd = NULL;
     }
   }
@@ -1350,9 +1348,7 @@ clobber_check (char *original_file, mhstoreinfo_t info) {
     original_file = file;
   } while (check_again);
 
-  if (cwd) {
-    free (cwd);
-  }
+  free (cwd);
 
   return file;
 }
index d444cd6..3c2107c 100644 (file)
@@ -459,7 +459,7 @@ newnexus (int (*action)())
 {
     register struct nexus *p;
 
-    if ((p = (struct nexus *) calloc ((size_t) 1, sizeof *p)) == NULL)
+    if ((p = (struct nexus *) mh_xcalloc ((size_t) 1, sizeof *p)) == NULL)
        adios (NULL, "unable to allocate component storage");
 
     p->n_action = action;
index f843728..a9f03b3 100644 (file)
@@ -17,7 +17,7 @@
     X("annotate", 0, ANNOSW) \
     X("noannotate", 0, NANNOSW) \
     X("cc all|to|cc|me", 0, CCSW) \
-    X("nocc type", 0, NCCSW) \
+    X("nocc all|to|cc|me", 0, NCCSW) \
     X("draftfolder +folder", 0, DFOLDSW) \
     X("draftmessage msg", 0, DMSGSW) \
     X("nodraftfolder", 0, NDFLDSW) \
index afc0901..7391a3b 100644 (file)
@@ -126,10 +126,10 @@ scan (FILE *inb, int innum, int outnum, char *nfs, int width, int curflg,
         * and it's our responsibility to free it.
         */
 
-       nxtbuf = compbuffers = (char **) calloc((size_t) ncomps, sizeof(char *));
+       nxtbuf = compbuffers = (char **) mh_xcalloc ((size_t) ncomps, sizeof(char *));
        if (nxtbuf == NULL)
            adios (NULL, "unable to allocate component buffers");
-       used_buf = (struct comp **) calloc((size_t) (ncomps+1),
+       used_buf = (struct comp **) mh_xcalloc ((size_t) (ncomps+1),
            sizeof(struct comp *));
        if (used_buf == NULL)
            adios (NULL, "unable to allocate component buffer stack");
@@ -327,7 +327,7 @@ finished:
            if (! datecomp->c_text) {
                if (datecomp->c_tws == NULL)
                    datecomp->c_tws = (struct tws *)
-                       calloc((size_t) 1, sizeof(*datecomp->c_tws));
+                       mh_xcalloc ((size_t) 1, sizeof(*datecomp->c_tws));
                if (datecomp->c_tws == NULL)
                    adios (NULL, "unable to allocate tws buffer");
                *datecomp->c_tws = *dlocaltime ((time_t *) &st.st_mtime);
index 84a4939..0b8e185 100644 (file)
@@ -253,7 +253,7 @@ main (int argc, char **argv)
         * the collection of messages with the same subj
         * given a message number.
         */
-       il = (struct smsg ***) calloc (mp->hghsel+1, sizeof(*il));
+       il = (struct smsg ***) mh_xcalloc (mp->hghsel+1, sizeof(*il));
        if (! il)
            adios (NULL, "couldn't allocate msg list");
        for (i = 0; i < nmsgs; i++)
@@ -318,7 +318,7 @@ read_hdrs (struct msgs *mp, char *datesw)
     twscopy (&tb, dlocaltimenow ());
 
     smsgs = (struct smsg *)
-       calloc ((size_t) (mp->hghsel - mp->lowsel + 2),
+       mh_xcalloc ((size_t) (mp->hghsel - mp->lowsel + 2),
            sizeof(*smsgs));
     if (smsgs == NULL)
        adios (NULL, "unable to allocate sort storage");