Initial revision
authorDoug Morris <doug@mhost.com>
Fri, 30 Apr 1999 18:08:34 +0000 (18:08 +0000)
committerDoug Morris <doug@mhost.com>
Fri, 30 Apr 1999 18:08:34 +0000 (18:08 +0000)
333 files changed:
COPYRIGHT [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
DIFFERENCES [new file with mode: 0644]
INSTALL [new file with mode: 0644]
MACHINES [new file with mode: 0644]
MAIL.FILTERING [new file with mode: 0644]
Makefile.in [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
VERSION [new file with mode: 0644]
ZSH.COMPLETION [new file with mode: 0644]
acconfig.h [new file with mode: 0644]
aclocal.m4 [new file with mode: 0644]
config.h.in [new file with mode: 0644]
config/Makefile.in [new file with mode: 0644]
config/config.c [new file with mode: 0644]
config/version.sh [new file with mode: 0755]
configure [new file with mode: 0755]
configure.in [new file with mode: 0644]
etc/MailAliases [new file with mode: 0644]
etc/Makefile.in [new file with mode: 0644]
etc/components [new file with mode: 0644]
etc/digestcomps [new file with mode: 0644]
etc/distcomps [new file with mode: 0644]
etc/forwcomps [new file with mode: 0644]
etc/mhl.body [new file with mode: 0644]
etc/mhl.digest [new file with mode: 0644]
etc/mhl.format [new file with mode: 0644]
etc/mhl.forward [new file with mode: 0644]
etc/mhl.headers [new file with mode: 0644]
etc/mhl.reply [new file with mode: 0644]
etc/mhn.defaults.sh [new file with mode: 0755]
etc/mhn.find.sh [new file with mode: 0755]
etc/mts.conf.in [new file with mode: 0644]
etc/rcvdistcomps [new file with mode: 0644]
etc/replcomps [new file with mode: 0644]
etc/replgroupcomps [new file with mode: 0644]
etc/scan.default [new file with mode: 0644]
etc/scan.mailx [new file with mode: 0644]
etc/scan.nomime [new file with mode: 0644]
etc/scan.size [new file with mode: 0644]
etc/scan.time [new file with mode: 0644]
etc/scan.timely [new file with mode: 0644]
etc/scan.unseen [new file with mode: 0644]
etc/sendfiles [new file with mode: 0755]
h/Makefile.in [new file with mode: 0644]
h/addrsbr.h [new file with mode: 0644]
h/aliasbr.h [new file with mode: 0644]
h/dropsbr.h [new file with mode: 0644]
h/fmt_compile.h [new file with mode: 0644]
h/fmt_scan.h [new file with mode: 0644]
h/md5.h [new file with mode: 0644]
h/mh.h [new file with mode: 0644]
h/mhcachesbr.h [new file with mode: 0644]
h/mhparse.h [new file with mode: 0644]
h/mime.h [new file with mode: 0644]
h/msh.h [new file with mode: 0644]
h/netdb.h [new file with mode: 0644]
h/nmh.h [new file with mode: 0644]
h/nntp.h [new file with mode: 0644]
h/picksbr.h [new file with mode: 0644]
h/popsbr.h [new file with mode: 0644]
h/prototypes.h [new file with mode: 0644]
h/rcvmail.h [new file with mode: 0644]
h/scansbr.h [new file with mode: 0644]
h/signals.h [new file with mode: 0644]
h/vmhsbr.h [new file with mode: 0644]
install-sh [new file with mode: 0755]
man/Makefile.in [new file with mode: 0644]
man/ali.man [new file with mode: 0644]
man/anno.man [new file with mode: 0644]
man/ap.man [new file with mode: 0644]
man/burst.man [new file with mode: 0644]
man/comp.man [new file with mode: 0644]
man/conflict.man [new file with mode: 0644]
man/dist.man [new file with mode: 0644]
man/dp.man [new file with mode: 0644]
man/flist.man [new file with mode: 0644]
man/fmtdump.man [new file with mode: 0644]
man/folder.man [new file with mode: 0644]
man/forw.man [new file with mode: 0644]
man/inc.man [new file with mode: 0644]
man/install-mh.man [new file with mode: 0644]
man/mark.man [new file with mode: 0644]
man/mh-alias.man [new file with mode: 0644]
man/mh-chart.man [new file with mode: 0644]
man/mh-draft.man [new file with mode: 0644]
man/mh-format.man [new file with mode: 0644]
man/mh-mail.man [new file with mode: 0644]
man/mh-mts.man [new file with mode: 0644]
man/mh-profile.man [new file with mode: 0644]
man/mh-sequence.man [new file with mode: 0644]
man/mh-tailor.man [new file with mode: 0644]
man/mhbuild.man [new file with mode: 0644]
man/mhl.man [new file with mode: 0644]
man/mhlist.man [new file with mode: 0644]
man/mhmail.man [new file with mode: 0644]
man/mhn.man [new file with mode: 0644]
man/mhparam.man [new file with mode: 0644]
man/mhpath.man [new file with mode: 0644]
man/mhshow.man [new file with mode: 0644]
man/mhstore.man [new file with mode: 0644]
man/msgchk.man [new file with mode: 0644]
man/msh.man [new file with mode: 0644]
man/next.man [new file with mode: 0644]
man/nmh.man [new file with mode: 0644]
man/packf.man [new file with mode: 0644]
man/pick.man [new file with mode: 0644]
man/post.man [new file with mode: 0644]
man/prev.man [new file with mode: 0644]
man/prompter.man [new file with mode: 0644]
man/rcvdist.man [new file with mode: 0644]
man/rcvpack.man [new file with mode: 0644]
man/rcvstore.man [new file with mode: 0644]
man/rcvtty.man [new file with mode: 0644]
man/refile.man [new file with mode: 0644]
man/repl.man [new file with mode: 0644]
man/rmf.man [new file with mode: 0644]
man/rmm.man [new file with mode: 0644]
man/scan.man [new file with mode: 0644]
man/send.man [new file with mode: 0644]
man/sendfiles.man [new file with mode: 0644]
man/show.man [new file with mode: 0644]
man/slocal.man [new file with mode: 0644]
man/sortm.man [new file with mode: 0644]
man/tmac.h.in [new file with mode: 0644]
man/vmh.man [new file with mode: 0644]
man/whatnow.man [new file with mode: 0644]
man/whom.man [new file with mode: 0644]
mkinstalldirs [new file with mode: 0755]
mts/Makefile.in [new file with mode: 0644]
mts/mmdf/Makefile.in [new file with mode: 0644]
mts/mmdf/hosts.c [new file with mode: 0644]
mts/sendmail/Makefile.in [new file with mode: 0644]
mts/sendmail/hosts.c [new file with mode: 0644]
mts/sendmail/sendmail.c [new file with mode: 0644]
mts/smtp/Makefile.in [new file with mode: 0644]
mts/smtp/hosts.c [new file with mode: 0644]
mts/smtp/smtp.c [new file with mode: 0644]
mts/smtp/smtp.h [new file with mode: 0644]
sbr/Makefile.in [new file with mode: 0644]
sbr/add.c [new file with mode: 0644]
sbr/addrsbr.c [new file with mode: 0644]
sbr/ambigsw.c [new file with mode: 0644]
sbr/atooi.c [new file with mode: 0644]
sbr/brkstring.c [new file with mode: 0644]
sbr/check_charset.c [new file with mode: 0644]
sbr/closefds.c [new file with mode: 0644]
sbr/concat.c [new file with mode: 0644]
sbr/context_del.c [new file with mode: 0644]
sbr/context_find.c [new file with mode: 0644]
sbr/context_foil.c [new file with mode: 0644]
sbr/context_read.c [new file with mode: 0644]
sbr/context_replace.c [new file with mode: 0644]
sbr/context_save.c [new file with mode: 0644]
sbr/copy.c [new file with mode: 0644]
sbr/copyip.c [new file with mode: 0644]
sbr/cpydata.c [new file with mode: 0644]
sbr/cpydgst.c [new file with mode: 0644]
sbr/discard.c [new file with mode: 0644]
sbr/done.c [new file with mode: 0644]
sbr/error.c [new file with mode: 0644]
sbr/fdcompare.c [new file with mode: 0644]
sbr/fmt_addr.c [new file with mode: 0644]
sbr/fmt_compile.c [new file with mode: 0644]
sbr/fmt_def.c [new file with mode: 0644]
sbr/fmt_new.c [new file with mode: 0644]
sbr/fmt_rfc2047.c [new file with mode: 0644]
sbr/fmt_scan.c [new file with mode: 0644]
sbr/folder_addmsg.c [new file with mode: 0644]
sbr/folder_delmsgs.c [new file with mode: 0644]
sbr/folder_free.c [new file with mode: 0644]
sbr/folder_pack.c [new file with mode: 0644]
sbr/folder_read.c [new file with mode: 0644]
sbr/folder_realloc.c [new file with mode: 0644]
sbr/gans.c [new file with mode: 0644]
sbr/getans.c [new file with mode: 0644]
sbr/getanswer.c [new file with mode: 0644]
sbr/getarguments.c [new file with mode: 0644]
sbr/getcpy.c [new file with mode: 0644]
sbr/getfolder.c [new file with mode: 0644]
sbr/lock_file.c [new file with mode: 0644]
sbr/m_atoi.c [new file with mode: 0644]
sbr/m_backup.c [new file with mode: 0644]
sbr/m_convert.c [new file with mode: 0644]
sbr/m_draft.c [new file with mode: 0644]
sbr/m_getfld.c [new file with mode: 0644]
sbr/m_gmprot.c [new file with mode: 0644]
sbr/m_maildir.c [new file with mode: 0644]
sbr/m_msgdef.c [new file with mode: 0644]
sbr/m_name.c [new file with mode: 0644]
sbr/m_scratch.c [new file with mode: 0644]
sbr/m_tmpfil.c [new file with mode: 0644]
sbr/makedir.c [new file with mode: 0644]
sbr/path.c [new file with mode: 0644]
sbr/peekc.c [new file with mode: 0644]
sbr/pidstatus.c [new file with mode: 0644]
sbr/pidwait.c [new file with mode: 0644]
sbr/print_help.c [new file with mode: 0644]
sbr/print_sw.c [new file with mode: 0644]
sbr/print_version.c [new file with mode: 0644]
sbr/push.c [new file with mode: 0644]
sbr/putenv.c [new file with mode: 0644]
sbr/pwd.c [new file with mode: 0644]
sbr/r1bindex.c [new file with mode: 0644]
sbr/readconfig.c [new file with mode: 0644]
sbr/refile.c [new file with mode: 0644]
sbr/remdir.c [new file with mode: 0644]
sbr/ruserpass.c [new file with mode: 0644]
sbr/seq_add.c [new file with mode: 0644]
sbr/seq_bits.c [new file with mode: 0644]
sbr/seq_del.c [new file with mode: 0644]
sbr/seq_getnum.c [new file with mode: 0644]
sbr/seq_list.c [new file with mode: 0644]
sbr/seq_nameok.c [new file with mode: 0644]
sbr/seq_print.c [new file with mode: 0644]
sbr/seq_read.c [new file with mode: 0644]
sbr/seq_save.c [new file with mode: 0644]
sbr/seq_setcur.c [new file with mode: 0644]
sbr/seq_setprev.c [new file with mode: 0644]
sbr/seq_setunseen.c [new file with mode: 0644]
sbr/showfile.c [new file with mode: 0644]
sbr/sigmsg.awk [new file with mode: 0755]
sbr/signals.c [new file with mode: 0644]
sbr/smatch.c [new file with mode: 0644]
sbr/snprintb.c [new file with mode: 0644]
sbr/snprintf.c [new file with mode: 0644]
sbr/ssequal.c [new file with mode: 0644]
sbr/strcasecmp.c [new file with mode: 0644]
sbr/strdup.c [new file with mode: 0644]
sbr/strerror.c [new file with mode: 0644]
sbr/strindex.c [new file with mode: 0644]
sbr/trimcpy.c [new file with mode: 0644]
sbr/uprf.c [new file with mode: 0644]
sbr/vfgets.c [new file with mode: 0644]
stamp-h.in [new file with mode: 0644]
uip/Makefile.in [new file with mode: 0644]
uip/ali.c [new file with mode: 0644]
uip/aliasbr.c [new file with mode: 0644]
uip/anno.c [new file with mode: 0644]
uip/annosbr.c [new file with mode: 0644]
uip/ap.c [new file with mode: 0644]
uip/burst.c [new file with mode: 0644]
uip/comp.c [new file with mode: 0644]
uip/conflict.c [new file with mode: 0644]
uip/dist.c [new file with mode: 0644]
uip/distsbr.c [new file with mode: 0644]
uip/dp.c [new file with mode: 0644]
uip/dropsbr.c [new file with mode: 0644]
uip/flist.c [new file with mode: 0644]
uip/fmtdump.c [new file with mode: 0644]
uip/folder.c [new file with mode: 0644]
uip/forw.c [new file with mode: 0644]
uip/ftpsbr.c [new file with mode: 0644]
uip/inc.c [new file with mode: 0644]
uip/install-mh.c [new file with mode: 0644]
uip/mark.c [new file with mode: 0644]
uip/md5.c [new file with mode: 0644]
uip/mhbuild.c [new file with mode: 0644]
uip/mhbuildsbr.c [new file with mode: 0644]
uip/mhcachesbr.c [new file with mode: 0644]
uip/mhfree.c [new file with mode: 0644]
uip/mhl.c [new file with mode: 0644]
uip/mhlist.c [new file with mode: 0644]
uip/mhlistsbr.c [new file with mode: 0644]
uip/mhlsbr.c [new file with mode: 0644]
uip/mhmail.c [new file with mode: 0644]
uip/mhmisc.c [new file with mode: 0644]
uip/mhn.c [new file with mode: 0644]
uip/mhoutsbr.c [new file with mode: 0644]
uip/mhparam.c [new file with mode: 0644]
uip/mhparse.c [new file with mode: 0644]
uip/mhpath.c [new file with mode: 0644]
uip/mhshow.c [new file with mode: 0644]
uip/mhshowsbr.c [new file with mode: 0644]
uip/mhstore.c [new file with mode: 0644]
uip/mhstoresbr.c [new file with mode: 0644]
uip/mhtest.c [new file with mode: 0644]
uip/msgchk.c [new file with mode: 0644]
uip/msh.c [new file with mode: 0644]
uip/mshcmds.c [new file with mode: 0644]
uip/packf.c [new file with mode: 0644]
uip/pick.c [new file with mode: 0644]
uip/picksbr.c [new file with mode: 0644]
uip/popi.c [new file with mode: 0644]
uip/popsbr.c [new file with mode: 0644]
uip/post.c [new file with mode: 0644]
uip/prompter.c [new file with mode: 0644]
uip/rcvdist.c [new file with mode: 0644]
uip/rcvpack.c [new file with mode: 0644]
uip/rcvstore.c [new file with mode: 0644]
uip/rcvtty.c [new file with mode: 0644]
uip/refile.c [new file with mode: 0644]
uip/repl.c [new file with mode: 0644]
uip/replsbr.c [new file with mode: 0644]
uip/rmf.c [new file with mode: 0644]
uip/rmm.c [new file with mode: 0644]
uip/scan.c [new file with mode: 0644]
uip/scansbr.c [new file with mode: 0644]
uip/send.c [new file with mode: 0644]
uip/sendsbr.c [new file with mode: 0644]
uip/show.c [new file with mode: 0644]
uip/slocal.c [new file with mode: 0644]
uip/sortm.c [new file with mode: 0644]
uip/spost.c [new file with mode: 0644]
uip/termsbr.c [new file with mode: 0644]
uip/viamail.c [new file with mode: 0644]
uip/vmh.c [new file with mode: 0644]
uip/vmhsbr.c [new file with mode: 0644]
uip/vmhtest.c [new file with mode: 0644]
uip/whatnow.c [new file with mode: 0644]
uip/whatnowproc.c [new file with mode: 0644]
uip/whatnowsbr.c [new file with mode: 0644]
uip/whom.c [new file with mode: 0644]
uip/wmh.c [new file with mode: 0644]
zotnet/Makefile.in [new file with mode: 0644]
zotnet/bboards/Makefile.in [new file with mode: 0644]
zotnet/bboards/bboards.h [new file with mode: 0644]
zotnet/bboards/getbbent.c [new file with mode: 0644]
zotnet/mf/Makefile.in [new file with mode: 0644]
zotnet/mf/mf.c [new file with mode: 0644]
zotnet/mf/mf.h [new file with mode: 0644]
zotnet/mts/Makefile.in [new file with mode: 0644]
zotnet/mts/client.c [new file with mode: 0644]
zotnet/mts/mts.c [new file with mode: 0644]
zotnet/mts/mts.h [new file with mode: 0644]
zotnet/tws/Makefile.in [new file with mode: 0644]
zotnet/tws/dtime.c [new file with mode: 0644]
zotnet/tws/dtimep.c-lexed [new file with mode: 0644]
zotnet/tws/dtimep.lex [new file with mode: 0644]
zotnet/tws/lexedit.sed [new file with mode: 0644]
zotnet/tws/lexstring.c [new file with mode: 0644]
zotnet/tws/tws.h [new file with mode: 0644]

diff --git a/COPYRIGHT b/COPYRIGHT
new file mode 100644 (file)
index 0000000..1948505
--- /dev/null
+++ b/COPYRIGHT
@@ -0,0 +1,20 @@
+
+Copyright (c) 1997-1998 Richard Coleman
+All rights reserved.
+
+Permission is hereby granted, without written agreement and without
+license or royalty fees, to use, copy, modify, and distribute this
+software and to distribute modified versions of this software for any
+purpose, provided that the above copyright notice and the following two
+paragraphs appear in all copies of this software.
+
+In no event shall Richard Coleman be liable to any party for direct,
+indirect, special, incidental, or consequential damages arising out of
+the use of this software and its documentation, even if Richard Coleman
+has been advised of the possibility of such damage.
+
+Richard Coleman specifically disclaims any warranties, including, but
+not limited to, the implied warranties of merchantability and fitness
+for a particular purpose.  The software provided hereunder is on an "as
+is" basis, and Richard Coleman has no obligation to provide maintenance,
+support, updates, enhancements, or modifications.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..fa6ef47
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,1504 @@
+
+1999-02-06  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-1.0.
+
+       * Merged mbx_open and mbx_Xopen in dropsbr.c.  Fixed
+         mbx_open so that the mode of zero length maildrops
+         would not be changed.
+
+       * Replaced the substitute version of snprintf() with the
+         one from the Apache web server.
+
+       * Changed to default mode for creating new messages to 0600
+         (this should have been done a long time ago).
+
+       * Changed "flist" to handle searching for multiple sequences
+         for each folder.  Also flist will now correctly split
+         Unseen-Sequence if it consists of multiple sequences.
+
+       * Added new switches `-unlink' and `-nounlink' to "refile".
+
+       * Added new switches `-unlink' and `-nounlink' to "rmm".
+
+       * More cleanups of slocal output.  Changed adorn() to
+         send to stdout, instead of stderr (to match rest of
+         verbose printing).
+
+       * Merged mbx_create() into mbx_open, so that creating and
+         opening a nonexistent maildrop is done atomically.  This
+         removes a bad race condition.
+
+       * Fixed bug that caused slocal to be unable to save to MMDF
+         style drop file.
+
+       * Added new wrapper function usr_folder() to slocal.c to
+         handle adding message to folder (currently, it still uses
+         usr_pipe() to call rcvstore).
+
+       * seq_list() checks for empty folder before scanning for
+         sequence information.
+
+       * num_digits() in flist.c and folder.c now returns correct
+         value for 0.  Also added sanity check.
+
+       * folder_delmsgs() now correctly decrements internal message
+         count.
+
+       * Don't attempt to read sequence information if folder
+         is empty.
+
+       * Split seq_read into seq_public and seq_private.
+
+       * Small change to sigmsg.awk, since newer versions of gawk
+         interpret 034 as octal.
+
+       * In flist, don't scan for sequence information in empty folder.
+
+       * Updated mhn.defaults.sh to output profile entries for mhshow,
+         mhstore, and mhbuild.
+
+       * Changed configuration parameter "mhn-access-ftp" to
+         "nmh-access-ftp".  Updated man pages
+
+       * Moved the code in InitMultipart to reverse the order of the
+         parts in a multipart, into its own function "reverse_parts()".
+
+       * Changed code in mhbuildsbr.c to store unencoded content
+         in the c_cefile structure when building.
+
+       * Changed code in mhoutsbr.c to look for unencoded content
+         in the c_cefile structure when outputing message.
+
+       * Changed configuration parameter "mhn-cache" and
+         "mhn-private-cache", to "nmh-cache" and "nmh-private-cache",
+         since it is used in mhstore, mhlist, and mhshow.  Updated man pages
+
+       * Change configuration parameter "mhn-storage" to
+         "nmh-storage", since it is now used in mhstore, mhlist,
+         and mhshow.  Updated man pages
+
+       * Add autoconf support for KPOP (kerberized pop).
+
+       * Add autoconf support for Hesiod.
+
+       * Split routines to output a message given a Content structure
+         (output_message, output_content, write7Bit, etc..) to a new
+         file "mhoutsbr.c".
+
+       * Split output_content(), into output_content() and build_headers().
+
+       * Changed copy_some_headers() in mhstoresbr.c, to use the linked
+         list of header fields, rather than reopening the message.
+
+       * Added free_header() to mhfree.c to free structures containing
+         header field information.
+
+       * Changed get_content() to use the linked list of header fields
+         when parsing the various MIME headers (Content-XXX).
+
+       * Changed get_content() to store linked list of header field
+         values when parsing a content.
+
+       * Changed mhbuild, mhn, mhlist, mhshow, mhstore, to use the
+         routines in mhcachesbr.c to handle the content cache.
+
+       * Split various funtions (find_cache, find_cache_aux, find_cache_aux2,
+         cache_content) into new file mhcachesbr.c.
+
+       * More calls to sprintf/strcpy (primarily in mhparse.c
+         and mhbuildsbr.c) converted to snprintf/strncpy.
+
+       * When a message is displayed with `mhshow', it is now
+         removed from the "unseen" sequence.
+
+       * Change the default "showmimeproc" to "mhshow".
+
+       * Split "mhn -show" off into separate command "mhshow".
+
+       * Split "mhn -store" off into separate command "mhstore".
+
+       * Split "mhn -list" off into separate command "mhlist".
+
+       * Add sanity checks to context_find(), context_replace(),
+         and context_del(), to abort if context file hasn't been
+         read.
+
+       * Add calls to context_read(), to the beginning of all nmh
+         commands (instead of being called indirectly by context_find).
+
+       * Changes the "substitute" version of vsnprintf/snprintf for
+         operating systems without native versions, to just call the
+         native vsprintf(), and ignore the buffer length.   This is
+         faster, but less secure than the previous version that used
+         temporary files.  This should only be a problem for systems
+         which do not have a native snprintf(), and require `inc' to
+         be setuid/setgid.
+
+       * Lots more calls to sprintf/strcpy converted to snprintf/strncpy.
+
+       * Changes client() routine to take additional parameter, which is
+         the buffer length of the parameter "response".  Then added
+         buffer length checks for this parameter.
+
+       * Changed getws() to get_fields(), since that is apparently the
+         name of a wide character version of gets() on some archetitures.
+
+       * Lots of sprintf/strcpy calls converted to snprintf/strncpy.
+
+       * Change the code in most of the commands that take multiple
+         message names/sequences/ranges on the command, such that
+         the msgs array is expanded dynamically.  This removes most
+         of the limits on the length of command lines.
+
+       * Add additional parameter to copyip(), to specify the
+         maximum number of strings that can be copied (security
+         fix).
+
+       * Create new function getarguments(), to massage the argument
+         vector before parsing it (add any arguments from your
+         profile to the beginning of the argument vector).  This
+         also removed the general limit on the number of command line
+         arguments.
+
+1998-07-04  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.27.
+
+       * Added a new command "delete", that is available during
+         a "whatnow" session.  It is equivalent to "quit -delete".
+
+       * Added another parameter to editfile (in whatnowsbr.c),
+         that controls whether editfile should remember the last
+         program that was exec'ed.  This way the whatnow command
+         "mime", will not be re-executed if "edit" is later given
+         with no arguments.
+
+       * Changed whatnowsbr.c, so that whatnow doesn't abort if
+         mhbuild returns an error.
+
+       * Added parameter to sendsbr(), so you may specify whether to
+         rename the draft file.
+
+       * Pass delay time to splitmsg() as a parameter, rather than
+         use a global variable.
+
+       * Moved code to rename draft file after sending message from
+         splitmsg and sendaux, to sendsbr.
+
+       * Removed all the code in viamail to split messages and then
+         mail them.  Replaced this with the standard sendsbr.c routines.
+
+       * Changed sendsbr(), so that when splitting messages into
+         messages of type "message/partial", the header fields that
+         are copied are more compliant with RFC-2046.
+
+       * Fixed mhbuild to track temporary files better.  They are
+         now correctly removed when mhbuild aborts.
+
+       * Created a new man page for "sendfiles".  The information
+         about "mhn -viamail" in the "mhn" man page was moved to
+         this new page.
+
+       * Changed the name of the "viamail" shell script to
+         "sendfiles".  Modified "sendfiles" to use the new
+         viamail program.
+
+       * Moved the functionality for "mhn -viamail" out of mhn,
+         and into a separate executable called "viamail".
+
+       * When storing MIME contents to a folder using mhn -store,
+         they are now accumulated in a temporary file, and then added
+         to the folder using folder_addmsg().
+
+       * Moved code to save content to a folder from store_content
+         to new function output_content_folder.
+
+       * Moved code to save content to file from store_content to
+         new function output_content_file.
+
+       * Moved code to parse storage format string from store_content
+         to new function parse_format_string.
+
+       * Fix copy_some_headers() in mhstoresbr.c, so that the
+         correct header fields in the first enclosing message/partial
+         will be copied (according to RFC2046), when using mhn -store
+         to reassemble messages of type message/partial.
+
+       * Fixed bug to openFTP() in mhparse.c, that caused the
+         tmp file to not be removed, when transferring a
+         message/external file from ftp.
+
+       * Moved the code in mhparse.c to process -auto switch (scan
+         contents for the attribute "name"), to a new function
+         "get_storeproc" in mhstoresbr.c.
+
+       * Moved routines to free data structures related to MIME
+         content from mhparse.c and mhbuildsbr.c, to new file
+         mhfree.c.
+
+       * Moved code to show/display MIME content into new
+         file mhshowsbr.c.
+
+       * Moved code to store MIME content from into
+         new file mhstoresbr.c
+
+       * Moved code to parse MIME content into new
+         file mhparse.c.
+
+       * Moved code to list information about MIME content
+         into new file mhlistsbr.c.
+
+       * Move part_ok(), type_ok(), content_error(), flush_errors(),
+         and set_endian() to new file mhmisc.c.
+
+       * Start to isolate the code to show, list, and store MIME
+         messages.  One side effect is that only one flag (-show,
+         -list, or -store) can be used at a time now.
+
+       * mhn -store -auto wasn't storing file in correct directory.
+
+       * Removed a few dead variables from sbr/ruserpass.c
+
+       * move code for creating tmp files, and renaming the
+         the composition draft in mhbuild, from build_mime()
+         to main().
+
+       * remove left-over code in mhbuild.c, mhbuildsbr.c, for
+         the -[no]auto switch (which isn't used in mhbuild).
+
+       * split mhn.c into mhn.c and mhnsbr.c (name later changed
+         to mhparse.c).
+
+       * split mhbuild.c into mhbuild.c and mhbuildsbr.c.
+
+1998-05-25  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.26.
+
+       * Added (unlisted) options [no]dashstuffing to send, post,
+         and whatnow to determine whether to do RFC934 quoting
+         (dashstuffing) for encapsulated BCC messages.  The default
+         is still the same (dashstuffing).
+
+       * Changed the undocumented feature "nodashmunging" in forw
+         and mhl, into the documented feature "nodashstuffing".  The
+         default for forw, is still "dashstuffing" for backward
+         compatibility, although I don't believe that bursting
+         RFC934 digests is very common anymore.
+
+       * Added an option to define REALLYDUMB in the default config.h.
+         But it is not on by default.
+
+       * moved creation of config file mts.conf from zotnet/mts
+         to etc.  This simplified the Makefile in zotnet/mts.
+
+       * simplified directory support/general to etc.
+
+       * removed unneeded directory support/bboards.
+
+       * split getusername() into getusername() and getuserinfo().
+
+       * Changed getusr() routine to getusername().
+
+       * Slight cleanup in folder_pack.c on code that records the new
+         number of the "cur" message when packing.
+
+1998-05-08  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.25.
+
+       * Change install process, so that hard linking the correct mts
+         library to libmts.a, is not necessary.  The final link process
+         uses the original name of the library.
+
+       * Fixed bug in flist.c and folder.c, so that symbolic links which
+         point to directories, will not decrement the number of directory
+         links remaining.
+
+       * Split the function list_content (in mhn.c and mhbuild.c) into
+         list_content and list_debug.
+
+       * Don't pack (folder -pack) an empty folder.
+
+       * Exit gracefully in flist.c, if no sequence is specified,
+         and no "Unseen-Sequence" is given in nmh profile.
+
+1998-02-27  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.24.
+
+       * Small clarification to the man page for `ali'.
+
+       * Fix bug in inc.c so that if both flags `-file' and `-truncate'
+         are given, that order doesn't matter.
+
+       * Fix bug in seq_list.c when realloc'ing for
+         large sequence line.
+
+1998-02-23  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.23.
+
+       * Add new section on "Transfer Encodings" to man page for mhbuild.
+
+       * In mhbuild.c, split compose_content into compose_content
+         (parse and execute composition string), and scan_content (scan content,
+         decided transfer encoding, check for clash with boundary string).
+         I did a good amount of rearranging of this code.
+
+       * Moved definitions for data structures for parsing MIME
+         messages from mhn.c and mhbuild.c to a new include
+         file h/mhnsbr.h.
+
+       * Small amount of rearranging in sendsbr.c
+
+       * Small changes to MAIL.FILTERING file.
+
+       * Add the file MAIL.FILTERING to nmh distribution.
+
+       * Add line to packf so that if message begins with
+         "X-Envelope-From:" field, it is converted to "From ".
+
+       * Fix packf to add "From " line to beginning of message,
+         even if Return-Path doesn't exist.
+
+       * Add note to MACHINES file that on Linux, configure
+         doesn't find the functions sigsetjmp/siglongjmp.
+
+       * Fix configuration for machines that don't have (or find)
+         sigsetjmp/siglongjmp.
+
+1998-02-11  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.22.
+
+       * Add a configure check for sigsetjmp.  Add some conditional
+         #define's in h/signals.h in case it's not found.
+
+       * Added additional notes about -auto switch in mhn man page.
+
+       * Added note about MM_CHARSET environment variable to
+         mh-profile(5) man page.
+
+       * Fix signal problem in mhn.c (change setjmp/longjmp to
+         sigsetjmp/siglongjmp).
+
+1998-02-09  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.22-pre1.
+
+       * Changed the first line in mhl.format from
+         " -- using template mhl.format -- " to a blank line.
+
+       * Added note about automimeproc to mh-profile man page.
+
+       * Reorganize the main entry point for parsing a MIME message
+         or file in mhn.  Add new function parse_file() as new main
+         entry point for parsing MIME files.
+
+       * Add note to mhn man page, that "mhn -file -" will accept the
+         source message on the standard input.
+
+       * Changed a sanity check in folder_realloc that was too strict.
+
+       * -norfc934mode is now the default for mhbuild,
+         rather than -rfc934mode.
+
+       * Fix mhbuild, so that Content-Description and RFC-822 comments
+         from #forw directive will be correctly included if there is
+         only one message.
+
+       * Change mhn to correctly default parts of multipart/digest to
+         message/rfc822 (leftover code from rfc934mode was removed).
+
+       * Restore HP specific code to zotnet/tws/lexstring.c.  Apparently
+         it is still needed.
+
+1998-02-06  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.21.
+
+       * If the file given to mhbuild is "-", then accept the draft on
+         standard input, and output the MIME message to standard output.
+
+       * Cleaned up code in mhbuild.c that decides what transfer
+         encoding to use.
+
+       * Cleaned up code in mhbuild.c that decides what character set
+         to use for text contents.
+
+       * Removed old hpux specific code from zotnet/tws/lexstring.c
+
+1998-02-02  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.21-pre2.
+
+       * Added the "decode" variable to mhl.format and mhl.header.
+
+       * Added new variable "decode" to mhlsbr.c to decode text in
+         header fields as per RFC-2047.
+
+       * Make sure that when decoding RFC-2047 header fields, that any
+         spaces at the ends of the encoded text are not ignored, but the
+         spaces between encoded word are.
+
+       * Removed #ifdef's for MIME.  MIME support is always compiled in.
+
+       * scan/inc will now decode both Subject and From lines as
+         RFC-2047 encoded header fields.
+
+       * Added new function write_charset_8bit() to sbr.  It returns
+         the character set to use for 8bit text in composition draft.
+         Changed mhbuild to use this function.
+
+       * Split mhn man page into man pages for mhn and mhbuild.
+
+       * mhn -show will only now only use default method for content
+         of type plain, if it is NOT a part of a multipart/alternative.
+
+       * Split mhn -build into mhbuild.  Did some code cleanup.
+
+       * Added support for %(decode) to fmtdump.c.
+
+       * check_charset() now accepts US-ASCII as a subset of any
+         ISO-8859-X character set.
+
+       * Changed the default "showproc" to mhl, instead of the
+         pager more.
+
+       * When reading file into mhn composition file, only need read
+         permissions, not write permissions.
+
+       * Added own version of strcasecmp to distribution, since
+         nmh calls it frequently with NULL pointers (ughh).
+
+       * Replaced uleq.c with strcasecmp.  Removed uleq.c from
+         distribution.
+
+1998-01-22  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.21-pre1.
+
+       * If a message is missing charset parameter to text/plain, show
+         will assume US-ASCII, rather than just calling showmimeproc.
+
+       * Change show.c and mshcmds.c to use check_charset to see if text
+         message contains valid character set.
+
+       * Added new scan format file "scan.nomime" to support/general
+         that doesn't do any RFC-2047 decoding.
+
+       * Modified all the scan format files in support/general to do
+         RFC-2047 decoding of Subject field.
+
+       * Did more work on sbr/fmt_rfc2047.c, so that it will correctly
+         ignore whitespace between two valid encoded words, but not
+         between an encoded word and normal text.
+
+       * Created new file sbr/check_charset.c.  Moved code from
+         fmt_rfc2047.c to check for valid character set to this file.
+
+       * Added format escape %(decode) to decode contents of "str" register
+         as a RFC-2047 header field.
+
+       * The command install-mh now recognizes the switches -version
+         and -help.
+
+       * Added a new argument to print_help.c to decide whether to
+         print profile entries (needed for install-mh to prevent weird
+         loops).
+
+       * Changed folder_read.c and folder_realloc.c so that mp->lowoff
+         is initialize to max (mp->lowmsg, 1) rather than always 1.
+
+       * Changed macros for sequence/attribute manipulation so that
+         message status array doesn't need to always start at 1.
+
+       * Small cleanups in folder_realloc().
+
+1998-01-09  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.20.
+
+       * Added configure option --with-pager=PAGER.
+
+       * Added configure option --with-editor=EDITOR.
+
+       * Changed the default format file for mhl (mhl.format) to
+         also ignore (not display) the header fields Content-Type,
+         Content-Transfer-Encoding, and Content-ID
+
+       * Fixed core dump in addrsbr.c when using %(proper) format function
+         and the To: line was missing.
+
+       * Added the file ZSH.COMPLETION to the distribution.
+
+1998-01-04  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.20-pre2.
+
+       * Added new switch -snoop to both `msgchk' and `inc', so you can
+         watch the POP transaction.
+
+       * Changed "replgroupcomps" to check for Mail-Followup-To header
+         first, and use it if available.
+
+       * Changed "replcomps" to check for Mail-Reply-To header
+         first, and use it if available.
+
+1998-01-03  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.20-pre1.
+
+       * Changed seq_list.c to dynamically enlarge the buffer for
+         collecting the message ranges in a long sequence line.
+         This should remove the last hard limit on the size of a
+         sequence line.
+
+       * Changed seq_read.c so that can read long sequence lines.
+         It will use multiple calls to m_getfld() when m_getfld()
+         returns the state FLDPLUS.
+
+       * Changed brkstring.c to dynamically add more space for pointers
+         if necessary.  This is needed when splitting up large sequence
+         lines.
+
+       * Did some small cleanups in seq_save.c.
+
+       * Added new switches `-[no]unseen' to rcvstore, to control
+         whether new messages are added to Unseen-Sequence.
+
+       * Moved locking routines (zotnet/mts/lock.c) to sbr/lock_file.c
+
+       * Changed the internal UNSEEN flag to SELECT_UNSEEN which is
+         more appropriate.  Changed the MHPATH flag to ALLOW_NEW.
+
+       * Changed "replcomps" to not include CC and TO lines so that
+         that reply message is only directed at the author of the
+         message to which you are replying.
+
+       * Added new switch `-group' to command repl, which causes repl
+         to use new forms file "replgroupcomps".  This is intended for
+         making group replies.
+
+       * Removed #ifdef for ATHENA.
+
+1997-12-28  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.19.
+
+       * Fix repl,forw so that switch `-form file' will not abort
+         as ambiguious (silly mistake on my part).
+
+       * Cleaned up the mhn man page.  Added info about a few escapes
+         for the formatting/display strings that were not documented
+         (%%, %t).  Moved the BNF grammar for the mime composition file,
+         to the end of the man page.
+
+       * Added the options -[no]format to the command repl.  The
+         switch `-format' will filter the message to which you are
+         replying with the standard message filter "mhl.reply", which
+         is now included in the distribution.  The `-noformat' option
+         will negate the use of -format or -filter and not include
+         the message to which you are replying in the draft.
+
+       * Did some cleaning and reorganization on many of the man
+         pages.
+
+       * Added debugging switch `-debug' to mhparam, which displays
+         the values of all `procs' (and some other misc configuration
+         info) that nmh keeps in global variables.
+
+       * When using `refile -preserve', if a conflict occurs, then use
+         the next available number above the message number you wish
+         to preserve.
+
+       * In forw.c, split the code for creating MIME style forwarding
+         out of copy_draft, and into copy_mime_draft.
+
+       * Move routines in mark.c to print sequences, into new
+         file sbr/seq_print.c
+
+       * flist will now update the current folder.
+
+       * Added the switches -[no]fast to flist, to replace
+         -[no]total.  The previous switches are still accepted
+         but now undocumented.
+
+       * More reorganization in flist of the code for
+         traversing folders.
+
+       * The command "flist +foo -all" will now scan the folder
+         "foo" and all its 1st level children.
+
+       * Add missing include file <h/mh.h> to sbr/snprintf.c
+
+       * Fix alarm bug in rcvtty, so that when it calls external
+         process, the alarm is never longer than 30 minutes.
+
+1997-12-17  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.18.
+
+       * Fixed bug in mark, so that "mark -list -seq foo" will
+         correctly indicate if "foo" is a private sequence.  I found
+         this bug mentioned in Jerry Peek's book.
+
+       * Simplified the code in seq_setcur(), since seq_addmsg() now
+         retains the public/private status of sequences.
+
+       * Changed sequence handling so that if the switches -public
+         or -nopublic, are not specified for the commands mark, pick,
+         or rcvstore, then existing sequences will retain their
+         previous public/private status.
+
+       * mhparam now handles the mh-sequences profile entry
+         correctly.
+
+       * flist -all will now also check readonly folders (for
+         private sequences).
+
+       * Improve the leaf optimization for folder command.
+         It will now track the number of directories in a folder,
+         and stop stat'ing files once it has hit all the subfolders.
+
+       * Renamed m_getfolder to getfolder.  Changed getfolder to
+         take option to determine whether it should get current
+         folder, or just default folder (Inbox).  Changed rcvstore,
+         inc, and rmf to use the new getfolder.
+
+       * flist now indicates if a sequence is private.
+
+       * Change WUNTRACED to 0, in pidwait.c, so that commands will
+         wait for stopped processes.
+
+       * conflict will dynamically allocate space for group names,
+         so it can now handle system with more than 100 groups.
+
+1997-12-09  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.18-pre4.
+
+       * Check if we have enough message status space, before we
+         call folder_realloc() in burst, mhpath, and m_draft().
+
+       * mhn will now correctly identify a formatting string of "-"
+         for the option -store, and send content to stdout.
+
+       * Change the way that memory for message status is
+         allocated.  It is dynamcially allocated separately from
+         the folder/message structure.  This required changing
+         folder_read.c, folder_realloc.c, folder_free.c.
+
+       * Removed all the MTR code (experimental code for message
+         status allocation).
+
+       * Renamed m_readfolder.c to folder_read.c and simplified
+         the code.
+
+       * Renamed m_freefolder.c to folder_free.c.
+
+       * Add function trim() to slocal.c to pretty print
+         the debugging output.
+
+       * Changed the name of m_packfolder() to folder_pack().
+         Changed the name of m_remsg() to folder_realloc().
+
+Wed Dec  3 23:33:38 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.18-pre3.
+
+       * Changed installation to add `flists' which is hard linked
+         to `flist'.  This is a equivalent to `flist -all'.
+
+       * For flist, -showzero is on by default.
+
+       * Major changes to flist.  Default is now for flist to search
+         current folder.  The switch `-all' is now used to specify
+         searching all top level folders.  The new switch `-showzero'
+         is used to print out folders that don't contain any messages
+         in the given sequence.
+
+       * Split BuildFolderList in flist.c into 2 functions
+         (BuildFolderList, BuildFolderListR).  Changed these functions
+         so that flist now does better leaf optimization, and will stop
+         stat'ing directory entries when it knows it has hit all the
+         subdirectories of a given directory.
+
+       * Reorganized code in folder.c, so that all relevant folders
+         are scanned first and information recorded.  Then all the
+         folder summaries at printed out at one time.
+
+       * Made the options of folder(s) more orthogonal.  Now
+         "folder -all -noheader -nototal" will do the right thing.
+
+       * Added `-noall' switch to folder, for completeness.
+
+       * Changed the default mode for creation of new folders
+         to 0700 (was 0711).
+
+       * Slightly changed the format for flist.  It now indicates
+         if a folder is current.  Also the width of the various
+         fields are now calculated at runtime.
+
+       * Changed the format for folder(s).  Folder names
+         are now left justified.  The width of the various fields
+         are calculated at runtime.
+
+Sun Nov 30 19:14:53 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.18-pre2.
+
+       * Add paragraph to man page for install-mh and to INSTALL file
+         about checking for global mh.profile.
+
+       * Renamed m_find() to context_find().
+         Renamed m_replace() to context_replace().
+         Renamed m_delete() to context_del().
+         Renamed m_update() to context_save().
+         Renamed m_getdefs() to context_read().
+         Renamed m_foil() to context_foil().
+
+       * Change rcvstore to use routine folder_addmsg(), instead of
+         adding message to folder itself.
+
+       * Changed refile, so that if the switch -preserve is used,
+         and a conflict occurs for a particular folder, then folder_addmsg()
+         will just use next highest available number for that folder,
+         instead of exiting.
+
+       * Make folder_addmsg() more robust.  It will make repeated
+         attempts to link file into folder if link returns with
+         the error EEXIST.
+
+       * Fix bug, so that that if forking sendmail, HELO will be sent
+         unless clientname: option is defined but empty (so now it
+         is the same as the direct smtp code).
+
+       * Changed sprintb to snprintb (now we pass the buffer length
+         to new routine).  Changed code to use new function.
+
+       * Added snprintf to sbr. Added configure check to build it
+         if you don't have a native version (but haven't changed much
+         code to use it yet).
+
+Thu Nov 13 18:42:18 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.18-pre1.
+
+       * Fixed alarm bug in slocal, so that alarm is never
+         called with a value larger than 30 mintues.
+
+       * Fixed race condition in rmm and refile, so that
+         context is updated before external rmmproc is called.
+
+       * Removed all the OVERHEAD code.
+
+       * Move code to add message to folder from refile.c
+         to folder_addmsg.c
+
+Fri Jul 25 19:39:29 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Did some rearranging of the internals of inc.c.
+
+       * Make -inplace the default for anno, forw, dist, and repl.
+
+       * Changed --enable-smtp to --with-mts={smtp,sendmail}
+
+       * Created new directory mts/sendmail for direct sendmail
+         interface (although it currently still uses SMTP).
+
+       * Removed all the TMA (trusted mail agent) code
+
+       * Removed all the TTYD (terminal access daemon) code
+
+       * Removed all the MF (uucp filtering) code.
+
+       * Removed all the code for BERK.
+
+       * Removed all the code for stand-alone delivery (MHMTS).
+
+       * Split the file mts/sendmail/smail.c into sendmail.c and
+         smtp.c.  Changed the name of the directory to mts/smtp.
+
+       * Changed autoconf to use @sysconfdir@ for location of
+         configuration files.
+
+       * Changed #define in mhn.c from FTP to BUILTIN_FTP.
+
+Mon Jul 21 03:22:34 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.17.
+
+       * MAKEDEFS weren't passed down to recursive makes correctly.
+
+       * slocal.c now checks for UTMP_FILE and _PATH_UTMP instead
+         of hard-coding "/etc/utmp".
+
+       * rcvtty.c check for _PATH_UTMP if UTMP_FILE is not
+         defined.
+
+       * Remove configure checks for ulong and ushort.  Changed
+         code to just use unsigned {short, long}.
+
+       * Change addmsg function in refile.c to return new
+         number of refiled message.
+
+       * Added check in get_returnpath for empty unixbuf.
+
+       * Cleanup of sbr/pidstatus to use more POSIX macros
+         for return value of wait().
+
+       * Change configure to also check /bin for "more".
+
+Sat Jul 12 00:02:23 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.16.
+
+Mon Jun 23 20:13:24 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Added automimeproc, which should replace automhnproc.
+
+       * multipart messages will no longer abort for messages
+         of type 8bit or binary (although we still can't really
+         deal with binary messages, yet).
+
+       * Fix double free of c_storage.  From John MacMillan.
+
+       * mhn now treats unknown subtypes of "text" as text/plain.
+
+       * mhn changed so that specifying mhn-show-multipart, or
+         mhn-show-multipart/{mixed, alternate, etc...) will override
+         the use of the internal method for displaying these types.
+         Previously mhn would always use the internal method for subtypes
+         mixed, alternate, digest, and parallel (even if an alternate
+         method was specified in mhn.defaults).
+
+       * mhn show treats unknown subtypes of multipart, as type
+         multipart/mixed (as specified RFC2046).
+
+       * mhn checks for the parameter "name" rather than "x-name".
+         From MH-6.8.4 patch.
+
+       * Fix double free of ctinfo in user_content when using
+         #forw with single message.  From John MacMillan (and
+         MH-6.8.4 patch).
+
+       * Changed -mhnproc switch for show, to -showmimeproc.
+
+       * Changed profile entry "mhnproc" to "showmimeproc".
+
+       * Added "mime" option to "whatnow", which calls the program
+         "buildmimeproc" (default is mhn -build) to process MIME
+          composition files.
+
+       * Added -build switch to mhn, to process MIME composition
+         files.
+
+       * Did some reorganizing of mhn.c.
+
+       * Changed casting in mts/sendmail/smail.c from (char) to
+         (signed char) so SMTP reply codes work correctly for machines
+         which used unsigned chars by default.
+
+Sat Jun 21 01:21:47 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.15.
+
+       * Added new form "scan.unseen" to distribution.  It marks messages
+         which are in any sequence in Unseen-Sequence.
+
+       * Do some rearranging of date/time code in zotnet/tws/dtime.c
+
+       * Fix sign extension bugs in fmt_scan.c.
+
+       * Fix m_atoi.c so that strings ending in non-digit characters
+         return 0.
+
+       * Split code in burst.c so that finding delimiters of digested
+         messages and bursting a message into multiple messages are
+         two separate functions (find_delim and burst).
+
+       * Add workaround fo AC_PATH_PROG in configure.in, so
+         that BSD4.4 machines can find sendmail, vi, more.
+
+       * Added "-width" option to rcvtty.
+
+       * Change a few variable names in zotnet/mts/client.c since
+         they conflict with defines on AIX.
+
+       * Makefile in zotnet/tws assumes lexing of dtimep.lex was
+         unsuccessful if resulting file is less than 500 lines long
+         (rather than 10, which was previous value), since AIX
+         sed gives mangled file of about 200 lines.
+
+       * Extract code in rcvstore.c to link message into folder,
+         and put in own subroutine.
+
+       * Extract code in refile.c to link message into folder,
+         and put in own subroutine.
+
+       * Moved code to remove messages from folder into own
+         routine "folder_delmsgs" in sbr.  Changed rmm.c and
+         refile.c to use new routine.
+
+Fri May 16 06:09:31 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Renamed m_seqok to seq_nameok.
+
+       * Changed m_setunseen, msh, mshcmds, flist, and scan to use
+         seq_getnum.
+
+       * Changed m_seqflag to return the number of a sequence rather
+         than its bit flag.  Changed its name to seq_getnum and renamed
+         file to sbr/seq_getnum.c.
+
+       * Removed function m_seqnew and file sbr/m_seqnew.c since it is
+         no longer used.
+
+       * Added zero switch to m_seqadd function to zero out bits before
+         adding message to sequence.
+
+       * Renamed function m_setvis to m_setunseen, and renamed
+         corresponding file in sbr.
+
+       * Renamed function m_setseq to m_setprev, and renamed corresponding
+         file in sbr.
+
+       * Changed mark.c and pick.c to use m_seqaddsel and m_seqdelsel.
+
+       * Added new function m_seqdelsel to m_seqdel.c, which deletes
+         all selected messages from a sequence.
+
+       * Added new function m_seqaddsel to m_seqadd.c, which adds all
+         selected messages to a sequence.
+
+       * Split sbr/m_seqnew.c into m_seqadd.c, m_seqdel.c, m_seqnew.c,
+         and m_seqok.c.
+
+Thu May 15 00:53:17 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Renamed function pack_folder to m_packfolder, and moved it
+         from uip/folder.c into its own file sbr/m_packfolder.c
+
+Wed May 14 23:38:00 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Changed function m_gmsg to m_readfolder.  Renamed file
+         sbr/m_gmsg.c to sbr/m_readfolder.c.
+
+Mon May  5 19:57:11 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Expanded rcvtty man page, and added small patch from
+         MH-6.8.4 distribution.
+
+Fri May  2 15:24:34 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.14.
+
+       * Comment out configure test and code for tgetent to allocate its
+         own termcap buffer when passed a NULL argument.
+
+Sat Apr 26 03:46:38 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Added new options `-checkmime', `-nocheckmime', and `-mhnproc'
+         to show.  Restructured code to handle options to various
+         `procs' better.  Deprecated `-noshowproc' option and NOMHNPROC
+         environment variable.
+
+       * Added new man page `mh-draft' which documents the
+         draft folder facility in nmh.
+
+       * Renamed fmtsbr.h to fmt_scan.h.  Renamed fmtcompile.h
+         to fmt_compile.h.
+
+       * split fmtsbr.c into fmt_scan.c and fmt_new.c.  Renamed
+         fmtcompile.c to fmt_compile.c, and formataddr.c to
+         fmt_addr.c.
+
+       * `send -help' wasn't showing the -(no)mime and -split
+         options.
+
+Fri Apr 25 02:50:36 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.13.
+
+       * Changed mhpath so it doesn't abort if a message sequence
+         such as "mhpath all" expands to more than 1000 messages.
+         Also mhpath now dynamically reallocated space for message
+         names (The number of command line arguments is still limited
+         to MAXARGS).
+
+       * Did some general restructuring of the code in folder.c
+         that checks for folder information, and prints it.
+
+Thu Apr 24 01:04:37 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Changed `folder' to reallocate space for folder names if
+         necessary.  So `folders' can now handle more than 300 folders.
+
+Tue Apr 22 14:01:26 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Change configure to use a compile check to see if the tm struct
+         has tm_gmtoff, rather than using egrep.
+
+Mon Apr 21 02:19:17 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.12.
+
+       * Had set_exists and unset_exists macros backwards.
+
+       * Released nmh-0.11.
+
+Thu Apr 10 02:39:53 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Added documentation to mh-profile.man about the various
+         `procs' (mhlproc, showproc, lproc, etc...).
+
+       * Replace the bit twiddling for SELECTED, UNSEEN, and
+         mp->attrstats with macros.
+
+       * If system doesn't have SIGEMT (like Linux), then use SIGTERM
+         in msh.c instead.
+
+       * Change fstat to stat in m_gmsg.c since Linux wants
+         to hide dd->dd_fd.
+
+       * Merge Linux patch sent in by Michel Oosterhof (original
+         patch from bsa@kf8nh.wariat.org).
+
+       * Document an undocumented MH feature.  mhn -form mhl.null
+         will suppress the display of the message header.
+
+       * mhparam will now return "mhparam etcdir".
+
+       * Add catproc to /config/config.c and use that in show.c
+         and mshcmds.c, rather than hard coding in /bin/cat.
+
+       * Add mhnproc to the list of `procs' in mh-profile.man.
+
+       * Add configure test for lorder and tsort commands.
+
+       * Commented out the padding in the `msgs` struct in h/mh.h
+
+       * Change m_gmsg.c to allocate elements to the `info' array by
+         500 elements at a time (rather than MAXFOLDERS / 5).
+
+       * Add note to man page for mhmail that zero length messages are
+         not sent.  Need to use -body "" to send empty messages.
+
+       * zotnet/mts/mts.c : compare character with '\0', not NULL.
+
+       * sbr/getcpy.c : assign '\0' to character, not NULL.
+
+       * add m_fmsg to most programs in uip so that they explicitly free
+         folder/message structure when done with folder.
+
+       * uip/slocal.c : cleanup processing of sender.  Make sure it is
+         defined even if message is missing "From " line.
+
+Mon Mar 31 03:37:35 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.10.
+
+Sun Mar 30 21:46:17 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Add configure check for <locale.h>.  Turn on LOCALE support
+         by default.
+
+Thu Mar 20 03:21:24 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Reversed previous decision to retain "From " lines in slocal.
+         The "From " line is now removed from all messages.
+
+       * inc now saves the date from the "From " envelope in the
+         Delivery-Date header for all messages.
+
+       * sbr/m_getfld.c: Clean up processing of Return-Path and
+         Delivery-Date from the "From " envelope.
+
+Mon Mar 17 19:03:36 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * client.c: cast iaddr to int before comparing return value
+         of inet_addr with NOTOK.
+
+Tue Mar 11 04:38:10 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Grep test for signal names was failing on some OS'es because
+         of missing tabs in regex.
+
+Sat Mar  8 01:58:22 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.09.
+
+       * Move config files and format files to *.old before installing.
+
+       * Add configure check for killpg.
+
+       * msh.c: include <termios.h> instead of <termio.h> and
+         <sys/ioctl.h>.
+
+       * prompter.c: don't include <sys/ioctl.h> anymore.
+
+Thu Mar  6 04:03:24 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Added `-mime' and `-nomime' options to `repl'.
+         From MH-6.8.4 diff.
+
+Tue Mar  4 03:10:37 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * ruserpass.c : removed conflicting prototypes.
+
+       * rcvtty.c : Fixed rcvtty to obey terminal permissions granted
+         by `mesg' command.  Previously only worked on BSD machines.
+
+Mon Mar  3 00:18:59 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * rcvtty.c : Changed to use #define UTMP_FILE (if exists) rather
+         than hard coded "/etc/utmp".
+
+       * Released nmh-0.08.
+
+       * Changed slocal to lock .maildelivery (or file given by -maildelivery)
+         when accessing ndbm/db file for duplicate suppression, instead of
+         locking database itself.
+
+Thu Feb 27 05:28:09 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Added slocal action `mmdf' to deliver to a file in mmdf format.
+
+       * Changed the slocal actions `file' and `>' to always deliver in
+         mbox (uucp) format rather than be determined by RPATHS config
+         option.
+
+       * Changed the slocal action `mbox' to deliver in mbox (uucp) format
+         rather than mmdf format.
+
+       * slocal now adds Delivery-Date field to all messages (previously it
+         only added it to messages when delivering them to a file). The
+         "From " line is now retained on all messages if compiling with
+         RPATHS, rather than being discarded.
+
+       * rcvpack no longer adds the Delivery-Date field to messages.
+
+Sun Feb 23 22:03:54 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Removed the script packmbox, since it's functionality has been
+         added to packf.
+
+       * Changed packf so that it uses mbox (uucp) format by default
+         rather than mmdf format.  Added options -mbox and -mmdf to
+         packf so you can choose the preferred format.
+
+       * Changed rcvpack so that it uses mbox (uucp) format by default
+         rather than mmdf format.  Added options -mbox and -mmdf to
+         rcvpack so you can choose the preferred format.
+
+Tue Feb 18 00:01:05 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Changed nmh to use dot locking by default (although you
+         can still easily change this in config.h).
+
+       * Simplified locking code.  Removed code allowing setting of
+         locking type in mts.conf.  Now the locking type and locking
+         directory (if any) can only be set at compile time.
+
+Fri Feb 14 02:49:18 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Prefer getting timezone information from tm->gmtoff rather
+         than tzset and external timezone variable.
+
+Thu Feb 13 00:35:45 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Fixed typo in ruserpass.c in the variable toktabs.
+
+       * When ruserpass was added to LIBOBJS, it was missing
+         the suffix.
+
+       * Released nmh-0.07.
+
+Tue Feb 11 01:29:47 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Add check to configure, so that if ruserpass, or _ruserpass
+         is not found, build version of ruserpass in sbr.
+
+       * Added define's to discard.c, m_getfld.c, and scansbr.c so
+         the code that manipulates internals of stdio, will build
+         on SCO 5.x.
+
+       * Added #define to control whether to compile the simple
+         built-in FTP client in mhn.
+
+       * Added configure check for ushort and ulong.  Change code
+         to use ushort/ulong rather than u_short/u_long.
+
+       * A couple of small cleanups in locking code.
+
+       * Added configure check for gmtoff element in struct tm.
+
+       * Added configure check for tzset.
+
+Fri Feb  7 03:01:57 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.06.
+
+       * Removed code for machines that don't have socket
+         interface (how could they get mail anyway?).
+
+       * Removed code for BSD41 machines.  I don't think there are
+         many such machines around anymore.
+
+       * Add configure check for function uname, and prefer it
+         over gethostname.  General cleanup of zotnet/mts/mts.c.
+
+       * Change all `lseek' calls to use POSIX symbolic constants
+         SEEK_SET, SEEK_CUR, SEEK_END.
+
+Thu Feb  6 01:16:30 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Check lex generated file in zotnet/tws and use
+         pre-generated version if necessary.
+
+       * Released nmh-0.05.
+
+       * Change to use reliable signals on all platforms that have
+         sigaction.  Change so that interrupted system calls are
+         restarted for all signals except SIGALRM.  This fixes alarm
+         handling code in smail.c for BSD based systems.
+
+       * Added lorder and tsort commands so that created libs can
+         be linked in one pass.
+
+Tue Feb  4 01:33:00 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Changed pidwait so that while it is waiting for a child,
+         it should block signals rather than ignore them.
+
+Mon Feb  3 21:05:30 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Add checks to configure for dbm_open and -lndbm.
+
+Thu Jan 30 05:15:42 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * folder -pop and folder -push were freeing some memory too
+         quickly, which caused the entry popped from the stack to not
+         become the current folder.
+
+Wed Jan 29 01:28:02 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.04.
+
+       * Define ospeed and PC in termsbr.c is OS doesn't have
+         it.
+
+Sun Jan 26 20:25:10 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * editfile will create a symbolic link to the altmsg if it
+         can't make a link, on any machine supporting lstat.  Formerly
+         this would happen only on BSD42 based machines.
+
+Sat Jan 25 22:54:26 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * traverse (in popsbr.c) wasn't calling va_start before using
+         variable argument list.  Fixes core dump in inc when using POP.
+
+Fri Jan 24 03:27:59 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * The variable pass in remotemail needed to be set to
+         NULL.  (From MH-6.8.4 diff).  Fixes core dump of msgchk when
+         using POP.
+
+       * inc and msgchk were using -rpop by default when configured
+         with POP support.  Default is now -norpop.
+
+Thu Jan 23 02:01:17 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * By default, post will now give the SMTP HELO command with
+         the local hostname.  If you specify a hostname with the
+         clientname: option in mts.conf file, post will give the
+         HELO command with that name instead.  If the argument to the
+         clientname: option is empty, no HELO command is given.
+         (From the MH-6.8.4 diff)
+
+Wed Jan 22 01:55:45 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * When using `-help' for a command, it will also print its
+         profile compents from .mh_profile. (From MH-6.8.4 diff)
+
+       * "slocal -file" will now correctly takes its input from
+         a file (currently need to specify full path).
+
+Sun Jan 19 20:37:21 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * "slocal -debug" will now issue a warning if a non-blank
+         line in the .maildelivery file has less than 5 fields.
+
+Sat Jan 18 02:26:41 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Changed slocal so that code for duplicate suppression
+         (MH config was MSGID) is always built. Added the options
+         -[no]suppressdup to slocal to turn this on/off.
+
+Thu Jan 16 00:26:34 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.03.
+
+       * Fixed problem where mark would core dump if no
+         .mh_sequence file existed.
+
+       * Fixed problem where slocal would core dump if -debug
+         option was given, and certain headers were missing.
+
+       * Added patch to slocal to add `folder' (+) action, which
+         is shorthand for piping message to rcvstore.  Updated
+         man page.
+
+Wed Jan 15 21:30:17 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Changed flist option -unseen to -[no]all.  Cleaned up
+         flist man page.
+
+Fri Jan 10 20:36:33 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Fixed flist.  Changed the profile component `Folder-Order'
+         to `Flist-Order.  Added option `-sequence' to flist, so
+         you can specify the name of the sequence to search for.
+
+Thu Jan  9 00:20:48 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * A few minor portability cleanups.  Changed to use PATH_MAX
+         rather than MAXPATHLEN.  Don't assume ospeed variable exists
+         in termsbr.c.  Removed some conflicting prototypes.
+
+Wed Jan  8 11:05:02 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Add configure test to check if tgetent will accept NULL
+         and allocate its own buffer.  Borrowed from zsh.
+
+       * Changed libpath to etcpath.
+
+Mon Jan  6 04:15:35 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Cleaned up source code and Makefiles, so that if your `make'
+         supports the VPATH option, you can build nmh in a different
+         directory from where the source code is located.
+
+Fri Jan  3 05:05:18 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Released nmh-0.02.
+
+Wed Jan  1 17:41:52 1997  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Split mhook man page into man pages for rcvdist, rcvpack,
+         and rcvtty.
+
+Tue Dec 31 03:07:48 1996  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Changed code to use strerror, rather than using sys_errlist
+         and sys_nerr directly.
+
+Mon Dec 30 02:15:25 1996  Richard Coleman  <coleman@math.gatech.edu>
+
+       * -compat switch from install-mh removed.
+
+       * Changed the default POP port from "pop" to "pop3".
+
+Sat Dec 28 13:25:05 1996  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Changed mhn_defaults to mhn.defaults.  Changed create_mhn_defaults
+         (again) to mhn.defaults.sh.  Changed find_program (again) to
+         mhn.find.sh.  mhn.defaults.sh now takes the search path
+         as an argument.  Default search path is now specified in Makefile
+         rather than in script.
+
+Fri Dec 27 16:34:01 1996  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Changed mtstailor file to mts.conf.  Updated man pages.
+
+       * Changed si_value to si_val in mhn.c, since it conflicts with
+         macro defined on Solaris.
+
+Thu Dec 26 02:50:15 1996  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Added --enable-nmh-mhe (and --disable-nmh-mhe) to enable/disable
+         support for Emacs front-end mhe.  It is on by default.
+
+       * Added the following configure options: --enable-nmh-pop to
+         enable client side pop support, --enable-nmh-smtp to enable
+         SMTP support.  Client-side pop support now compiles.  Man
+         pages for inc, msgchk, mh-chart now correctly added pop
+         options if enabled.
+
+Tue Dec 24 14:33:20 1996  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Added configure test for bug in C libraries where linker
+         can't find ruserpass, but can find _ruserpass.
+
+       * Fixed configure test so that termcap variable ospeed is
+         correctly found.
+
+Mon Dec 23 19:40:17 1996  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Source files converted to ANSI C.
+       
+       * md5 now compiled separately rather than being included
+         in mhn.c.  Changed md5 to use memset and memcpy.
+
+Fri Dec 20 02:29:37 1996  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Collected the error routines adios, advise, admonish, and advertise
+         into one file (error.c), and did some rearranging of the code.
+
+Thu Dec 19 19:05:29 1996  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Added awk script sigmsg.awk (originally written by
+         Geoff Wing <mason@werple.apana.org.au> for zsh) to
+         automatically generate signal messages for pidstatus.c.
+         Added files sbr/signals.c, h/signals.h.  Code now uses
+         sigprocmask to block signals (if available).  Code now uses
+         signal blocking on non-BSD machines.
+
+Wed Dec 18 01:55:17 1996  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Add configure check for ATTVIBUG.  From Soren's mh autoconf work.
+
+       * Released nmh-0.01.
+
+       * Added configure code to check for type of signals functions
+         you have (POSIX or BSD style signals).  Added function
+         SIGPROCMASK to simulate sigprocmask on machines that don't
+         have POSIX signals.
+
+Fri Dec 13 19:40:48 1996  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Added -version switch to all commands.  Also added to
+         their man pages.
+
+Mon Dec  9 16:36:54 1996  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Renamed uip/trmsbr.c to termsbr.c and changed it to use
+         POSIX termios.h style functions if present.
+
+Tue Dec  3 16:18:39 1996  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Changed support/general/bootmhn.sh to output new mhn_defaults
+         file to standard output by default (makes it easier for testing).
+         Changed name of script to create_mhn_defaults.  Changed bootmhn.findit
+         script to find_program.
+
+Sun Dec  1 10:00:00 1996  Richard Coleman  <coleman@math.gatech.edu>
+
+       * Added patch to uip/folder.c from exmh distribution to
+         speed up -recurse option.
+
+       * Added flist command from exmh distribution.  It doesn't work
+         yet, but it compiles :-)
+
+       * Changed default location for install to /usr/local/nmh/{bin,etc,lib,man}.
+         Split files so that format and configuration files go in nmh/etc, and
+         support binaries go in nmh/lib.  Of course, all this can now be changed
+         in the top level Makefile.
+
+       * Started with mh-6.8.3 as based and converted to autoconf.
+         Rewrote all the Makefiles.  Currently only works with sendmail/smtp.
+         Pop support and plenty of other things, are now broken.
diff --git a/DIFFERENCES b/DIFFERENCES
new file mode 100644 (file)
index 0000000..1a24037
--- /dev/null
@@ -0,0 +1,278 @@
+
+The following are the differences between nmh and MH-6.8.3.  UCI has
+since released MH-6.8.4.  Most of the new features it adds have
+also been added to nmh, but those differences are not listed here.
+There are a few new features in MH-6.8.4 that are missing from nmh,
+but they are primarily undocumented in MH-6.8.4 (and no one has
+ever asked me for them).
+
+GENERAL
+-------
+*) nmh has been converted to autoconf (configure) and should be
+   more portable and easier to install than MH.  In particular, nmh
+   will now compile on Linux.
+*) All of MH's Makefiles have been rewritten for nmh.  You can now
+   use GNU make without any problems.  Also, if your make supports
+   the VPATH variable (such as GNU make), you can now compile in a
+   different directory from the one containing the source code.
+*) The source code for nmh has been substantially cleaned up.
+   It now requires an ANSI C compiler (gcc is fine) to compile.
+*) A new option `-version' has been added to all the commands.
+*) The POP server (popd) has been removed from the distribution.
+   But the client-side support for POP and KPOP is still present.
+   Also nmh doesn't currently support some of the alternate forms of
+   POP such as APOP or RPOP that are contained in MH (although they
+   could easily be resurrected, if necessary).
+*) The support for MH-style bulletin boards has been removed
+   (NNTP makes this obsolete anyway).
+*) Currently nmh doesn't support using shared libraries for libmh.
+   This may return in the future, but is not a high priority, since
+   the nmh commands are not that large, and disk space gets cheaper
+   every day.
+*) Almost all of the commands in nmh have been modified to accept
+   an arbitrary number of command line arguments (most MH commands
+   will not accept more than 1000 arguments on the command line).
+*) nmh should be more secure than MH.  Hundreds of buffer overflow
+   problems have been fixed in the source code.  Of course, I still
+   don't make any guarantees.
+
+DOCUMENTATION
+-------------
+*) Many of the man pages have been cleaned up or clarified.
+*) The mhook man page has been split into separate man pages for
+   rcvtty, rcvdist, and rcvpack.
+*) Added new man page `mh-draft' which discusses the nmh draft
+   folder facility.
+*) The various `procs' (rmmproc, moreproc, showmimeproc, etc...)
+   are now documented in the "mh-profile" man page.  Many of these
+   were previously undocumented.
+
+FORMATTING
+----------
+*) Added a new formatting string escape %(decode) to decode and
+   display RFC-2047 encoded header fields.
+
+SEQUENCES
+---------
+*) The is no longer a limitation on the length of lines in the file
+   containing your public sequences (.mh_sequences).  That should be
+   the end of the error message ".mh_sequences is poorly formatted".
+
+ANNO
+----
+*) The switch -inplace is now on by default.
+
+CONFLICT
+--------
+*) Conflict now works on systems that define more
+   than 100 groups.
+
+DIST
+----
+*) The switch -inplace is now on by default.
+
+FLIST
+-----
+*) A new command `flist' has been added to nmh, that will list the
+   folders that contain messages in a given sequence (such as the
+   unseen sequence).  This was a much needed command in the MH suite
+   of programs.
+
+FOLDER
+------
+*) `folder -all' now dynamically allocates space for folder names and can
+   handle more than 300 folders.
+*) `folder' now uses the standard Unix trick of looking at the number of
+   links to a directory entry, in order to avoid doing a stat(2) call
+   on messages in folders with no subfolders.  This greatly increases
+   the speed of `folder -all -recurse' on large mail setups.
+*) The switches `-header' and `-total' are more orthogonal.  The command
+   `folder -all -noheader -nototal' now does the right thing.
+
+FORW
+----
+*) The switch -inplace is now on by default.
+*) Added switches `-dashstuffing' and `-nodashstuffing', to determine
+   whether forwarded messages are RFC934 quotes (dashstuffed).
+   (This corresponds to the undocumented switch "nodashmunging"
+   in MH).
+
+INC
+---
+*) If compiled with RPATHS, a Delivery-Date header is now added to all
+   messages incorporated with `inc'.
+*) Using the new format string escape %(decode), the scan lines for `inc'
+   will correctly decode RFC-2047 encoded headers.
+
+MARK
+----
+*) If neither of the switches -public/-nopublic are specified, then
+   existing sequences will retain their current public/private status,
+   instead of being changed to public.
+*) The command "mark -list -sequence foo" will now indicate if the
+   sequence "foo" is a private sequence.
+
+MHBUILD
+-------
+*) The functionality in `mhn' to create MIME messages, has been cleaned
+   up substantially, and moved to separate command `mhbuild'.
+*) If given a file argument of "-", mhbuild will now accept the MIME
+   composition file on the standard input, and output the new MIME
+   message to the standard output.  This makes it much easier to use
+   this functionality in shell scripts.
+*) The switch -norfc934mode is now the default.
+
+MHL
+---
+*) There is a new variable "decode" which instructs `mhl' to decode
+   a component as a RFC-2047 encoded header field.
+
+MHLIST
+------
+*) The functionality of `mhn -list' has been moved to the new
+   command `mhlist'.
+
+MHN
+---
+*) mhn is now obsolete.  It has been split into the commands mhbuild,
+   mhlist, mhshow, mhstore, and viamail.  mhn still exists for
+   backward compatibility, but the new commands should be preferred.
+*) Changed the profile entry automhnproc to automimeproc
+   (which has slightly different semantics).
+
+MHPATH
+------
+*) `mhpath all' will no longer abort if the folder has more than
+   998 messages.
+
+MHSHOW
+------
+*) The functionality of `mhn -show' has been moved to the new
+   command `mhshow'.
+*) mhshow now correctly treats unknown subtypes of text as
+   text/plain, as specified by RFC-2046.
+*) mhshow now correctly treats unknown subtypes of multipart as
+   multipart/mixed, as specified by RFC-2046.
+*) You can now override the default method by which mhshow handles
+   subtypes of multipart that are known internally (mixed, alternate,
+   etc...).  Previously the behavior on these types could not be
+   changed.
+
+MHSTORE
+-------
+*) The functionality of `mhn -store' has been moved to the new
+   command `mhstore'.
+*) mhstore will now correctly identify a formatting string of "-"
+   and send the content to stdout.
+
+PACKF
+-----
+*) When packing a folder, the default format is now `mbox' format, rather
+   than `mmdf' format.  The options -mbox and -mmdf have been added to
+   `packf' so you can choose the desired format.
+
+PACKMBOX
+--------
+*) The script packmbox has been removed from the nmh distribution, since
+   its functionality has been added to the command packf.
+
+PICK
+----
+*) If neither of the switches -public/-nopublic are specified, then
+   existing sequences will retain their current public/private status,
+   instead of being changed to public.
+
+RCVPACK
+-------
+*) The default format for `rcvpack' is now `mbox' format, rather than
+   `mmdf' format.  The options -mbox and -mmdf have been added to
+   `rcvpack' so you can choose the desired format.
+*) Rcvpack no longer adds the field "Delivery-Date", as that is added
+   by `slocal'.
+
+RCVSTORE
+--------
+*) Added new switches -unseen/-nounseen to control whether new messages
+   are added to the Unseen-Sequence.
+
+RCVTTY
+------
+*) The option `-width' has been added.
+
+REFILE
+------
+*) If an conflict occurs when using the `-preserve' switch,
+   then refile will search for and use the next available
+   message number above the one you wish to preserve, rather
+   than aborting with an error.
+*) Added new options `-unlink' and `-nounlink' which specify
+   that that messages "removed" from the source folder should
+   just be unlinked, rather than moved to name with prefix.
+
+REPL
+----
+*) Added new options `-format' and `-noformat'.  The switch `-format'
+   will filter the message to which you are replying with a standard
+   message filter "mhl.reply" which is included in the distribution.
+   The switch `-noformat' will negate `-format' and `-filter', so that
+   the message to which you are replying is not included in the draft.
+*) Added new options `-group' and `-nogroup'.  These switches direct
+   repl as to whether or not this is a group reply.  A group reply uses
+   a different forms (components) file (default is replgroupcomps).
+*) The standard forms files "replcomps" and "replgroupcomps" now have
+   support for the "Mail-Reply-To:" and "Mail-Followup-To:" header fields.
+*) The switch -inplace is now on by default.
+
+RMM
+---
+*) Added new options `-unlink' and `-nounlink' which specify
+   that that "removed" messages should just be unlinked, rather
+   than moved to name with prefix.
+
+SCAN
+----
+*) Using the new format string escape %(decode), the scan lines created
+   by `scan' will correctly decode RFC-2047 encoded headers.
+
+SHOW/NEXT/PREV
+--------------
+*) Added new options `-checkmime' and `-nocheckmime' which allow you
+   to enable and disable the test for MIME messages.
+*) The "mhnproc" profile entry has been changed to "showmimeproc".
+*) Added `-showmimeproc' switch to specify the showmimeproc at the
+   command line.
+*) The default "showproc" has been changed to `mhl'.
+*) The default "showmimeproc" is now `mhshow'.
+*) `show' is better at handling alternate character sets.  It knows that
+   US-ASCII is a subset of any ISO-8859-X character set.
+
+SLOCAL
+------
+*) Added new action `folder' or `+' to slocal, which adds new message
+   to a nmh folder.  Currently, this is handled by piping the new
+   message to rcvstore, although this may change in the future.
+*) The action `mbox' now delivers to a file in mbox format.  Previously
+   it delivered to a file in mmdf format.
+*) Added new action `mmdf' to deliver to a file in mmdf format.
+*) Added new options -[no]suppressdup to slocal to check for duplicate
+   messages.  The code for suppression of duplicate messages (MH config
+   was MSGID) is now always built into slocal.
+*) Improved the debugging of slocal ".maildelivery" files.  It will now
+   warn when an entry in this file is skipped because there are not
+   enough fields.  Also the debugging output of slocal has been cleaned up,
+   so that it is much easier to read.
+*) Slocal now adds the Delivery-Date header to all delivered messages.
+   Previously it only added them to messages delivered to a file.
+
+VIAMAIL
+-------
+*) The functionality of this new command, was formerly part of
+   `mhn' as the (undocumented) option `mhn -viamail'.
+
+WHATNOW
+-------
+*) Added new action "mime" to whatnow, which causes whatnow to call
+   program specified by "buildmimeproc" profile entry, to process
+   MIME composition files (default is mhbuild).
+*) Added new action "delete" to whatnow, which deletes draft file
+   and exits.
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..92bffa2
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,229 @@
+#
+# INSTALL -- installation instructions
+#
+# $Id$
+#
+
+--------------
+Installing nmh
+--------------
+Please read all of the following instructions before you begin
+building nmh.
+
+You should check the MACHINES file to see if there are any specific
+build instructions for your operating system.  To build nmh, you will
+need an ANSI C compiler such as gcc.
+
+1) Run the command
+
+   sh configure [options]
+
+   This will check the configuration of your OS, and create
+   the include file config.h, as well as the various Makefiles.
+
+   The configure script accepts various options.  The options of
+   most interest are listed below.  To see the list of all available
+   options, you can run
+
+   sh configure --help
+
+2) (IMPORTANT)  Edit the user configuration section at the beginning
+   of the generated include file `config.h'.  Currently, not everything
+   is auto-configured, so some #defines must be set manually.
+
+3) Edit the user configuration section at the top of the main Makefile.
+
+4) make
+
+5) make install
+
+6) Edit the file `mts.conf' (installed in the nmh `etc' directory)
+   and make any necessary changes for the mail transport interface
+   you are using.
+
+   The default `mts.conf' file assumes you retrieve new mail from
+   a local (or NFS mounted) maildrop, and send outgoing mail by
+   injecting the message to a mail transfer agent (such as sendmail)
+   on the local machine via SMTP.
+
+   If you have enabled POP support and you want this to be the
+   default method of accessing new mail, you will need to change
+   the values of the variables "servers", "pophost", "localname",
+   and possibly "mmailid".
+
+   a) "servers" defines the server to which you send outgoing SMTP
+      traffic.
+
+   b) "pophost" defines the server that runs the POP daemon, and to
+      which `inc' and `msgchk' will query for new mail.
+
+   c) "localname" defines the hostname that nmh considers local.
+      If not set, then nmh queries your OS for this value.  You may
+      want to change this if you wish your e-mail to appear as if it
+      originated on the POP server.
+
+   d) "mmailid" is checked to see if nmh should do username
+      masquerading.  If the value of this field is non-zero, then
+      nmh will check if the pw_gecos field in the password file
+      has the form
+
+          Full Name <fakeusername>
+
+      If the pw_gecos field has this form, then the internal nmh
+      routines that find the username and full name of a user will
+      return "fakeusername" and "Full Name" respectively.  This is
+      useful if you wish messages that you send to appear to come
+      from the username of your POP account, rather than your username
+      on the local machine.
+
+   If you compile with POP support, but only want to use it occasionally,
+   then you can always use the `-host' and `-user' options to `inc'
+   and `msgchk' instead of changing `mts.conf'.
+
+   Check the `mh-tailor' man page for a list of all the available
+   options for this file.
+
+7) If you have enabled POP support, make sure that `pop3' (or more
+   precisely the value of the define POPSERVICE in config.h) is defined
+   in the /etc/services file (or its NIS/NIS+ equivalent) on the client
+   machine.  It should be something equivalent to "110/tcp".  This might
+   have already been done when the pop daemon was installed.
+
+8) Edit the file `mhn.defaults' (installed in the nmh `etc' directory).
+   This file contains the default profile entries for the nmh command
+   `mhn' and is created by the script `mhn.defaults.sh'.  This script
+   will search a generic path (essentially your $PATH) for programs to
+   handle various content types (for example, xv to display images).
+   You can re-run this script and give it a more tailored path.  You may
+   want to re-run this script later if you install new programs to
+   display content.  An example of this is:
+
+   cd support/general
+   ./mhn.defaults.sh /usr/local/bin:/usr/X11/bin:/usr/ucb > mhn.defaults
+
+   and then move `mhn.defaults' into the nmh `etc' directory.
+
+   The `mhn.defaults.sh' script only searches for a simple set of programs.
+   If you have specialized programs to handle various types, you will need
+   to edit the `mhn.defaults' file manually.  The syntax of this file is
+   described in the man page for `mhn', and in section 9.4 of the book
+   "MH & xmh: Email for Users and Programmers", 3rd edition, by Jerry Peek.
+
+9) Add an optional global mh.profile, if desired.  This profile should be
+   placed in the nmh `etc' directory with the name `mh.profile'.  This
+   file will be used to construct the initial .mh_profile of a new nmh
+   user, but will not be consulted after that.
+
+-----------------------------------------------
+Compiler options, or using a different compiler
+-----------------------------------------------
+By default, configure will use the "gcc" compiler if found.  You can use a
+different compiler, or add unusual options for compiling or linking that
+the "configure" script does not know about, by either editing the user
+configuration section of the top level Makefile (after running configure)
+or giving "configure" initial values for these variables by setting them
+in the environment.  Using a Bourne-compatible shell (such as sh,ksh,zsh),
+you can do that on the command line like this:
+    CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+Or on systems that have the "env" program, you can do it like this:
+    env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+
+----------------------------------------
+Building nmh on additional architectures
+----------------------------------------
+To build nmh on additional architectures, you can do a "make distclean".
+This should restore the nmh source distribution back to its original
+state.  You can then configure nmh as above on other architectures in
+which you wish to build nmh.  Or alternatively, you can use a different
+build directory for each architecture.
+---------------------------------
+Using a different build directory
+---------------------------------
+You can compile the nmh in a different directory from the one containing
+the source code.  Doing so allows you to compile it on more than one
+architecture at the same time.  To do this, you must use a version of
+"make" that supports the "VPATH" variable, such as GNU "make".  "cd" to
+the directory where you want the object files and executables to go and
+run the "configure" script.  "configure" automatically checks for the
+source code in the directory that "configure" is in.  For example,
+    cd /usr/local/solaris/nmh
+    /usr/local/src/nmh-1.0/configure
+    make
+
+---------------------
+Options for configure
+---------------------
+--prefix=DIR     (DEFAULT is /usr/local/nmh)
+     This will change the base prefix for the installation location
+     for the various parts of nmh.  Unless overridden, nmh is installed
+     in ${prefix}/bin, ${prefix}/etc, ${prefix}/lib, ${prefix}/man.
+
+--bindir=DIR     (DEFAULT is ${prefix}/bin)
+     nmh's binaries (show, inc, comp, ...) are installed here.
+
+--libdir=DIR     (DEFAULT is ${prefix}/lib)
+     nmh's support binaries (post, slocal, mhl, ...) are installed here.
+
+--sysconfdir=DIR     (DEFAULT is ${prefix}/etc)
+     nmh's config files (mts.conf, mhn.defaults, ...) are installed here.
+
+--mandir=DIR     (DEFAULT is ${prefix}/man)
+     nmh's man pages are installed here.
+
+--with-mts=MTS   (DEFAULT is smtp)
+     specify the mail transport system you want to use.  The two
+     acceptable options are "smtp" (which is the default), and
+     "sendmail".
+
+     If you use "smtp", this will enable a direct SMTP (simple
+     mail transport protocol) interface in nmh.  When sending
+     mail, instead of passing the message to the mail transport
+     agent, `post' will open a socket connection to the mail
+     port on the machine specified in the `mts.conf' file
+     (default is localhost), and speak SMTP directly.
+
+     If you use "sendmail", then `post' will send messages by
+     passing forking a local copy of sendmail.  Currently it
+     will still speak SMTP with this local copy of sendmail.
+
+     If you wish to use a transport agent other than sendmail, you will
+     need to use a `sendmail wrapper'.
+
+--with-editor=EDITOR  (DEFAULT is vi)
+     specify the full path of the default editor to use.  If this
+     option is not given, then the configuration process will search
+     for the `vi' command and use it as the default.  If you wish to
+     specify an interface which is compatible with MH, then use the
+     nmh command `prompter'.  If you specify `prompter', then you don't
+     need to give the full pathname.
+
+--with-pager=PAGER    (DEFAULT is more)
+     specify the default pager (file lister) to use.  If this option
+     is not given, then the configuration process will search for the
+     command `more' and use it as the default.
+
+--enable-nmh-mhe    (DEFAULT)
+     Add support for the Emacs front-end `mhe'.
+
+--enable-nmh-pop
+    Enable client-side support for pop.
+
+--with-krb4=PREFIX
+    Specify the location of Kerberos V4 for KPOP support.  You will
+    also need to specify the option `--enable-nmh-pop'.  After running
+    configure, you will probably need to change the POPSERVICE define
+    in config.h.  See the comments inside config.h for details.
+
+--with-hesiod=PREFIX
+    Specify the location of Hesiod.
+
+--enable-nmh-debug
+    Enable debugging support.
+
+--
+Richard Coleman
+coleman@math.gatech.edu
diff --git a/MACHINES b/MACHINES
new file mode 100644 (file)
index 0000000..71f5362
--- /dev/null
+++ b/MACHINES
@@ -0,0 +1,92 @@
+#
+# MACHINE -- operating system specific information
+#
+# $Id$
+#
+
+--------------------------------------
+
+FreeBSD:
+OpenBSD:
+NetBSD:
+
+Some BSD4.4 machines have problems when running nmh's configure script.
+They will be unable to find the location of vi and sendmail.  This is
+due to POSIX features (breakage?) in the shell sh.  The solution is to
+run the configure script under the shell `bash'
+
+bash configure
+
+--------------------------------------
+
+HPUX:
+
+Lots of problems have been reported with using HPUX `cc'.  In particular,
+problems with `scan' giving incorrect dates (everything is 01/00).
+It is highly recommended that you use `gcc' instead.
+
+Also, new versions of HPUX (10.20?) will core dump in `scan' because
+of some workaround code in zotnet/tws/lexstring.c.  This workaround is
+needed for older versions of HPUX, but causes problems on newer versions.
+The solution is the added the line
+
+#undef hpux
+
+after line 15 of the file zotnet/tws/lexstring.c.
+
+--------------------------------------
+
+Irix (SGI):
+
+If you are compiling nmh with POP support, then the configuration
+process will search for (and find) the Irix version of "ruserpass".
+Unfortunately, this version is buggy and causes core dumps.  The best
+bet is to use the version that comes with nmh.  After running configure,
+edit the Makefile in the "sbr" directory, and add "ruserpass.o" to the
+LIBOBJS line.  Then run "make" as normal.
+
+--------------------------------------
+
+Linux:
+
+Make sure you uncomment the Linux section in the config.h file after
+running configure.
+
+The configuration script does a test to discover the functions
+sigsetjmp/siglongjmp.  Since they are macros on Linux, the configuration
+process doesn't find them.  After running configure, you should change
+the line in config.h to define HAVE_SIGSETJMP.
+
+For some Linux distributions, the configure script doesn't find the
+ndbm/gdbm library (dbm_open, dbm_close).  In this case, you should try to
+configure nmh like this:
+
+    LIBS=-lgdbm ./configure [configure options]
+
+The configuration script does a test to discover if your vi is broken
+(if it reports non-zero exit codes on certain pseudo-errors).  This test
+will hang if the program `ex' on your system is a link to the vi clone
+`vile'.  The workaround is to replace the command ex as a link to another
+vi clone such as nvi or elvis.
+
+--------------------------------------
+
+SCO:
+
+Make sure you uncomment the SCO section in the config.h file after
+running configure.
+
+--------------------------------------
+
+Solaris:
+
+Builds ok.
+
+--------------------------------------
+
+SunOS 4.1.3:
+
+You can't use the C compiler that comes with SunOS 4.1.3 since
+it isn't ANSI C.  But nmh builds just fine with gcc.
+
+--------------------------------------
diff --git a/MAIL.FILTERING b/MAIL.FILTERING
new file mode 100644 (file)
index 0000000..becf31a
--- /dev/null
@@ -0,0 +1,102 @@
+
+INTRODUCTION
+------------
+It is a common practice when using nmh to filter your inbound mail
+directly into nmh folders.  There are several programs which allow you
+to do this, of which two common ones are procmail and slocal.
+
+SLOCAL
+------
+The slocal command is part of the nmh distribution.  It is a fairly
+simple mail filtering program.  Check the slocal man page for an example
+filtering file (called .maildelivery).
+
+PROCMAIL
+--------
+Probably the most popular mail filtering command is procmail.  It can
+filter mail into standard mbox-style spool files, as well as into MH/nmh
+style folders.
+
+Although procmail knows how to put a message directly into an nmh folder,
+this is not recommended.  Procmail doesn't know about nmh sequences.
+Instead you should have procmail use the nmh command `rcvstore' to put
+the message into the folder.  The `rcvstore' command will (by default)
+add each new message to the "unseen" sequence, so you can detect new
+messages in folders with the `flist' command.
+
+Also, nmh commands generally like to keep mail messages in RFC-822
+format.  But by default, procmail will leave the first line of the
+message unchanged.  This line (which usually begins with "From ") is
+not in the standard RFC-822 format.  It is recommended that you use the
+command `formail' (which comes in the procmail distribution) to rewrite
+this line so that it begins with the header name "X-Envelope-From:".
+An example of how to do this is given below.
+
+The reason the header name "X-Envelope-From:" is recommended, is that the
+nmh command `packf' (as of version 0.23) will check for this header when
+packing folders.  The `packf' command knows how to undo the rewriting
+of the "From " line to the "X-Envelope-From:" line.  By checking for
+this header name, `packf' is able to pack the folder into exactly the
+form that is used if procmail delivers to the standard mail spool.
+
+If you do not rewrite the "From " line into this format, the `packf'
+command will still work.  But it may create fake "From " lines which
+are not the same as the originals.
+
+Here is a typical .procmailrc file for using procmail in conjunction
+with nmh.  For more information, see the manual pages for procmail,
+procmailrc and procmailex.
+
+###################################################################
+# .procmailrc
+###################################################################
+# To use procmail, put the next line in your .forward file:
+# "|IFS=' ' && exec /usr/local/bin/procmail -f- || exit 75 #XXX"
+# Do not remove the double quotes.  Change XXX to your username.
+# Edit path to procmail above, and the VARIABLES below, as needed.
+# Adapt the MAILING LIST section below for lists you subscribe to.
+# Your .forward needs to be world-readable, but not world-writable.
+###################################################################
+# This .procmailrc is written for use with nmh/mh/exmh/mh-e
+###################################################################
+
+### VARIABLES ###
+VERBOSE=off
+SHELL=/bin/sh
+PATH=/usr/local/nmh/lib:/usr/local/nmh/bin:/usr/bin:/usr/local/bin
+MAILDIR=$HOME/Mail
+LOGFILE=$MAILDIR/procmail.log
+LOCKEXT=.lock
+
+#################
+# CLEANUP MESSAGE
+#################
+
+# Force the "From user date" to become part of header
+:0 Whf
+| formail -z -R 'From ' X-Envelope-From:
+
+###############
+# MAILING LISTS
+###############
+
+:0 w: nmh-workers/$LOCKEXT
+* ^Resent-from: *nmh-workers
+| rcvstore +nmh-workers
+
+# catches exmh-{announce,users,workers}
+:0 w: exmh/$LOCKEXT
+* ^TOexmh
+| rcvstore +exmh
+
+# Catch junk.  Don't add it to "unseen" sequence (nmh only)
+:0 w: junk/$LOCKEXT
+* ^(reply-to|from|sender):.*(spammer|flamer|evil-host)
+| rcvstore -nounseen +junk
+
+################
+# DEFAULT ACTION
+################
+:0 w: inbox/$LOCKEXT
+| rcvstore +inbox
+
diff --git a/Makefile.in b/Makefile.in
new file mode 100644 (file)
index 0000000..97313fc
--- /dev/null
@@ -0,0 +1,187 @@
+#
+# Makefile for top level of nmh distribution
+#
+# $Id$
+#
+
+# nmh version
+VERSION = @VERSION@
+
+SHELL = /bin/sh
+@SET_MAKE@
+
+srcdir = @srcdir@
+VPATH  = @srcdir@
+
+# ========== USER CONFIGURATION SECTION ==========
+#
+# If `make' is executed in the directory containing this Makefile,
+# any changes made in this section will override the values of
+# these parameters in makefiles in any of the subdirectories.
+prefix      = @prefix@
+exec_prefix = @exec_prefix@
+
+# location of standard commands
+bindir      = @bindir@
+
+# location of support binaries and scripts
+libdir      = @libdir@
+
+# location of nmh configuration and formats files
+etcdir      = @sysconfdir@
+
+# location of man pages
+mandir      = @mandir@
+# location of incoming mail
+mailspool   = @mailspool@
+
+# location of mail transport agent
+sendmailpath = @sendmailpath@
+
+# default editor
+default_editor = @editorpath@
+
+# default pager
+default_pager = @pagerpath@
+
+CC       = @CC@
+CPPFLAGS = @CPPFLAGS@
+DEFS     = @DEFS@
+CFLAGS   = @CFLAGS@
+LDFLAGS  = @LDFLAGS@
+LIBS     = @LIBS@
+# ========== YOU SHOULDN'T HAVE TO CHANGE ANYTHING BELOW HERE ==========
+
+# flags passed to recursive makes in subdirectories
+MAKEDEFS = CC='$(CC)' CPPFLAGS='$(CPPFLAGS)' DEFS='$(DEFS)' \
+CFLAGS='$(CFLAGS)' LDFLAGS='$(LDFLAGS)' LIBS='$(LIBS)' \
+prefix='$(prefix)' exec_prefix='$(exec_prefix)' bindir='$(bindir)' \
+etcdir='$(etcdir)' libdir='$(libdir)' mandir='$(mandir)' \
+mailspool='$(mailspool)' sendmailpath='$(sendmailpath)' \
+default_editor='$(default_editor)' default_pager='$(default_pager)'
+
+INSTALL         = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA    = @INSTALL_DATA@
+
+.SUFFIXES:
+
+# all files in this directory included in the distribution
+DIST = README INSTALL MACHINES COPYRIGHT VERSION DIFFERENCES FAQ \
+       TODO ZSH.COMPLETION MAIL.FILTERING ChangeLog install-sh \
+       mkinstalldirs Makefile.in aclocal.m4 acconfig.h config.h.in \
+       configure.in configure stamp-h.in
+
+# subdirectories in distribution
+SUBDIRS = h config sbr zotnet mts uip etc man
+
+# ========== DEPENDENCIES FOR BUILDING AND INSTALLING ==========
+
+# default target
+all: config.h Makefile all-recursive
+
+all-recursive:
+       for subdir in $(SUBDIRS); do \
+         (cd $$subdir && $(MAKE) $(MAKEDEFS) all) || exit 1; \
+       done
+
+install uninstall:
+       for subdir in $(SUBDIRS); do \
+         (cd $$subdir && $(MAKE) $(MAKEDEFS) $@) || exit 1; \
+       done
+
+# ========== DEPENDENCIES FOR CLEANUP ==========
+
+mostlyclean: mostlyclean-recursive mostlyclean-local
+clean:       clean-recursive       clean-local
+distclean:   distclean-recursive   distclean-local
+realclean:   realclean-recursive   realclean-local
+superclean:  superclean-recursive  superclean-local
+
+mostlyclean-local:
+       rm -f *~
+
+clean-local: mostlyclean-local
+
+distclean-local: clean-local
+       rm -f Makefile config.h config.status config.log config.cache stamp-h distname
+
+realclean-local: distclean-local
+
+superclean-local: realclean-local
+       cd $(srcdir) && rm -f config.h.in stamp-h.in configure
+
+mostlyclean-recursive clean-recursive distclean-recursive realclean-recursive superclean-recursive:
+       for subdir in $(SUBDIRS); do \
+         target=`echo $@ | sed 's/-recursive//'`; \
+         (cd $$subdir && $(MAKE) $(MAKEDEFS) $$target) || exit 1; \
+       done
+
+# ========== DEPENDENCIES FOR MAINTENANCE ==========
+
+Makefile: Makefile.in config.status
+       CONFIG_FILES=$@ CONFIG_HEADERS= ./config.status
+
+config.status: configure VERSION
+       ./config.status --recheck
+
+configure: configure.in aclocal.m4
+       cd $(srcdir) && autoconf
+
+config.h: stamp-h
+stamp-h: config.h.in config.status
+       CONFIG_FILES= CONFIG_HEADERS=config.h ./config.status
+
+config.h.in: stamp-h.in
+stamp-h.in: configure.in acconfig.h aclocal.m4
+       cd $(srcdir) && autoheader
+       echo > $@
+
+# rebuild all autoconf files
+reset:
+       cd $(srcdir) && autoheader
+       cd $(srcdir) && autoconf
+       cd $(srcdir) && echo > stamp-h.in
+
+# name of new nmh distribution tar file
+tarfile = nmh-$(VERSION).tar.gz
+
+# ftp directory location
+ftpdir = /ftp/nmh
+
+# file containing name of new nmh distribution
+distname:
+       @echo nmh-$(VERSION) > distname
+
+# build nmh distribution
+distdir = `cat distname`
+nmhdist: $(DIST) distname
+       @echo "Begin building nmh-$(VERSION) distribution"
+       rm -rf $(distdir)
+       mkdir $(distdir)
+       @chmod 755 $(distdir)
+       @echo "Copying distribution files in main directory"
+       @for file in $(DIST); do \
+         cp -p $(srcdir)/$$file $(distdir); \
+       done
+       @for subdir in $(SUBDIRS); do \
+         mkdir $(distdir)/$$subdir; \
+         chmod 755 $(distdir)/$$subdir; \
+         (cd $$subdir && $(MAKE) $@) || exit 1; \
+       done
+       chmod -R a+r $(distdir)
+       tar chf - $(distdir) | gzip -c > $(tarfile)
+       rm -rf $(distdir) distname
+       @echo "Done building nmh-$(VERSION) distribution"
+
+# release a new nmh distribution into ftp directory
+nmhrelease:
+       rm -f $(ftpdir)/$(tarfile)
+       rm -f $(ftpdir)/nmh.tar.gz
+       mv $(tarfile) $(ftpdir)/$(tarfile)
+       cd $(ftpdir) && ln -s $(tarfile) nmh.tar.gz
+       cd $(ftpdir) && md5sum *.gz > MD5SUM
+
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..595e444
--- /dev/null
+++ b/README
@@ -0,0 +1,56 @@
+#
+# README -- I love README files.
+#
+# $Id$
+#
+
+-----------
+what is it?
+-----------
+nmh (new MH) is an electronic mail handling system.  It was
+originally based on the package MH-6.8.3, and is intended to be
+a (mostly) compatible drop-in replacement for MH.
+
+Although development of nmh is ongoing, it appears to be generally
+stable and is in current use.  But it is possible that I may break
+things as changes are made.
+
+--------------
+installing nmh
+--------------
+To install nmh, check the INSTALL and MACHINES files.  If you have
+previously used MH, check the file DIFFERENCES for a list of the
+differences between nmh and MH.
+
+--------------------------------
+ftp and web sites, mailing lists
+--------------------------------
+To find out about the mailing lists, ftp sites, and web page
+for nmh, check the FAQ included in this distribution.
+
+---------------
+nmh maintenance
+---------------
+nmh is currently being developed and maintained by
+Richard Coleman <coleman@math.gatech.edu>.  Please send bug reports
+and suggestions to the nmh development mailing list at
+nmh-workers@math.gatech.edu.
+
+----------------
+acknowledgments
+----------------
+I would like to give credit where it is due.  nmh could never have
+been developed without all the hard work that the RAND Corporation
+and the University of California put into the development of the
+MH message system, that nmh is based on.
+
+Also, since I've used the version of (v)snprintf() from the
+Apache web server, I need to make the following acknowlegement:
+
+This product includes software developed by the Apache Group
+for use in the Apache HTTP server project (http://www.apache.org/).
+
+--
+Richard Coleman
+coleman@math.gatech.edu
+http://www.math.gatech.edu/~coleman/
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..861da67
--- /dev/null
+++ b/TODO
@@ -0,0 +1,205 @@
+[TODO]
+
+* Write different function to read configuration files, instead
+  of using m_getfld.c
+* convert calls from sprintf/vsprintf to snprintf/vsnprintf
+* convert calls from getcpy to strdup
+* modularize access to context/profile list.
+* add command printm to print messages
+* finish changing to macros for msgstats and msgflags
+* Add support for Mail-Followup-To and Mail-Reply-To
+* Add support for profile entry "Mailing-Lists"
+* let mhn and mhl accept files on standard in, and output to
+  standard out, when filtering files.
+* Add switch -more to `show' to call moreproc.
+* Add new command "show" at WhatNow? prompt.
+
+[POSSIBILITIES]
+
+MAN PAGES
+---------
+* Update mh-tailor man page.
+* generate mh-chart man page from other man pages
+* update default mode in man pages with sed
+
+SEQUENCES
+---------
+* Change so you can have more than 26 sequences.  Unfortunately,
+  given the way that the bit flags for sequences work, this is
+  not easy.
+* Maybe add option gracefully handle empty sequences (-force).
+
+ENVIRONMENT/PROFILE
+-------------------
+* Maybe add profile entry "Pager" to change the default pager?
+* Should nmh check for EDITOR or PAGER environment variable?
+
+POP
+---
+* Clean up uip/spop.c (I dont' think it's needed any longer).
+* Need to decide if want to support APOP, RPOP, MPOP.  APOP
+  and RPOP still work, but need autoconf support added.  Does
+  anyone still use this stuff?
+
+OPTIONS
+-------
+* change switches to accept two dashes (--help)
+* can we move option checking to its own function?  Currently each
+  command is doing that itself.
+* make the option parsing code dynamic, so that there is no limit
+  to the number of command line arguments (this has been done for
+  most all the commands).
+* Add switch descriptions to -help output.
+
+COMP
+----
+*) add option so that prompter can be used to input addresses,
+   before the real editor is called to edit message.
+
+FLIST
+-----
+* add -format option so you can specify the look of the output
+  of flist.
+* add Flist-Exclude profile component
+
+FOLDER
+------
+* add Folder-Order profile component (same as flist)
+* add Folder-Exclude profile component
+
+FORMATS
+-------
+* add escape %(sequence{foobar}) to detect arbitrary sequences.
+
+FORW
+----
+* Decode RFC-2047 headers in messages included when replying
+  or forwarding.
+* Merge code for forw and repl.
+
+INC
+---
+* Add ability to do filtering (call filterproc) when incorporating mail
+* Change inc to use libary function folder_addmsg().
+
+MHBUILD
+-------
+* add ability to specify Content-Transfer-Encoding in composition
+  drafts.
+* add support for Content-Disposition header (rfc1806).
+* remove the code for caching from mhbuild.
+
+MHL
+---
+* remove naming hack in mhlsbr.c for adios and done.
+* add ability to filter parts of the message by calling
+  an external filtering program.
+* fix internal pager for mhl?
+
+MHMAIL
+------
+* add -attach option (send could use this).  This should
+  be done by hooking in mhbuildsbr.c
+
+MHN/MHSHOW/MHLIST/MHSTORE
+-------------------------
+* add way so user can tell mhn to use internal method for
+  handling type, such as multipart/mixed.
+* add way so user can tell mhn to use a certain `proc' such
+  as moreproc, for certain content types.
+* add support for Content-Disposition header (rfc1806).
+* merge the two places in which mhshowsbr.c reads display
+  strings.
+* split mhn into mhshow, mhlist, mhstore, and mhcache.
+* when storing to a folder, should we save the folder context
+  first, so that storage string of "+" stores to the new
+  folder?
+
+MSH
+---
+* change conditional includes in msh.c to use termios.h
+* Add -version to mshcmd.c for each command.
+* Change msh to use mbox style files, instead of mmdf.  Add options -mbox,
+  -mmdf to choose.
+*) There are couple calls to copyip() which should be changed to
+   getarguments().  One problem is freeing the string getcopy'ed by
+   getarguments().
+
+PICK
+----
+* split regex code out into library.
+* replace regex code with Henry Spencer's regex library.
+
+POST
+----
+* make -msgid the default
+* factor msgid code into own function
+
+PROMPTER
+--------
+* maybe add ability to use prompter just for headers, and
+  then use primary editor for editing message.
+
+RCVSTORE
+--------
+* Change rcvstore so that it can store into multiple folders.
+* Add folder locking.
+
+REPL
+----
+* Decode RFC-2047 headers in messages included when replying
+  or forwarding.
+* Merge code for forw and repl.
+
+SEND/SENDSBR
+------------
+* Maybe add `-server' and `-client' to documentation.
+* Add ability for returned messages from "send -push" to be
+  in MIME format (this is actually a change in mhmail).
+* make -msgid the default.
+* Add RFC-2047 encoding support for out-going messages.  This
+  will probably require hooking mhparse into sendsbr.c, and doing
+  a complete MIME parsing.  Then all handling of Content encoding
+  can be on the backend.
+
+SLOCAL
+-----
+* Change slocal to use .slocalrc file, instead of .maildelivery?
+* Add ability to use regular expressions in header matching.
+* Add support for Berkeley db.
+* Clean up output from -debug option.
+* Add -debuglevel to control the amount of debug info that is output.
+* Add -debuglog to specify file to save debugging output.
+* Add -logfile (or -audit) to specify where to record info about successful
+  deliveries.
+
+VMH
+---
+* Fix vmh (or remove it).  vmh seems to be using internal
+  knowledge of curses.
+
+MTA INTERFACE
+-------------
+* Fix locking code.  Add lockfile command.
+* Think about support for DSN (Delivery Status Notification)
+* Test nmh with qmail.  Add qmail's maildir format.
+* Relax restrictions on what can be done with headers in send/post.
+* figure out why smail.c and client.c need their own copies of
+  getcpy, copyip, etc... (funny linking problem)
+
+GENERAL
+-------
+* see if the various versions of copyfile and copyf can be
+  merged.
+* change time functions to use POSIX functions by default.
+* Add MH-6.8.4 features into nmh (mostly done).
+* Maybe should move etcpath to sbr and add to libmh.
+* collect winsize, struct termio, etc... together into a ttyinfo
+  structure.
+* change adios to take exit code argument.
+* use wait3 if not waitpid (maybe)
+* some of the calls to setjmp/longjmp should be replaced with
+  sigsetjmp/siglongjmp.
+* When do we need to add -lresolv for SunOS 4.1.x?
+* replace use of getcpy with strdup.
+* replace use of ftell with fgetpos.
diff --git a/VERSION b/VERSION
new file mode 100644 (file)
index 0000000..1beee2b
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+nmh-1.0
diff --git a/ZSH.COMPLETION b/ZSH.COMPLETION
new file mode 100644 (file)
index 0000000..5c10cc8
--- /dev/null
@@ -0,0 +1,188 @@
+#
+# The following several shell functions and `compctl' commands
+# that will configure the programmable command completion of
+# the Z Shell (zsh) for the nmh mail system.
+#
+# You may need to edit where it says EDIT ME.
+# These were orginally written for MH by Peter Stephenson
+
+# The following three functions are best autoloaded.
+#
+# mhcomp completes folders (including subfolders).
+# mhfseq completes sequence names and message numbers.
+# mhfile completes files in standard nmh locations.
+
+#
+# Completion function for nmh folders.  Works with
+# both + (relative to top) and @ (relative to current).
+#
+function mhcomp {
+  local nword args pref char mhpath
+  read -nc nword
+  read -cA args
+
+  pref=$args[$nword]
+  char=$pref[1]
+  pref=$pref[2,-1]
+
+  # The $(...) here accounts for most of the time spent in this function.
+  if [[ $char = + ]]; then
+  #    mhpath=$(mhpath +)
+  # EDIT ME: use a hard wired value here: it's faster.
+    mhpath=~/Mail
+  elif [[ $char = @ ]]; then
+    mhpath=$(mhpath)
+  fi
+
+  eval "reply=($mhpath/$pref*(N-/))"
+
+  # I'm frankly amazed that this next step works, but it does.
+  reply=(${reply#$mhpath/})
+}
+
+#
+# Extract nmh message names and numbers for completion.  Use of the
+# correct folder, if it is not the current one, requires that it
+# should be the previous command line argument.  If the previous
+# argument is `-draftmessage', a hard wired draft folder name is used.
+#
+mhfseq() {
+  local folder foldpath words pos nums
+  read -cA words
+  read -cn pos
+
+  # Look for a folder name.
+  # First try the previous word.
+  if [[ $words[$pos-1] = [@+]* ]]; then
+    folder=$words[$pos-1]
+  # Next look and see if we're looking for a draftmessage
+  elif [[ $words[$pos-1] = -draftmessage ]]; then
+    # EDIT ME:  shortcut -- hard-wire draftfolder here
+    # Should really look for a +draftfolder argument.
+    folder=+drafts
+  fi
+  # Else use the current folder ($folder empty)
+
+  if [[ $folder = +* ]]; then
+  # EDIT ME:  use hard-wired path with + for speed.
+    foldpath=~/Mail/$folder[2,-1]
+  else
+    foldpath=$(mhpath $folder)
+  fi
+
+  # Extract all existing message numbers from the folder.
+  nums=($foldpath/<->(N:t))
+  # If that worked, look for marked sequences.
+  # EDIT ME
+  # if you never use non-standard sequences, comment out
+  # or delete the next three lines.
+  if (( $#nums )); then
+    nums=($nums $(mark $folder | awk -F: '{print $1}'))
+  fi
+
+  # EDIT ME:  `unseen' is the value of Unseen-Sequence, if it exists;
+  set -A reply next cur prev first last all unseen $nums
+
+}
+
+#
+# Find an nmh file; for use with -form arguments and the like.
+# Use with compctl -K mhfile.
+#
+mhfile () {
+  local mhfpath file
+  # EDIT ME
+  # Array containing all the places nmh will look for templates etc.
+  mhfpath=(~/Mail /usr/local/nmh/lib)
+
+  # Emulate completeinword behaviour as appropriate
+  local wordstr
+  if [[ -o completeinword ]]; then
+    wordstr='$1*$2'
+  else
+    wordstr='$1$2*'
+  fi
+
+  if [[ $1$2 = */* ]]; then
+    # path given: don't search nmh locations
+    eval "reply=($wordstr(.N))"
+  else
+    # no path:  only search nmh locations.
+    eval "reply=(\$mhfpath/$wordstr(.N:t))"
+  fi
+}
+
+# Note: you must type the initial + or @ of a folder name to get
+# completion, even in places where only folder names are allowed.
+# Abbreviations for options are not recognised.  Hit tab to complete
+# the option name first.
+
+compctl -K mhfseq -x 's[+][@]' -K mhcomp -S / -q - \
+  's[-]' -k "(all noall fast nofast header noheader help list nolist \
+  pack nopack pop push recurse norecurse total nototal)" -- folder folders
+
+compctl -K mhfseq -x 's[+][@]' -K mhcomp -S / -q - \
+  's[-]' -k "(sequence all noall recurse norecurse showzero noshowzero \
+  alpha noalpha fast nofast help)" -- flist flists
+
+compctl -K mhfseq -x 's[+][@],c[-1,-draftfolder] s[+][@]' \
+  -K mhcomp -S / -q - 'c[-1,-draftmessage]' -K mhfseq - \
+  'C[-1,-(editor|whatnowproc)]' -c - \
+  's[-]' -k "(draftfolder draftmessage nodraftfolder editor noedit \
+  file form use nouse whatnowproc nowhatnowproc help)" - \
+  'c[-1,-form]' -K mhfile -- comp
+
+compctl -K mhfseq -x 's[+][@]' \
+  -K mhcomp -S / -q - 'c[-1,-draftmessage]'  -K mhfseq -\
+  's[-]' -k "(annotate noannotate cc nocc draftfolder nodraftfolder \
+  draftmessage editor noedit fcc filter form group nogroup inplace noinplace
+  query noquery width whatnowproc nowhatnowproc help)" - 'c[-1,(cc|nocc)]' \
+  -k "(all to cc me)" - 'C[-1,-(filter|form)]' -K mhfile - \
+  'C[-1,-(editor|whatnowproc)]' -c -- repl
+
+compctl -K mhfseq -x 's[+][@]' -K mhcomp -S / -q - \
+  's[-]' -k "(audit noaudit changecur nochangecur form format \
+  file silent nosilent truncate notruncate width help)" - \
+  'C[-1,-(audit|form)]' -K mhfile - 'c[-1,-file]' -f + -- inc
+
+compctl -K mhfseq -x 's[+][@]' -K mhcomp -S / -q - \
+  's[-]' -k "(sequence add delete list public nopublic zero nozero help)" -- \
+  mark
+
+compctl -K mhfseq -x 's[+][@]' \
+  -K mhcomp -S / -q - 'c[-1,-file]' -f - 'c[-1,-rmmprov]' -c - \
+  's[-]' -k "(draft link nolink preserve nopreserve src file \
+  rmmproc normmproc help)" -- refile
+
+compctl -K mhfseq -x 's[+][@]' -K mhcomp -S / -q - \
+  's[-]' -k "(clear noclear form format header noheader reverse noreverse \
+  file help width)" - 'c[-1,-file]' -f - 'c[-1,-form]' -K mhfile -- scan
+
+compctl -K mhfseq -x 's[+][@]'  -K mhcomp -S / -q - \
+  's[-]' -k "(draft form moreproc nomoreproc header noheader \
+   showproc noshowproc length width help)" - 'C[-1,-(show|more)proc]' -c - \
+   'c[-1,-file]' -f - 'c[-1,-form]' -K mhfile - \
+   'c[-1,-length]' -s '$LINES' - 'c[-1,-width]' -s '$COLUMNS' -- show next prev
+
+compctl -K mhfseq -x 's[+][@]' -K mhcomp -S / -q - 's[-]' \
+  -k "(help)" -- rmm
+
+compctl -K mhfseq -x 's[+][@]' -K mhcomp -S / -q - \
+  's[-]' -k "(after before cc date datefield from help list nolist \
+  public nopublic search sequence subject to zero nozero not or and \
+  lbrace rbrace)" -- pick
+
+compctl -K mhfseq -x 's[+][@]' -K mhcomp -S / -q - 's[-]' \
+  -k "(alias check draft draftfolder draftmessage help nocheck \
+  nodraftfolder)" -- whom
+
+compctl -K mhfseq -x 's[+][@]' -K mhcomp -S / -q - 's[-]' \
+  -k "(file part type list headers noheaders realsize norealsize nolist \
+  show serialonly noserialonly form pause nopause noshow store auto noauto \
+  nostore cache nocache rcache wcache check nocheck ebcdicsafe noebcdicsafe \
+  rfc934mode norfc934mode verbose noverbose help)" - \
+  'c[-1,-file]' -f - 'c[-1,-form]' -K mhfile - \
+  'C[-1,-[rw]cache]' -k '(public private never ask)' -- mhn
+
+compctl -K mhfseq -x 's[+][@]' -K mhcomp -S / -q - 's[-]' -k '(help)' -- mhpath
+
diff --git a/acconfig.h b/acconfig.h
new file mode 100644 (file)
index 0000000..5c3f602
--- /dev/null
@@ -0,0 +1,263 @@
+
+/****** BEGIN USER CONFIGURATION SECTION *****/
+
+/*
+ * IMPORTANT: UNCOMMENT THE DEFINES FOR YOUR OPERATING SYSTEM
+ *
+ * These are slowly being phased out, but currently
+ * not everyone is auto-configured.  Then decide if you
+ * wish to change the features that are compiled into nmh.
+ */
+
+/*
+ *  Solaris 2.x
+ *  Irix
+ *  OSF/1
+ *  HP-UX
+ *  AIX
+ */
+/* #define SYS5  1 */
+/* #define SVR4  1 */
+
+/*
+ *  SunOS 4.1.x
+ */
+/* #define BIND     1 */
+/* #define BSD42    1 */
+
+/*
+ *  Linux
+ */
+/* #define LINUX_STDIO  1 */
+
+/*
+ *  FreeBSD 2.x
+ *  NetBSD 1.x,
+ *  OpenBSD 2.x
+ *  BSD/OS 2.x
+ */
+/* #define BIND     1 */
+/* #define BSD42    1 */
+/* #define BSD44    1 */
+
+/*
+ *  SCO 4.x
+ *  SCO 5.x
+ *
+ * I believe the second `define' is only necessary
+ * for SCO 5.x, not SCO 4.x
+ */
+/* #define SYS5         1 */
+/* #define SCO_5_STDIO  1 */
+
+/*
+ * Define to 1 if you need to make `inc' set-group-id
+ * because your mail spool is not world writable.  This
+ * will add some extra security checks, although I can't
+ * guarantee it is safe.  Also, you will need to change the
+ * group and add the setgid bit to `inc' manually after
+ * installation.
+ */
+/* #define MAILGROUP  1 */
+
+/*
+ * Turn on locale (setlocale) support
+ */
+#define LOCALE  1
+
+/*
+ * Define to 1 the type of file locking to use.  You need to
+ * make sure the type of locking you use is compatible with
+ * other programs which may modify your maildrops.
+ * Currently you can only use one type.
+ */
+#define DOT_LOCKING   1
+/* #define FCNTL_LOCKING 1 */
+/* #define LOCKF_LOCKING 1 */
+/* #define FLOCK_LOCKING 1 */
+
+/*
+ * If you have defined DOT_LOCKING, then the default is to
+ * place the lock files in the same directory as the file that
+ * is to be locked.  Alternately, if you define LOCKDIR, you
+ * can specify that all lock files go in a specific directory.
+ * Don't define this unless you know you need it.
+ */
+/* #define LOCKDIR "/usr/spool/locks" */
+
+/*
+ * Define this if your passwords are stored in some type of
+ * distributed name service, such as NIS, or NIS+.
+ */
+#define DBMPWD  1
+
+/*
+ * Directs nmh not to try and rewrite addresses
+ * to their official form.  You probably don't
+ * want to change this without good reason.
+ */
+#define DUMB    1
+
+/*
+ * Define this if you do not want nmh to attach the local hostname
+ * to local addresses.  You must also define DUMB.  You probably
+ * dont' need this unless you are behind a firewall.
+ */
+/* #define REALLYDUMB  1 */
+
+/*
+ * Directs inc/slocal to extract the envelope sender from "From "
+ * line.  If inc/slocal is saving message to folder, then this
+ * sender information is then used to create a Return-Path
+ * header which is then added to the message.
+ */
+#define RPATHS  1
+
+/*
+ * If defined, slocal will use `mbox' format when saving to
+ * your standard mail spool.  If not defined, it will use
+ * mmdf format.
+ */
+#define SLOCAL_MBOX  1
+
+/*
+ * If this is defined, nmh will recognize the ~ construct.
+ */
+#define MHRC    1
+
+/*
+ * Compile simple ftp client into mhn.  This will be used by mhn
+ * for ftp access unless you have specified another access method
+ * in your .mh_profile or mhn.defaults.  Use the "mhn-access-ftp"
+ * profile entry to override this.  Check mhn(1) man page for
+ * details.
+ */
+#define BUILTIN_FTP 1
+
+/*
+ * If you enable POP support, this is the the port name
+ * that nmh will use.  Make sure this is defined in your
+ * /etc/services file (or its NIS/NIS+ equivalent).  If you
+ * are using KPOP, you will probably need to change this
+ * to "kpop".
+ */
+#define POPSERVICE "pop3"
+
+/*
+ * Define the default creation modes for folders and messages.
+ */
+#define DEFAULT_FOLDER_MODE "0700"
+#define DEFAULT_MESSAGE_MODE "0600"
+
+/*
+ * The prefix which is prepended to the name of messages when they
+ * are "removed" by rmm.  This should typically be `,' or `#'
+ */
+#define BACKUP_PREFIX ","
+
+/*
+ * Name of link to file to which you are replying.
+ */
+#define LINK "@"
+
+/*
+ * If wait/waitpid returns an int (no union wait).
+ */
+#define WAITINT 1
+
+/***** END USER CONFIGURATION SECTION *****/
+@TOP@
+
+/*
+ * Define this if you want SMTP (simple mail transport protocol)
+ * support.  When sending mail, instead of passing the message to
+ * the mail transport agent (typically sendmail), nmh will open a
+ * socket connection to the mail port on the machine specified in
+ * the `mts.conf' file (default is localhost), and speak SMTP directly.
+ */
+#undef SMTPMTS
+
+/*
+ * Use sendmail as transport agent.  Post messages by piping
+ * them directly to sendmail.
+ */
+#undef SENDMTS
+
+/*
+ * Define this to compile client-side support for pop into
+ * inc and msgchk.  Do not change this value manually.  You
+ * must run configure with the '--enable-nmh-pop' option
+ * to correctly build the pop client support.
+ */
+#undef POP
+
+/*
+ * Define this to compile client-side support for kpop
+ * (kerberized pop) into inc and msgchk.  Do not change this
+ * value manually.  You must run configure with the option
+ * '--with-krb4=PREFIX' to correctly build the kpop client support.
+ */
+#undef KPOP
+
+/*
+ * Define this to "pop" when using Kerberos V4
+ */
+#undef KPOP_PRINCIPAL
+
+/*
+ * Define this to compile support for using Hesiod to locate
+ * pop servers into inc and msgchk.  Do not change this value
+ * manually.  You must run configure with the option
+ * '--with-hesiod=PREFIX' to correctly build Hesiod support.
+ */
+#undef HESIOD
+
+/*
+ * Compile in support for the Emacs front-end mh-e.
+ */
+#undef MHE
+
+/* Define to 1 if your termcap library has the ospeed variable */
+#undef HAVE_OSPEED
+/* Define to 1 if you have ospeed, but it is not defined in termcap.h */
+#undef MUST_DEFINE_OSPEED
+
+/* Define to 1 if tgetent() accepts NULL as a buffer */
+#undef TGETENT_ACCEPTS_NULL
+
+/* Define to 1 if you have reliable signals */
+#undef RELIABLE_SIGNALS
+
+/* Define to 1 if you use POSIX style signal handling */
+#undef POSIX_SIGNALS
+/* Define to 1 if you use BSD style signal handling (and can block signals) */
+#undef BSD_SIGNALS
+/* Define to 1 if you use SYS style signal handling (and can block signals) */
+#undef SYSV_SIGNALS
+/* Define to 1 if you have no signal blocking at all (bummer) */
+#undef NO_SIGNAL_BLOCKING
+
+/* Define to `unsigned int' if <sys/types.h> or <signal.h> doesn't define */
+#undef sigset_t
+
+/*
+ * Define to 1 if your vi has ATT bug, such that it returns
+ * non-zero exit codes on `pseudo-errors'.
+ */
+#undef ATTVIBUG
+
+/* Define ruserpass as _ruserpass if your libraries have a bug *
+ * such that it can't find ruserpass, but can find _ruserpass. */
+#undef ruserpass
+
+/* Define if your system defines TIOCGWINSZ in sys/ioctl.h.  */
+#undef GWINSZ_IN_SYS_IOCTL
+
+/* Define if your system defines `struct winsize' in sys/ptem.h.  */
+#undef WINSIZE_IN_PTEM
+
+/* Define to 1 if struct tm has gmtoff */
+#undef HAVE_TM_GMTOFF
diff --git a/aclocal.m4 b/aclocal.m4
new file mode 100644 (file)
index 0000000..cbfb498
--- /dev/null
@@ -0,0 +1,41 @@
+
+# Originally by John Hawkinson <jhawk@mit.edu>
+# Under Solaris, those
+# applications need to link with "-lsocket -lnsl".  Under IRIX, they
+# need to link with "-lnsl" but should *not* link with "-lsocket"
+# because libsocket.a breaks a number of things (for instance,
+# gethostbyname() under IRIX 5.2, and snoop sockets under most versions
+# of IRIX).
+#
+# The check for libresolv is in case you are attempting to link
+# statically and happen to have a libresolv.a lying around (and no
+# libnsl.a). An example of such a case would be Solaris with
+# BIND 4.9.5 installed.
+
+AC_DEFUN(AC_CHECK_NETLIBS,
+[AC_CHECK_FUNC(gethostbyname, ,
+  AC_CHECK_LIB(nsl, gethostbyname, ,
+    AC_CHECK_LIB(resolv, gethostbyname)))
+AC_CHECK_FUNC(socket, ,
+  AC_CHECK_LIB(socket, socket))
+])
+
+
+# This checks for the function ruserpass.
+#
+# 1) first, check for ruserpass
+# 2) else, check for _ruserpass
+# 3) else, check for _ruserpass in libsocket
+# 4) else, build version of ruserpass in nmh/sbr
+AC_DEFUN(AC_CHECK_RUSERPASS,
+[AC_CHECK_FUNC(ruserpass, ,
+  AC_CHECK_FUNC(_ruserpass, ,
+    AC_CHECK_LIB(socket, _ruserpass)))
+if test x$ac_cv_func_ruserpass = xno; then
+  if test x$ac_cv_func__ruserpass = xyes -o x$ac_cv_lib_socket__ruserpass = xyes; then
+    AC_DEFINE(ruserpass, _ruserpass)
+  else
+    LIBOBJS="$LIBOBJS ruserpass.o"
+  fi
+fi
+])
diff --git a/config.h.in b/config.h.in
new file mode 100644 (file)
index 0000000..3915bde
--- /dev/null
@@ -0,0 +1,434 @@
+/* config.h.in.  Generated automatically from configure.in by autoheader.  */
+
+/****** BEGIN USER CONFIGURATION SECTION *****/
+
+/*
+ * IMPORTANT: UNCOMMENT THE DEFINES FOR YOUR OPERATING SYSTEM
+ *
+ * These are slowly being phased out, but currently
+ * not everyone is auto-configured.  Then decide if you
+ * wish to change the features that are compiled into nmh.
+ */
+
+/*
+ *  Solaris 2.x
+ *  Irix
+ *  OSF/1
+ *  HP-UX
+ *  AIX
+ */
+/* #define SYS5  1 */
+/* #define SVR4  1 */
+
+/*
+ *  SunOS 4.1.x
+ */
+/* #define BIND     1 */
+/* #define BSD42    1 */
+
+/*
+ *  Linux
+ */
+/* #define LINUX_STDIO  1 */
+
+/*
+ *  FreeBSD 2.x
+ *  NetBSD 1.x,
+ *  OpenBSD 2.x
+ *  BSD/OS 2.x
+ */
+/* #define BIND     1 */
+/* #define BSD42    1 */
+/* #define BSD44    1 */
+
+/*
+ *  SCO 4.x
+ *  SCO 5.x
+ *
+ * I believe the second `define' is only necessary
+ * for SCO 5.x, not SCO 4.x
+ */
+/* #define SYS5         1 */
+/* #define SCO_5_STDIO  1 */
+
+/*
+ * Define to 1 if you need to make `inc' set-group-id
+ * because your mail spool is not world writable.  This
+ * will add some extra security checks, although I can't
+ * guarantee it is safe.  Also, you will need to change the
+ * group and add the setgid bit to `inc' manually after
+ * installation.
+ */
+/* #define MAILGROUP  1 */
+
+/*
+ * Turn on locale (setlocale) support
+ */
+#define LOCALE  1
+
+/*
+ * Define to 1 the type of file locking to use.  You need to
+ * make sure the type of locking you use is compatible with
+ * other programs which may modify your maildrops.
+ * Currently you can only use one type.
+ */
+#define DOT_LOCKING   1
+/* #define FCNTL_LOCKING 1 */
+/* #define LOCKF_LOCKING 1 */
+/* #define FLOCK_LOCKING 1 */
+
+/*
+ * If you have defined DOT_LOCKING, then the default is to
+ * place the lock files in the same directory as the file that
+ * is to be locked.  Alternately, if you define LOCKDIR, you
+ * can specify that all lock files go in a specific directory.
+ * Don't define this unless you know you need it.
+ */
+/* #define LOCKDIR "/usr/spool/locks" */
+
+/*
+ * Define this if your passwords are stored in some type of
+ * distributed name service, such as NIS, or NIS+.
+ */
+#define DBMPWD  1
+
+/*
+ * Directs nmh not to try and rewrite addresses
+ * to their official form.  You probably don't
+ * want to change this without good reason.
+ */
+#define DUMB    1
+
+/*
+ * Define this if you do not want nmh to attach the local hostname
+ * to local addresses.  You must also define DUMB.  You probably
+ * dont' need this unless you are behind a firewall.
+ */
+/* #define REALLYDUMB  1 */
+
+/*
+ * Directs inc/slocal to extract the envelope sender from "From "
+ * line.  If inc/slocal is saving message to folder, then this
+ * sender information is then used to create a Return-Path
+ * header which is then added to the message.
+ */
+#define RPATHS  1
+
+/*
+ * If defined, slocal will use `mbox' format when saving to
+ * your standard mail spool.  If not defined, it will use
+ * mmdf format.
+ */
+#define SLOCAL_MBOX  1
+
+/*
+ * If this is defined, nmh will recognize the ~ construct.
+ */
+#define MHRC    1
+
+/*
+ * Compile simple ftp client into mhn.  This will be used by mhn
+ * for ftp access unless you have specified another access method
+ * in your .mh_profile or mhn.defaults.  Use the "mhn-access-ftp"
+ * profile entry to override this.  Check mhn(1) man page for
+ * details.
+ */
+#define BUILTIN_FTP 1
+
+/*
+ * If you enable POP support, this is the the port name
+ * that nmh will use.  Make sure this is defined in your
+ * /etc/services file (or its NIS/NIS+ equivalent).  If you
+ * are using KPOP, you will probably need to change this
+ * to "kpop".
+ */
+#define POPSERVICE "pop3"
+
+/*
+ * Define the default creation modes for folders and messages.
+ */
+#define DEFAULT_FOLDER_MODE "0700"
+#define DEFAULT_MESSAGE_MODE "0600"
+
+/*
+ * The prefix which is prepended to the name of messages when they
+ * are "removed" by rmm.  This should typically be `,' or `#'
+ */
+#define BACKUP_PREFIX ","
+
+/*
+ * Name of link to file to which you are replying.
+ */
+#define LINK "@"
+
+/*
+ * If wait/waitpid returns an int (no union wait).
+ */
+#define WAITINT 1
+
+/***** END USER CONFIGURATION SECTION *****/
+
+/* Define to empty if the keyword does not work.  */
+#undef const
+
+/* Define to `int' if <sys/types.h> doesn't define.  */
+#undef gid_t
+
+/* Define if your struct stat has st_blksize.  */
+#undef HAVE_ST_BLKSIZE
+
+/* Define if you have <sys/wait.h> that is POSIX.1 compatible.  */
+#undef HAVE_SYS_WAIT_H
+
+/* Define if you have <vfork.h>.  */
+#undef HAVE_VFORK_H
+
+/* Define to `int' if <sys/types.h> doesn't define.  */
+#undef mode_t
+
+/* Define to `long' if <sys/types.h> doesn't define.  */
+#undef off_t
+
+/* Define to `int' if <sys/types.h> doesn't define.  */
+#undef pid_t
+
+/* Define as the return type of signal handlers (int or void).  */
+#undef RETSIGTYPE
+
+/* Define to `unsigned' if <sys/types.h> doesn't define.  */
+#undef size_t
+
+/* Define if the `S_IS*' macros in <sys/stat.h> do not work properly.  */
+#undef STAT_MACROS_BROKEN
+
+/* Define if you have the ANSI C header files.  */
+#undef STDC_HEADERS
+
+/* Define if you can safely include both <sys/time.h> and <time.h>.  */
+#undef TIME_WITH_SYS_TIME
+
+/* Define to `int' if <sys/types.h> doesn't define.  */
+#undef uid_t
+
+/* Define vfork as fork if vfork does not work.  */
+#undef vfork
+
+/*
+ * Define this if you want SMTP (simple mail transport protocol)
+ * support.  When sending mail, instead of passing the message to
+ * the mail transport agent (typically sendmail), nmh will open a
+ * socket connection to the mail port on the machine specified in
+ * the `mts.conf' file (default is localhost), and speak SMTP directly.
+ */
+#undef SMTPMTS
+
+/*
+ * Use sendmail as transport agent.  Post messages by piping
+ * them directly to sendmail.
+ */
+#undef SENDMTS
+
+/*
+ * Define this to compile client-side support for pop into
+ * inc and msgchk.  Do not change this value manually.  You
+ * must run configure with the '--enable-nmh-pop' option
+ * to correctly build the pop client support.
+ */
+#undef POP
+
+/*
+ * Define this to compile client-side support for kpop
+ * (kerberized pop) into inc and msgchk.  Do not change this
+ * value manually.  You must run configure with the option
+ * '--with-krb4=PREFIX' to correctly build the kpop client support.
+ */
+#undef KPOP
+
+/*
+ * Define this to "pop" when using Kerberos V4
+ */
+#undef KPOP_PRINCIPAL
+
+/*
+ * Define this to compile support for using Hesiod to locate
+ * pop servers into inc and msgchk.  Do not change this value
+ * manually.  You must run configure with the option
+ * '--with-hesiod=PREFIX' to correctly build Hesiod support.
+ */
+#undef HESIOD
+
+/*
+ * Compile in support for the Emacs front-end mh-e.
+ */
+#undef MHE
+
+/* Define to 1 if your termcap library has the ospeed variable */
+#undef HAVE_OSPEED
+/* Define to 1 if you have ospeed, but it is not defined in termcap.h */
+#undef MUST_DEFINE_OSPEED
+
+/* Define to 1 if you have reliable signals */
+#undef RELIABLE_SIGNALS
+
+/* Define to 1 if you use POSIX style signal handling */
+#undef POSIX_SIGNALS
+
+/* Define to 1 if you use BSD style signal handling (and can block signals) */
+#undef BSD_SIGNALS
+
+/* Define to 1 if you use SYS style signal handling (and can block signals) */
+#undef SYSV_SIGNALS
+
+/* Define to 1 if you have no signal blocking at all (bummer) */
+#undef NO_SIGNAL_BLOCKING
+
+/* Define to `unsigned int' if <sys/types.h> or <signal.h> doesn't define */
+#undef sigset_t
+
+/*
+ * Define to 1 if your vi has ATT bug, such that it returns
+ * non-zero exit codes on `pseudo-errors'.
+ */
+#undef ATTVIBUG
+
+/* Define ruserpass as _ruserpass if your libraries have a bug *
+ * such that it can't find ruserpass, but can find _ruserpass. */
+#undef ruserpass
+
+/* Define if your system defines TIOCGWINSZ in sys/ioctl.h.  */
+#undef GWINSZ_IN_SYS_IOCTL
+
+/* Define if your system defines `struct winsize' in sys/ptem.h.  */
+#undef WINSIZE_IN_PTEM
+
+/* Define to 1 if struct tm has gmtoff */
+#undef HAVE_TM_GMTOFF
+
+/* Define if you have the killpg function.  */
+#undef HAVE_KILLPG
+
+/* Define if you have the lstat function.  */
+#undef HAVE_LSTAT
+
+/* Define if you have the sigaction function.  */
+#undef HAVE_SIGACTION
+
+/* Define if you have the sigblock function.  */
+#undef HAVE_SIGBLOCK
+
+/* Define if you have the sighold function.  */
+#undef HAVE_SIGHOLD
+
+/* Define if you have the sigprocmask function.  */
+#undef HAVE_SIGPROCMASK
+
+/* Define if you have the sigrelse function.  */
+#undef HAVE_SIGRELSE
+
+/* Define if you have the sigsetjmp function.  */
+#undef HAVE_SIGSETJMP
+
+/* Define if you have the sigsetmask function.  */
+#undef HAVE_SIGSETMASK
+
+/* Define if you have the snprintf function.  */
+#undef HAVE_SNPRINTF
+
+/* Define if you have the strdup function.  */
+#undef HAVE_STRDUP
+
+/* Define if you have the strerror function.  */
+#undef HAVE_STRERROR
+
+/* Define if you have the tzset function.  */
+#undef HAVE_TZSET
+
+/* Define if you have the uname function.  */
+#undef HAVE_UNAME
+
+/* Define if you have the wait3 function.  */
+#undef HAVE_WAIT3
+
+/* Define if you have the waitpid function.  */
+#undef HAVE_WAITPID
+
+/* Define if you have the writev function.  */
+#undef HAVE_WRITEV
+
+/* Define if you have the <arpa/ftp.h> header file.  */
+#undef HAVE_ARPA_FTP_H
+
+/* Define if you have the <arpa/inet.h> header file.  */
+#undef HAVE_ARPA_INET_H
+
+/* Define if you have the <crypt.h> header file.  */
+#undef HAVE_CRYPT_H
+
+/* Define if you have the <dirent.h> header file.  */
+#undef HAVE_DIRENT_H
+
+/* Define if you have the <errno.h> header file.  */
+#undef HAVE_ERRNO_H
+
+/* Define if you have the <fcntl.h> header file.  */
+#undef HAVE_FCNTL_H
+
+/* Define if you have the <limits.h> header file.  */
+#undef HAVE_LIMITS_H
+
+/* Define if you have the <locale.h> header file.  */
+#undef HAVE_LOCALE_H
+
+/* Define if you have the <memory.h> header file.  */
+#undef HAVE_MEMORY_H
+
+/* Define if you have the <ndir.h> header file.  */
+#undef HAVE_NDIR_H
+
+/* Define if you have the <stdlib.h> header file.  */
+#undef HAVE_STDLIB_H
+
+/* Define if you have the <string.h> header file.  */
+#undef HAVE_STRING_H
+
+/* Define if you have the <sys/dir.h> header file.  */
+#undef HAVE_SYS_DIR_H
+
+/* Define if you have the <sys/ndir.h> header file.  */
+#undef HAVE_SYS_NDIR_H
+
+/* Define if you have the <sys/param.h> header file.  */
+#undef HAVE_SYS_PARAM_H
+
+/* Define if you have the <sys/time.h> header file.  */
+#undef HAVE_SYS_TIME_H
+
+/* Define if you have the <sys/utsname.h> header file.  */
+#undef HAVE_SYS_UTSNAME_H
+
+/* Define if you have the <termcap.h> header file.  */
+#undef HAVE_TERMCAP_H
+
+/* Define if you have the <termio.h> header file.  */
+#undef HAVE_TERMIO_H
+
+/* Define if you have the <termios.h> header file.  */
+#undef HAVE_TERMIOS_H
+
+/* Define if you have the <unistd.h> header file.  */
+#undef HAVE_UNISTD_H
+
+/* Define if you have the ndbm library (-lndbm).  */
+#undef HAVE_LIBNDBM
+
+/* Define if you have the nsl library (-lnsl).  */
+#undef HAVE_LIBNSL
+
+/* Define if you have the resolv library (-lresolv).  */
+#undef HAVE_LIBRESOLV
+
+/* Define if you have the socket library (-lsocket).  */
+#undef HAVE_LIBSOCKET
diff --git a/config/Makefile.in b/config/Makefile.in
new file mode 100644 (file)
index 0000000..8468646
--- /dev/null
@@ -0,0 +1,95 @@
+#
+# Makefile for config subdirectory
+#
+# $Id$
+#
+
+# nmh version
+VERSION = @VERSION@
+
+SHELL = /bin/sh
+
+top_srcdir = @top_srcdir@
+srcdir     = @srcdir@
+VPATH      = @srcdir@
+
+prefix      = @prefix@
+exec_prefix = @exec_prefix@
+bindir      = @bindir@
+libdir      = @libdir@
+etcdir      = @sysconfdir@
+
+default_editor = @editorpath@
+default_pager  = @pagerpath@
+
+CC         = @CC@
+CFLAGS     = @CFLAGS@
+DEFS       = @DEFS@
+INCLUDES   = -I.. -I$(top_srcdir)
+CONFIGDEFS = -DNMHBINDIR='"$(bindir)"' -DNMHETCDIR='"$(etcdir)"' -DNMHLIBDIR='"$(libdir)"' \
+             -DDEFAULT_EDITOR='"$(default_editor)"' -DDEFAULT_PAGER='"$(default_pager)"'
+
+COMPILE  = $(CC) -c $(DEFS) $(INCLUDES) $(CFLAGS)
+COMPILE2 = $(CC) -c $(DEFS) $(CONFIGDEFS) $(INCLUDES) $(CFLAGS)
+
+.SUFFIXES:
+.SUFFIXES: .c .o
+
+.c.o:
+       $(COMPILE) $<
+
+# source files
+SRCS = config.c
+
+# object files
+OBJS = config.o version.o
+
+# auxiliary files
+AUX = Makefile.in version.sh
+
+# all files in this directory included in the distribution
+DIST = $(SRCS) $(AUX)
+
+# ========= DEPENDENCIES FOR BUILDING ==========
+
+all: $(OBJS)
+
+version.c:
+       ${srcdir}/version.sh $(VERSION) > version.c
+
+config.o: config.c
+       $(COMPILE2) $<
+
+install:
+
+uninstall:
+
+# ========== DEPENDENCIES FOR CLEANUP ==========
+
+mostlyclean:
+       rm -f *.o *~
+
+clean: mostlyclean
+       rm -f version.c
+
+distclean: clean
+       rm -f Makefile
+
+realclean: distclean
+
+superclean: realclean
+
+# ========== DEPENDENCIES FOR MAINTENANCE ==========
+
+subdir = config
+
+Makefile: Makefile.in ../config.status
+       cd .. && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+distdir = ../`cat ../distname`/$(subdir)
+nmhdist: $(DIST)
+       @echo "Copying distribution files in $(subdir)"
+       @for file in $(DIST); do \
+         cp -p $(srcdir)/$$file $(distdir); \
+       done
+
diff --git a/config/config.c b/config/config.c
new file mode 100644 (file)
index 0000000..2a01c1b
--- /dev/null
@@ -0,0 +1,364 @@
+
+/*
+ * config.c -- master nmh configuration file
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+#ifdef MHRC
+# include <pwd.h>
+#endif
+
+#define nmhbindir(file) NMHBINDIR#file
+#define nmhetcdir(file) NMHETCDIR#file
+#define nmhlibdir(file) NMHLIBDIR#file
+
+
+/*
+ * Find the location of a format or configuration
+ * file, and return its absolute pathname.
+ *
+ * 1) If already absolute pathname, then leave unchanged.
+ * 2) Next, if it begins with ~user, then expand it.
+ * 3) Next, check in nmh Mail directory.
+ * 4) Next, check in nmh `etc' directory.
+ *
+ */
+
+char *
+etcpath (char *file)
+{
+    static char epath[PATH_MAX];
+    char *cp;
+#ifdef MHRC
+    char *pp;
+    struct passwd *pw;
+#endif
+
+#ifdef MHRC
+    context_read();
+#endif
+
+    switch (*file) {
+       case '/': 
+           /* If already absolute pathname, return it */
+           return file;
+
+#ifdef MHRC
+       case '~': 
+           /* Expand ~username */
+           if ((cp = strchr(pp = file + 1, '/')))
+               *cp++ = '\0';
+           if (*pp == '\0') {
+               pp = mypath;
+           } else {
+               if ((pw = getpwnam (pp)))
+                   pp = pw->pw_dir;
+               else {
+                   if (cp)
+                       *--cp = '/';
+                   goto try_it;
+               }
+           }
+
+           snprintf (epath, sizeof(epath), "%s/%s", pp, cp ? cp : "");
+           if (cp)
+               *--cp = '/';
+
+           if (access (epath, R_OK) != NOTOK)
+               return epath;   /* else fall */
+try_it:
+#endif /* MHRC */
+
+       default: 
+           /* Check nmh Mail directory */
+           if (access ((cp = m_mailpath (file)), R_OK) != NOTOK)
+               return cp;
+    }
+
+    /* Check nmh `etc' directory */
+    snprintf (epath, sizeof(epath), nmhetcdir(/%s), file);
+    return (access (epath, R_OK) != NOTOK ? epath : file);
+}
+
+
+/* 
+ * Standard yes/no switches structure
+ */
+
+struct swit anoyes[] = {
+    { "no", 0 },
+    { "yes", 0 },
+    { NULL, 0 }
+};
+
+/* 
+ * nmh constants
+ */
+
+/* initial profile for new users */
+char *mh_defaults = nmhetcdir (/mh.profile);
+
+/* default name of user profile */
+char *mh_profile = ".mh_profile";
+
+/* name of current message "sequence" */
+char *current = "cur";
+
+/* standard component files */
+char *components = "components";               /* comp         */
+char *replcomps = "replcomps";                 /* repl         */
+char *replgroupcomps = "replgroupcomps";       /* repl -group  */
+char *forwcomps = "forwcomps";                 /* forw         */
+char *distcomps = "distcomps";                 /* dist         */
+char *rcvdistcomps = "rcvdistcomps";           /* rcvdist      */
+char *digestcomps = "digestcomps";             /* forw -digest */
+
+/* standard format (filter) files */
+char *mhlformat = "mhl.format";                        /* show         */
+char *mhlreply = "mhl.reply";                  /* repl -filter */
+char *mhlforward = "mhl.forward";              /* forw -filter */
+
+char *draft = "draft";
+
+char *inbox = "Inbox";
+char *defaultfolder = "inbox";
+
+char *pfolder = "Current-Folder";
+char *usequence = "Unseen-Sequence";
+char *psequence = "Previous-Sequence";
+char *nsequence = "Sequence-Negation";
+
+/* profile entries for storage locations */
+char *nmhstorage = "nmh-storage";
+char *nmhcache = "nmh-cache";
+char *nmhprivcache = "nmh-private-cache";
+
+/* profile entry for external ftp access command */
+char *nmhaccessftp = "nmh-access-ftp";
+
+char *mhlibdir = NMHLIBDIR;
+char *mhetcdir = NMHETCDIR;
+
+/* 
+ * nmh not-so constants
+ */
+
+/*
+ * Default name for the nmh context file.
+ */
+char *context = "context";
+
+/*
+ * Default name of file for public sequences.  If NULL,
+ * then nmh will use private sequences by default, unless the
+ * user defines a value using the "mh-sequences" profile entry.
+ */
+#ifdef NOPUBLICSEQ
+char *mh_seq = NULL;
+#else
+char *mh_seq = ".mh_sequences";
+#endif
+
+/* 
+ * nmh globals
+ */
+
+char ctxflags;         /* status of user's context   */
+char *invo_name;       /* command invocation name    */
+char *mypath;          /* user's $HOME               */
+char *defpath;         /* pathname of user's profile */
+char *ctxpath;         /* pathname of user's context */
+struct node *m_defs;   /* profile/context structure  */
+
+/* 
+ * nmh processes
+ */
+
+/*
+ * This is the program to process MIME composition files
+ */
+char *buildmimeproc = nmhbindir (/mhbuild);
+/*
+ * This is the program to `cat' a file.
+ */
+char *catproc = "/bin/cat";
+
+/*
+ * mhl runs this program as a visual-end.
+ */
+
+char *faceproc = NULL;
+
+/*
+ * This program is usually called directly by users, but it is
+ * also invoked by the post program to process an "Fcc", or by
+ * comp/repl/forw/dist to refile a draft message.
+ */
+
+char *fileproc = nmhbindir (/refile);
+
+/* 
+ * This program is called to incorporate messages into a folder.
+ */
+
+char *incproc = nmhbindir (/inc);
+
+/*
+ * When a user runs an nmh program for the first time, this program
+ * is called to create his nmh profile, and mail directory.
+ */
+
+char *installproc = nmhlibdir (/install-mh);
+
+/*
+ * This is the default program invoked by a "list" response
+ * at the "What now?" prompt.  It is also used by the draft
+ * folder facility in comp/dist/forw/repl to display the
+ * draft message.
+ */
+
+char *lproc = DEFAULT_PAGER;
+
+/*
+ * This is the path for the Bell equivalent mail program.
+ */
+
+char *mailproc = nmhbindir (/mhmail);
+
+/*
+ * This is used by mhl as a front-end.  It is also used
+ * by mhn as the default method of displaying message bodies
+ * or message parts of type text/plain.
+ */
+
+char *moreproc = DEFAULT_PAGER;
+
+/* 
+ * This is the program (mhl) used to filter messages.  It is
+ * used by mhn to filter and display the message headers of
+ * MIME messages.  It is used by repl/forw (with -filter)
+ * to filter the message to which you are replying/forwarding.
+ * It is used by send/post (with -filter) to filter the message
+ * for "Bcc:" recipients.
+ */
+
+char *mhlproc = nmhlibdir (/mhl);
+
+/* 
+ * This is the super handy BBoard reading program, which is
+ * really just the nmh shell program.
+ */
+
+char *mshproc = nmhbindir (/msh);
+
+/* 
+ * This program is called to pack a folder.  
+ */
+
+char *packproc = nmhbindir (/packf);
+
+/*
+ * This is the delivery program called by send to actually
+ * deliver mail to users.  This is the interface to the MTS.
+ */
+
+char *postproc = nmhlibdir (/post);
+
+/*
+ * This is program is called by slocal to handle
+ * the action `folder' or `+'.
+ */
+
+char *rcvstoreproc = nmhlibdir (/rcvstore);
+
+/* 
+ * This program is called to remove a folder.  
+ */
+
+char *rmfproc = nmhbindir (/rmf);
+
+/* 
+ * This program is called to remove a message by rmm or refile -nolink.
+ * It's usually empty, which means to rename the file to a backup name.
+ */
+
+char *rmmproc = NULL;
+
+/*
+ * This program is usually called by the user's whatnowproc, but it
+ * may also be called directly to send a message previously composed.
+ */
+
+char *sendproc = nmhbindir (/send);
+
+/*
+ * This is the path to the program used by "show"
+ * to display non-text (MIME) messages.
+ */
+
+char *showmimeproc = nmhbindir (/mhshow);
+
+/*
+ * This is the default program called by "show" to filter
+ * and display standard text (non-MIME) messages.  It can be
+ * changed to a pager (such as "more" or "less") if you prefer
+ * that such message not be filtered in any way.
+ */
+
+char *showproc = nmhlibdir (/mhl);
+
+/* 
+ * This program is called by vmh as the back-end to the window management
+ * protocol
+ */
+
+char *vmhproc = nmhbindir (/msh);
+
+/* 
+ * This program is called after comp, et. al., have built a draft
+ */
+
+char *whatnowproc = nmhbindir (/whatnow);
+
+/* 
+ * This program is called to list/validate the addresses in a message.
+ */
+
+char *whomproc = nmhbindir (/whom);
+
+/*
+ * This is the editor invoked by the various message
+ * composition programs.  It SHOULD be a full screen
+ * editor, such as vi or emacs, but any editor will work.
+ */
+
+char *defaulteditor = DEFAULT_EDITOR;
+
+/* 
+ * This is the global nmh alias file.  It is somewhat obsolete, since
+ * global aliases should be handled by the Mail Transport Agent (MTA).
+ */
+
+char *AliasFile = nmhetcdir (/MailAliases);
+
+/* 
+ * File protections
+ */
+
+/*
+ * Folders (directories) are created with this protection (mode)
+ */
+
+char *foldprot = DEFAULT_FOLDER_MODE;
+
+/*
+ * Every NEW message will be created with this protection.  When a
+ * message is filed it retains its protection, so this only applies
+ * to messages coming in through inc.
+ */
+
+char *msgprot = DEFAULT_MESSAGE_MODE;
+
diff --git a/config/version.sh b/config/version.sh
new file mode 100755 (executable)
index 0000000..6257186
--- /dev/null
@@ -0,0 +1,44 @@
+#!/bin/sh
+#
+# version.sh -- script to create version string(s) for nmh.
+#
+# You need to pass the script the version number to use.
+#
+# $Id$
+#
+
+if [ -z "$1" ]; then
+    echo "usage: version.sh VERSION" 1>&2
+    exit 1
+fi
+
+VERSION=$1
+OFS="$IFS"
+IFS=:
+HOSTNAME=unknown
+
+# Find out the name of the host we are compiling on
+for prog in uname hostname
+do
+    for dir in $PATH
+    do
+       if [ ! -f $dir/$prog ]; then
+           continue
+       fi
+       case $prog in
+           uname)      HOSTNAME=`$prog -n`
+                       ;;
+           hostname)   HOSTNAME=`$prog`
+                       ;;
+       esac
+       break
+    done
+    if [ X"$HOSTNAME" != X  -a  X"$HOSTNAME" != Xunknown ]; then
+       break
+    fi
+done
+
+IFS=" "
+
+echo "char *version_str = \"nmh-$VERSION [compiled on $HOSTNAME at `date`]\";"
+echo "char *version_num = \"nmh-$VERSION\";"
diff --git a/configure b/configure
new file mode 100755 (executable)
index 0000000..8786037
--- /dev/null
+++ b/configure
@@ -0,0 +1,4024 @@
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.12 
+# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+ac_help="$ac_help
+  --with-mts=MTS          specify the mail transport agent"
+ac_help="$ac_help
+  --with-editor=EDITOR    specify the default editor"
+ac_help="$ac_help
+  --with-pager=PAGER      specify the default pager"
+ac_help="$ac_help
+  --enable-nmh-mhe        enable mhe support (DEFAULT)"
+ac_help="$ac_help
+  --enable-nmh-pop        enable client-side support for pop"
+ac_help="$ac_help
+  --with-krb4=PREFIX      specify location of Kerberos V4 for kpop support"
+ac_help="$ac_help
+  --with-hesiod=PREFIX    specify location of Hesiod"
+ac_help="$ac_help
+  --enable-nmh-debug      enable nmh code debugging"
+ac_default_prefix=/usr/local/nmh
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+# Maximum number of lines to put in a shell here document.
+ac_max_here_lines=12
+
+ac_prev=
+for ac_option
+do
+
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval "$ac_prev=\$ac_option"
+    ac_prev=
+    continue
+  fi
+
+  case "$ac_option" in
+  -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+  *) ac_optarg= ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case "$ac_option" in
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir="$ac_optarg" ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build="$ac_optarg" ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file="$ac_optarg" ;;
+
+  -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+  | --da=*)
+    datadir="$ac_optarg" ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    eval "enable_${ac_feature}=no" ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix="$ac_optarg" ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he)
+    # Omit some internal or obsolete options to make the list less imposing.
+    # This message is too long to be a string in the A/UX 3.1 sh.
+    cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+  --cache-file=FILE       cache test results in FILE
+  --help                  print this message
+  --no-create             do not create output files
+  --quiet, --silent       do not print \`checking...' messages
+  --version               print the version of autoconf that created configure
+Directory and file names:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [same as prefix]
+  --bindir=DIR            user executables in DIR [EPREFIX/bin]
+  --sbindir=DIR           system admin executables in DIR [EPREFIX/sbin]
+  --libexecdir=DIR        program executables in DIR [EPREFIX/libexec]
+  --datadir=DIR           read-only architecture-independent data in DIR
+                          [PREFIX/share]
+  --sysconfdir=DIR        read-only single-machine data in DIR [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data in DIR
+                          [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data in DIR [PREFIX/var]
+  --libdir=DIR            object code libraries in DIR [EPREFIX/lib]
+  --includedir=DIR        C header files in DIR [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc in DIR [/usr/include]
+  --infodir=DIR           info documentation in DIR [PREFIX/info]
+  --mandir=DIR            man documentation in DIR [PREFIX/man]
+  --srcdir=DIR            find the sources in DIR [configure dir or ..]
+  --program-prefix=PREFIX prepend PREFIX to installed program names
+  --program-suffix=SUFFIX append SUFFIX to installed program names
+  --program-transform-name=PROGRAM
+                          run sed PROGRAM on installed program names
+EOF
+    cat << EOF
+Host type:
+  --build=BUILD           configure for building on BUILD [BUILD=HOST]
+  --host=HOST             configure for HOST [guessed]
+  --target=TARGET         configure for TARGET [TARGET=HOST]
+Features and packages:
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --x-includes=DIR        X include files are in DIR
+  --x-libraries=DIR       X library files are in DIR
+EOF
+    if test -n "$ac_help"; then
+      echo "--enable and --with options recognized:$ac_help"
+    fi
+    exit 0 ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host="$ac_optarg" ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir="$ac_optarg" ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir="$ac_optarg" ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir="$ac_optarg" ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir="$ac_optarg" ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst \
+  | --locals | --local | --loca | --loc | --lo)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+  | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+    localstatedir="$ac_optarg" ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir="$ac_optarg" ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir="$ac_optarg" ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix="$ac_optarg" ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix="$ac_optarg" ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix="$ac_optarg" ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name="$ac_optarg" ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir="$ac_optarg" ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir="$ac_optarg" ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site="$ac_optarg" ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir="$ac_optarg" ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir="$ac_optarg" ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target="$ac_optarg" ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers)
+    echo "configure generated by autoconf version 2.12"
+    exit 0 ;;
+
+  -with-* | --with-*)
+    ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "with_${ac_package}='$ac_optarg'" ;;
+
+  -without-* | --without-*)
+    ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    eval "with_${ac_package}=no" ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes="$ac_optarg" ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries="$ac_optarg" ;;
+
+  -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+    ;;
+
+  *)
+    if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+      echo "configure: warning: $ac_option: invalid host type" 1>&2
+    fi
+    if test "x$nonopt" != xNONE; then
+      { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+    fi
+    nonopt="$ac_option"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+  exec 6>/dev/null
+else
+  exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+  case "$ac_arg" in
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c) ;;
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+  *" "*|*"     "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+  ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+  *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+  esac
+done
+
+# NLS nuisances.
+# Only set these to C if already set.  These must not be set unconditionally
+# because not all systems understand e.g. LANG=C (notably SCO).
+# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
+# Non-C LC_CTYPE values break the ctype check.
+if test "${LANG+set}"   = set; then LANG=C;   export LANG;   fi
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
+if test "${LC_CTYPE+set}"    = set; then LC_CTYPE=C;    export LC_CTYPE;    fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=h/nmh.h
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then its parent.
+  ac_prog=$0
+  ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+  test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+  srcdir=$ac_confdir
+  if test ! -r $srcdir/$ac_unique_file; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+  if test "$ac_srcdir_defaulted" = yes; then
+    { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+  else
+    { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+  fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+  if test "x$prefix" != xNONE; then
+    CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+  else
+    CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+  fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+  if test -r "$ac_site_file"; then
+    echo "loading site script $ac_site_file"
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  echo "loading cache $cache_file"
+  . $cache_file
+else
+  echo "creating cache $cache_file"
+  > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+  # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+  if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+    ac_n= ac_c='
+' ac_t='       '
+  else
+    ac_n=-n ac_c= ac_t=
+  fi
+else
+  ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+
+VERSION=`sed -e 's/nmh-//' ${srcdir}/VERSION`
+echo "configuring for nmh-$VERSION"
+
+# Check whether --with-mts or --without-mts was given.
+if test "${with_mts+set}" = set; then
+  withval="$with_mts"
+  :
+fi
+
+
+if test x$with_mts = xsmtp; then
+  MTS="smtp"
+  MTSLIB="mts/smtp/libsmtp.a"
+  cat >> confdefs.h <<\EOF
+#define SMTPMTS 1
+EOF
+elif test x$with_mts = xsendmail; then
+  MTS="sendmail"
+  MTSLIB="mts/sendmail/libsend.a"
+  cat >> confdefs.h <<\EOF
+#define SENDMTS 1
+EOF
+else
+  MTS="smtp"
+  MTSLIB="mts/smtp/libsmtp.a"
+  cat >> confdefs.h <<\EOF
+#define SMTPMTS 1
+EOF
+fi
+
+
+
+
+# Check whether --with-editor or --without-editor was given.
+if test "${with_editor+set}" = set; then
+  withval="$with_editor"
+  :
+fi
+
+
+if test -n "$with_editor"; then
+  editorpath="$with_editor"
+fi
+
+# Check whether --with-pager or --without-pager was given.
+if test "${with_pager+set}" = set; then
+  withval="$with_pager"
+  :
+fi
+
+
+if test -n "$with_pager"; then
+  pagerpath="$with_pager"
+fi
+
+# Check whether --enable-nmh-mhe or --disable-nmh-mhe was given.
+if test "${enable_nmh_mhe+set}" = set; then
+  enableval="$enable_nmh_mhe"
+  :
+fi
+
+
+if test x$enable_nmh_mhe != xno; then
+  cat >> confdefs.h <<\EOF
+#define MHE 1
+EOF
+fi
+
+# Check whether --enable-nmh-pop or --disable-nmh-pop was given.
+if test "${enable_nmh_pop+set}" = set; then
+  enableval="$enable_nmh_pop"
+  :
+fi
+
+if test x$enable_nmh_pop = xyes; then
+  cat >> confdefs.h <<\EOF
+#define POP 1
+EOF
+  POPLIB=popsbr.o
+  POPSED='/^%nmhbeginpop%/d;/^%nmhendpop%/d'
+else
+  POPSED='/^%nmhbeginpop%/,/^%nmhendpop%/d'
+fi
+
+# Check whether --with-krb4 or --without-krb4 was given.
+if test "${with_krb4+set}" = set; then
+  withval="$with_krb4"
+  :
+fi
+
+if test x$with_krb4 != x -a x$with_krb4 != xno; then
+  cat >> confdefs.h <<\EOF
+#define KPOP 1
+EOF
+  cat >> confdefs.h <<\EOF
+#define KPOP_PRINCIPAL "pop"
+EOF
+fi
+
+# Check whether --with-hesiod or --without-hesiod was given.
+if test "${with_hesiod+set}" = set; then
+  withval="$with_hesiod"
+  :
+fi
+
+if test x$with_hesiod != x -a x$with_hesiod != xno; then
+  cat >> confdefs.h <<\EOF
+#define HESIOD 1
+EOF
+fi
+
+# Check whether --enable-nmh-debug or --disable-nmh-debug was given.
+if test "${enable_nmh_debug+set}" = set; then
+  enableval="$enable_nmh_debug"
+  :
+fi
+
+
+
+
+test -z "$CFLAGS" && CFLAGS= auto_cflags=1
+if test x$enable_nmh_debug = xyes; then
+  test -z "$LDFLAGS" && LDFLAGS=-g
+fi
+
+# Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:669: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CC="gcc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:698: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  ac_prog_rejected=no
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then
+        ac_prog_rejected=yes
+       continue
+      fi
+      ac_cv_prog_CC="cc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# -gt 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    set dummy "$ac_dir/$ac_word" "$@"
+    shift
+    ac_cv_prog_CC="$@"
+  fi
+fi
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+  test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure:746: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+cat > conftest.$ac_ext <<EOF
+#line 756 "configure"
+#include "confdefs.h"
+main(){return(0);}
+EOF
+if { (eval echo configure:760: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  ac_cv_prog_cc_works=yes
+  # If we can't run a trivial program, we are probably using a cross compiler.
+  if (./conftest; exit) 2>/dev/null; then
+    ac_cv_prog_cc_cross=no
+  else
+    ac_cv_prog_cc_cross=yes
+  fi
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  ac_cv_prog_cc_works=no
+fi
+rm -fr conftest*
+
+echo "$ac_t""$ac_cv_prog_cc_works" 1>&6
+if test $ac_cv_prog_cc_works = no; then
+  { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure:780: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+echo "configure:785: checking whether we are using GNU C" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.c <<EOF
+#ifdef __GNUC__
+  yes;
+#endif
+EOF
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:794: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+  ac_cv_prog_gcc=yes
+else
+  ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+
+if test $ac_cv_prog_gcc = yes; then
+  GCC=yes
+  ac_test_CFLAGS="${CFLAGS+set}"
+  ac_save_CFLAGS="$CFLAGS"
+  CFLAGS=
+  echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+echo "configure:809: checking whether ${CC-cc} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+  ac_cv_prog_cc_g=yes
+else
+  ac_cv_prog_cc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cc_g" 1>&6
+  if test "$ac_test_CFLAGS" = set; then
+    CFLAGS="$ac_save_CFLAGS"
+  elif test $ac_cv_prog_cc_g = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-O2"
+  fi
+else
+  GCC=
+  test "${CFLAGS+set}" = set || CFLAGS="-g"
+fi
+
+
+if test -n "$auto_cflags"; then
+  if test x$enable_nmh_debug = xyes; then
+    if test -n "$GCC"; then
+      test -z "$CFLAGS" && CFLAGS="-Wall -g" || CFLAGS="$CFLAGS -Wall -g"
+    else
+      test -z "$CFLAGS" && CFLAGS=-g || CFLAGS="$CFLAGS -g"
+    fi
+  else
+    test -z "$LDFLAGS" && LDFLAGS=-s
+    if test -n "$GCC"; then
+      test -z "$CFLAGS" && CFLAGS=-O2 || CFLAGS="$CFLAGS -O2"
+    else
+      test -z "$CFLAGS" && CFLAGS=-O  || CFLAGS="$CFLAGS -O"
+    fi
+  fi
+fi
+
+echo $ac_n "checking for working const""... $ac_c" 1>&6
+echo "configure:855: checking for working const" >&5
+if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 860 "configure"
+#include "confdefs.h"
+
+int main() {
+
+/* Ultrix mips cc rejects this.  */
+typedef int charset[2]; const charset x;
+/* SunOS 4.1.1 cc rejects this.  */
+char const *const *ccp;
+char **p;
+/* NEC SVR4.0.2 mips cc rejects this.  */
+struct point {int x, y;};
+static struct point const zero = {0,0};
+/* AIX XL C 1.02.0.0 rejects this.
+   It does not let you subtract one const X* pointer from another in an arm
+   of an if-expression whose if-part is not a constant expression */
+const char *g = "string";
+ccp = &g + (g ? g-g : 0);
+/* HPUX 7.0 cc rejects these. */
+++ccp;
+p = (char**) ccp;
+ccp = (char const *const *) p;
+{ /* SCO 3.2v4 cc rejects this.  */
+  char *t;
+  char const *s = 0 ? (char *) 0 : (char const *) 0;
+
+  *t++ = 0;
+}
+{ /* Someone thinks the Sun supposedly-ANSI compiler will reject this.  */
+  int x[] = {25, 17};
+  const int *foo = &x[0];
+  ++foo;
+}
+{ /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
+  typedef const int *iptr;
+  iptr p = 0;
+  ++p;
+}
+{ /* AIX XL C 1.02.0.0 rejects this saying
+     "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
+  struct s { int j; const int *ap[3]; };
+  struct s *b; b->j = 5;
+}
+{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
+  const int foo = 10;
+}
+
+; return 0; }
+EOF
+if { (eval echo configure:909: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_c_const=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_c_const=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_c_const" 1>&6
+if test $ac_cv_c_const = no; then
+  cat >> confdefs.h <<\EOF
+#define const 
+EOF
+
+fi
+              
+echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6
+echo "configure:930: checking whether ${MAKE-make} sets \${MAKE}" >&5
+set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftestmake <<\EOF
+all:
+       @echo 'ac_maketemp="${MAKE}"'
+EOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=`
+if test -n "$ac_maketemp"; then
+  eval ac_cv_prog_make_${ac_make}_set=yes
+else
+  eval ac_cv_prog_make_${ac_make}_set=no
+fi
+rm -f conftestmake
+fi
+if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  SET_MAKE=
+else
+  echo "$ac_t""no" 1>&6
+  SET_MAKE="MAKE=${MAKE-make}"
+fi
+       ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+  if test -f $ac_dir/install-sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f $ac_dir/install.sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; }
+fi
+ac_config_guess=$ac_aux_dir/config.guess
+ac_config_sub=$ac_aux_dir/config.sub
+ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# ./install, which can be erroneously created by make from ./install.sh.
+echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
+echo "configure:985: checking for a BSD compatible install" >&5
+if test -z "$INSTALL"; then
+if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+    IFS="${IFS=        }"; ac_save_IFS="$IFS"; IFS="${IFS}:"
+  for ac_dir in $PATH; do
+    # Account for people who put trailing slashes in PATH elements.
+    case "$ac_dir/" in
+    /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;;
+    *)
+      # OSF1 and SCO ODT 3.0 have their own names for install.
+      for ac_prog in ginstall installbsd scoinst install; do
+        if test -f $ac_dir/$ac_prog; then
+         if test $ac_prog = install &&
+            grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then
+           # AIX install.  It has an incompatible calling convention.
+           # OSF/1 installbsd also uses dspmsg, but is usable.
+           :
+         else
+           ac_cv_path_install="$ac_dir/$ac_prog -c"
+           break 2
+         fi
+       fi
+      done
+      ;;
+    esac
+  done
+  IFS="$ac_save_IFS"
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL="$ac_cv_path_install"
+  else
+    # As a last resort, use the slow shell script.  We don't cache a
+    # path for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the path is relative.
+    INSTALL="$ac_install_sh"
+  fi
+fi
+echo "$ac_t""$INSTALL" 1>&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+               # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1036: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$RANLIB"; then
+  ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_RANLIB="ranlib"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":"
+fi
+fi
+RANLIB="$ac_cv_prog_RANLIB"
+if test -n "$RANLIB"; then
+  echo "$ac_t""$RANLIB" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+               for ac_prog in mawk gawk nawk awk
+do
+# Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1066: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_AWK'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$AWK"; then
+  ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_AWK="$ac_prog"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+AWK="$ac_cv_prog_AWK"
+if test -n "$AWK"; then
+  echo "$ac_t""$AWK" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+test -n "$AWK" && break
+done
+             # Extract the first word of "flex", so it can be a program name with args.
+set dummy flex; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1096: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_LEX'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$LEX"; then
+  ac_cv_prog_LEX="$LEX" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_LEX="flex"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_prog_LEX" && ac_cv_prog_LEX="lex"
+fi
+fi
+LEX="$ac_cv_prog_LEX"
+if test -n "$LEX"; then
+  echo "$ac_t""$LEX" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+if test -z "$LEXLIB"
+then
+  case "$LEX" in
+  flex*) ac_lib=fl ;;
+  *) ac_lib=l ;;
+  esac
+  echo $ac_n "checking for yywrap in -l$ac_lib""... $ac_c" 1>&6
+echo "configure:1129: checking for yywrap in -l$ac_lib" >&5
+ac_lib_var=`echo $ac_lib'_'yywrap | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-l$ac_lib  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1137 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char yywrap();
+
+int main() {
+yywrap()
+; return 0; }
+EOF
+if { (eval echo configure:1148: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  LEXLIB="-l$ac_lib"
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+fi
+             
+# Extract the first word of "lorder", so it can be a program name with args.
+set dummy lorder; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1173: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_LORDER'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$LORDER"; then
+  ac_cv_prog_LORDER="$LORDER" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_LORDER="lorder"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_prog_LORDER" && ac_cv_prog_LORDER="no"
+fi
+fi
+LORDER="$ac_cv_prog_LORDER"
+if test -n "$LORDER"; then
+  echo "$ac_t""$LORDER" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+# Extract the first word of "tsort", so it can be a program name with args.
+set dummy tsort; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1201: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_TSORT'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$TSORT"; then
+  ac_cv_prog_TSORT="$TSORT" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_TSORT="tsort"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_prog_TSORT" && ac_cv_prog_TSORT="no"
+fi
+fi
+TSORT="$ac_cv_prog_TSORT"
+if test -n "$TSORT"; then
+  echo "$ac_t""$TSORT" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+if test x$ac_cv_prog_LORDER != xlorder -o x$ac_cv_prog_TSORT != xtsort; then
+  LORDER=echo
+  TSORT=cat
+    fi
+
+pathtmp=/usr/lib:/usr/sbin:/usr/etc:/usr/ucblib:/usr/bin:/bin
+# Extract the first word of "sendmail", so it can be a program name with args.
+set dummy sendmail; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1236: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_path_sendmailpath'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  case "$sendmailpath" in
+  /*)
+  ac_cv_path_sendmailpath="$sendmailpath" # Let the user override the test with a path.
+  ;;
+  *)
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $pathtmp$ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_path_sendmailpath="$ac_dir/$ac_word"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_path_sendmailpath" && ac_cv_path_sendmailpath="no"
+  ;;
+esac
+fi
+sendmailpath="$ac_cv_path_sendmailpath"
+if test -n "$sendmailpath"; then
+  echo "$ac_t""$sendmailpath" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+
+pathtmp=/usr/bin:/bin:/usr/ucb:/usr/local/bin
+# Extract the first word of "more", so it can be a program name with args.
+set dummy more; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1270: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_path_morepath'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  case "$morepath" in
+  /*)
+  ac_cv_path_morepath="$morepath" # Let the user override the test with a path.
+  ;;
+  *)
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $pathtmp$ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_path_morepath="$ac_dir/$ac_word"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_path_morepath" && ac_cv_path_morepath="no"
+  ;;
+esac
+fi
+morepath="$ac_cv_path_morepath"
+if test -n "$morepath"; then
+  echo "$ac_t""$morepath" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+
+if test -z "$pagerpath"; then
+  pagerpath="$morepath"
+fi
+
+pathtmp=/usr/bin:/bin:/usr/ucb:/usr/local/bin
+# Extract the first word of "vi", so it can be a program name with args.
+set dummy vi; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1308: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_path_vipath'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  case "$vipath" in
+  /*)
+  ac_cv_path_vipath="$vipath" # Let the user override the test with a path.
+  ;;
+  *)
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $pathtmp$ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_path_vipath="$ac_dir/$ac_word"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_path_vipath" && ac_cv_path_vipath="no"
+  ;;
+esac
+fi
+vipath="$ac_cv_path_vipath"
+if test -n "$vipath"; then
+  echo "$ac_t""$vipath" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+
+if test -z "$editorpath"; then
+  editorpath="$vipath"
+fi
+
+echo $ac_n "checking for broken vi""... $ac_c" 1>&6
+echo "configure:1343: checking for broken vi" >&5
+if eval "test \"`echo '$''{'nmh_cv_attvibug'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if echo 'r /nonexist-file
+q' | ex > /dev/null 2>&1
+then
+        nmh_cv_attvibug=no
+else
+        nmh_cv_attvibug=yes
+fi
+fi
+
+echo "$ac_t""$nmh_cv_attvibug" 1>&6
+if test "$nmh_cv_attvibug" = yes; then
+  cat >> confdefs.h <<\EOF
+#define ATTVIBUG 1
+EOF
+
+fi
+
+echo $ac_n "checking where mail spool is located""... $ac_c" 1>&6
+echo "configure:1366: checking where mail spool is located" >&5
+if eval "test \"`echo '$''{'nmh_cv_mailspool'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  for mailspool in /var/mail                          /var/spool/mail                    /usr/spool/mail                    /dev/null;       do
+  test -d $mailspool && break
+done
+nmh_cv_mailspool=$mailspool
+
+fi
+
+echo "$ac_t""$nmh_cv_mailspool" 1>&6
+mailspool=$nmh_cv_mailspool
+
+ac_header_dirent=no
+for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr that defines DIR""... $ac_c" 1>&6
+echo "configure:1385: checking for $ac_hdr that defines DIR" >&5
+if eval "test \"`echo '$''{'ac_cv_header_dirent_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1390 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <$ac_hdr>
+int main() {
+DIR *dirp = 0;
+; return 0; }
+EOF
+if { (eval echo configure:1398: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  eval "ac_cv_header_dirent_$ac_safe=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_dirent_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_dirent_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+ ac_header_dirent=$ac_hdr; break
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix.
+if test $ac_header_dirent = dirent.h; then
+echo $ac_n "checking for opendir in -ldir""... $ac_c" 1>&6
+echo "configure:1423: checking for opendir in -ldir" >&5
+ac_lib_var=`echo dir'_'opendir | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-ldir  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1431 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char opendir();
+
+int main() {
+opendir()
+; return 0; }
+EOF
+if { (eval echo configure:1442: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  LIBS="$LIBS -ldir"
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+else
+echo $ac_n "checking for opendir in -lx""... $ac_c" 1>&6
+echo "configure:1464: checking for opendir in -lx" >&5
+ac_lib_var=`echo x'_'opendir | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lx  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1472 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char opendir();
+
+int main() {
+opendir()
+; return 0; }
+EOF
+if { (eval echo configure:1483: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  LIBS="$LIBS -lx"
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
+echo "configure:1506: checking how to run the C preprocessor" >&5
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+    # This must be in double quotes, not single quotes, because CPP may get
+  # substituted into the Makefile and "${CC-cc}" will confuse make.
+  CPP="${CC-cc} -E"
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp.
+  cat > conftest.$ac_ext <<EOF
+#line 1521 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1527: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP="${CC-cc} -E -traditional-cpp"
+  cat > conftest.$ac_ext <<EOF
+#line 1538 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1544: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+  ac_cv_prog_CPP="$CPP"
+fi
+  CPP="$ac_cv_prog_CPP"
+else
+  ac_cv_prog_CPP="$CPP"
+fi
+echo "$ac_t""$CPP" 1>&6
+
+echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6
+echo "configure:1567: checking for ANSI C header files" >&5
+if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1572 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1580: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  ac_cv_header_stdc=yes
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1597 "configure"
+#include "confdefs.h"
+#include <string.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "memchr" >/dev/null 2>&1; then
+  :
+else
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1615 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "free" >/dev/null 2>&1; then
+  :
+else
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+if test "$cross_compiling" = yes; then
+  :
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1636 "configure"
+#include "confdefs.h"
+#include <ctype.h>
+#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int main () { int i; for (i = 0; i < 256; i++)
+if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
+exit (0); }
+
+EOF
+if { (eval echo configure:1647: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null
+then
+  :
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  ac_cv_header_stdc=no
+fi
+rm -fr conftest*
+fi
+
+fi
+fi
+
+echo "$ac_t""$ac_cv_header_stdc" 1>&6
+if test $ac_cv_header_stdc = yes; then
+  cat >> confdefs.h <<\EOF
+#define STDC_HEADERS 1
+EOF
+
+fi
+
+echo $ac_n "checking whether time.h and sys/time.h may both be included""... $ac_c" 1>&6
+echo "configure:1671: checking whether time.h and sys/time.h may both be included" >&5
+if eval "test \"`echo '$''{'ac_cv_header_time'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1676 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+int main() {
+struct tm *tp;
+; return 0; }
+EOF
+if { (eval echo configure:1685: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_header_time=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_header_time=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_header_time" 1>&6
+if test $ac_cv_header_time = yes; then
+  cat >> confdefs.h <<\EOF
+#define TIME_WITH_SYS_TIME 1
+EOF
+
+fi
+
+echo $ac_n "checking for sys/wait.h that is POSIX.1 compatible""... $ac_c" 1>&6
+echo "configure:1706: checking for sys/wait.h that is POSIX.1 compatible" >&5
+if eval "test \"`echo '$''{'ac_cv_header_sys_wait_h'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1711 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/wait.h>
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+#define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+int main() {
+int s;
+wait (&s);
+s = WIFEXITED (s) ? WEXITSTATUS (s) : 1;
+; return 0; }
+EOF
+if { (eval echo configure:1727: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_header_sys_wait_h=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_header_sys_wait_h=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_header_sys_wait_h" 1>&6
+if test $ac_cv_header_sys_wait_h = yes; then
+  cat >> confdefs.h <<\EOF
+#define HAVE_SYS_WAIT_H 1
+EOF
+
+fi
+
+echo $ac_n "checking whether stat file-mode macros are broken""... $ac_c" 1>&6
+echo "configure:1748: checking whether stat file-mode macros are broken" >&5
+if eval "test \"`echo '$''{'ac_cv_header_stat_broken'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1753 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#if defined(S_ISBLK) && defined(S_IFDIR)
+# if S_ISBLK (S_IFDIR)
+You lose.
+# endif
+#endif
+
+#if defined(S_ISBLK) && defined(S_IFCHR)
+# if S_ISBLK (S_IFCHR)
+You lose.
+# endif
+#endif
+
+#if defined(S_ISLNK) && defined(S_IFREG)
+# if S_ISLNK (S_IFREG)
+You lose.
+# endif
+#endif
+
+#if defined(S_ISSOCK) && defined(S_IFREG)
+# if S_ISSOCK (S_IFREG)
+You lose.
+# endif
+#endif
+
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "You lose" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_header_stat_broken=yes
+else
+  rm -rf conftest*
+  ac_cv_header_stat_broken=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_header_stat_broken" 1>&6
+if test $ac_cv_header_stat_broken = yes; then
+  cat >> confdefs.h <<\EOF
+#define STAT_MACROS_BROKEN 1
+EOF
+
+fi
+
+for ac_hdr in string.h memory.h stdlib.h unistd.h errno.h fcntl.h \
+                 limits.h crypt.h termcap.h termio.h termios.h locale.h \
+                 sys/param.h sys/time.h sys/utsname.h arpa/inet.h \
+                 arpa/ftp.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1810: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1815 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1820: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+
+echo $ac_n "checking POSIX termios""... $ac_c" 1>&6
+echo "configure:1848: checking POSIX termios" >&5
+if eval "test \"`echo '$''{'nmh_cv_sys_posix_termios'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1853 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <unistd.h>
+#include <termios.h>
+int main() {
+/* SunOS 4.0.3 has termios.h but not the library calls.  */
+tcgetattr(0, 0);
+; return 0; }
+EOF
+if { (eval echo configure:1863: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  nmh_cv_sys_posix_termios=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  nmh_cv_sys_posix_termios=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$nmh_cv_sys_posix_termios" 1>&6
+if test $nmh_cv_sys_posix_termios = yes; then
+  echo $ac_n "checking TIOCGWINSZ in termios.h""... $ac_c" 1>&6
+echo "configure:1879: checking TIOCGWINSZ in termios.h" >&5
+if eval "test \"`echo '$''{'nmh_cv_header_termios_h_tiocgwinsz'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1884 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <termios.h>
+int main() {
+int x = TIOCGWINSZ;
+; return 0; }
+EOF
+if { (eval echo configure:1892: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  nmh_cv_header_termios_h_tiocgwinsz=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  nmh_cv_header_termios_h_tiocgwinsz=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$nmh_cv_header_termios_h_tiocgwinsz" 1>&6
+else
+  nmh_cv_header_termios_h_tiocgwinsz=no
+fi
+if test $nmh_cv_header_termios_h_tiocgwinsz = no; then
+  echo $ac_n "checking TIOCGWINSZ in sys/ioctl.h""... $ac_c" 1>&6
+echo "configure:1911: checking TIOCGWINSZ in sys/ioctl.h" >&5
+if eval "test \"`echo '$''{'nmh_cv_header_sys_ioctl_h_tiocgwinsz'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1916 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/ioctl.h>
+int main() {
+int x = TIOCGWINSZ;
+; return 0; }
+EOF
+if { (eval echo configure:1924: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  nmh_cv_header_sys_ioctl_h_tiocgwinsz=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  nmh_cv_header_sys_ioctl_h_tiocgwinsz=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$nmh_cv_header_sys_ioctl_h_tiocgwinsz" 1>&6
+  if test $nmh_cv_header_sys_ioctl_h_tiocgwinsz = yes; then
+    cat >> confdefs.h <<\EOF
+#define GWINSZ_IN_SYS_IOCTL 1
+EOF
+
+  fi
+fi
+ac_safe=`echo "sys/ptem.h" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for sys/ptem.h""... $ac_c" 1>&6
+echo "configure:1947: checking for sys/ptem.h" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1952 "configure"
+#include "confdefs.h"
+#include <sys/ptem.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1957: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  cat >> confdefs.h <<\EOF
+#define WINSIZE_IN_PTEM 1
+EOF
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+
+echo $ac_n "checking for pid_t""... $ac_c" 1>&6
+echo "configure:1983: checking for pid_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1988 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "pid_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_pid_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_pid_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_pid_t" 1>&6
+if test $ac_cv_type_pid_t = no; then
+  cat >> confdefs.h <<\EOF
+#define pid_t int
+EOF
+
+fi
+
+ac_safe=`echo "vfork.h" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for vfork.h""... $ac_c" 1>&6
+echo "configure:2017: checking for vfork.h" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2022 "configure"
+#include "confdefs.h"
+#include <vfork.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2027: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  cat >> confdefs.h <<\EOF
+#define HAVE_VFORK_H 1
+EOF
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+echo $ac_n "checking for working vfork""... $ac_c" 1>&6
+echo "configure:2052: checking for working vfork" >&5
+if eval "test \"`echo '$''{'ac_cv_func_vfork_works'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+  echo $ac_n "checking for vfork""... $ac_c" 1>&6
+echo "configure:2058: checking for vfork" >&5
+if eval "test \"`echo '$''{'ac_cv_func_vfork'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2063 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char vfork(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char vfork();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_vfork) || defined (__stub___vfork)
+choke me
+#else
+vfork();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2086: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  eval "ac_cv_func_vfork=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_vfork=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'vfork`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  :
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2107 "configure"
+#include "confdefs.h"
+/* Thanks to Paul Eggert for this test.  */
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_VFORK_H
+#include <vfork.h>
+#endif
+/* On some sparc systems, changes by the child to local and incoming
+   argument registers are propagated back to the parent.
+   The compiler is told about this with #include <vfork.h>,
+   but some compilers (e.g. gcc -O) don't grok <vfork.h>.
+   Test for this by using a static variable whose address
+   is put into a register that is clobbered by the vfork.  */
+static
+#ifdef __cplusplus
+sparc_address_test (int arg)
+#else
+sparc_address_test (arg) int arg;
+#endif
+{
+  static pid_t child;
+  if (!child) {
+    child = vfork ();
+    if (child < 0) {
+      perror ("vfork");
+      _exit(2);
+    }
+    if (!child) {
+      arg = getpid();
+      write(-1, "", 0);
+      _exit (arg);
+    }
+  }
+}
+main() {
+  pid_t parent = getpid ();
+  pid_t child;
+
+  sparc_address_test ();
+
+  child = vfork ();
+
+  if (child == 0) {
+    /* Here is another test for sparc vfork register problems.
+       This test uses lots of local variables, at least
+       as many local variables as main has allocated so far
+       including compiler temporaries.  4 locals are enough for
+       gcc 1.40.3 on a Solaris 4.1.3 sparc, but we use 8 to be safe.
+       A buggy compiler should reuse the register of parent
+       for one of the local variables, since it will think that
+       parent can't possibly be used any more in this routine.
+       Assigning to the local variable will thus munge parent
+       in the parent process.  */
+    pid_t
+      p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(),
+      p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid();
+    /* Convince the compiler that p..p7 are live; otherwise, it might
+       use the same hardware register for all 8 local variables.  */
+    if (p != p1 || p != p2 || p != p3 || p != p4
+       || p != p5 || p != p6 || p != p7)
+      _exit(1);
+
+    /* On some systems (e.g. IRIX 3.3),
+       vfork doesn't separate parent from child file descriptors.
+       If the child closes a descriptor before it execs or exits,
+       this munges the parent's descriptor as well.
+       Test for this by closing stdout in the child.  */
+    _exit(close(fileno(stdout)) != 0);
+  } else {
+    int status;
+    struct stat st;
+
+    while (wait(&status) != child)
+      ;
+    exit(
+        /* Was there some problem with vforking?  */
+        child < 0
+
+        /* Did the child fail?  (This shouldn't happen.)  */
+        || status
+
+        /* Did the vfork/compiler bug occur?  */
+        || parent != getpid()
+
+        /* Did the file descriptor bug occur?  */
+        || fstat(fileno(stdout), &st) != 0
+        );
+  }
+}
+EOF
+if { (eval echo configure:2202: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null
+then
+  ac_cv_func_vfork_works=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  ac_cv_func_vfork_works=no
+fi
+rm -fr conftest*
+fi
+
+fi
+
+echo "$ac_t""$ac_cv_func_vfork_works" 1>&6
+if test $ac_cv_func_vfork_works = no; then
+  cat >> confdefs.h <<\EOF
+#define vfork fork
+EOF
+
+fi
+
+for ac_func in waitpid wait3 sigaction sigprocmask sigblock sigsetmask \
+               sighold sigrelse writev lstat uname tzset killpg \
+               sigsetjmp
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:2229: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2234 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2257: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+
+for ac_func in snprintf strerror strdup
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:2285: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2290 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2313: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+LIBOBJS="$LIBOBJS ${ac_func}.o"
+fi
+done
+
+
+
+echo $ac_n "checking for gethostbyname""... $ac_c" 1>&6
+echo "configure:2341: checking for gethostbyname" >&5
+if eval "test \"`echo '$''{'ac_cv_func_gethostbyname'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2346 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char gethostbyname(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char gethostbyname();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_gethostbyname) || defined (__stub___gethostbyname)
+choke me
+#else
+gethostbyname();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2369: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  eval "ac_cv_func_gethostbyname=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_gethostbyname=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'gethostbyname`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  :
+else
+  echo "$ac_t""no" 1>&6
+echo $ac_n "checking for gethostbyname in -lnsl""... $ac_c" 1>&6
+echo "configure:2387: checking for gethostbyname in -lnsl" >&5
+ac_lib_var=`echo nsl'_'gethostbyname | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lnsl  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2395 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char gethostbyname();
+
+int main() {
+gethostbyname()
+; return 0; }
+EOF
+if { (eval echo configure:2406: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_lib=HAVE_LIB`echo nsl | sed -e 's/^a-zA-Z0-9_/_/g' \
+    -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+  LIBS="-lnsl $LIBS"
+
+else
+  echo "$ac_t""no" 1>&6
+echo $ac_n "checking for gethostbyname in -lresolv""... $ac_c" 1>&6
+echo "configure:2432: checking for gethostbyname in -lresolv" >&5
+ac_lib_var=`echo resolv'_'gethostbyname | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lresolv  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2440 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char gethostbyname();
+
+int main() {
+gethostbyname()
+; return 0; }
+EOF
+if { (eval echo configure:2451: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_lib=HAVE_LIB`echo resolv | sed -e 's/^a-zA-Z0-9_/_/g' \
+    -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+  LIBS="-lresolv $LIBS"
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+fi
+
+echo $ac_n "checking for socket""... $ac_c" 1>&6
+echo "configure:2483: checking for socket" >&5
+if eval "test \"`echo '$''{'ac_cv_func_socket'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2488 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char socket(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char socket();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_socket) || defined (__stub___socket)
+choke me
+#else
+socket();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2511: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  eval "ac_cv_func_socket=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_socket=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'socket`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  :
+else
+  echo "$ac_t""no" 1>&6
+echo $ac_n "checking for socket in -lsocket""... $ac_c" 1>&6
+echo "configure:2529: checking for socket in -lsocket" >&5
+ac_lib_var=`echo socket'_'socket | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lsocket  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2537 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char socket();
+
+int main() {
+socket()
+; return 0; }
+EOF
+if { (eval echo configure:2548: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_lib=HAVE_LIB`echo socket | sed -e 's/^a-zA-Z0-9_/_/g' \
+    -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+  LIBS="-lsocket $LIBS"
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+
+
+echo $ac_n "checking for ruserpass""... $ac_c" 1>&6
+echo "configure:2580: checking for ruserpass" >&5
+if eval "test \"`echo '$''{'ac_cv_func_ruserpass'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2585 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char ruserpass(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char ruserpass();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_ruserpass) || defined (__stub___ruserpass)
+choke me
+#else
+ruserpass();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2608: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  eval "ac_cv_func_ruserpass=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_ruserpass=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'ruserpass`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  :
+else
+  echo "$ac_t""no" 1>&6
+echo $ac_n "checking for _ruserpass""... $ac_c" 1>&6
+echo "configure:2626: checking for _ruserpass" >&5
+if eval "test \"`echo '$''{'ac_cv_func__ruserpass'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2631 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char _ruserpass(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char _ruserpass();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub__ruserpass) || defined (__stub____ruserpass)
+choke me
+#else
+_ruserpass();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2654: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  eval "ac_cv_func__ruserpass=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func__ruserpass=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'_ruserpass`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  :
+else
+  echo "$ac_t""no" 1>&6
+echo $ac_n "checking for _ruserpass in -lsocket""... $ac_c" 1>&6
+echo "configure:2672: checking for _ruserpass in -lsocket" >&5
+ac_lib_var=`echo socket'_'_ruserpass | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lsocket  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2680 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char _ruserpass();
+
+int main() {
+_ruserpass()
+; return 0; }
+EOF
+if { (eval echo configure:2691: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_lib=HAVE_LIB`echo socket | sed -e 's/^a-zA-Z0-9_/_/g' \
+    -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+  LIBS="-lsocket $LIBS"
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+fi
+
+if test x$ac_cv_func_ruserpass = xno; then
+  if test x$ac_cv_func__ruserpass = xyes -o x$ac_cv_lib_socket__ruserpass = xyes; then
+    cat >> confdefs.h <<\EOF
+#define ruserpass _ruserpass
+EOF
+
+  else
+    LIBOBJS="$LIBOBJS ruserpass.o"
+  fi
+fi
+
+
+termcap_curses_order="termcap curses ncurses"
+for lib in $termcap_curses_order; do
+  echo $ac_n "checking for tgetent in -l${lib}""... $ac_c" 1>&6
+echo "configure:2737: checking for tgetent in -l${lib}" >&5
+ac_lib_var=`echo ${lib}'_'tgetent | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-l${lib}  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2745 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char tgetent();
+
+int main() {
+tgetent()
+; return 0; }
+EOF
+if { (eval echo configure:2756: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  TERMLIB="-l$lib"; break
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+done
+
+echo $ac_n "checking for dbm_open""... $ac_c" 1>&6
+echo "configure:2779: checking for dbm_open" >&5
+if eval "test \"`echo '$''{'ac_cv_func_dbm_open'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2784 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char dbm_open(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char dbm_open();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_dbm_open) || defined (__stub___dbm_open)
+choke me
+#else
+dbm_open();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2807: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  eval "ac_cv_func_dbm_open=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_dbm_open=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'dbm_open`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  :
+else
+  echo "$ac_t""no" 1>&6
+echo $ac_n "checking for dbm_open in -lndbm""... $ac_c" 1>&6
+echo "configure:2825: checking for dbm_open in -lndbm" >&5
+ac_lib_var=`echo ndbm'_'dbm_open | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lndbm  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2833 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char dbm_open();
+
+int main() {
+dbm_open()
+; return 0; }
+EOF
+if { (eval echo configure:2844: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_lib=HAVE_LIB`echo ndbm | sed -e 's/^a-zA-Z0-9_/_/g' \
+    -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+  LIBS="-lndbm $LIBS"
+
+else
+  echo "$ac_t""no" 1>&6
+echo $ac_n "checking for dbm_open in -ldbm""... $ac_c" 1>&6
+echo "configure:2870: checking for dbm_open in -ldbm" >&5
+ac_lib_var=`echo dbm'_'dbm_open | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-ldbm  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2878 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char dbm_open();
+
+int main() {
+dbm_open()
+; return 0; }
+EOF
+if { (eval echo configure:2889: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_lib=HAVE_LIB`echo dbm | sed -e 's/^a-zA-Z0-9_/_/g' \
+    -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+  LIBS="-ldbm $LIBS"
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+fi
+
+
+if test x$with_hesiod != x -a x$with_hesiod != xno; then
+  if test x$with_hesiod != xyes; then
+    HESIOD_INCLUDES="-I$with_hesiod/include"
+    HESIOD_LIBS="-L$with_hesiod/lib"
+  fi
+  echo $ac_n "checking for res_send""... $ac_c" 1>&6
+echo "configure:2927: checking for res_send" >&5
+if eval "test \"`echo '$''{'ac_cv_func_res_send'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2932 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char res_send(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char res_send();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_res_send) || defined (__stub___res_send)
+choke me
+#else
+res_send();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2955: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  eval "ac_cv_func_res_send=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_res_send=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'res_send`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  :
+else
+  echo "$ac_t""no" 1>&6
+echo $ac_n "checking for res_send in -lresolv""... $ac_c" 1>&6
+echo "configure:2973: checking for res_send in -lresolv" >&5
+ac_lib_var=`echo resolv'_'res_send | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lresolv  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2981 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char res_send();
+
+int main() {
+res_send()
+; return 0; }
+EOF
+if { (eval echo configure:2992: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_lib=HAVE_LIB`echo resolv | sed -e 's/^a-zA-Z0-9_/_/g' \
+    -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+  LIBS="-lresolv $LIBS"
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+  echo $ac_n "checking for hes_resolve in -lhesiod""... $ac_c" 1>&6
+echo "configure:3022: checking for hes_resolve in -lhesiod" >&5
+ac_lib_var=`echo hesiod'_'hes_resolve | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lhesiod $HESIOD_LIBS $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 3030 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char hes_resolve();
+
+int main() {
+hes_resolve()
+; return 0; }
+EOF
+if { (eval echo configure:3041: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  HESIOD_LIBS="$HESIOD_LIBS -lhesiod"
+else
+  echo "$ac_t""no" 1>&6
+{ echo "configure: error: Hesiod library not found" 1>&2; exit 1; }
+fi
+
+fi
+
+if test x$with_krb4 != x -a x$with_krb4 != xno; then
+  if test x$with_krb4 != xyes; then
+    KRB4_INCLUDES="-I$with_krb4/include"
+    if test -d "$with_krb4/include/kerberosIV"; then
+      KRB4_INCLUDES="$KRB4_INCLUDES -I$with_krb4/include/kerberosIV"
+    fi
+    KRB4_LIBS="-L$with_krb4/lib"
+  elif test -d /usr/include/kerberosIV; then
+    KRB4_INCLUDES="-I/usr/include/kerberosIV"
+  fi
+  echo $ac_n "checking for krb_rd_req in -lkrb4""... $ac_c" 1>&6
+echo "configure:3075: checking for krb_rd_req in -lkrb4" >&5
+ac_lib_var=`echo krb4'_'krb_rd_req | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lkrb4 $KRB4_LIBS -ldes425 -lkrb5 -lcrypto -lcom_err $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 3083 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char krb_rd_req();
+
+int main() {
+krb_rd_req()
+; return 0; }
+EOF
+if { (eval echo configure:3094: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  KRB4_LIBS="$KRB4_LIBS -lkrb4 -ldes425 -lkrb5 -lcrypto -lcom_err"
+else
+  echo "$ac_t""no" 1>&6
+echo $ac_n "checking for krb_rd_req in -lkrb""... $ac_c" 1>&6
+echo "configure:3113: checking for krb_rd_req in -lkrb" >&5
+ac_lib_var=`echo krb'_'krb_rd_req | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lkrb $KRB4_LIBS -ldes $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 3121 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char krb_rd_req();
+
+int main() {
+krb_rd_req()
+; return 0; }
+EOF
+if { (eval echo configure:3132: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  KRB4_LIBS="-lkrb -ldes"
+else
+  echo "$ac_t""no" 1>&6
+{ echo "configure: error: Kerberos 4 libraries not found" 1>&2; exit 1; }
+fi
+
+fi
+
+fi
+
+
+nmh_save_LIBS="$LIBS"
+LIBS="$TERMLIB $LIBS"
+
+echo $ac_n "checking if an include file defines ospeed""... $ac_c" 1>&6
+echo "configure:3162: checking if an include file defines ospeed" >&5
+if eval "test \"`echo '$''{'nmh_cv_decl_ospeed_include_defines'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 3167 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+#if HAVE_TERMCAP_H
+#include <termcap.h>
+#endif
+int main() {
+ospeed = 0;
+; return 0; }
+EOF
+if { (eval echo configure:3180: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  nmh_cv_decl_ospeed_include_defines=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  nmh_cv_decl_ospeed_include_defines=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$nmh_cv_decl_ospeed_include_defines" 1>&6
+if test $nmh_cv_decl_ospeed_include_defines = no; then
+  echo $ac_n "checking if you must define ospeed""... $ac_c" 1>&6
+echo "configure:3196: checking if you must define ospeed" >&5
+if eval "test \"`echo '$''{'nmh_cv_decl_ospeed_must_define'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 3201 "configure"
+#include "confdefs.h"
+
+int main() {
+extern short ospeed; ospeed = 0;
+; return 0; }
+EOF
+if { (eval echo configure:3208: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  nmh_cv_decl_ospeed_must_define=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  nmh_cv_decl_ospeed_must_define=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$nmh_cv_decl_ospeed_must_define" 1>&6
+fi
+if test $nmh_cv_decl_ospeed_include_defines = yes; then
+  cat >> confdefs.h <<\EOF
+#define HAVE_OSPEED 1
+EOF
+
+elif test $nmh_cv_decl_ospeed_must_define = yes; then
+  cat >> confdefs.h <<\EOF
+#define HAVE_OSPEED 1
+EOF
+
+  cat >> confdefs.h <<\EOF
+#define MUST_DEFINE_OSPEED 1
+EOF
+
+fi
+
+
+LIBS="$nmh_save_LIBS"
+
+echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&6
+echo "configure:3243: checking return type of signal handlers" >&5
+if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 3248 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <signal.h>
+#ifdef signal
+#undef signal
+#endif
+#ifdef __cplusplus
+extern "C" void (*signal (int, void (*)(int)))(int);
+#else
+void (*signal ()) ();
+#endif
+
+int main() {
+int i;
+; return 0; }
+EOF
+if { (eval echo configure:3265: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_type_signal=void
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_type_signal=int
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_type_signal" 1>&6
+cat >> confdefs.h <<EOF
+#define RETSIGTYPE $ac_cv_type_signal
+EOF
+
+
+echo $ac_n "checking for pid_t""... $ac_c" 1>&6
+echo "configure:3284: checking for pid_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 3289 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "pid_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_pid_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_pid_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_pid_t" 1>&6
+if test $ac_cv_type_pid_t = no; then
+  cat >> confdefs.h <<\EOF
+#define pid_t int
+EOF
+
+fi
+
+echo $ac_n "checking for off_t""... $ac_c" 1>&6
+echo "configure:3317: checking for off_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_off_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 3322 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "off_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_off_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_off_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_off_t" 1>&6
+if test $ac_cv_type_off_t = no; then
+  cat >> confdefs.h <<\EOF
+#define off_t long
+EOF
+
+fi
+
+echo $ac_n "checking for uid_t in sys/types.h""... $ac_c" 1>&6
+echo "configure:3350: checking for uid_t in sys/types.h" >&5
+if eval "test \"`echo '$''{'ac_cv_type_uid_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 3355 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "uid_t" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_uid_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_uid_t=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_type_uid_t" 1>&6
+if test $ac_cv_type_uid_t = no; then
+  cat >> confdefs.h <<\EOF
+#define uid_t int
+EOF
+
+  cat >> confdefs.h <<\EOF
+#define gid_t int
+EOF
+
+fi
+
+echo $ac_n "checking for mode_t""... $ac_c" 1>&6
+echo "configure:3384: checking for mode_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_mode_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 3389 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "mode_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_mode_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_mode_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_mode_t" 1>&6
+if test $ac_cv_type_mode_t = no; then
+  cat >> confdefs.h <<\EOF
+#define mode_t int
+EOF
+
+fi
+
+echo $ac_n "checking for size_t""... $ac_c" 1>&6
+echo "configure:3417: checking for size_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 3422 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_size_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_size_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_size_t" 1>&6
+if test $ac_cv_type_size_t = no; then
+  cat >> confdefs.h <<\EOF
+#define size_t unsigned
+EOF
+
+fi
+
+
+echo $ac_n "checking for sigset_t""... $ac_c" 1>&6
+echo "configure:3451: checking for sigset_t" >&5
+if eval "test \"`echo '$''{'nmh_cv_type_sigset_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 3456 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <signal.h>
+int main() {
+sigset_t tempsigset;
+; return 0; }
+EOF
+if { (eval echo configure:3464: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  nmh_cv_type_sigset_t=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  nmh_cv_type_sigset_t=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$nmh_cv_type_sigset_t" 1>&6
+if test $nmh_cv_type_sigset_t = no; then
+  cat >> confdefs.h <<\EOF
+#define sigset_t unsigned int
+EOF
+
+fi
+
+echo $ac_n "checking for st_blksize in struct stat""... $ac_c" 1>&6
+echo "configure:3485: checking for st_blksize in struct stat" >&5
+if eval "test \"`echo '$''{'ac_cv_struct_st_blksize'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 3490 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+int main() {
+struct stat s; s.st_blksize;
+; return 0; }
+EOF
+if { (eval echo configure:3498: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_struct_st_blksize=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_struct_st_blksize=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_struct_st_blksize" 1>&6
+if test $ac_cv_struct_st_blksize = yes; then
+  cat >> confdefs.h <<\EOF
+#define HAVE_ST_BLKSIZE 1
+EOF
+
+fi
+
+
+echo $ac_n "checking for tm_gmtoff in struct tm""... $ac_c" 1>&6
+echo "configure:3520: checking for tm_gmtoff in struct tm" >&5
+if eval "test \"`echo '$''{'nmh_cv_struct_tm_gmtoff'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 3525 "configure"
+#include "confdefs.h"
+#ifdef TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# ifdef TM_IN_SYS_TIME
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+int main() {
+struct tm temptm; temptm.tm_gmtoff = 0;
+; return 0; }
+EOF
+if { (eval echo configure:3541: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  nmh_cv_struct_tm_gmtoff=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  nmh_cv_struct_tm_gmtoff=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$nmh_cv_struct_tm_gmtoff" 1>&6
+if test $nmh_cv_struct_tm_gmtoff = yes; then
+  cat >> confdefs.h <<\EOF
+#define HAVE_TM_GMTOFF 1
+EOF
+
+fi
+
+echo $ac_n "checking what style of signals to use""... $ac_c" 1>&6
+echo "configure:3562: checking what style of signals to use" >&5
+if test $ac_cv_func_sigaction = yes -a $ac_cv_func_sigprocmask = yes; then
+  signals_style=POSIX_SIGNALS
+  cat >> confdefs.h <<\EOF
+#define POSIX_SIGNALS 1
+EOF
+
+  cat >> confdefs.h <<\EOF
+#define RELIABLE_SIGNALS 1
+EOF
+
+elif test $ac_cv_func_sigblock = yes -a $ac_cv_func_sigsetmask = yes; then
+  signals_style=BSD_SIGNALS
+  cat >> confdefs.h <<\EOF
+#define BSD_SIGNALS 1
+EOF
+
+  cat >> confdefs.h <<\EOF
+#define RELIABLE_SIGNALS 1
+EOF
+
+elif test $ac_cv_func_sighold = yes -a $ac_cv_func_sigrelse = yes; then
+  signals_style=SYSV_SIGNALS
+  cat >> confdefs.h <<\EOF
+#define SYSV_SIGNALS 1
+EOF
+
+else
+  signals_style=NO_SIGNAL_BLOCKING
+  cat >> confdefs.h <<\EOF
+#define NO_SIGNAL_BLOCKING 1
+EOF
+
+fi
+
+echo "$ac_t""$signals_style" 1>&6
+
+echo $ac_n "checking where signal.h is located""... $ac_c" 1>&6
+echo "configure:3600: checking where signal.h is located" >&5
+if eval "test \"`echo '$''{'nmh_cv_path_signal_h'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  for SIGNAL_H in /usr/include/bsd/sys/signal.h                   /usr/include/asm/signal.h                       /usr/include/asm/signum.h                       /usr/include/linux/signal.h                     /usr/include/sys/signal.h                       /dev/null;                     do
+  test -f $SIGNAL_H && \
+  grep '#[     ]*define[       ][      ]*SIG[0-9A-Z]*[         ]*[0-9][0-9]*' $SIGNAL_H > /dev/null && \
+  break
+done
+nmh_cv_path_signal_h=$SIGNAL_H
+
+fi
+
+echo "$ac_t""$nmh_cv_path_signal_h" 1>&6
+SIGNAL_H=$nmh_cv_path_signal_h
+
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs.  It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already.  You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+  case `(ac_space=' '; set) 2>&1` in
+  *ac_space=\ *)
+    # `set' does not quote correctly, so add quotes (double-quote substitution
+    # turns \\\\ into \\, and sed turns \\ into \).
+    sed -n \
+      -e "s/'/'\\\\''/g" \
+      -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+    ;;
+  *)
+    # `set' quotes correctly as required by POSIX, so do not add quotes.
+    sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+    ;;
+  esac >> confcache
+if cmp -s $cache_file confcache; then
+  :
+else
+  if test -w $cache_file; then
+    echo "updating cache $cache_file"
+    cat confcache > $cache_file
+  else
+    echo "not updating unwritable cache $cache_file"
+  fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[        ]*VPATH[        ]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+DEFS=-DHAVE_CONFIG_H
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+  case "\$ac_option" in
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+    exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+  -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+    echo "$CONFIG_STATUS generated by autoconf version 2.12"
+    exit 0 ;;
+  -help | --help | --hel | --he | --h)
+    echo "\$ac_cs_usage"; exit 0 ;;
+  *) echo "\$ac_cs_usage"; exit 1 ;;
+  esac
+done
+
+ac_given_srcdir=$srcdir
+ac_given_INSTALL="$INSTALL"
+
+trap 'rm -fr `echo "Makefile config/Makefile h/Makefile sbr/Makefile uip/Makefile \
+          zotnet/Makefile zotnet/mts/Makefile zotnet/tws/Makefile \
+          zotnet/mf/Makefile zotnet/bboards/Makefile mts/Makefile \
+          mts/smtp/Makefile mts/sendmail/Makefile mts/mmdf/Makefile \
+          etc/Makefile man/Makefile config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+s%@VERSION@%$VERSION%g
+s%@MTS@%$MTS%g
+s%@MTSLIB@%$MTSLIB%g
+s%@POPLIB@%$POPLIB%g
+s%@POPSED@%$POPSED%g
+s%@CC@%$CC%g
+s%@SET_MAKE@%$SET_MAKE%g
+s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
+s%@INSTALL_DATA@%$INSTALL_DATA%g
+s%@RANLIB@%$RANLIB%g
+s%@AWK@%$AWK%g
+s%@LEX@%$LEX%g
+s%@LEXLIB@%$LEXLIB%g
+s%@LORDER@%$LORDER%g
+s%@TSORT@%$TSORT%g
+s%@sendmailpath@%$sendmailpath%g
+s%@morepath@%$morepath%g
+s%@pagerpath@%$pagerpath%g
+s%@vipath@%$vipath%g
+s%@editorpath@%$editorpath%g
+s%@mailspool@%$mailspool%g
+s%@CPP@%$CPP%g
+s%@LIBOBJS@%$LIBOBJS%g
+s%@TERMLIB@%$TERMLIB%g
+s%@HESIOD_INCLUDES@%$HESIOD_INCLUDES%g
+s%@HESIOD_LIBS@%$HESIOD_LIBS%g
+s%@KRB4_INCLUDES@%$KRB4_INCLUDES%g
+s%@KRB4_LIBS@%$KRB4_LIBS%g
+s%@SIGNAL_H@%$SIGNAL_H%g
+
+CEOF
+EOF
+
+cat >> $CONFIG_STATUS <<\EOF
+
+# Split the substitutions into bite-sized pieces for seds with
+# small command number limits, like on Digital OSF/1 and HP-UX.
+ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # Line after last line for current file.
+ac_more_lines=:
+ac_sed_cmds=""
+while $ac_more_lines; do
+  if test $ac_beg -gt 1; then
+    sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
+  else
+    sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+  fi
+  if test ! -s conftest.s$ac_file; then
+    ac_more_lines=false
+    rm -f conftest.s$ac_file
+  else
+    if test -z "$ac_sed_cmds"; then
+      ac_sed_cmds="sed -f conftest.s$ac_file"
+    else
+      ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+    fi
+    ac_file=`expr $ac_file + 1`
+    ac_beg=$ac_end
+    ac_end=`expr $ac_end + $ac_max_sed_cmds`
+  fi
+done
+if test -z "$ac_sed_cmds"; then
+  ac_sed_cmds=cat
+fi
+EOF
+
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"Makefile config/Makefile h/Makefile sbr/Makefile uip/Makefile \
+          zotnet/Makefile zotnet/mts/Makefile zotnet/tws/Makefile \
+          zotnet/mf/Makefile zotnet/bboards/Makefile mts/Makefile \
+          mts/smtp/Makefile mts/sendmail/Makefile mts/mmdf/Makefile \
+          etc/Makefile man/Makefile"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case "$ac_file" in
+  *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+       ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+  *) ac_file_in="${ac_file}.in" ;;
+  esac
+
+  # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+  # Remove last slash and all that follows it.  Not all systems have dirname.
+  ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+  if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+    # The file is in a subdirectory.
+    test ! -d "$ac_dir" && mkdir "$ac_dir"
+    ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+    # A "../" for each directory in $ac_dir_suffix.
+    ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+  else
+    ac_dir_suffix= ac_dots=
+  fi
+
+  case "$ac_given_srcdir" in
+  .)  srcdir=.
+      if test -z "$ac_dots"; then top_srcdir=.
+      else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+  /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+  *) # Relative path.
+    srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+    top_srcdir="$ac_dots$ac_given_srcdir" ;;
+  esac
+
+  case "$ac_given_INSTALL" in
+  [/$]*) INSTALL="$ac_given_INSTALL" ;;
+  *) INSTALL="$ac_dots$ac_given_INSTALL" ;;
+  esac
+
+  echo creating "$ac_file"
+  rm -f "$ac_file"
+  configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+  case "$ac_file" in
+  *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+  *) ac_comsub= ;;
+  esac
+
+  ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+  sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+s%@INSTALL@%$INSTALL%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s%^\([  ]*\)#\([        ]*define[       ][      ]*\)'
+ac_dB='\([     ][      ]*\)[^  ]*%\1#\2'
+ac_dC='\3'
+ac_dD='%g'
+# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
+ac_uA='s%^\([  ]*\)#\([        ]*\)undef\([    ][      ]*\)'
+ac_uB='\([     ]\)%\1#\2define\3'
+ac_uC=' '
+ac_uD='\4%g'
+# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_eA='s%^\([  ]*\)#\([        ]*\)undef\([    ][      ]*\)'
+ac_eB='$%\1#\2define\3'
+ac_eC=' '
+ac_eD='%g'
+
+if test "${CONFIG_HEADERS+set}" != set; then
+EOF
+cat >> $CONFIG_STATUS <<EOF
+  CONFIG_HEADERS="config.h"
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+fi
+for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case "$ac_file" in
+  *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+       ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+  *) ac_file_in="${ac_file}.in" ;;
+  esac
+
+  echo creating $ac_file
+
+  rm -f conftest.frag conftest.in conftest.out
+  ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+  cat $ac_file_inputs > conftest.in
+
+EOF
+
+# Transform confdefs.h into a sed script conftest.vals that substitutes
+# the proper values into config.h.in to produce config.h.  And first:
+# Protect against being on the right side of a sed subst in config.status.
+# Protect against being in an unquoted here document in config.status.
+rm -f conftest.vals
+cat > conftest.hdr <<\EOF
+s/[\\&%]/\\&/g
+s%[\\$`]%\\&%g
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp
+s%ac_d%ac_u%gp
+s%ac_u%ac_e%gp
+EOF
+sed -n -f conftest.hdr confdefs.h > conftest.vals
+rm -f conftest.hdr
+
+# This sed command replaces #undef with comments.  This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >> conftest.vals <<\EOF
+s%^[   ]*#[    ]*undef[        ][      ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */%
+EOF
+
+# Break up conftest.vals because some shells have a limit on
+# the size of here documents, and old seds have small limits too.
+
+rm -f conftest.tail
+while :
+do
+  ac_lines=`grep -c . conftest.vals`
+  # grep -c gives empty output for an empty file on some AIX systems.
+  if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi
+  # Write a limited-size here document to conftest.frag.
+  echo '  cat > conftest.frag <<CEOF' >> $CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS
+  echo 'CEOF
+  sed -f conftest.frag conftest.in > conftest.out
+  rm -f conftest.in
+  mv conftest.out conftest.in
+' >> $CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail
+  rm -f conftest.vals
+  mv conftest.tail conftest.vals
+done
+rm -f conftest.vals
+
+cat >> $CONFIG_STATUS <<\EOF
+  rm -f conftest.frag conftest.h
+  echo "/* $ac_file.  Generated automatically by configure.  */" > conftest.h
+  cat conftest.in >> conftest.h
+  rm -f conftest.in
+  if cmp -s $ac_file conftest.h 2>/dev/null; then
+    echo "$ac_file is unchanged"
+    rm -f conftest.h
+  else
+    # Remove last slash and all that follows it.  Not all systems have dirname.
+      ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+      if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+      # The file is in a subdirectory.
+      test ! -d "$ac_dir" && mkdir "$ac_dir"
+    fi
+    rm -f $ac_file
+    mv conftest.h $ac_file
+  fi
+fi; done
+
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+\
+          test -z "$CONFIG_HEADERS" || echo > stamp-h
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
+
+eval "nmhbin=${bindir}";         eval "nmhbin2=${nmhbin}"
+eval "nmhsysconf=${sysconfdir}"; eval "nmhsysconf2=${nmhsysconf}"
+eval "nmhlib=${libdir}";         eval "nmhlib2=${nmhlib}"
+eval "nmhman=${mandir}"
+
+echo "
+nmh configuration
+-----------------
+nmh version               : ${VERSION}
+compiler                  : ${CC}
+compiler flags            : ${CFLAGS}
+linker flags              : ${LDFLAGS}
+source code location      : ${srcdir}
+binary install path       : ${nmhbin2}
+libary install path       : ${nmhlib2}
+config files install path : ${nmhsysconf2}
+man page install path     : ${nmhman}
+transport system          : ${MTS}
+default editor            : ${editorpath}
+default pager             : ${pagerpath}"
+echo ""
diff --git a/configure.in b/configure.in
new file mode 100644 (file)
index 0000000..f544ac6
--- /dev/null
@@ -0,0 +1,512 @@
+dnl
+dnl configure.in -- autoconf template for nmh
+dnl
+dnl $Id$
+dnl
+
+AC_INIT(h/nmh.h)
+AC_CONFIG_HEADER(config.h)
+
+dnl What version of nmh are we building?
+VERSION=`sed -e 's/nmh-//' ${srcdir}/VERSION`
+echo "configuring for nmh-$VERSION"
+AC_SUBST(VERSION)dnl
+
+dnl -------------------------
+dnl CHECK COMMAND LINE OPTION
+dnl -------------------------
+dnl What method of posting should post use?
+undefine([mts])dnl
+AC_ARG_WITH(mts,
+[  --with-mts=MTS          specify the mail transport agent])
+
+if test x$with_mts = xsmtp; then
+  MTS="smtp"
+  MTSLIB="mts/smtp/libsmtp.a"
+  AC_DEFINE(SMTPMTS)dnl
+elif test x$with_mts = xsendmail; then
+  MTS="sendmail"
+  MTSLIB="mts/sendmail/libsend.a"
+  AC_DEFINE(SENDMTS)dnl
+else
+  MTS="smtp"
+  MTSLIB="mts/smtp/libsmtp.a"
+  AC_DEFINE(SMTPMTS)dnl
+fi
+
+AC_SUBST(MTS)
+AC_SUBST(MTSLIB)
+
+dnl What should be the default editor?
+undefine([editor])dnl
+AC_ARG_WITH(editor,
+[  --with-editor=EDITOR    specify the default editor])
+
+if test -n "$with_editor"; then
+  editorpath="$with_editor"
+fi
+
+dnl What should be the default pager?
+undefine([pager])dnl
+AC_ARG_WITH(pager,
+[  --with-pager=PAGER      specify the default pager])
+
+if test -n "$with_pager"; then
+  pagerpath="$with_pager"
+fi
+
+dnl Do you want mhe support?
+undefine([nmh-mhe])dnl
+AC_ARG_ENABLE(nmh-mhe,
+[  --enable-nmh-mhe        enable mhe support (DEFAULT)])
+
+dnl mhe support is on by default, so define it unless
+dnl explicitly disabled.
+if test x$enable_nmh_mhe != xno; then
+  AC_DEFINE(MHE)dnl
+fi
+
+dnl Do you want client-side support for pop
+undefine([nmh-pop])dnl
+AC_ARG_ENABLE(nmh-pop,
+[  --enable-nmh-pop        enable client-side support for pop])
+if test x$enable_nmh_pop = xyes; then
+  AC_DEFINE(POP)dnl
+  POPLIB=popsbr.o
+  POPSED='/^%nmhbeginpop%/d;/^%nmhendpop%/d'
+else
+  POPSED='/^%nmhbeginpop%/,/^%nmhendpop%/d'
+fi
+AC_SUBST(POPLIB)dnl
+AC_SUBST(POPSED)dnl
+
+dnl Do you want client-side support for kpop
+AC_ARG_WITH(krb4,
+[  --with-krb4=PREFIX      specify location of Kerberos V4 for kpop support])
+if test x$with_krb4 != x -a x$with_krb4 != xno; then
+  AC_DEFINE(KPOP)dnl
+  AC_DEFINE(KPOP_PRINCIPAL, "pop")dnl
+fi
+
+dnl Do you want support for hesiod
+AC_ARG_WITH(hesiod,
+[  --with-hesiod=PREFIX    specify location of Hesiod])
+if test x$with_hesiod != x -a x$with_hesiod != xno; then
+  AC_DEFINE(HESIOD)dnl
+fi
+
+dnl Do you want to debug nmh?
+undefine([nmh-debug])dnl
+AC_ARG_ENABLE(nmh-debug,
+[  --enable-nmh-debug      enable nmh code debugging])
+
+dnl ----------------------------------------------------
+dnl Default location is /usr/local/nmh/{bin,etc,lib,man}
+dnl ----------------------------------------------------
+AC_PREFIX_DEFAULT(/usr/local/nmh)
+
+dnl ------------------
+dnl CHECK THE COMPILER
+dnl ------------------
+dnl We want these before the checks,
+dnl so the checks can modify their values.
+test -z "$CFLAGS" && CFLAGS= auto_cflags=1
+if test x$enable_nmh_debug = xyes; then
+  test -z "$LDFLAGS" && LDFLAGS=-g
+fi
+
+AC_PROG_CC
+
+dnl if the user hasn't specified CFLAGS, then
+dnl   if compiler is gcc, then
+dnl     use -O2 and some warning flags
+dnl   else use -O
+if test -n "$auto_cflags"; then
+  if test x$enable_nmh_debug = xyes; then
+    if test -n "$GCC"; then
+      test -z "$CFLAGS" && CFLAGS="-Wall -g" || CFLAGS="$CFLAGS -Wall -g"
+    else
+      test -z "$CFLAGS" && CFLAGS=-g || CFLAGS="$CFLAGS -g"
+    fi
+  else
+    test -z "$LDFLAGS" && LDFLAGS=-s
+    if test -n "$GCC"; then
+      test -z "$CFLAGS" && CFLAGS=-O2 || CFLAGS="$CFLAGS -O2"
+    else
+      test -z "$CFLAGS" && CFLAGS=-O  || CFLAGS="$CFLAGS -O"
+    fi
+  fi
+fi
+
+AC_C_CONST              dnl Does compiler support `const'.
+
+dnl ------------------
+dnl CHECK FOR PROGRAMS
+dnl ------------------
+AC_PROG_MAKE_SET       dnl Does make define $MAKE
+AC_PROG_INSTALL                dnl Check for BSD compatible `install'
+AC_PROG_RANLIB         dnl Check for `ranlib'
+AC_PROG_AWK             dnl Check for mawk,gawk,nawk, then awk
+AC_PROG_LEX             dnl Check for lex/flex
+
+dnl Check for lorder and tsort commands
+AC_CHECK_PROG(LORDER, lorder, lorder, no)dnl
+AC_CHECK_PROG(TSORT, tsort, tsort, no)dnl
+
+dnl If either doesn't exist, replace them with echo and cat
+if test x$ac_cv_prog_LORDER != xlorder -o x$ac_cv_prog_TSORT != xtsort; then
+  LORDER=echo
+  TSORT=cat
+  AC_SUBST(LORDER)dnl
+  AC_SUBST(TSORT)dnl
+fi
+
+dnl Look for `sendmail'
+pathtmp=/usr/lib:/usr/sbin:/usr/etc:/usr/ucblib:/usr/bin:/bin
+AC_PATH_PROG(sendmailpath, sendmail, no, [$pathtmp])
+
+dnl Look for `more'
+pathtmp=/usr/bin:/bin:/usr/ucb:/usr/local/bin
+AC_PATH_PROG(morepath, more, no, [$pathtmp])
+
+dnl If pager is not specified yet,
+dnl then use `more' as the default.
+if test -z "$pagerpath"; then
+  pagerpath="$morepath"
+fi
+AC_SUBST(pagerpath)dnl
+
+dnl Look for `vi'
+pathtmp=/usr/bin:/bin:/usr/ucb:/usr/local/bin
+AC_PATH_PROG(vipath, vi, no, [$pathtmp])
+
+dnl If editor is not specified yet,
+dnl then use `vi' as the default.
+if test -z "$editorpath"; then
+  editorpath="$vipath"
+fi
+AC_SUBST(editorpath)dnl
+
+dnl Check for broken vi
+AC_CACHE_CHECK(for broken vi, nmh_cv_attvibug,
+[if echo 'r /nonexist-file
+q' | ex > /dev/null 2>&1
+then
+        nmh_cv_attvibug=no
+else
+        nmh_cv_attvibug=yes
+fi])
+if test "$nmh_cv_attvibug" = yes; then
+  AC_DEFINE(ATTVIBUG)
+fi
+
+dnl ---------------
+dnl FIND MAIL SPOOL
+dnl ---------------
+AC_CACHE_CHECK(where mail spool is located, nmh_cv_mailspool,
+[for mailspool in /var/mail        dnl
+                  /var/spool/mail  dnl
+                  /usr/spool/mail  dnl
+                  /dev/null;       dnl Just in case we fall through
+do
+  test -d $mailspool && break
+done
+nmh_cv_mailspool=$mailspool
+])
+mailspool=$nmh_cv_mailspool
+AC_SUBST(mailspool)dnl
+
+dnl ------------------
+dnl CHECK HEADER FILES
+dnl ------------------
+AC_HEADER_DIRENT
+AC_HEADER_STDC
+AC_HEADER_TIME
+AC_HEADER_SYS_WAIT
+AC_HEADER_STAT
+AC_CHECK_HEADERS(string.h memory.h stdlib.h unistd.h errno.h fcntl.h \
+                 limits.h crypt.h termcap.h termio.h termios.h locale.h \
+                 sys/param.h sys/time.h sys/utsname.h arpa/inet.h \
+                 arpa/ftp.h)
+
+AC_CACHE_CHECK(POSIX termios, nmh_cv_sys_posix_termios,
+[AC_TRY_LINK([#include <sys/types.h>
+#include <unistd.h>
+#include <termios.h>],
+[/* SunOS 4.0.3 has termios.h but not the library calls.  */
+tcgetattr(0, 0);],
+  nmh_cv_sys_posix_termios=yes, nmh_cv_sys_posix_termios=no)])
+if test $nmh_cv_sys_posix_termios = yes; then
+  AC_CACHE_CHECK(TIOCGWINSZ in termios.h,
+  nmh_cv_header_termios_h_tiocgwinsz,
+  [AC_TRY_LINK([#include <sys/types.h>
+#include <termios.h>],
+  [int x = TIOCGWINSZ;],
+  nmh_cv_header_termios_h_tiocgwinsz=yes,
+  nmh_cv_header_termios_h_tiocgwinsz=no)])
+else
+  nmh_cv_header_termios_h_tiocgwinsz=no
+fi
+if test $nmh_cv_header_termios_h_tiocgwinsz = no; then
+  AC_CACHE_CHECK(TIOCGWINSZ in sys/ioctl.h,
+  nmh_cv_header_sys_ioctl_h_tiocgwinsz,
+  [AC_TRY_LINK([#include <sys/types.h>
+#include <sys/ioctl.h>],
+  [int x = TIOCGWINSZ;],
+  nmh_cv_header_sys_ioctl_h_tiocgwinsz=yes,
+  nmh_cv_header_sys_ioctl_h_tiocgwinsz=no)])
+  if test $nmh_cv_header_sys_ioctl_h_tiocgwinsz = yes; then
+    AC_DEFINE(GWINSZ_IN_SYS_IOCTL)
+  fi
+fi
+AC_CHECK_HEADER([sys/ptem.h], AC_DEFINE(WINSIZE_IN_PTEM))
+
+dnl ---------------
+dnl CHECK FUNCTIONS
+dnl ---------------
+AC_FUNC_VFORK
+AC_CHECK_FUNCS(waitpid wait3 sigaction sigprocmask sigblock sigsetmask \
+               sighold sigrelse writev lstat uname tzset killpg \
+               sigsetjmp)
+
+AC_REPLACE_FUNCS(snprintf strerror strdup)
+
+dnl -------------------
+dnl CHECK FOR LIBRARIES
+dnl -------------------
+dnl Checks for network libraries (nsl, socket)
+AC_CHECK_NETLIBS
+
+dnl Check for bug in libraries such that ruserpass
+dnl needs to be linked as _ruserpass.
+AC_CHECK_RUSERPASS
+
+termcap_curses_order="termcap curses ncurses"
+for lib in $termcap_curses_order; do
+  AC_CHECK_LIB(${lib}, tgetent, [TERMLIB="-l$lib"; break])
+done
+AC_SUBST(TERMLIB)dnl
+
+dnl --------------
+dnl CHECK FOR NDBM
+dnl --------------
+dnl Checks for ndbm
+AC_CHECK_FUNC(dbm_open, ,
+  AC_CHECK_LIB(ndbm, dbm_open, ,
+    AC_CHECK_LIB(dbm, dbm_open)))
+
+dnl ----------------
+dnl CHECK FOR HESIOD
+dnl ----------------
+if test x$with_hesiod != x -a x$with_hesiod != xno; then
+  if test x$with_hesiod != xyes; then
+    HESIOD_INCLUDES="-I$with_hesiod/include"
+    HESIOD_LIBS="-L$with_hesiod/lib"
+  fi
+  AC_CHECK_FUNC(res_send, ,
+    AC_CHECK_LIB(resolv, res_send))
+  AC_CHECK_LIB(hesiod, hes_resolve, [HESIOD_LIBS="$HESIOD_LIBS -lhesiod"],
+    [AC_MSG_ERROR(Hesiod library not found)], $HESIOD_LIBS)
+fi
+AC_SUBST(HESIOD_INCLUDES)dnl
+AC_SUBST(HESIOD_LIBS)dnl
+
+dnl ----------------------------------
+dnl CHECK FOR KRB4 (Kerberos4 support)
+dnl ----------------------------------
+if test x$with_krb4 != x -a x$with_krb4 != xno; then
+  if test x$with_krb4 != xyes; then
+    KRB4_INCLUDES="-I$with_krb4/include"
+    if test -d "$with_krb4/include/kerberosIV"; then
+      KRB4_INCLUDES="$KRB4_INCLUDES -I$with_krb4/include/kerberosIV"
+    fi
+    KRB4_LIBS="-L$with_krb4/lib"
+  elif test -d /usr/include/kerberosIV; then
+    KRB4_INCLUDES="-I/usr/include/kerberosIV"
+  fi
+  AC_CHECK_LIB(krb4, krb_rd_req,
+    [KRB4_LIBS="$KRB4_LIBS -lkrb4 -ldes425 -lkrb5 -lcrypto -lcom_err"],
+    [AC_CHECK_LIB(krb, krb_rd_req,
+      [KRB4_LIBS="-lkrb -ldes"],
+      [AC_MSG_ERROR(Kerberos 4 libraries not found)],
+      $KRB4_LIBS -ldes)],
+    $KRB4_LIBS -ldes425 -lkrb5 -lcrypto -lcom_err)
+fi
+AC_SUBST(KRB4_INCLUDES)dnl
+AC_SUBST(KRB4_LIBS)dnl
+
+dnl ---------------------
+dnl CHECK TERMCAP LIBRARY
+dnl ---------------------
+
+dnl Add the termcap library, so that the following configure
+dnl tests will find it when it tries to link test programs.
+nmh_save_LIBS="$LIBS"
+LIBS="$TERMLIB $LIBS"
+
+dnl Checks for external variable ospeed in the termcap library.
+AC_CACHE_CHECK(if an include file defines ospeed,
+nmh_cv_decl_ospeed_include_defines,
+[AC_TRY_LINK(
+[#include <sys/types.h>
+#if HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+#if HAVE_TERMCAP_H
+#include <termcap.h>
+#endif], [ospeed = 0;],
+nmh_cv_decl_ospeed_include_defines=yes,
+nmh_cv_decl_ospeed_include_defines=no)])
+if test $nmh_cv_decl_ospeed_include_defines = no; then
+  AC_CACHE_CHECK(if you must define ospeed,
+  nmh_cv_decl_ospeed_must_define,
+  [AC_TRY_LINK( ,[extern short ospeed; ospeed = 0;],
+  nmh_cv_decl_ospeed_must_define=yes,
+  nmh_cv_decl_ospeed_must_define=no)])
+fi
+if test $nmh_cv_decl_ospeed_include_defines = yes; then
+  AC_DEFINE(HAVE_OSPEED)
+elif test $nmh_cv_decl_ospeed_must_define = yes; then
+  AC_DEFINE(HAVE_OSPEED)
+  AC_DEFINE(MUST_DEFINE_OSPEED)
+fi
+
+dnl dnl Checks if tgetent accepts NULL and will
+dnl dnl allocate its own termcap buffer.
+dnl AC_CACHE_CHECK(if tgetent accepts NULL,
+dnl nmh_cv_func_tgetent_accepts_null,
+dnl [AC_TRY_RUN([main(){int i = tgetent((char*)0,"vt100");exit(!i || i == -1);}],
+dnl   nmh_cv_func_tgetent_accepts_null=yes,
+dnl   nmh_cv_func_tgetent_accepts_null=no,
+dnl   nmh_cv_func_tgetent_accepts_null=no)])
+dnl if test $nmh_cv_func_tgetent_accepts_null = yes; then
+dnl   AC_DEFINE(TGETENT_ACCEPTS_NULL)
+dnl fi
+
+dnl Now put the libraries back to what it was before we
+dnl starting checking the termcap library.
+LIBS="$nmh_save_LIBS"
+
+dnl --------------
+dnl CHECK TYPEDEFS
+dnl --------------
+AC_TYPE_SIGNAL
+AC_TYPE_PID_T
+AC_TYPE_OFF_T
+AC_TYPE_UID_T
+AC_TYPE_MODE_T
+AC_TYPE_SIZE_T
+
+dnl Check for sigset_t.  Currently I'm looking in
+dnl <sys/types.h> and <signal.h>.  Others might need
+dnl to be added.
+AC_CACHE_CHECK(for sigset_t, nmh_cv_type_sigset_t,
+[AC_TRY_COMPILE(
+[#include <sys/types.h>
+#include <signal.h>], [sigset_t tempsigset;],
+  nmh_cv_type_sigset_t=yes, nmh_cv_type_sigset_t=no)])
+if test $nmh_cv_type_sigset_t = no; then
+  AC_DEFINE(sigset_t, unsigned int)
+fi
+
+dnl ----------------
+dnl CHECK STRUCTURES
+dnl ----------------
+AC_STRUCT_ST_BLKSIZE
+
+AC_CACHE_CHECK(for tm_gmtoff in struct tm, nmh_cv_struct_tm_gmtoff,
+[AC_TRY_COMPILE(
+[#ifdef TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# ifdef TM_IN_SYS_TIME
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif],
+[struct tm temptm; temptm.tm_gmtoff = 0;],
+  nmh_cv_struct_tm_gmtoff=yes, nmh_cv_struct_tm_gmtoff=no)])
+if test $nmh_cv_struct_tm_gmtoff = yes; then
+  AC_DEFINE(HAVE_TM_GMTOFF)
+fi
+
+dnl -------------
+dnl CHECK SIGNALS
+dnl -------------
+dnl What style of signal do you have (POSIX, BSD, or SYSV)?
+AC_MSG_CHECKING(what style of signals to use)
+if test $ac_cv_func_sigaction = yes -a $ac_cv_func_sigprocmask = yes; then
+  signals_style=POSIX_SIGNALS
+  AC_DEFINE(POSIX_SIGNALS)
+  AC_DEFINE(RELIABLE_SIGNALS)
+elif test $ac_cv_func_sigblock = yes -a $ac_cv_func_sigsetmask = yes; then
+  signals_style=BSD_SIGNALS
+  AC_DEFINE(BSD_SIGNALS)
+  AC_DEFINE(RELIABLE_SIGNALS)
+elif test $ac_cv_func_sighold = yes -a $ac_cv_func_sigrelse = yes; then
+  signals_style=SYSV_SIGNALS
+  AC_DEFINE(SYSV_SIGNALS)
+else
+  signals_style=NO_SIGNAL_BLOCKING
+  AC_DEFINE(NO_SIGNAL_BLOCKING)
+fi
+
+AC_MSG_RESULT($signals_style)
+
+dnl Where is <signal.h> located?  Needed as input for signames.awk
+AC_CACHE_CHECK(where signal.h is located, nmh_cv_path_signal_h,
+[for SIGNAL_H in /usr/include/bsd/sys/signal.h  dnl Next
+                 /usr/include/asm/signal.h      dnl Linux 1.3.0 and above
+                 /usr/include/asm/signum.h      dnl some versions of Linux/Alpha
+                 /usr/include/linux/signal.h    dnl Linux up to 1.2.11
+                 /usr/include/sys/signal.h      dnl Almost everybody else
+                 /dev/null;                     dnl Just in case we fall through
+do
+  test -f $SIGNAL_H && \
+  grep '#[     ]*define[       ][      ]*SIG[0-9A-Z]*[         ]*[0-9][0-9]*' $SIGNAL_H > /dev/null && \
+  break
+done
+nmh_cv_path_signal_h=$SIGNAL_H
+])
+SIGNAL_H=$nmh_cv_path_signal_h
+AC_SUBST(SIGNAL_H)dnl
+
+dnl ----------------
+dnl OUTPUT MAKEFILES
+dnl ----------------
+AC_OUTPUT(Makefile config/Makefile h/Makefile sbr/Makefile uip/Makefile \
+          zotnet/Makefile zotnet/mts/Makefile zotnet/tws/Makefile \
+          zotnet/mf/Makefile zotnet/bboards/Makefile mts/Makefile \
+          mts/smtp/Makefile mts/sendmail/Makefile mts/mmdf/Makefile \
+          etc/Makefile man/Makefile, \
+          [test -z "$CONFIG_HEADERS" || echo > stamp-h])
+
+eval "nmhbin=${bindir}";         eval "nmhbin2=${nmhbin}"
+eval "nmhsysconf=${sysconfdir}"; eval "nmhsysconf2=${nmhsysconf}"
+eval "nmhlib=${libdir}";         eval "nmhlib2=${nmhlib}"
+eval "nmhman=${mandir}"
+
+echo "
+nmh configuration
+-----------------
+nmh version               : ${VERSION}
+compiler                  : ${CC}
+compiler flags            : ${CFLAGS}
+linker flags              : ${LDFLAGS}
+source code location      : ${srcdir}
+binary install path       : ${nmhbin2}
+libary install path       : ${nmhlib2}
+config files install path : ${nmhsysconf2}
+man page install path     : ${nmhman}
+transport system          : ${MTS}
+default editor            : ${editorpath}
+default pager             : ${pagerpath}"
+echo ""
diff --git a/etc/MailAliases b/etc/MailAliases
new file mode 100644 (file)
index 0000000..c5be141
--- /dev/null
@@ -0,0 +1,29 @@
+;
+; MailAliases -- nmh global aliases file
+;
+; $Id$
+;
+; This file is used to define aliases that are valid for all mh users.
+; This file is almost empty as MH now supports personal aliases.
+;
+; If you need to define system wide aliases such as "everyone", it is
+; preferable that this be done as the mail transport level, so that they
+; will be valid for users of other mail clients.
+
+; everyone: *
+
+; Blank lines and lines beginning with a ; are ignored.
+; < file       -> read more aliases from "file"
+; foo: fum      -> simple replacement
+; foo: fum, fie -> list replacement
+; foo: < file   -> list replacement from "file"
+; foo: = group  -> list replacement from UNIX group
+; foo: + group  -> list replacement by ALL users in /etc/passwd
+;                    with gid == group
+; foo: *        -> list replacement by ALL users in /etc/passwd
+;                     with uid >= 200
+; foo*: fum     -> matches foo<string> (including the empty string)
+;
+; using a ';' instead of a ':' indicates that the alias should be displayed
+; along with the addresses used (normally, the addresses replace the alias
+; completely)
diff --git a/etc/Makefile.in b/etc/Makefile.in
new file mode 100644 (file)
index 0000000..67eed05
--- /dev/null
@@ -0,0 +1,134 @@
+#
+# Makefile for etc subdirectory
+#
+# $Id$
+#
+
+SHELL = /bin/sh
+
+top_srcdir = @top_srcdir@
+srcdir     = @srcdir@
+VPATH      = @srcdir@
+
+prefix      = @prefix@
+exec_prefix = @exec_prefix@
+bindir      = @bindir@
+libdir      = @libdir@
+etcdir      = @sysconfdir@
+
+mailspool    = @mailspool@
+
+INSTALL         = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA    = @INSTALL_DATA@
+
+# Path to search for programs to handle MIME
+# content.  Used to create mhn.defaults
+MHNSEARCHPATH = "$(PATH):/usr/demo/SOUND"
+
+# Program used to search path for various programs to
+# handle MIME content.  Used to create mhn.defaults
+MHNSEARCHPROG = $(srcdir)/mhn.find.sh
+
+SED = sed
+
+.SUFFIXES:
+
+# format and components files
+DIST_FILES = mhl.body mhl.digest mhl.format mhl.forward mhl.headers \
+             mhl.reply scan.default scan.mailx scan.nomime scan.size scan.time \
+             scan.timely scan.unseen components digestcomps distcomps \
+             forwcomps rcvdistcomps replcomps replgroupcomps MailAliases
+
+# format and configuration files to generate
+GEN_FILES = mhn.defaults mts.conf
+
+# data files we need to install
+FILES = $(DIST_FILES) $(GEN_FILES)
+
+# scripts to install
+SCRIPTS = sendfiles
+
+# auxiliary files
+AUX = Makefile.in mhn.defaults.sh mhn.find.sh mts.conf.in
+
+# all files in this directory included in the distribution
+DIST = $(DIST_FILES) $(SCRIPTS) $(AUX)
+
+# ========= DEPENDENCIES FOR BUILDING ==========
+
+all: $(GEN_FILES)
+
+mhn.defaults: $(srcdir)/mhn.defaults.sh $(MHNSEARCHPROG)
+       rm -f $@
+       $(srcdir)/mhn.defaults.sh $(MHNSEARCHPATH) $(MHNSEARCHPROG) > $@
+
+mts.conf: $(srcdir)/mts.conf.in
+       rm -f $@
+       $(SED) -e 's,%mailspool%,$(mailspool),' \
+              -e 's,%etcdir%,$(etcdir),' < $(srcdir)/mts.conf.in > $@
+
+install: install-files install-scripts
+
+install-files:
+       $(top_srcdir)/mkinstalldirs $(etcdir)
+       for file in $(DIST_FILES); do \
+         if [ -f $(etcdir)/$$file ]; then \
+           mv $(etcdir)/$$file $(etcdir)/$$file.old; \
+         fi; \
+         $(INSTALL_DATA) $(srcdir)/$$file $(etcdir)/$$file; \
+       done
+       for file in $(GEN_FILES); do \
+         if [ -f $(etcdir)/$$file ]; then \
+           mv $(etcdir)/$$file $(etcdir)/$$file.old; \
+         fi; \
+         $(INSTALL_DATA) $$file $(etcdir)/$$file; \
+       done
+
+install-scripts:
+       $(top_srcdir)/mkinstalldirs $(libdir)
+       for script in $(SCRIPTS); do \
+         $(INSTALL_PROGRAM) $(srcdir)/$$script $(libdir)/$$script; \
+       done
+
+uninstall: uninstall-files uninstall-scripts
+
+uninstall-files:
+       for file in $(FILES); do \
+         rm -f $(etcdir)/$$file; \
+       done
+
+uninstall-scripts:
+       for script in $(SCRIPTS); do \
+         rm -f $(libdir)/$$script; \
+       done
+
+# ========== DEPENDENCIES FOR CLEANUP ==========
+
+mostlyclean:
+       rm -f *~
+
+clean: mostlyclean
+       rm -f $(GEN_FILES)
+
+distclean: clean
+       rm -f Makefile
+
+realclean: distclean
+
+superclean: realclean
+
+# ========== DEPENDENCIES FOR MAINTENANCE ==========
+
+subdir = etc
+
+Makefile: Makefile.in ../config.status
+       cd .. && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+distdir = ../`cat ../distname`/$(subdir)
+nmhdist: $(DIST)
+       @echo "Copying distribution files in $(subdir)"
+       @for file in $(DIST); do \
+         cp -p $(srcdir)/$$file $(distdir); \
+       done
+
diff --git a/etc/components b/etc/components
new file mode 100644 (file)
index 0000000..2669f04
--- /dev/null
@@ -0,0 +1,4 @@
+To:
+cc:
+Subject:
+--------
diff --git a/etc/digestcomps b/etc/digestcomps
new file mode 100644 (file)
index 0000000..d527e1d
--- /dev/null
@@ -0,0 +1,9 @@
+From:    %{digest}-Request
+To:      %{digest} Distribution: dist-%{digest};
+Subject:  %{digest} Digest V%(cur) #%(msg)
+Reply-To: %{digest}
+--------
+%{digest} Digest       %(weekday{date}), %2(mday{date}) %(month{date}) 19%02(year{date})
+               Volume %(cur) : Issue %(msg)
+
+Today's Topics:
diff --git a/etc/distcomps b/etc/distcomps
new file mode 100644 (file)
index 0000000..c6dc39a
--- /dev/null
@@ -0,0 +1,2 @@
+Resent-To:
+Resent-cc:
diff --git a/etc/forwcomps b/etc/forwcomps
new file mode 100644 (file)
index 0000000..2669f04
--- /dev/null
@@ -0,0 +1,4 @@
+To:
+cc:
+Subject:
+--------
diff --git a/etc/mhl.body b/etc/mhl.body
new file mode 100644 (file)
index 0000000..70ae463
--- /dev/null
@@ -0,0 +1,2 @@
+width=10000
+body:nocomponent,overflowoffset=0
diff --git a/etc/mhl.digest b/etc/mhl.digest
new file mode 100644 (file)
index 0000000..ea4a6d5
--- /dev/null
@@ -0,0 +1,7 @@
+width=80,overflowoffset=10
+leftadjust,compress,compwidth=9
+Date:formatfield="%<(nodate{text})%{text}%|%(tws{text})%>"
+From:
+Subject:
+:
+body:nocomponent,overflowoffset=0,noleftadjust,nocompress
diff --git a/etc/mhl.format b/etc/mhl.format
new file mode 100644 (file)
index 0000000..7f8a567
--- /dev/null
@@ -0,0 +1,17 @@
+; mhl.format
+;
+; default message filter for `show'
+;
+:
+overflowtext="***",overflowoffset=5
+leftadjust,compwidth=9
+ignores=msgid,message-id,received,content-type,content-transfer-encoding,content-id
+Date:formatfield="%<(nodate{text})%{text}%|%(pretty{text})%>"
+To:
+cc:
+From:decode
+Subject:decode
+:
+extras:nocomponent
+:
+body:nocomponent,overflowtext=,overflowoffset=0,noleftadjust
diff --git a/etc/mhl.forward b/etc/mhl.forward
new file mode 100644 (file)
index 0000000..82b4ebc
--- /dev/null
@@ -0,0 +1,13 @@
+; mhl.forward
+;
+; default message filter for `forw' (forw -format)
+;
+width=80,overflowtext=,overflowoffset=10
+leftadjust,compress,compwidth=9
+Date:formatfield="%<(nodate{text})%{text}%|%(tws{text})%>"
+From:
+To:
+cc:
+Subject:
+:
+body:nocomponent,overflowoffset=0,noleftadjust,nocompress
diff --git a/etc/mhl.headers b/etc/mhl.headers
new file mode 100644 (file)
index 0000000..8fee181
--- /dev/null
@@ -0,0 +1,17 @@
+; mhl.headers
+;
+; Default format file for displaying headers in
+; MIME messages.  mhn calls the mhlproc with this
+; filter to display message header.
+;
+overflowtext="***",overflowoffset=5
+leftadjust,compwidth=9
+ignores=msgid,message-id,received,content-type,content-transfer-encoding,content-id
+Date:formatfield="%<(nodate{text})%{text}%|%(pretty{text})%>"
+To:
+cc:
+From:decode
+Subject:decode
+:
+extras:nocomponent
+:
diff --git a/etc/mhl.reply b/etc/mhl.reply
new file mode 100644 (file)
index 0000000..0caf5db
--- /dev/null
@@ -0,0 +1,5 @@
+; mhl.reply
+;
+; default message filter for `repl' (repl -format)
+;
+body:component="> ",overflowtext="> ",overflowoffset=0
diff --git a/etc/mhn.defaults.sh b/etc/mhn.defaults.sh
new file mode 100755 (executable)
index 0000000..358baf1
--- /dev/null
@@ -0,0 +1,166 @@
+#! /bin/sh
+#
+# mhn.defaults.sh -- create extra profile file for MIME handling
+#
+# $Id$
+#
+# USAGE: mhn.defaults.sh [ search-path [ search-prog ]]
+
+# If a search path is passed to the script, we
+# use that, else we use a default search path.
+if [ -n "$1" ]; then
+    SEARCHPATH=$1
+else
+    SEARCHPATH="$PATH:/usr/demo/SOUND"
+fi
+
+# If a search program is passed to the script, we
+# use that, else we use a default search program.
+if [ -n "$2" ]; then
+    SEARCHPROG=$2
+else
+    SEARCHPROG="mhn.find.sh"
+fi
+
+# put output into a temporary file, so we
+# can sort it before output.
+TMP=/tmp/nmh_temp.$$
+trap "rm -f $TMP" 0 1 2 3 13 15
+
+echo "mhstore-store-text: %m%P.txt" >> $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
+
+PGM="`$SEARCHPROG $SEARCHPATH xwud`"
+if [ ! -z "$PGM" ]; then
+    XWUD="$PGM" X11DIR="`echo $PGM | awk -F/ '{ for(i=2;i<NF;i++)printf "/%s", $i;}'`"/
+else
+    XWUD= X11DIR=
+fi
+
+PGM="`$SEARCHPROG $SEARCHPATH pbmtoxwd`"
+if [ ! -z "$PGM" ]; then
+    PBM="$PGM" PBMDIR="`echo $PGM | awk -F/ '{ for(i=2;i<NF;i++)printf "/%s", $i;}'`"/
+else
+    PBM= PBMDIR=
+fi
+
+PGM="`$SEARCHPROG $SEARCHPATH xv`"
+if [ ! -z "$PGM" ]; then
+    echo "mhshow-show-image: %p$PGM -geometry =-0+0 '%f'" >> $TMP
+elif [ ! -z $"PBM" -a ! -z "$XWUD" ]; then
+    echo "mhshow-show-image/gif: %p${PBMDIR}giftoppm | ${PBMDIR}ppmtopgm | ${PBMDIR}pgmtopbm | ${PBMDIR}pbmtoxwd | $XWUD -geometry =-0+0" >> $TMP
+    echo "mhshow-show-image/x-pbm: %p${PBMDIR}pbmtoxwd | $XWUD -geometry =-0+0" >> $TMP
+    echo "mhshow-show-image/x-pgm: %p${PBMDIR}pgmtopbm | ${PBMDIR}pbmtoxwd | $XWUD -geometry =-0+0" >> $TMP
+    echo "mhshow-show-image/x-ppm: %p${PBMDIR}ppmtopgm | ${PBMDIR}pgmtopbm | ${PBMDIR}pbmtoxwd | $XWUD -geometry =-0+0" >> $TMP
+    echo "mhshow-show-image/x-xwd: %p$XWUD -geometry =-0+0" >> $TMP
+
+    PGM="`$SEARCHPROG $SEARCHPATH djpeg`"
+    if [ ! -z "$PGM" ]; then
+       echo "mhshow-show-image/jpeg: %p$PGM -Pg | ${PBMDIR}ppmtopgm | ${PBMDIR}pgmtopbm | ${PBMDIR}pbmtoxwd | $XWUD -geometry =-0+0" >> $TMP
+    fi
+fi
+
+if [ -f "/dev/audioIU" ]; then
+    PGM="`$SEARCHPROG $SEARCHPATH recorder`"
+    if [ ! -z "$PGM" ]; then
+       echo "mhstore-store-audio/basic: %m%P.au" >> $TMP
+        echo "mhbuild-compose-audio/basic: ${AUDIODIR}recorder '%f' -au -pause > /dev/tty" >> $TMP
+        echo "mhshow-show-audio/basic: %p${AUDIODIR}splayer -au" >> $TMP
+    fi
+elif [ -f "/dev/audio" ]; then
+    PGM="`$SEARCHPROG $SEARCHPATH raw2audio`"
+    if [ ! -z "$PGM" ]; then
+       AUDIODIR="`echo $PGM | awk -F/ '{ for(i=2;i<NF;i++)printf "/%s", $i;}'`"/
+       echo "mhstore-store-audio/basic: | ${AUDIODIR}raw2audio -e ulaw -s 8000 -c 1 > %m%P.au" >> $TMP
+        echo "mhstore-store-audio/x-next: %m%P.au" >> $TMP
+       AUDIOTOOL="`$SEARCHPROG $SEARCHPATH audiotool`"
+       if [ ! -z "$AUDIOTOOL" ]; then
+           echo "mhbuild-compose-audio/basic: $AUDIOTOOL %f && ${AUDIODIR}raw2audio -F < %f" >> $TMP
+       else
+           echo "mhbuild-compose-audio/basic: trap \"exit 0\" 2 && ${AUDIODIR}record | ${AUDIODIR}raw2audio -F" >> $TMP
+       fi
+       echo "mhshow-show-audio/basic: %p${AUDIODIR}raw2audio 2>/dev/null | ${AUDIODIR}play" >> $TMP
+
+       PGM="`$SEARCHPROG $SEARCHPATH adpcm_enc`"
+       if [ ! -z "$PGM" ]; then
+           DIR="`echo $PGM | awk -F/ '{ for(i=2;i<NF;i++)printf "/%s", $i;}'`"/
+           if [ ! -z "$AUDIOTOOL" ]; then
+               echo "mhbuild-compose-audio/x-next: $AUDIOTOOL %f && ${DIR}adpcm_enc < %f" >> $TMP
+           else
+               echo "mhbuild-compose-audio/x-next: ${AUDIODIR}record | ${DIR}adpcm_enc" >> $TMP
+           fi
+           echo "mhshow-show-audio/x-next: %p${DIR}adpcm_dec | ${AUDIODIR}play" >> $TMP
+       else
+           if [ ! -z "$AUDIOTOOL" ]; then
+               echo "mhbuild-compose-audio/x-next: $AUDIOTOOL %f" >> $TMP
+           else
+               echo "mhbuild-compose-audio/x-next: ${AUDIODIR}record" >> $TMP
+           fi
+           echo "mhshow-show-audio/x-next: %p${AUDIODIR}play" >> $TMP
+       fi
+    else
+       echo "mhbuild-compose-audio/basic: cat < /dev/audio" >> $TMP
+        echo "mhshow-show-audio/basic: %pcat > /dev/audio" >> $TMP
+    fi
+fi
+
+PGM="`$SEARCHPROG $SEARCHPATH mpeg_play`"
+if [ ! -z "$PGM" ]; then
+       echo "mhshow-show-video/mpeg: %p$PGM '%f'" >> $TMP
+fi
+
+PGM="`$SEARCHPROG $SEARCHPATH lpr`"
+if [ ! -z "$PGM" ]; then
+       echo "mhshow-show-application/PostScript: %plpr -Pps" >> $TMP
+else
+    PGM="`$SEARCHPROG $SEARCHPATH lp`"
+    if [ ! -z "$PGM" ]; then    
+       echo "mhshow-show-application/PostScript: %plp -dps" >> $TMP
+    fi
+fi
+
+PGM="`$SEARCHPROG $SEARCHPATH ivs_replay`"
+if [ ! -z "$PGM" ]; then
+       echo "mhshow-show-application/x-ivs: %p$PGM -o '%F'" >> $TMP
+fi
+
+PGM="`$SEARCHPROG $SEARCHPATH richtext`"
+if [ ! -z "$PGM" ]; then
+       echo "mhshow-show-text/richtext: %p$PGM -p '%F'" >> $TMP
+else
+    PGM="`$SEARCHPROG $SEARCHPATH rt2raw`"
+    if [ ! -z "$PGM" ]; then
+       echo "mhshow-show-text/richtext: %p$PGM < '%f' | fmt -78 | more" >> $TMP
+    fi
+fi
+
+PGM="`$SEARCHPROG $SEARCHPATH xterm`"
+if [ ! -z "$PGM" ]; then
+       echo "mhshow-charset-iso-8859-1: xterm -fn '-*-*-medium-r-normal-*-*-120-*-*-c-*-iso8859-*' -e %s" >> $TMP
+fi
+
+# output a sorted version of the file
+sort < $TMP
+
+exit 0
+
+: not until we get a "safe" postscript environment...
+
+PGM="`$SEARCHPROG $SEARCHPATH pageview`"
+if [ "$DISPLAY" = "unix:0.0" -a ! -z "$PGM" ]; then
+    echo "mhshow-show-application/PostScript: %p$PGM -" >> $TMP
+else
+    PGM="`$SEARCHPROG $SEARCHPATH gs`"
+    if [ ! -z "$PGM" ]; then
+       echo "mhshow-show-application/PostScript: %p$PGM -- '%F'" >> $TMP
+    fi
+fi
+
+: have to experiment more with this
+
+PGM="`$SEARCHPROG $SEARCHPATH ivs_record`"
+if [ ! -z "$PGM" ]; then
+       echo "mhbuild-compose-application/x-ivs: $PGM -u localhost '%F'" >> $TMP
+fi
diff --git a/etc/mhn.find.sh b/etc/mhn.find.sh
new file mode 100755 (executable)
index 0000000..8f128f7
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/sh
+#
+# mhn.find.sh -- check if a particular command is available
+#
+# $Id$
+
+if test -z "$2"; then
+    echo "usage: mhn.find.sh search-path program" 1>&2
+    exit 1
+fi
+
+# PATH to search for programs
+SEARCHPATH=$1
+
+# program to search for
+PROGRAM=$2
+
+PGM= oIFS="$IFS" IFS=":"
+for A in $SEARCHPATH; do
+
+    # skip the directories `.' and `..'
+    if test "$A" = "." -o "$A" = ".."; then
+       continue
+    fi
+
+    # if program was found in /usr/local/bin, then
+    # just echo program name, else echo full pathname
+    if test -f "$A/$PROGRAM"; then
+       if test "$A" = "/usr/local/bin"; then
+           PGM="$PROGRAM"
+       else
+           PGM="$A/$PROGRAM"
+       fi
+
+       echo "$PGM"
+       exit 0
+    fi
+done
+IFS="$oIFS"
+
diff --git a/etc/mts.conf.in b/etc/mts.conf.in
new file mode 100644 (file)
index 0000000..c892f0f
--- /dev/null
@@ -0,0 +1,31 @@
+#
+# nmh mail transport interface customization file.
+# 
+# Check the mh-tailor(5) man page for a list of
+# all the available options for this file.
+#
+
+# Default location of mail drops.  If this option is
+# set, but empty, the user's home directory is used.
+mmdfldir: %mailspool%
+
+# The name of the maildrop file in the directory where maildrops
+# are kept.  If this is empty, the user's login name is used.
+mmdflfil:
+
+# The exceptions file for /etc/hosts used by
+# `post' to try to find official names.
+hostable: %etcdir%/hosts
+
+# List of smtp servers to try if using smtp support
+servers: localhost
+
+# Name of POP server
+# pophost: localhost
+
+# Name that nmh considers `local'.  If not set, nmh will
+# query the system for this value (gethostname, etc...).
+# localname: foo.bar.com
+
+# Uncomment this to turn on username masquerading.
+# mmailid: 1
diff --git a/etc/rcvdistcomps b/etc/rcvdistcomps
new file mode 100644 (file)
index 0000000..13fe808
--- /dev/null
@@ -0,0 +1,3 @@
+%(lit)%(formataddr{addresses})\
+%<(nonnull)%(void(width))%(putaddr Resent-To: )\n%>\
+Resent-Fcc: outbox
diff --git a/etc/replcomps b/etc/replcomps
new file mode 100644 (file)
index 0000000..52170a7
--- /dev/null
@@ -0,0 +1,21 @@
+%; replcomps
+%;
+%; default form (components) file for `repl'
+%;
+%; Check for the following headers (in this order)
+%; to construct the return address
+%;
+%; Mail-Reply-To
+%; Reply-To
+%; From
+%; Sender
+%; Return-Path
+%;
+%(lit)%(formataddr %<{mail-reply-to}%?{reply-to}%?{from}%?{sender}%?{return-path}%>)\
+%<(nonnull)%(void(width))%(putaddr To: )\n%>\
+%<{fcc}Fcc: %{fcc}\n%>\
+%<{subject}Subject: Re: %{subject}\n%>\
+%<{date}In-Reply-To: Your message of "\
+%<(nodate{date})%{date}%|%(pretty{date})%>."%<{message-id}
+             %{message-id}%>\n%>\
+--------
diff --git a/etc/replgroupcomps b/etc/replgroupcomps
new file mode 100644 (file)
index 0000000..9295fd0
--- /dev/null
@@ -0,0 +1,36 @@
+%; replgroupcomps
+%;
+%; form (components) file for `repl -group'
+%;
+%; Check the following headers to create reply addresses.
+%;
+%; To: Mail-Followup-To
+%;
+%; OR
+%;
+%; To: Mail-Reply-To   (or)
+%;     Reply-To        (or)
+%;     From            (or)
+%;     Sender          (or)
+%;     Return-Path
+%;
+%; AND
+%;
+%; cc: To              (and)
+%;     cc              (and)
+%;     personal address
+%;
+%(lit)%(formataddr{mail-followup-to})\
+%<(nonnull)%(void(width))%(putaddr To: )\n\
+%|\
+%(lit)%(formataddr %<{mail-reply-to}%?{reply-to}%?{from}%?{sender}%?{return-path}%>)\
+%<(nonnull)%(void(width))%(putaddr To: )\n%>\
+%(lit)%(formataddr{to})%(formataddr{cc})%(formataddr(me))\
+%<(nonnull)%(void(width))%(putaddr cc: )\n%>%>\
+%;
+%<{fcc}Fcc: %{fcc}\n%>\
+%<{subject}Subject: Re: %{subject}\n%>\
+In-Reply-To: Message from %<{from}%{from}%?{sender}%{sender}%|%{return-path}%>\n\
+   of "%<(nodate{date})%{date}%|%(pretty{date})%>."\
+%<{message-id} %{message-id}%>\n\
+--------
diff --git a/etc/scan.default b/etc/scan.default
new file mode 100644 (file)
index 0000000..f898e86
--- /dev/null
@@ -0,0 +1,11 @@
+%; scan.default
+%;
+%; This file is supplied for reference only; it shows the default
+%; format string (for non-UK sites) which was compiled into the
+%; command "scan".  See the source file "h/scansbr.h" for details.
+%;
+%4(msg)%<(cur)+%| %>%<{replied}-%?{encrypted}E%| %>\
+%02(mon{date})/%02(mday{date})%<{date} %|*%>\
+%<(mymbox{from})%<{to}To:%14(decode(friendly{to}))%>%>\
+%<(zero)%17(decode(friendly{from}))%>  \
+%(decode{subject})%<{body}<<%{body}>>%>
diff --git a/etc/scan.mailx b/etc/scan.mailx
new file mode 100644 (file)
index 0000000..27d18d3
--- /dev/null
@@ -0,0 +1,9 @@
+%<(cur)>%| %>\
+%<{status} %|N%>\
+%<{replied}R%?{encrypted}E%| %>\
+%4(msg) \
+%<(mymbox{from})%<{to}To: %13(decode(friendly{to}))%>%>\
+%<(zero)%17(decode(friendly{from}))%> \
+%3(day{date}) %3(month{date}) %02(mday{date}) \
+%02(hour{date}):%02(min{date}) \
+%(decode{subject})
diff --git a/etc/scan.nomime b/etc/scan.nomime
new file mode 100644 (file)
index 0000000..8e2cedf
--- /dev/null
@@ -0,0 +1,10 @@
+%; scan.nomime
+%;
+%; This file is a modification of the standard (non-UK version)
+%; format for scan, that doesn't do any RFC-2047 decoding of
+%; header components.
+%;
+%4(msg)%<(cur)+%| %>%<{replied}-%?{encrypted}E%| %>\
+%02(mon{date})/%02(mday{date})%<{date} %|*%>\
+%<(mymbox{from})%<{to}To:%14(friendly{to})%>%>%<(zero)%17(friendly{from})%>  \
+%{subject}%<{body}<<%{body}>>%>
diff --git a/etc/scan.size b/etc/scan.size
new file mode 100644 (file)
index 0000000..a6b6698
--- /dev/null
@@ -0,0 +1,6 @@
+%4(msg)%<(cur)+%| %>%<{replied}-%?{encrypted}E%| %>\
+%02(mon{date})/%02(mday{date})%<{date} %|*%>\
+%5(size) \
+%<(mymbox{from})%<{to}To:%14(decode(friendly{to}))%>%>\
+%<(zero)%17(decode(friendly{from}))%>  \
+%(decode{subject})%<{body}<<%{body}%>
diff --git a/etc/scan.time b/etc/scan.time
new file mode 100644 (file)
index 0000000..ee54a52
--- /dev/null
@@ -0,0 +1,7 @@
+%4(msg)%<(cur)+%| %>%<{replied}-%?{encrypted}E%| %>\
+%02(mon{date})/%02(mday{date}) \
+%02(hour{date}):%02(min{date})%3(tzone{date})\
+%<{date} %|*%>\
+%<(mymbox{from})%<{to}To:%14(decode(friendly{to}))%>%>\
+%<(zero)%17(decode(friendly{from}))%>  \
+%(decode{subject})%<{body}<<%{body}%>
diff --git a/etc/scan.timely b/etc/scan.timely
new file mode 100644 (file)
index 0000000..06c068c
--- /dev/null
@@ -0,0 +1,10 @@
+%4(msg)%<(cur)+%| %>%<{replied}-%?{encrypted}E%| %>\
+%(void(rclock{date}))\
+%<(gt 15768000)%03(month{date})%(void(year{date}))%02(modulo 100)\
+%?(gt 604800)%02(mday{date})%03(month{date})\
+%?(gt 86400) %(day{date}) %|\
+%02(hour{date}):%02(min{date})%>\
+%<{date} %|*%>\
+%<(mymbox{from})%<{to}To:%14(decode(friendly{to}))%>%>\
+%<(zero)%17(decode(friendly{from}))%>  \
+%(decode{subject})%<{body}<<%{body}%>
diff --git a/etc/scan.unseen b/etc/scan.unseen
new file mode 100644 (file)
index 0000000..d1cd195
--- /dev/null
@@ -0,0 +1,5 @@
+%4(msg)%<(cur)+%| %>%<(unseen)U%| %>%<{replied}-%?{encrypted}E%| %>\
+%02(mon{date})/%02(mday{date})%<{date} %|*%>\
+%<(mymbox{from})%<{to}To:%14(decode(friendly{to}))%>%>\
+%<(zero)%17(decode(friendly{from}))%>  \
+%(decode{subject})%<{body}<<%{body}>>%>
diff --git a/etc/sendfiles b/etc/sendfiles
new file mode 100755 (executable)
index 0000000..d53ed82
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/sh
+#
+# $Id$
+#
+# Send multiples files and/or directories as a tar/compressed
+# image, in a MIME message.
+#
+
+DELAY=0
+FROM=
+
+case "$1" in
+    -*)        DELAY="`echo $1 | sed -e 's%-%%'`"
+       shift
+       ;;
+esac
+
+if [ ! -z "$PERSON" ]; then
+    FROM="-from $PERSON"
+fi
+
+if [ $# -lt 3 ]; then
+    echo 'usage: sendfiles: "mailpath" "subject-string" directory-or-file ...' 1>&2
+    exit 1;
+fi
+
+mailpath="$1"
+echo "mailpath = $mailpath" 1>&2
+shift
+
+subject="$1"
+echo "subject-string = $subject" 1>&2
+shift
+
+echo "files = $*" 1>&2
+
+tar cvf - "$@" | compress | \
+    viamail -to "$mailpath" -subject "$subject" \
+       -parameters "type=tar; x-conversions=compress" \
+       -comment "extract with uncompress | tar xvpf -" \
+       -delay "$DELAY" \
+       -verbose $FROM
diff --git a/h/Makefile.in b/h/Makefile.in
new file mode 100644 (file)
index 0000000..4f99259
--- /dev/null
@@ -0,0 +1,59 @@
+#
+# Makefile for h subdirectory
+#
+# $Id$
+#
+
+SHELL = /bin/sh
+
+srcdir = @srcdir@
+VPATH  = @srcdir@
+
+# header files included in distribution
+HDRS = addrsbr.h aliasbr.h dropsbr.h fmt_compile.h fmt_scan.h \
+       md5.h mh.h mhcachesbr.h mhparse.h mime.h msh.h netdb.h nmh.h \
+       nntp.h picksbr.h popsbr.h prototypes.h rcvmail.h scansbr.h \
+       signals.h vmhsbr.h
+
+# auxiliary files
+AUX = Makefile.in
+
+# all files in this directory included in the distribution
+DIST = $(HDRS) $(AUX)
+
+# ========== DEPENDENCIES FOR BUILDING AND INSTALLING ==========
+
+all:
+
+install:
+
+uninstall:
+
+# ========== DEPENDENCIES FOR CLEANUP ==========
+
+mostlyclean:
+       rm -f *~
+
+clean: mostlyclean
+
+distclean: clean
+       rm -f Makefile
+
+realclean: distclean
+
+superclean: realclean
+
+# ========== DEPENDENCIES FOR MAINTENANCE ==========
+
+subdir = h
+
+Makefile: Makefile.in ../config.status
+       cd .. && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+distdir = ../`cat ../distname`/$(subdir)
+nmhdist: $(DIST)
+       @echo "Copying distribution files in $(subdir)"
+       @for file in $(DIST); do \
+         cp -p $(srcdir)/$$file $(distdir); \
+       done
+
diff --git a/h/addrsbr.h b/h/addrsbr.h
new file mode 100644 (file)
index 0000000..e3aa7e8
--- /dev/null
@@ -0,0 +1,42 @@
+
+/*
+ * addrsbr.h -- definitions for the address parsing system
+ *
+ * $Id$
+ */
+
+#define        AD_HOST 1               /* getm(): lookup official hostname    */
+#define        AD_NHST 0               /* getm(): do not lookup official name */
+#define        AD_NAME AD_NHST         /* AD_HOST is TOO slow                 */
+
+#define        UUCPHOST        (-1)
+#define        LOCALHOST       0
+#define        NETHOST         1
+#define        BADHOST         2
+
+struct mailname {
+    struct mailname *m_next;
+    char *m_text;
+    char *m_pers;
+    char *m_mbox;
+    char *m_host;
+    char *m_path;
+    int m_type;
+    char m_nohost;
+    char m_bcc;
+    int m_ingrp;
+    char *m_gname;
+    char *m_note;
+};
+
+#define        adrformat(m) auxformat ((m), 1)
+
+/*
+ *  prototypes
+ */
+void mnfree(struct mailname *);
+int ismymbox(struct mailname *);
+char *getname(char *);
+char *adrsprintf(char *, char *);
+char *auxformat(struct mailname *, int);
+struct mailname *getm(char *, char *, int, int, char *);
diff --git a/h/aliasbr.h b/h/aliasbr.h
new file mode 100644 (file)
index 0000000..2e464d9
--- /dev/null
@@ -0,0 +1,64 @@
+
+/*
+ * aliasbr.h -- definitions for the aliasing system
+ *
+ * $Id$
+ */
+
+extern char *AliasFile;                /* mh-alias(5)             */
+#define        PASSWD  "/etc/passwd"   /* passwd(5)               */
+#define GROUP   "/etc/group"   /* group(5)                */
+#define EVERYONE 200           /* lowest uid for everyone */
+
+struct aka {
+    char *ak_name;             /* name to match against             */
+    struct adr *ak_addr;       /* list of addresses that it maps to */
+    struct aka *ak_next;       /* next aka in list                  */
+    char ak_visible;           /* should be visible in headers      */
+};
+
+struct adr {
+    char *ad_text;             /* text of this address in list        */
+    struct adr *ad_next;       /* next adr in list                    */
+    char ad_local;             /* text is local (check for expansion) */
+};
+
+/*
+ * incore version of /etc/passwd
+ */
+struct home {
+    char *h_name;              /* user name                             */
+    uid_t h_uid;               /* user id                               */
+    gid_t h_gid;               /* user's group                          */
+    char *h_home;              /* user's home directory                 */
+    char *h_shell;             /* user's shell                          */
+    int        h_ngrps;                /* number of groups this user belongs to */
+    struct home *h_next;       /* next home in list                     */
+};
+
+#ifndef        MMDFMTS
+struct home *seek_home ();
+#endif /* MMDFMTS */
+
+/*
+ * prototypes
+ */
+int alias (char *);
+int akvisible (void);
+void init_pw (void);
+char *akresult (struct aka *);
+char *akvalue (char *);
+char *akerror (int);
+
+/* codes returned by alias() */
+
+#define        AK_OK           0       /* file parsed ok        */
+#define        AK_NOFILE       1       /* couldn't read file    */
+#define        AK_ERROR        2       /* error parsing file    */
+#define        AK_LIMIT        3       /* memory limit exceeded */
+#define        AK_NOGROUP      4       /* no such group         */
+
+/* should live here, not in mts.c */
+
+extern int Everyone;
+extern char *NoShell;
diff --git a/h/dropsbr.h b/h/dropsbr.h
new file mode 100644 (file)
index 0000000..146a63b
--- /dev/null
@@ -0,0 +1,59 @@
+
+/*
+ * dropsbr.h -- definitions for maildrop-style files
+ *
+ * $Id$
+ */
+
+/*
+ * A file which is formatted like a maildrop may have a corresponding map
+ * file which is an index to the bounds of each message.  The first record
+ * of such an map is special, it contains:
+ *
+ *      d_id    = number of messages in file
+ *      d_size = version number of map
+ *      d_start = last message read
+ *      d_stop  = size of file
+ *
+ *  Each record after that contains:
+ *
+ *      d_id   = BBoard-ID: of message, or similar info
+ *      d_size = size of message in ARPA Internet octets (\n == 2 octets)
+ *      d_start        = starting position of message in file
+ *      d_stop = stopping position of message in file
+ *
+ * Note that d_start/d_stop do NOT include the message delimiters, so
+ * programs using the map can simply fseek to d_start and keep reading
+ * until the position is at d_stop.
+ */
+
+/*
+ * various formats for maildrop files
+ */
+#define OTHER_FORMAT 0
+#define MBOX_FORMAT  1
+#define MMDF_FORMAT  2
+
+#define        DRVRSN 3
+
+struct drop {
+    int   d_id;
+    int          d_size;
+    off_t d_start;
+    off_t d_stop;
+};
+
+/*
+ * prototypes
+ */
+int mbx_open (char *, int, uid_t, gid_t, mode_t);
+int mbx_read (FILE *, long, struct drop **, int);
+int mbx_write(char *, int, FILE *, int, long, long, off_t, int, int);
+int mbx_copy (char *, int, int, int, int, char *, int);
+int mbx_size (int, off_t, off_t);
+int mbx_close (char *, int);
+char *map_name (char *);
+int map_read (char *, long, struct drop **, int);
+int map_write (char *, int, int, long, off_t, off_t, long, int, int);
+int map_chk (char *, int, struct drop *, long, int);
+
diff --git a/h/fmt_compile.h b/h/fmt_compile.h
new file mode 100644 (file)
index 0000000..f911504
--- /dev/null
@@ -0,0 +1,109 @@
+
+/*
+ * fmt_compile.h -- format types
+ *
+ * $Id$
+ */
+
+/* types that output text */
+#define FT_COMP                1       /* the text of a component                 */
+#define FT_COMPF       2       /* comp text, filled                       */
+#define FT_LIT         3       /* literal text                            */
+#define FT_LITF                4       /* literal text, filled                    */
+#define FT_CHAR                5       /* a single ascii character                */
+#define FT_NUM         6       /* "value" as decimal number               */
+#define FT_NUMF                7       /* "value" as filled dec number            */
+#define FT_STR         8       /* "str" as text                           */
+#define FT_STRF                9       /* "str" as text, filled                   */
+#define FT_STRFW       10      /* "str" as text, filled, width in "value" */
+#define FT_PUTADDR     11      /* split and print address line            */
+
+/* types that modify the "str" or "value" registers                     */
+#define FT_LS_COMP     12      /* set "str" to component text          */
+#define FT_LS_LIT      13      /* set "str" to literal text            */
+#define FT_LS_GETENV   14      /* set "str" to getenv(text)            */
+#define FT_LS_CFIND    15      /* set "str" to context_find(text)      */
+#define FT_LS_DECODECOMP 16    /* set "str" to decoded component text  */
+#define FT_LS_DECODE   17      /* decode "str" as RFC-2047 header      */
+#define FT_LS_TRIM     18      /* trim trailing white space from "str" */
+#define FT_LV_COMP     19      /* set "value" to comp (as dec. num)    */
+#define FT_LV_COMPFLAG 20      /* set "value" to comp flag word        */
+#define FT_LV_LIT      21      /* set "value" to literal num           */
+#define FT_LV_DAT      22      /* set "value" to dat[n]                */
+#define FT_LV_STRLEN   23      /* set "value" to length of "str"       */
+#define FT_LV_PLUS_L   24      /* set "value" += literal               */
+#define FT_LV_MINUS_L  25      /* set "value" -= literal               */
+#define FT_LV_DIVIDE_L 26      /* set "value" to value / literal       */
+#define FT_LV_MODULO_L 27      /* set "value" to value % literal       */
+#define FT_LV_CHAR_LEFT 28     /* set "value" to char left in output   */
+
+#define FT_LS_MONTH    29      /* set "str" to tws month                   */
+#define FT_LS_LMONTH   30      /* set "str" to long tws month              */
+#define FT_LS_ZONE     31      /* set "str" to tws timezone                */
+#define FT_LS_DAY      32      /* set "str" to tws weekday                 */
+#define FT_LS_WEEKDAY  33      /* set "str" to long tws weekday            */
+#define FT_LS_822DATE  34      /* set "str" to 822 date str                */
+#define FT_LS_PRETTY   35      /* set "str" to pretty (?) date str         */
+#define FT_LV_SEC      36      /* set "value" to tws second                */
+#define FT_LV_MIN      37      /* set "value" to tws minute                */
+#define FT_LV_HOUR     38      /* set "value" to tws hour                  */
+#define FT_LV_MDAY     39      /* set "value" to tws day of month          */
+#define FT_LV_MON      40      /* set "value" to tws month                 */
+#define FT_LV_YEAR     41      /* set "value" to tws year                  */
+#define FT_LV_YDAY     42      /* set "value" to tws day of year           */
+#define FT_LV_WDAY     43      /* set "value" to tws weekday               */
+#define FT_LV_ZONE     44      /* set "value" to tws timezone              */
+#define FT_LV_CLOCK    45      /* set "value" to tws clock                 */
+#define FT_LV_RCLOCK   46      /* set "value" to now - tws clock           */
+#define FT_LV_DAYF     47      /* set "value" to tws day flag              */
+#define FT_LV_DST      48      /* set "value" to tws daylight savings flag */
+#define FT_LV_ZONEF    49      /* set "value" to tws timezone flag         */
+
+#define FT_LS_PERS     50      /* set "str" to person part of addr    */
+#define FT_LS_MBOX     51      /* set "str" to mbox part of addr      */
+#define FT_LS_HOST     52      /* set "str" to host part of addr      */
+#define FT_LS_PATH     53      /* set "str" to route part of addr     */
+#define FT_LS_GNAME    54      /* set "str" to group part of addr     */
+#define FT_LS_NOTE     55      /* set "str" to comment part of addr   */
+#define FT_LS_ADDR     56      /* set "str" to mbox@host              */
+#define FT_LS_822ADDR  57      /* set "str" to 822 format addr        */
+#define FT_LS_FRIENDLY 58      /* set "str" to "friendly" format addr */
+#define FT_LV_HOSTTYPE 59      /* set "value" to addr host type       */
+#define FT_LV_INGRPF   60      /* set "value" to addr in-group flag   */
+#define FT_LV_NOHOSTF  61      /* set "value" to addr no-host flag    */
+
+/* Date Coercion */
+#define FT_LOCALDATE   62      /* Coerce date to local timezone */
+#define FT_GMTDATE     63      /* Coerce date to gmt            */
+
+/* pre-format processing */
+#define FT_PARSEDATE   64      /* parse comp into a date (tws) struct */
+#define FT_PARSEADDR   65      /* parse comp into a mailaddr struct   */
+#define FT_FORMATADDR  66      /* let external routine format addr    */
+#define FT_MYMBOX      67      /* do "mymbox" test on comp            */
+
+/* misc. */            /* ADDTOSEQ only works if you include "options LBL" */
+#define FT_ADDTOSEQ    68      /* add current msg to a sequence       */
+
+/* conditionals & control flow (must be last) */
+#define FT_SAVESTR     69      /* save current str reg               */
+#define FT_DONE                70      /* stop formatting                    */
+#define FT_PAUSE       71      /* pause                              */
+#define FT_NOP         72      /* nop                                */
+#define FT_GOTO                73      /* (relative) goto                    */
+#define FT_IF_S_NULL   74      /* test if "str" null                 */
+#define FT_IF_S                75      /* test if "str" non-null             */
+#define FT_IF_V_EQ     76      /* test if "value" = literal          */
+#define FT_IF_V_NE     77      /* test if "value" != literal         */
+#define FT_IF_V_GT     78      /* test if "value" > literal          */
+#define FT_IF_MATCH    79      /* test if "str" contains literal     */
+#define FT_IF_AMATCH   80      /* test if "str" starts with literal  */
+#define FT_S_NULL      81      /* V = 1 if "str" null                */
+#define FT_S_NONNULL   82      /* V = 1 if "str" non-null            */
+#define FT_V_EQ                83      /* V = 1 if "value" = literal         */
+#define FT_V_NE                84      /* V = 1 if "value" != literal        */
+#define FT_V_GT                85      /* V = 1 if "value" > literal         */
+#define FT_V_MATCH     86      /* V = 1 if "str" contains literal    */
+#define FT_V_AMATCH    87      /* V = 1 if "str" starts with literal */
+
+#define IF_FUNCS FT_S_NULL     /* start of "if" functions */
diff --git a/h/fmt_scan.h b/h/fmt_scan.h
new file mode 100644 (file)
index 0000000..fd4c555
--- /dev/null
@@ -0,0 +1,90 @@
+
+/*
+ * fmt_scan.h -- definitions for fmt_scan()
+ *
+ * $Id$
+ */
+
+/*
+ * This structure describes an "interesting" component.  It holds
+ * the name & text from the component (if found) and one piece of
+ * auxilary info.  The structure for a particular component is located
+ * by (open) hashing the name and using it as an index into the ptr array
+ * "wantcomp".  All format entries that reference a particular component
+ * point to its comp struct (so we only have to do component specific
+ * processing once.  e.g., parse an address.).
+ */
+struct comp {
+    char        *c_name;       /* component name (in lower case) */
+    char        *c_text;       /* component text (if found)      */
+    struct comp *c_next;       /* hash chain linkage             */
+    short        c_flags;      /* misc. flags (from fmt_scan)    */
+    short        c_type;       /* type info   (from fmt_compile) */
+    union {
+       struct tws *c_u_tws;
+       struct mailname *c_u_mn;
+    } c_un;
+};
+
+#define c_tws c_un.c_u_tws
+#define c_mn  c_un.c_u_mn
+
+/*
+ * c_type bits
+ */
+#define        CT_ADDR       (1<<0)    /* referenced as address    */
+#define        CT_DATE       (1<<1)    /* referenced as date       */
+#define        CT_MYMBOX     (1<<2)    /* "mymbox" test being done */
+#define        CT_ADDRPARSE  (1<<3)    /* address parse being done */
+
+extern int fmt_norm;
+
+/*
+ * Hash table for deciding if a component is "interesting".
+ */
+extern struct comp *wantcomp[128];
+
+/* 
+ * Hash function for component name.  The function should be
+ * case independent and probably shouldn't involve a routine
+ * call.  This function is pretty good but will not work on
+ * single character component names.  
+ */
+#define        CHASH(nm) (((((nm)[0]) - ((nm)[1])) & 0x1f) + (((nm)[2]) & 0x5f))
+
+/*
+ * Find a component in the hash table.
+ */
+#define FINDCOMP(comp,name) \
+               for (comp = wantcomp[CHASH(name)]; \
+                    comp && strcmp(comp->c_name,name); \
+                    comp = comp->c_next) ;
+
+/*
+ * This structure defines one formatting instruction.
+ */
+struct format {
+    unsigned char f_type;
+    char          f_fill;
+    short         f_width;     /* output field width   */
+    union {
+       struct comp *f_u_comp;  /* associated component */
+       char        *f_u_text;  /* literal text         */
+       char         f_u_char;  /* literal character    */
+       int          f_u_value; /* literal value        */
+    } f_un;
+};
+
+#define f_skip f_width         /* instr to skip (false "if") */
+
+#define f_comp  f_un.f_u_comp
+#define f_text  f_un.f_u_text
+#define f_char  f_un.f_u_char
+#define f_value f_un.f_u_value
+
+/*
+ * prototypes
+ */
+struct format *fmt_scan (struct format *, char *, int, int *);
+char *new_fs (char *, char *, char *);
+int fmt_compile (char *, struct format **);
diff --git a/h/md5.h b/h/md5.h
new file mode 100644 (file)
index 0000000..9a394b5
--- /dev/null
+++ b/h/md5.h
@@ -0,0 +1,81 @@
+/*
+ * md5.h -- header file for md5 message digest
+ *          taken from RFC-1321/Appendices A.1/A.2
+ *
+ * $Id$
+ */
+
+/*
+ * RSAREF types and constants
+ */
+
+/*
+ * Use prototypes for nmh/mh
+ */
+#define PROTOTYPES 1
+
+/*
+ * PROTOTYPES should be set to one if and only if the compiler
+ * supports function argument prototyping.  The following makes
+ * PROTOTYPES default to 0 if it has not already been defined
+ * with C compiler flags.
+ */
+#ifndef PROTOTYPES
+#define PROTOTYPES 0
+#endif
+
+/* POINTER defines a generic pointer type */
+typedef unsigned char *POINTER;
+
+/* UINT2 defines a two byte word */
+typedef unsigned short int UINT2;
+
+/* UINT4 defines a four byte word */
+typedef unsigned long int UINT4;
+
+/* PROTO_LIST is defined depending on how PROTOTYPES is defined above.
+If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it
+  returns an empty list.
+ */
+#if PROTOTYPES
+#define PROTO_LIST(list) list
+#else
+#define PROTO_LIST(list) ()
+#endif
+
+/* MD5.H - header file for MD5C.C
+ */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+ */
+
+/* MD5 context. */
+typedef struct {
+  UINT4 state[4];                                   /* state (ABCD) */
+  UINT4 count[2];        /* number of bits, modulo 2^64 (lsb first) */
+  unsigned char buffer[64];                         /* input buffer */
+} MD5_CTX;
+
+void MD5Init PROTO_LIST ((MD5_CTX *));
+void MD5Update PROTO_LIST ((MD5_CTX *, unsigned char *, unsigned int));
+void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *));
+
diff --git a/h/mh.h b/h/mh.h
new file mode 100644 (file)
index 0000000..c8ece5d
--- /dev/null
+++ b/h/mh.h
@@ -0,0 +1,324 @@
+
+/*
+ * mh.h -- main header file for all of nmh
+ *
+ * $Id$
+ */
+
+#include <h/nmh.h>
+
+/*
+ * Well-used constants
+ */
+#define        NOTOK        (-1)       /* syscall()s return this on error */
+#define        OK             0        /*  ditto on success               */
+#define        DONE           1        /* trinary logic                   */
+#define ALL           ""
+#define        Nbby           8        /* number of bits/byte */
+
+#define MAXARGS            1000        /* max arguments to exec                */
+#define NFOLDERS    1000       /* max folder arguments on command line */
+#define DMAXFOLDER     4       /* typical number of digits             */
+#define MAXFOLDER   1000       /* message increment                    */
+
+/*
+ * user context/profile structure
+ */
+struct node {
+    char *n_name;              /* key                  */
+    char *n_field;             /* value                */
+    char  n_context;           /* context, not profile */
+    struct node *n_next;       /* next entry           */
+};
+
+/*
+ * switches structure
+ */
+#define        AMBIGSW  (-2)   /* from smatch() on ambiguous switch */
+#define        UNKWNSW  (-1)   /* from smatch() on unknown switch   */
+
+struct swit {
+    char *sw;
+    int minchars;
+};
+
+extern struct swit anoyes[];   /* standard yes/no switches */
+
+/*
+ * general folder attributes
+ */
+#define READONLY   (1<<0)      /* No write access to folder    */
+#define        SEQMOD     (1<<1)       /* folder's sequences modifed   */
+#define        ALLOW_NEW  (1<<2)       /* allow the "new" sequence     */
+#define        OTHERS     (1<<3)       /* folder has other files       */
+#define        MODIFIED   (1<<4)       /* msh in-core folder modified  */
+
+#define        FBITS "\020\01READONLY\02SEQMOD\03ALLOW_NEW\04OTHERS\05MODIFIED"
+
+/*
+ * type for holding the sequence set of a message
+ */
+typedef unsigned int seqset_t;
+
+/*
+ * Determine the number of user defined sequences we
+ * can have.  The first 5 sequence flags are for
+ * internal nmh message flags.
+ */
+#define        NUMATTRS  ((sizeof(seqset_t) * Nbby) - 5)
+
+/*
+ * first free slot for user defined sequences
+ * and attributes
+ */
+#define        FFATTRSLOT  5
+
+/*
+ * internal messages attributes (sequences)
+ */
+#define EXISTS        (1<<0)   /* exists            */
+#define DELETED       (1<<1)   /* deleted           */
+#define SELECTED      (1<<2)   /* selected for use  */
+#define SELECT_EMPTY  (1<<3)   /* "new" message     */
+#define        SELECT_UNSEEN (1<<4)    /* inc/show "unseen" */
+
+#define        MBITS "\020\01EXISTS\02DELETED\03SELECTED\04NEW\05UNSEEN"
+
+/*
+ * Primary structure of folder/message information
+ */
+struct msgs {
+    int lowmsg;                /* Lowest msg number                 */
+    int hghmsg;                /* Highest msg number                */
+    int nummsg;                /* Actual Number of msgs             */
+
+    int lowsel;                /* Lowest selected msg number        */
+    int hghsel;                /* Highest selected msg number       */
+    int numsel;                /* Number of msgs selected           */
+
+    int curmsg;                /* Number of current msg if any      */
+
+    int msgflags;      /* Folder attributes (READONLY, etc) */
+    char *foldpath;    /* Pathname of folder                */
+
+    /*
+     * Name of sequences in this folder.  We add an
+     * extra slot, so we can NULL terminate the list.
+     */
+    char *msgattrs[NUMATTRS + 1];
+
+    /*
+     * bit flags for whether sequence
+     * is public (0), or private (1)
+     */
+    seqset_t attrstats;
+
+    /*
+     * These represent the lowest and highest possible
+     * message numbers we can put in the message status
+     * area, without calling folder_realloc().
+     */
+    int        lowoff;
+    int        hghoff;
+
+    /*
+     * This is an array of seqset_t which we allocate dynamically.
+     * Each seqset_t is a set of bits flags for a particular message.
+     * These bit flags represent general attributes such as
+     * EXISTS, SELECTED, etc. as well as track if message is
+     * in a particular sequence.
+     */
+    seqset_t *msgstats;                /* msg status */
+};
+
+/*
+ * Amount of space to allocate for msgstats.  Allocate
+ * the array to have space for messages numbers lo to hi.
+ */
+#define MSGSTATSIZE(mp,lo,hi) ((size_t) (((hi) - (lo) + 1) * sizeof(*(mp)->msgstats)))
+
+/*
+ * macros for message and sequence manipulation
+ */
+#define clear_msg_flags(mp,msgnum) ((mp)->msgstats[(msgnum) - mp->lowoff] = 0)
+#define copy_msg_flags(mp,i,j) \
+       ((mp)->msgstats[(i) - mp->lowoff] = (mp)->msgstats[(j) - mp->lowoff])
+#define get_msg_flags(mp,ptr,msgnum)  (*(ptr) = (mp)->msgstats[(msgnum) - mp->lowoff])
+#define set_msg_flags(mp,ptr,msgnum)  ((mp)->msgstats[(msgnum) - mp->lowoff] = *(ptr))
+
+#define does_exist(mp,msgnum)     ((mp)->msgstats[(msgnum) - mp->lowoff] & EXISTS)
+#define unset_exists(mp,msgnum)   ((mp)->msgstats[(msgnum) - mp->lowoff] &= ~EXISTS)
+#define set_exists(mp,msgnum)     ((mp)->msgstats[(msgnum) - mp->lowoff] |= EXISTS)
+
+#define is_selected(mp,msgnum)    ((mp)->msgstats[(msgnum) - mp->lowoff] & SELECTED)
+#define unset_selected(mp,msgnum) ((mp)->msgstats[(msgnum) - mp->lowoff] &= ~SELECTED)
+#define set_selected(mp,msgnum)   ((mp)->msgstats[(msgnum) - mp->lowoff] |= SELECTED)
+
+#define is_select_empty(mp,msgnum) ((mp)->msgstats[(msgnum) - mp->lowoff] & SELECT_EMPTY)
+#define set_select_empty(mp,msgnum) \
+       ((mp)->msgstats[(msgnum) - mp->lowoff] |= SELECT_EMPTY)
+
+#define is_unseen(mp,msgnum)      ((mp)->msgstats[(msgnum) - mp->lowoff] & SELECT_UNSEEN)
+#define unset_unseen(mp,msgnum)   ((mp)->msgstats[(msgnum) - mp->lowoff] &= ~SELECT_UNSEEN)
+#define set_unseen(mp,msgnum)     ((mp)->msgstats[(msgnum) - mp->lowoff] |= SELECT_UNSEEN)
+
+/* for msh only */
+#define set_deleted(mp,msgnum)    ((mp)->msgstats[(msgnum) - mp->lowoff] |= DELETED)
+
+#define in_sequence(mp,seqnum,msgnum) \
+           ((mp)->msgstats[(msgnum) - mp->lowoff] & (1 << (FFATTRSLOT + seqnum)))
+#define clear_sequence(mp,seqnum,msgnum) \
+           ((mp)->msgstats[(msgnum) - mp->lowoff] &= ~(1 << (FFATTRSLOT + seqnum)))
+#define add_sequence(mp,seqnum,msgnum) \
+           ((mp)->msgstats[(msgnum) - mp->lowoff] |= (1 << (FFATTRSLOT + seqnum)))
+
+#define is_seq_private(mp,seqnum) \
+           ((mp)->attrstats & (1 << (FFATTRSLOT + seqnum)))
+#define make_seq_public(mp,seqnum) \
+           ((mp)->attrstats &= ~(1 << (FFATTRSLOT + seqnum)))
+#define make_seq_private(mp,seqnum) \
+           ((mp)->attrstats |= (1 << (FFATTRSLOT + seqnum)))
+#define make_all_public(mp) \
+           ((mp)->attrstats = 0)
+
+/*
+ * macros for folder attributes
+ */
+#define clear_folder_flags(mp) ((mp)->msgflags = 0)
+
+#define is_readonly(mp)     ((mp)->msgflags & READONLY)
+#define set_readonly(mp)    ((mp)->msgflags |= READONLY)
+
+#define other_files(mp)     ((mp)->msgflags & OTHERS)
+#define set_other_files(mp) ((mp)->msgflags |= OTHERS)
+
+#define        NULLMP  ((struct msgs *) 0)
+
+/*
+ * m_getfld() message parsing
+ */
+
+#define NAMESZ  128            /* Limit on component name size     */
+
+#define LENERR  (-2)           /* Name too long error from getfld  */
+#define FMTERR  (-3)           /* Message Format error             */
+#define FLD      0             /* Field returned                   */
+#define FLDPLUS  1             /* Field returned with more to come */
+#define FLDEOF   2             /* Field returned ending at eom     */
+#define BODY     3             /* Body  returned with more to come */
+#define BODYEOF  4             /* Body  returned ending at eom     */
+#define FILEEOF  5             /* Reached end of input file        */
+
+/*
+ * Maildrop styles
+ */
+#define        MS_DEFAULT      0       /* default (one msg per file) */
+#define        MS_UNKNOWN      1       /* type not known yet         */
+#define        MS_MBOX         2       /* Unix-style "from" lines    */
+#define        MS_MMDF         3       /* string mmdlm2              */
+#define        MS_MSH          4       /* whacko msh                 */
+
+extern int msg_count;          /* m_getfld() indicators */
+extern int msg_style;          /*  .. */
+extern char *msg_delim;                /*  .. */
+
+#define        NOUSE   0               /* draft being re-used */
+
+#define TFOLDER 0              /* path() given a +folder */
+#define TFILE   1              /* path() given a file    */
+#define        TSUBCWF 2               /* path() given a @folder */
+
+#define OUTPUTLINELEN  72      /* default line length for headers */
+
+/*
+ * miscellaneous macros
+ */
+#define        pidXwait(pid,cp) pidstatus (pidwait (pid, NOTOK), stdout, cp)
+
+#ifndef max
+# define max(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef min
+# define min(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef abs
+# define abs(a) ((a) > 0 ? (a) : -(a))
+#endif
+
+/*
+ * GLOBAL VARIABLES
+ */
+#define CTXMOD 0x01            /* context information modified */
+#define        DBITS   "\020\01CTXMOD"
+extern char ctxflags;
+
+extern char *invo_name;                /* command invocation name         */
+extern char *mypath;           /* user's $HOME                    */
+extern char *defpath;          /* pathname of user's profile      */
+extern char *ctxpath;          /* pathname of user's context      */
+extern struct node *m_defs;    /* list of profile/context entries */
+
+/*
+ * These standard strings are defined in config.c.  They are the
+ * only system-dependent parameters in nmh, and thus by redefining
+ * their values and reloading the various modules, nmh will run
+ * on any system.
+ */
+extern char *buildmimeproc;
+extern char *catproc;
+extern char *components;
+extern char *context;
+extern char *current;
+extern char *defaulteditor;
+extern char *defaultfolder;
+extern char *digestcomps;
+extern char *distcomps;
+extern char *draft;
+extern char *faceproc;
+extern char *fileproc;
+extern char *foldprot;
+extern char *forwcomps;
+extern char *inbox;
+extern char *incproc;
+extern char *installproc;
+extern char *lproc;
+extern char *mailproc;
+extern char *mh_defaults;
+extern char *mh_profile;
+extern char *mh_seq;
+extern char *mhlformat;
+extern char *mhlforward;
+extern char *mhlproc;
+extern char *mhlreply;
+extern char *moreproc;
+extern char *msgprot;
+extern char *mshproc;
+extern char *nmhaccessftp;
+extern char *nmhstorage;
+extern char *nmhcache;
+extern char *nmhprivcache;
+extern char *nsequence;
+extern char *packproc;
+extern char *postproc;
+extern char *pfolder;
+extern char *psequence;
+extern char *rcvdistcomps;
+extern char *rcvstoreproc;
+extern char *replcomps;
+extern char *replgroupcomps;
+extern char *rmfproc;
+extern char *rmmproc;
+extern char *sendproc;
+extern char *showmimeproc;
+extern char *showproc;
+extern char *usequence;
+extern char *version_num;
+extern char *version_str;
+extern char *vmhproc;
+extern char *whatnowproc;
+extern char *whomproc;
+
+#include <h/prototypes.h>
+
diff --git a/h/mhcachesbr.h b/h/mhcachesbr.h
new file mode 100644 (file)
index 0000000..a197738
--- /dev/null
@@ -0,0 +1,21 @@
+
+/*
+ * mhcachesbr.h -- definitions for manipulating MIME content cache
+ *
+ * $Id$
+ */
+
+/*
+ * various cache policies
+ */
+static struct swit caches[] = {
+#define CACHE_NEVER    0
+    { "never", 0 },
+#define CACHE_PRIVATE  1
+    { "private", 0 },
+#define CACHE_PUBLIC   2
+    { "public", 0 },
+#define CACHE_ASK      3
+    { "ask", 0 },
+    { NULL, 0 }
+};
diff --git a/h/mhparse.h b/h/mhparse.h
new file mode 100644 (file)
index 0000000..f99a1c7
--- /dev/null
@@ -0,0 +1,245 @@
+
+/*
+ * mhparse.h -- definitions for parsing/building of MIME content
+ *           -- (mhparse.c/mhbuildsbr.c)
+ *
+ * $Id$
+ */
+
+#define        NPARTS  50
+#define        NTYPES  20
+#define        NPARMS  10
+
+/*
+ * Abstract type for header fields
+ */
+typedef struct hfield *HF;
+
+/*
+ * Abstract types for MIME parsing/building
+ */
+typedef struct cefile  *CE;
+typedef struct CTinfo  *CI;
+typedef struct Content *CT;
+
+/*
+ * type for Init function (both type and transfer encoding)
+ */
+typedef int (*InitFunc) (CT);
+
+/*
+ * types for various transfer encoding access functions
+ */
+typedef int (*OpenCEFunc) (CT, char **);
+typedef void (*CloseCEFunc) (CT);
+typedef unsigned long (*SizeCEFunc) (CT);
+
+/*
+ * Structure for storing/encoding/decoding
+ * a header field and its value.
+ */
+struct hfield {
+    char *name;                /* field name */
+    char *value;       /* field body */
+    int hf_encoding;   /* internal flag for transfer encoding to use */
+    HF next;           /* link to next header field */
+};
+
+/*
+ * Structure for storing parsed elements
+ * of the Content-Type component.
+ */
+struct CTinfo {
+    char *ci_type;             /* content type     */
+    char *ci_subtype;          /* content subtype  */
+    char *ci_attrs[NPARMS + 2];        /* attribute names  */
+    char *ci_values[NPARMS];   /* attribute values */
+    char *ci_comment;          /* RFC-822 comments */
+    char *ci_magic;
+};
+
+/*
+ * Structure for storing decoded contents after
+ * removing Content-Transfer-Encoding.
+ */
+struct cefile {
+    char *ce_file;     /* decoded content (file)   */
+    FILE *ce_fp;       /* decoded content (stream) */
+    int          ce_unlink;    /* remove file when done?   */
+};
+
+/*
+ * Primary structure for handling Content (Entity)
+ */
+struct Content {
+    /* source (read) file */
+    char *c_file;              /* read contents (file)              */
+    FILE *c_fp;                        /* read contents (stream)            */
+    int        c_unlink;               /* remove file when done?            */
+
+    long c_begin;              /* where content body starts in file */
+    long c_end;                        /* where content body ends in file   */
+
+    /* linked list of header fields */
+    HF c_first_hf;             /* pointer to first header field     */
+    HF c_last_hf;              /* pointer to last header field      */
+
+    /* copies of MIME related header fields */
+    char *c_vrsn;              /* MIME-Version:                     */
+    char *c_ctline;            /* Content-Type:                     */
+    char *c_celine;            /* Content-Transfer-Encoding:        */
+    char *c_id;                        /* Content-ID:                       */
+    char *c_descr;             /* Content-Description:              */
+    char *c_partno;            /* within multipart content          */
+
+    /* Content-Type info */
+    struct CTinfo c_ctinfo;    /* parsed elements of Content-Type   */
+    int        c_type;                 /* internal flag for content type    */
+    int        c_subtype;              /* internal flag for content subtype */
+
+    /* Content-Transfer-Encoding info (decoded contents) */
+    CE c_cefile;               /* structure holding decoded content */
+    int        c_encoding;             /* internal flag for encoding type   */
+
+    /* Content-MD5 info */
+    int        c_digested;             /* have we seen this header before?  */
+    unsigned char c_digest[16];        /* decoded MD5 checksum              */
+
+    /* pointers to content-specific structures */
+    void *c_ctparams;          /* content type specific data        */
+    struct exbody *c_ctexbody; /* data for type message/external    */
+
+    /* function pointers */
+    InitFunc    c_ctinitfnx;   /* parse content body                */
+    OpenCEFunc  c_ceopenfnx;   /* get a stream to decoded contents  */
+    CloseCEFunc c_ceclosefnx;  /* release stream                    */
+    SizeCEFunc  c_cesizefnx;   /* size of decoded contents          */
+
+    int        c_umask;                /* associated umask                  */
+    pid_t c_pid;               /* process doing display             */
+    int        c_rfc934;               /* rfc934 compatibility flag         */
+
+    char *c_showproc;          /* default, if not in profile        */
+    char *c_termproc;          /* for charset madness...            */
+    char *c_storeproc;         /* overrides profile entry, if any   */
+
+    char *c_storage;           /* write contents (file)             */
+    char *c_folder;            /* write contents (folder)           */
+};
+
+/*
+ * Flags for Content-Type (Content->c_type)
+ */
+#define        CT_UNKNOWN      0x00
+#define        CT_APPLICATION  0x01
+#define        CT_AUDIO        0x02
+#define        CT_IMAGE        0x03
+#define        CT_MESSAGE      0x04
+#define        CT_MULTIPART    0x05
+#define        CT_TEXT         0x06
+#define        CT_VIDEO        0x07
+#define        CT_EXTENSION    0x08
+
+/*
+ * Flags for Content-Transfer-Encoding (Content->c_encoding)
+ */
+#define        CE_UNKNOWN      0x00
+#define        CE_BASE64       0x01
+#define        CE_QUOTED       0x02
+#define        CE_8BIT         0x03
+#define        CE_7BIT         0x04
+#define        CE_BINARY       0x05
+#define        CE_EXTENSION    0x06
+#define        CE_EXTERNAL     0x07    /* for external-body */
+
+/*
+ * TEXT content
+ */
+
+/* Flags for subtypes of TEXT */
+#define        TEXT_UNKNOWN    0x00
+#define        TEXT_PLAIN      0x01
+#define        TEXT_RICHTEXT   0x02
+#define TEXT_ENRICHED  0x03
+
+/* Flags for character sets */
+#define        CHARSET_UNKNOWN     0x00
+#define CHARSET_UNSPECIFIED 0x01  /* only needed when building drafts */
+#define        CHARSET_USASCII     0x01
+#define        CHARSET_LATIN       0x02
+
+/* Structure for text content */
+struct text {
+    int        tx_charset;             /* flag for character set */
+};
+
+/*
+ * MULTIPART content
+ */
+
+/* Flags for subtypes of MULTIPART */
+#define        MULTI_UNKNOWN   0x00
+#define        MULTI_MIXED     0x01
+#define        MULTI_ALTERNATE 0x02
+#define        MULTI_DIGEST    0x03
+#define        MULTI_PARALLEL  0x04
+
+/* Structure for subparts of a multipart content */
+struct part {
+    CT mp_part;                        /* Content structure for subpart     */
+    struct part *mp_next;      /* pointer to next subpart structure */
+};
+
+/* Main structure for multipart content */
+struct multipart {
+    char *mp_start;            /* boundary string separating parts   */
+    char *mp_stop;             /* terminating boundary string        */
+    struct part *mp_parts;     /* pointer to first subpart structure */
+};
+
+/*
+ * MESSAGE content
+ */
+
+/* Flags for subtypes of MESSAGE */
+#define        MESSAGE_UNKNOWN  0x00
+#define        MESSAGE_RFC822   0x01
+#define        MESSAGE_PARTIAL  0x02
+#define        MESSAGE_EXTERNAL 0x03
+
+/* Structure for message/partial */
+struct partial {
+    char *pm_partid;
+    int        pm_partno;
+    int        pm_maxno;
+    int        pm_marked;
+    int        pm_stored;
+};
+
+/* Structure for message/external */
+struct exbody {
+    CT eb_parent;      /* pointer to controlling content structure */
+    CT eb_content;     /* pointer to internal content structure    */
+    char *eb_partno;
+    char *eb_access;
+    int        eb_flags;
+    char *eb_name;
+    char *eb_permission;
+    char *eb_site;
+    char *eb_dir;
+    char *eb_mode;
+    unsigned long eb_size;
+    char *eb_server;
+    char *eb_subject;
+    char *eb_body;
+};
+
+/*
+ * APPLICATION content
+ */
+
+/* Flags for subtype of APPLICATION */
+#define        APPLICATION_UNKNOWN     0x00
+#define        APPLICATION_OCTETS      0x01
+#define        APPLICATION_POSTSCRIPT  0x02
+
diff --git a/h/mime.h b/h/mime.h
new file mode 100644 (file)
index 0000000..b741b2f
--- /dev/null
+++ b/h/mime.h
@@ -0,0 +1,38 @@
+
+/*
+ * mime.h -- definitions for MIME
+ *
+ * $Id$
+ */
+
+#define        VRSN_FIELD      "MIME-Version"
+#define        VRSN_VALUE      "1.0"
+#define        XXX_FIELD_PRF   "Content-"
+#define        TYPE_FIELD      "Content-Type"
+#define        ENCODING_FIELD  "Content-Transfer-Encoding"
+#define        ID_FIELD        "Content-ID"
+#define        DESCR_FIELD     "Content-Description"
+#define        MD5_FIELD       "Content-MD5"
+
+#define        isatom(c)   (!isspace (c) && !iscntrl (c) && (c) != '(' \
+                    && (c) != ')' && (c) != '<'  && (c) != '>' \
+                    && (c) != '@' && (c) != ','  && (c) != ';' \
+                    && (c) != ':' && (c) != '\\' && (c) != '"' \
+                    && (c) != '.' && (c) != '['  && (c) != ']')
+
+/*
+ * Test for valid characters used in "token"
+ * as defined in RFC2045
+ */
+#define        istoken(c)  (!isspace (c) && !iscntrl (c) && (c) != '(' \
+                    && (c) != ')' && (c) != '<'  && (c) != '>' \
+                    && (c) != '@' && (c) != ','  && (c) != ';' \
+                    && (c) != ':' && (c) != '\\' && (c) != '"' \
+                    && (c) != '/' && (c) != '['  && (c) != ']' \
+                    && (c) != '?' && (c) != '=')
+
+#define        CPERLIN 76
+#define        BPERLIN (CPERLIN / 4)
+#define        LPERMSG 632
+#define        CPERMSG (LPERMSG * CPERLIN)
+
diff --git a/h/msh.h b/h/msh.h
new file mode 100644 (file)
index 0000000..c6b0fa5
--- /dev/null
+++ b/h/msh.h
@@ -0,0 +1,114 @@
+
+/*
+ * msh.h -- definitions for msh
+ *
+ * $Id$
+ */
+
+/* flags for stream */
+#define        STDIO   0               /* regular stdoutput */
+#define        CRTIO   1               /* create  re-direct */
+#define        APPIO   2               /* append  re-direct */
+#define        PIPIO   3               /* pipe    re-direct */
+
+struct Cmd {
+    char line[BUFSIZ];
+    char *args[MAXARGS];
+    char *redirect;
+    int direction;
+    FILE *stream;
+};
+
+#define        NULLCMD ((struct Cmd *) 0)
+
+#define        MHNCHK 0x0001   /* did nontext check           */
+#define        MHNYES 0x0002   /* .. and known to be non-text */
+
+#define CUR (1 << (FFATTRSLOT + NUMATTRS - 1))
+
+#ifdef BPOP
+# define VIRTUAL SELECT_EMPTY
+
+# define is_virtual(mp,msgnum)    ((mp)->msgstats[msgnum] & VIRTUAL)
+# define unset_virtual(mp,msgnum) ((mp)->msgstats[msgnum] &= ~VIRTUAL)
+# define set_virtual(mp,msgnum)   ((mp)->msgstats[msgnum] |= VIRTUAL)
+#endif
+
+struct Msg {
+    struct drop m_drop;
+    char *m_scanl;
+    struct tws m_tb;
+    short m_flags;
+    seqset_t m_stats;
+};
+
+#define        m_bboard_id  m_drop.d_id
+#define        m_top        m_drop.d_size
+#define        m_start      m_drop.d_start
+#define        m_stop       m_drop.d_stop
+
+/*
+ * FOLDER
+ */
+extern char *fmsh;             /* folder instead of file  */
+extern int modified;           /* command modified folder */
+extern struct msgs *mp;                /* used a lot              */
+extern struct Msg *Msgs;       /* Msgs[0] not used        */
+
+FILE *msh_ready ();
+
+/*
+ * COMMAND
+ */
+extern int interactive;                /* running from a /dev/tty */
+extern int redirected;         /* re-directing output     */
+extern FILE *sp;               /* original stdout         */
+extern char *cmd_name;         /* command being run       */
+extern char myfilter[];                /* path to mhl.forward     */
+
+extern char *BBoard_ID;                /* BBoard-ID constant */
+
+/*
+ * SIGNALS
+ */
+extern SIGNAL_HANDLER istat;   /* original SIGINT  */
+extern SIGNAL_HANDLER qstat;   /* original SIGQUIT */
+extern int interrupted;                /* SIGINT detected  */
+extern int broken_pipe;                /* SIGPIPE detected */
+extern int told_to_quit;       /* SIGQUIT detected */
+
+#ifdef BSD42
+extern int should_intr;                /* signal handler should interrupt call */
+extern jmp_buf sigenv;         /* the environment pointer */
+#endif
+
+/*
+ * prototypes
+ */
+int readid (int);
+int expand (char *);
+void m_reset (void);
+void fsetup (char *);
+void setup (char *);
+void readids (int);
+void display_info (int);
+
+void forkcmd (char **s, char *);
+void distcmd (char **);
+void explcmd (char **);
+int filehak (char **);
+void filecmd (char **);
+void foldcmd (char **);
+void forwcmd (char **);
+void helpcmd (char **);
+void markcmd (char **);
+void mhncmd (char **);
+void showcmd (char **);
+int pack (char *, int, int);
+int packhak (char **);
+void packcmd (char **);
+void pickcmd (char **);
+void replcmd (char **);
+void rmmcmd (char **);
+void scancmd (char **);
+void sortcmd (char **);
diff --git a/h/netdb.h b/h/netdb.h
new file mode 100644 (file)
index 0000000..41b0226
--- /dev/null
+++ b/h/netdb.h
@@ -0,0 +1,71 @@
+/*
+ * netdb.h
+ *
+ * Copyright (c) 1980,1983,1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of California at Berkeley. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ *
+ * $Id$
+ */
+
+/*
+ * Structures returned by network
+ * data base library.  All addresses
+ * are supplied in host order, and
+ * returned in network order (suitable
+ * for use in system calls).
+ */
+struct hostent {
+       char    *h_name;        /* official name of host */
+       char    **h_aliases;    /* alias list */
+       int     h_addrtype;     /* host address type */
+       int     h_length;       /* length of address */
+       char    **h_addr_list;  /* list of addresses from name server */
+#define        h_addr  h_addr_list[0]  /* address, for backward compatiblity */
+};
+
+/*
+ * Assumption here is that a network number
+ * fits in 32 bits -- probably a poor one.
+ */
+struct netent {
+       char            *n_name;        /* official name of net */
+       char            **n_aliases;    /* alias list */
+       int             n_addrtype;     /* net address type */
+       unsigned long   n_net;          /* network # */
+};
+
+struct servent {
+       char    *s_name;        /* official service name */
+       char    **s_aliases;    /* alias list */
+       int     s_port;         /* port # */
+       char    *s_proto;       /* protocol to use */
+};
+
+struct protoent {
+       char    *p_name;        /* official protocol name */
+       char    **p_aliases;    /* alias list */
+       int     p_proto;        /* protocol # */
+};
+
+struct hostent *gethostbyname(), *gethostbyaddr(), *gethostent();
+struct netent  *getnetbyname(), *getnetbyaddr(), *getnetent();
+struct servent *getservbyname(), *getservbyport(), *getservent();
+struct protoent        *getprotobyname(), *getprotobynumber(), *getprotoent();
+
+/*
+ * Error return codes from gethostbyname() and gethostbyaddr()
+ * (left in extern int h_errno).
+ */
+
+#define        HOST_NOT_FOUND  1 /* Authoritative Answer Host not found */
+#define        TRY_AGAIN       2 /* Non-Authoritive Host not found, or SERVERFAIL */
+#define        NO_RECOVERY     3 /* Non recoverable errors, FORMERR, REFUSED, NOTIMP */
+#define        NO_DATA         4 /* Valid name, no data record of requested type */
+#define        NO_ADDRESS      NO_DATA         /* no address, look for MX record */
diff --git a/h/nmh.h b/h/nmh.h
new file mode 100644 (file)
index 0000000..f00a3ad
--- /dev/null
+++ b/h/nmh.h
@@ -0,0 +1,162 @@
+
+/*
+ * nmh.h -- system configuration header file
+ *
+ * $Id$
+ */
+
+#include <config.h>
+
+#ifdef HAVE_UNISTD_H
+# include <sys/types.h>
+# include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/stat.h>
+
+#if HAVE_DIRENT_H
+# include <dirent.h>
+# define NLENGTH(dirent) strlen((dirent)->d_name)
+#else
+# define dirent direct
+# define NLENGTH(dirent) (dirent)->d_namlen
+# if HAVE_SYS_NDIR_H
+#  include <sys/ndir.h>
+# endif
+# if HAVE_SYS_DIR_H
+#  include <sys/dir.h>
+# endif
+# if HAVE_NDIR_H
+#  include <ndir.h>
+# endif
+#endif
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+#include <stdarg.h>
+
+#if STDC_HEADERS || HAVE_STRING_H
+# include <string.h>
+/* An ANSI string.h and pre-ANSI memory.h might conflict.  */
+# if !STDC_HEADERS && HAVE_MEMORY_H
+#  include <memory.h>
+# endif /* not STDC_HEADERS and HAVE_MEMORY_H */
+#else   /* not STDC_HEADERS and not HAVE_STRING_H */
+# include <strings.h>
+/* memory.h and strings.h conflict on some systems.  */
+#endif /* not STDC_HEADERS and not HAVE_STRING_H */
+
+#ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+
+#ifdef HAVE_LOCALE_H
+# include <locale.h>
+#endif
+
+#ifdef HAVE_LIMITS_H
+# include <limits.h>
+#endif
+
+/*
+ * symbolic constants for lseek and fseek
+ */
+#ifndef SEEK_SET
+# define SEEK_SET 0
+#endif
+#ifndef SEEK_CUR
+# define SEEK_CUR 1
+#endif
+#ifndef SEEK_END
+# define SEEK_END 2
+#endif
+
+/*
+ * we should be getting this value from pathconf(_PC_PATH_MAX)
+ */
+#ifndef PATH_MAX
+# ifdef MAXPATHLEN
+#  define PATH_MAX MAXPATHLEN
+# else
+   /* so we will just pick something */
+#  define PATH_MAX 1024
+# endif
+#endif
+
+/*
+ * we should get this value from sysconf(_SC_NGROUPS_MAX)
+ */
+#ifndef NGROUPS_MAX
+# ifdef NGROUPS
+#  define NGROUPS_MAX NGROUPS
+# else
+#  define NGROUPS_MAX 16
+# endif
+#endif
+
+/*
+ * we should be getting this value from sysconf(_SC_OPEN_MAX)
+ */
+#ifndef OPEN_MAX
+# ifdef NOFILE
+#  define OPEN_MAX NOFILE
+# else
+   /* so we will just pick something */
+#  define OPEN_MAX 64
+# endif
+#endif
+
+#include <signal.h>
+#define bcmp(b1,b2,length)      memcmp(b1, b2, length)
+#define bcopy(b1,b2,length)     memcpy (b2, b1, length)
+#define bcpy(b1,b2,length)      memcmp (b1, b2, length)
+#define bzero(b,length)         memset (b, 0, length)
+
+#ifdef HAVE_KILLPG
+# define KILLPG(pgrp,sig) killpg(pgrp,sig);
+#else
+# define KILLPG(pgrp,sig) kill((-pgrp),sig);
+#endif
+
+/*
+ * If your stat macros are broken,
+ * we will just undefine them.
+ */
+#ifdef STAT_MACROS_BROKEN
+# ifdef S_ISBLK
+#  undef S_ISBLK
+# endif 
+# ifdef S_ISCHR
+#  undef S_ISCHR
+# endif 
+# ifdef S_ISDIR
+#  undef S_ISDIR
+# endif 
+# ifdef S_ISFIFO
+#  undef S_ISFIFO
+# endif 
+# ifdef S_ISLNK
+#  undef S_ISLNK
+# endif 
+# ifdef S_ISMPB
+#  undef S_ISMPB
+# endif 
+# ifdef S_ISMPC
+#  undef S_ISMPC
+# endif 
+# ifdef S_ISNWK
+#  undef S_ISNWK
+# endif 
+# ifdef S_ISREG
+#  undef S_ISREG
+# endif 
+# ifdef S_ISSOCK
+#  undef S_ISSOCK
+# endif 
+#endif  /* STAT_MACROS_BROKEN.  */
+
diff --git a/h/nntp.h b/h/nntp.h
new file mode 100644 (file)
index 0000000..b232949
--- /dev/null
+++ b/h/nntp.h
@@ -0,0 +1,73 @@
+/*
+ * nntp.h -- Response codes for NNTP server
+ *
+ * $Id$
+ *
+ * First digit:
+ *
+ *     1xx     Informative message
+ *     2xx     Command ok
+ *     3xx     Command ok so far, continue
+ *     4xx     Command was correct, but couldn't be performed
+ *             for some specified reason.
+ *     5xx     Command unimplemented, incorrect, or a
+ *             program error has occured.
+ *
+ * Second digit:
+ *
+ *     x0x     Connection, setup, miscellaneous
+ *     x1x     Newsgroup selection
+ *     x2x     Article selection
+ *     x3x     Distribution
+ *     x4x     Posting
+ */
+
+#define        CHAR_INF        '1'
+#define        CHAR_OK         '2'
+#define        CHAR_CONT       '3'
+#define        CHAR_ERR        '4'
+#define        CHAR_FATAL      '5'
+
+#define        INF_HELP        100     /* Help text on way */
+#define        INF_DEBUG       199     /* Debug output */
+
+#define        OK_CANPOST      200     /* Hello; you can post */
+#define        OK_NOPOST       201     /* Hello; you can't post */
+#define        OK_SLAVE        202     /* Slave status noted */
+#define        OK_GOODBYE      205     /* Closing connection */
+#define        OK_GROUP        211     /* Group selected */
+#define        OK_GROUPS       215     /* Newsgroups follow */
+#define        OK_ARTICLE      220     /* Article (head & body) follows */
+#define        OK_HEAD         221     /* Head follows */
+#define        OK_BODY         222     /* Body follows */
+#define        OK_NOTEXT       223     /* No text sent -- stat, next, last */
+#define        OK_NEWNEWS      230     /* New articles by message-id follow */
+#define        OK_NEWGROUPS    231     /* New newsgroups follow */
+#define        OK_XFERED       235     /* Article transferred successfully */
+#define        OK_POSTED       240     /* Article posted successfully */
+
+#define CONT_XFER      335     /* Continue to send article */
+#define        CONT_POST       340     /* Continue to post article */
+
+#define        ERR_GOODBYE     400     /* Have to hang up for some reason */
+#define        ERR_NOGROUP     411     /* No such newsgroup */
+#define        ERR_NCING       412     /* Not currently in newsgroup */
+#define        ERR_NOCRNT      420     /* No current article selected */
+#define        ERR_NONEXT      421     /* No next article in this group */
+#define        ERR_NOPREV      422     /* No previous article in this group */
+#define        ERR_NOARTIG     423     /* No such article in this group */
+#define ERR_NOART      430     /* No such article at all */
+#define ERR_GOTIT      435     /* Already got that article, don't send */
+#define ERR_XFERFAIL   436     /* Transfer failed */
+#define        ERR_XFERRJCT    437     /* Article rejected, don't resend */
+#define        ERR_NOPOST      440     /* Posting not allowed */
+#define        ERR_POSTFAIL    441     /* Posting failed */
+
+#define        ERR_COMMAND     500     /* Command not recognized */
+#define        ERR_CMDSYN      501     /* Command syntax error */
+#define        ERR_ACCESS      502     /* Access to server denied */
+#define ERR_FAULT      503     /* Program fault, command not performed */
+
+/* RFC 977 defines this; don't change it. */
+
+#define        NNTP_STRLEN     512
diff --git a/h/picksbr.h b/h/picksbr.h
new file mode 100644 (file)
index 0000000..811836d
--- /dev/null
@@ -0,0 +1,12 @@
+
+/*
+ * picksbr.h -- definitions for picksbr.c
+ *
+ * $Id$
+ */
+
+/*
+ * prototypes
+ */
+int pcompile (char **, char *);
+int pmatches (FILE *, int, long, long);
diff --git a/h/popsbr.h b/h/popsbr.h
new file mode 100644 (file)
index 0000000..a9e0883
--- /dev/null
@@ -0,0 +1,62 @@
+
+/*
+ * popsbr.h -- header for POP client subroutines
+ *
+ * $Id$
+ */
+
+#if 0
+#if !defined(NNTP) && defined(MPOP)
+# define command pop_command
+# define multiline pop_multiline
+#endif
+#endif
+
+#ifdef NNTP
+int pop_set (int, int, int, char *);
+#else
+int pop_set (int, int, int);
+#endif
+
+#ifdef NNTP
+int pop_exists (int (*)());
+#endif
+
+int pop_init (char *, char *, char *, int, int);
+int pop_fd (char *, int, char *, int);
+int pop_stat (int *, int *);
+int pop_retr (int, int (*)());
+int pop_dele (int);
+int pop_noop (void);
+int pop_rset (void);
+int pop_top (int, int, int (*)());
+int pop_quit (void);
+int pop_done (void);
+
+#ifdef BPOP
+int pop_list (int, int *, int *, int *, int *);
+#else
+int pop_list (int, int *, int *, int *);
+#endif
+
+#ifdef BPOP
+int pop_xtnd (int (*)(), char *, ...);
+#endif
+
+#if defined(MPOP) && !defined(NNTP)
+int pop_last (void);
+#endif
+
+#if !defined(NNTP) && defined(MPOP)
+/* otherwise they are static functions */
+int command(const char *, ...);
+int multiline(void);
+#endif
+
+/*
+ * Flags for the various pop authentication methods
+ */
+#define POP_APOP   -1
+#define POP_PASSWD  0
+#define POP_RPOP    1
+#define POP_KPOP    2
diff --git a/h/prototypes.h b/h/prototypes.h
new file mode 100644 (file)
index 0000000..0dccfa2
--- /dev/null
@@ -0,0 +1,159 @@
+
+/*
+ * prototypes.h -- various prototypes
+ *
+ * $Id$
+ */
+
+/*
+ * missing system prototypes
+ */
+#ifndef HAVE_TERMCAP_H
+extern int tgetent (char *bp, char *name);
+extern int tgetnum (char *id);
+extern int tgetflag (char *id);
+extern char *tgetstr (char *id, char **area);
+extern char *tgoto (char *cm, int destcol, int destline);
+extern int tputs (char *cp, int affcnt, int (*outc) (int));
+#endif
+
+/*
+ * prototype from config.h
+ */
+char *etcpath(char *);
+
+/*
+ * prototypes from the nmh subroutine library
+ */
+char *add (char *, char *);
+void adios (char *, char *, ...);
+void admonish (char *, char *, ...);
+void advertise (char *, char *, char *, va_list);
+void advise (char *, char *, ...);
+void ambigsw (char *, struct swit *);
+int atooi(char *);
+char **brkstring (char *, char *, char *);
+int check_charset (char *, int);
+void closefds(int);
+char *concat (char *, ...);
+int context_del (char *);
+char *context_find (char *);
+int context_foil (char *);
+void context_read (void);
+void context_replace (char *, char *);
+void context_save (void);
+char *copy (char *, char *);
+char **copyip (char **, char **, int);
+void cpydata (int, int, char *, char *);
+void cpydgst (int, int, char *, char *);
+int decode_rfc2047 (char *, char *);
+void discard (FILE *);
+void done (int);
+int fdcompare (int, int);
+int folder_addmsg (struct msgs **, char *, int, int, int);
+int folder_delmsgs (struct msgs *, int);
+void folder_free (struct msgs *);
+int folder_pack (struct msgs **, int);
+struct msgs *folder_read (char *);
+struct msgs *folder_realloc (struct msgs *, int, int);
+int gans (char *, struct swit *);
+char **getans (char *, struct swit *);
+int getanswer (char *);
+char **getarguments (char *, int, char **, int);
+char *getcpy (char *);
+char *getfolder(int);
+int lkclose(int, char*);
+int lkfclose(FILE *, char *);
+FILE *lkfopen(char *, char *);
+int lkopen(char *, int, mode_t);
+int m_atoi (char *);
+char *m_backup (char *);
+int m_convert (struct msgs *, char *);
+char *m_draft (char *, char *, int, int *);
+void m_eomsbr (int (*)());
+int m_getfld (int, unsigned char *, unsigned char *, int, FILE *);
+int m_gmprot (void);
+char *m_maildir (char *);
+char *m_mailpath (char *);
+char *m_name (int);
+int m_putenv (char *, char *);
+char *m_scratch (char *, char *);
+char *m_tmpfil (char *);
+void m_unknown(FILE *);
+int makedir (char *);
+char *new_fs (char *, char *, char *);
+char *path(char *, int);
+int peekc(FILE *ib);
+int pidwait (pid_t, int);
+int pidstatus (int, FILE *, char *);
+void print_help (char *, struct swit *, int);
+void print_sw (char *, struct swit *, char *);
+void print_version (char *);
+void push (void);
+char *pwd (void);
+char *r1bindex(char *, int);
+void readconfig (struct node **, FILE *, char *, int);
+int refile (char **, char *);
+int remdir (char *);
+int seq_addmsg (struct msgs *, char *, int, int, int);
+int seq_addsel (struct msgs *, char *, int, int);
+char *seq_bits (struct msgs *);
+int seq_delmsg (struct msgs *, char *, int);
+int seq_delsel (struct msgs *, char *, int, int);
+int seq_getnum (struct msgs *, char *);
+char *seq_list (struct msgs *, char *);
+int seq_nameok (char *);
+void seq_print (struct msgs *, char *);
+void seq_printall (struct msgs *);
+void seq_read (struct msgs *);
+void seq_save (struct msgs *);
+void seq_setcur (struct msgs *, int);
+void seq_setprev (struct msgs *);
+void seq_setunseen (struct msgs *, int);
+int showfile (char **, char *);
+int smatch(char *, struct swit *);
+char *snprintb (char *, size_t, unsigned, char *);
+int ssequal (char *, char *);
+int stringdex (char *, char *);
+char *trimcpy (char *);
+int unputenv (char *);
+int uprf (char *, char *);
+int vfgets (FILE *, char **);
+char *write_charset_8bit (void);
+
+#ifdef RPATHS
+int get_returnpath (char *, int, char *, int);
+#endif
+
+/*
+ * prototypes for compatibility functions in library
+ */
+#ifndef HAVE_SNPRINTF
+int snprintf (char *, size_t, const char *, ...);
+int vsnprintf (char *, size_t, const char *, va_list);
+#endif
+
+#ifndef HAVE_STRERROR
+char *strerror (int);
+#endif
+
+
+/*
+ * some prototypes for address parsing system
+ * (others are in addrsbr.h)
+ */
+char *LocalName(void);
+char *SystemName(void);
+char *OfficialName(char *);
+
+/*
+ * prototypes for some routines in uip
+ */
+int annotate (char *, char *, char *, int, int);
+int distout (char *, char *, char *);
+void replout (FILE *, char *, char *, struct msgs *, int,
+       int, char *, char *, char *);
+int sendsbr (char **, int, char *, struct stat *, int);
+int what_now (char *, int, int, char *, char *,
+       int, struct msgs *, char *, int, char *);
+
diff --git a/h/rcvmail.h b/h/rcvmail.h
new file mode 100644 (file)
index 0000000..856409e
--- /dev/null
@@ -0,0 +1,39 @@
+
+/*
+ * rcvmail.h -- rcvmail hook definitions
+ *
+ * $Id$
+ */
+
+#if defined(SENDMTS) || defined(SMTPMTS)
+# include <ctype.h>
+# include <errno.h>
+# include <setjmp.h>
+# include <stdio.h>
+# include <sys/types.h>
+# include <mts/smtp/smtp.h>
+#endif /* SENDMTS || SMTPMTS */
+
+#ifdef MMDFMTS
+# include <mts/mmdf/util.h>
+# include <mts/mmdf/mmdf.h>
+#endif /* MMDFMTS */
+
+
+#if defined(SENDMTS) || defined(SMTPMTS)
+# define RCV_MOK       0
+# define RCV_MBX       1
+#endif /* SENDMTS || SMTPMTS */
+
+#ifdef MMDFI
+# define RCV_MOK       RP_MOK
+# define RCV_MBX       RP_MECH
+#endif /* MMDFI */
+
+
+#ifdef NRTC                    /* sigh */
+# undef RCV_MOK
+# undef RCV_MBX
+# define RCV_MOK       RP_MOK
+# define RCV_MBX       RP_MECH
+#endif /* NRTC */
diff --git a/h/scansbr.h b/h/scansbr.h
new file mode 100644 (file)
index 0000000..b050d97
--- /dev/null
@@ -0,0 +1,42 @@
+
+/*
+ * scansbr.h -- definitions for scan()
+ *
+ * $Id$
+ */
+
+extern char *scanl;
+
+#define        SCNENC  2               /* message just fine, but encrypted(!!) */
+#define        SCNMSG  1               /* message just fine                    */
+#define        SCNEOF  0               /* empty message                        */
+#define        SCNERR  (-1)            /* error message                        */
+#define        SCNNUM  (-2)            /* number out of range                  */
+#define        SCNFAT  (-3)            /* fatal error                          */
+
+/*
+ * default format for `scan' and `inc'
+ */
+
+#ifndef        UK
+#define        FORMAT  \
+"%4(msg)%<(cur)+%| %>%<{replied}-%?{encrypted}E%| %>\
+%02(mon{date})/%02(mday{date})%<{date} %|*%>\
+%<(mymbox{from})%<{to}To:%14(decode(friendly{to}))%>%>\
+%<(zero)%17(decode(friendly{from}))%>  \
+%(decode{subject})%<{body}<<%{body}>>%>\n"
+#else
+#define        FORMAT  \
+"%4(msg)%<(cur)+%| %>%<{replied}-%?{encrypted}E%| %>\
+%02(mday{date})/%02(mon{date})%<{date} %|*%>\
+%<(mymbox{from})%<{to}To:%14(decode(friendly{to}))%>%>\
+%<(zero)%17(decode(friendly{from}))%>  \
+%(decode{subject})%<{body}<<%{body}>>%>\n"
+#endif
+
+#define        WIDTH  78
+
+/*
+ * prototypes
+ */
+int scan (FILE *, int, int, char *, int, int, int, char *, long, int);
diff --git a/h/signals.h b/h/signals.h
new file mode 100644 (file)
index 0000000..9648e04
--- /dev/null
@@ -0,0 +1,34 @@
+
+/*
+ * signals.h -- header file for nmh signal interface
+ *
+ * $Id$
+ */
+
+#include <config.h>
+
+/*
+ * The type for a signal handler
+ */
+typedef RETSIGTYPE (*SIGNAL_HANDLER)(int);
+
+/*
+ * If not a POSIX machine, then we create our
+ * own POSIX style signal sets functions. This
+ * currently assumes you have 31 signals, which
+ * should be true on most pure BSD machines.
+ */
+#ifndef POSIX_SIGNALS
+# define sigemptyset(s)    (*(s) = 0)
+# define sigfillset(s)     (*(s) = ~((sigset_t) 0), 0)
+# define sigaddset(s,n)    (*(s) |=  (1 << ((n) - 1)), 0)
+# define sigdelset(s,n)    (*(s) &= ~(1 << ((n) - 1)), 0)
+# define sigismember(s,n)  ((*(s) & (1 << ((n) - 1))) != 0)
+#endif
+
+/*
+ * prototypes
+ */
+int SIGPROCMASK (int, const sigset_t *, sigset_t *);
+SIGNAL_HANDLER SIGNAL (int, SIGNAL_HANDLER);
+SIGNAL_HANDLER SIGNAL2 (int, SIGNAL_HANDLER);
diff --git a/h/vmhsbr.h b/h/vmhsbr.h
new file mode 100644 (file)
index 0000000..452f2e3
--- /dev/null
@@ -0,0 +1,51 @@
+
+/*
+ * vmhsbr.h -- definitions for the vmh protocol
+ *
+ * $Id$
+ */
+
+#define        RC_VRSN 1
+
+/* flags for rh_type */
+#define        RC_INI  0x01            /* must be greater than OK */
+#define        RC_ACK  0x02
+#define        RC_ERR  0x03
+#define        RC_CMD  0x04
+#define        RC_QRY  0x05
+#define        RC_TTY  0x06
+#define        RC_WIN  0x07
+#define        RC_DATA 0x08
+#define        RC_EOF  0x09
+#define        RC_FIN  0x0a
+#define        RC_XXX  0x0b
+
+struct record {
+    struct rcheader {
+       char rh_type;           /* type of record   */
+       int  rh_len;            /* length of data   */
+    } rc_header;
+    char *rc_data;             /* extensible array */
+};
+
+#define        rc_head(rc)     (&rc->rc_header)
+#define        RHSIZE(rc)      (sizeof rc->rc_header)
+#define        rc_type         rc_header.rh_type
+#define        rc_len          rc_header.rh_len
+
+#define        initrc(rc) rc->rc_data = NULL
+
+/*
+ * prototypes
+ */
+int rcinit (int, int);
+int rcdone (void);
+int rc2rc (char, int, char *, struct record *);
+int str2rc (char, char *, struct record *);
+int peer2rc (struct record *);
+int rc2peer (char, int, char *);
+int str2peer (char, char *);
+int fmt2peer (char, char *, ...);
+int err2peer (char, char *, char *, ...);
+int verr2peer (char, char *, char *, va_list);
+
diff --git a/install-sh b/install-sh
new file mode 100755 (executable)
index 0000000..0d02bf2
--- /dev/null
@@ -0,0 +1,237 @@
+#! /bin/sh
+#
+# install -- install a program, script, or datafile
+# This comes from X11R5.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+       -c) instcmd="$cpprog"
+           shift
+           continue;;
+
+       -d) dir_arg=true
+           shift
+           continue;;
+
+       -m) chmodcmd="$chmodprog $2"
+           shift
+           shift
+           continue;;
+
+       -o) chowncmd="$chownprog $2"
+           shift
+           shift
+           continue;;
+
+       -g) chgrpcmd="$chgrpprog $2"
+           shift
+           shift
+           continue;;
+
+       -s) stripcmd="$stripprog"
+           shift
+           continue;;
+
+       -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+           shift
+           continue;;
+
+       -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+           shift
+           continue;;
+
+       *)  if [ x"$src" = x ]
+           then
+               src=$1
+           else
+               # this colon is to work around a 386BSD /bin/sh bug
+               :
+               dst=$1
+           fi
+           shift
+           continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+       echo "install:  no input file specified"
+       exit 1
+else
+       true
+fi
+
+if [ x"$dir_arg" != x ]; then
+       dst=$src
+       src=""
+       
+       if [ -d $dst ]; then
+               instcmd=:
+       else
+               instcmd=mkdir
+       fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad 
+# if $src (and thus $dsttmp) contains '*'.
+
+       if [ -f $src -o -d $src ]
+       then
+               true
+       else
+               echo "install:  $src does not exist"
+               exit 1
+       fi
+       
+       if [ x"$dst" = x ]
+       then
+               echo "install:  no destination specified"
+               exit 1
+       else
+               true
+       fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+       if [ -d $dst ]
+       then
+               dst="$dst"/`basename $src`
+       else
+               true
+       fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+#  this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='   
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+       pathcomp="${pathcomp}${1}"
+       shift
+
+       if [ ! -d "${pathcomp}" ] ;
+        then
+               $mkdirprog "${pathcomp}"
+       else
+               true
+       fi
+
+       pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+       $doit $instcmd $dst &&
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+       if [ x"$transformarg" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               dstfile=`basename $dst $transformbasename | 
+                       sed $transformarg`$transformbasename
+       fi
+
+# don't allow the sed command to completely eliminate the filename
+
+       if [ x"$dstfile" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               true
+       fi
+
+# Make a temp file name in the proper directory.
+
+       dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+       $doit $instcmd $src $dsttmp &&
+
+       trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing.  If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+       $doit $rmcmd -f $dstdir/$dstfile &&
+       $doit $mvcmd $dsttmp $dstdir/$dstfile 
+
+fi &&
+
+
+exit 0
diff --git a/man/Makefile.in b/man/Makefile.in
new file mode 100644 (file)
index 0000000..d08c3a2
--- /dev/null
@@ -0,0 +1,210 @@
+#
+# Makefile for man subdirectory
+#
+# $Id$
+#
+
+VERSION = @VERSION@
+
+SHELL = /bin/sh
+
+top_srcdir = @top_srcdir@
+srcdir     = @srcdir@
+VPATH      = @srcdir@
+
+prefix      = @prefix@
+exec_prefix = @exec_prefix@
+bindir      = @bindir@
+libdir      = @libdir@
+etcdir      = @sysconfdir@
+mandir      = @mandir@
+manext1     = 1
+manext5     = 5
+manext8     = 8
+
+mailspool    = @mailspool@
+sendmailpath = @sendmailpath@
+
+default_editor = @editorpath@
+default_pager = @pagerpath@
+
+INSTALL      = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+
+SED = sed
+SEDMAN = $(SED) -f man.sed $< > $@
+
+# sed line for editing pop information in man pages
+POPSED = @POPSED@
+
+.SUFFIXES:
+.SUFFIXES: .man .$(manext1) .$(manext5) .$(manext8)
+
+.man.$(manext1):
+       $(SEDMAN)
+
+.man.$(manext5):
+       $(SEDMAN)
+
+.man.$(manext8):
+       $(SEDMAN)
+
+# include file for the man pages.  It is installed
+# in the nmh library directory.
+HDR = tmac.h
+
+# input file for header
+DIST_HDR = tmac.h.in
+
+# man pages to install in $(mandir)/$(manext1)
+MAN1 = ali.$(manext1) anno.$(manext1) burst.$(manext1) comp.$(manext1) \
+       dist.$(manext1) flist.$(manext1) folder.$(manext1) forw.$(manext1) \
+       inc.$(manext1) mark.$(manext1) mh-chart.$(manext1) nmh.$(manext1) \
+       mhbuild.$(manext1) mhl.$(manext1) mhlist.$(manext1) mhmail.$(manext1) \
+       mhn.$(manext1) mhparam.$(manext1) mhpath.$(manext1) mhshow.$(manext1) \
+       mhstore.$(manext1) msgchk.$(manext1) msh.$(manext1) \
+       next.$(manext1) packf.$(manext1) pick.$(manext1) prev.$(manext1) \
+       prompter.$(manext1) rcvdist.$(manext1) rcvpack.$(manext1) \
+       rcvstore.$(manext1) rcvtty.$(manext1) refile.$(manext1) \
+       repl.$(manext1) rmf.$(manext1) rmm.$(manext1) scan.$(manext1) \
+       send.$(manext1) sendfiles.$(manext1) show.$(manext1) slocal.$(manext1) \
+       sortm.$(manext1) vmh.$(manext1) whatnow.$(manext1) whom.$(manext1)
+
+MAN5 = mh-alias.$(manext5) mh-draft.$(manext5) mh-format.$(manext5) \
+       mh-mail.$(manext5) mh-profile.$(manext5) mh-sequence.$(manext5) \
+       mh-tailor.$(manext5)
+
+MAN8 = ap.$(manext8) conflict.$(manext8) dp.$(manext8) \
+       fmtdump.$(manext8) install-mh.$(manext8) mh-mts.$(manext8) \
+       post.$(manext8)
+
+# source for man pages
+DIST_MAN = ali.man anno.man ap.man burst.man comp.man conflict.man \
+           dist.man dp.man flist.man fmtdump.man folder.man forw.man \
+           inc.man install-mh.man mark.man mh-alias.man mh-chart.man \
+           mh-draft.man mh-format.man mh-mail.man mh-mts.man mh-profile.man \
+           mh-sequence.man mh-tailor.man nmh.man mhbuild.man mhl.man \
+           mhlist.man mhmail.man mhn.man mhparam.man mhpath.man \
+           mhshow.man mhstore.man msgchk.man msh.man \
+           next.man packf.man pick.man post.man prev.man prompter.man \
+           rcvdist.man rcvpack.man rcvstore.man rcvtty.man refile.man \
+           repl.man rmf.man rmm.man scan.man send.man sendfiles.man show.man \
+           slocal.man sortm.man vmh.man whatnow.man whom.man
+
+# auxiliary files
+AUX = Makefile.in
+
+# all files in this directory included in the distribution
+DIST = $(DIST_HDR) $(DIST_MAN) $(AUX)
+
+# ========= DEFAULT TARGET ==========
+
+all: tmac.h $(MAN1) $(MAN5) $(MAN8)
+
+$(MAN1) $(MAN5) $(MAN8): man.sed
+
+tmac.h: man.sed tmac.h.in
+       $(SED) -f man.sed $(srcdir)/tmac.h.in > $@
+
+# create the sed file for building man pages
+man.sed: Makefile
+       echo 's,%nmhwarning%,THIS FILE HAS BEEN AUTOMATICALLY GENERATED.  DO NOT EDIT.,g' > $@
+       echo 's,%nmhversion%,nmh-$(VERSION),g' >> $@
+       echo 's,%bindir%,$(bindir),g' >> $@
+       echo 's,%etcdir%,$(etcdir),g' >> $@
+       echo 's,%libdir%,$(libdir),g' >> $@
+       echo 's,%mandir%,$(mandir),g' >> $@
+       echo 's,%mailspool%,$(mailspool),g' >> $@
+       echo 's,%sendmailpath%,$(sendmailpath),g' >> $@
+       echo 's,%default_editor%,$(default_editor),g' >> $@
+       echo 's,%default_pager%,$(default_pager),g' >> $@
+       echo 's,%manext1%,$(manext1),g' >> $@
+       echo 's,%manext5%,$(manext5),g' >> $@
+       echo 's,%manext8%,$(manext8),g' >> $@
+       echo '$(POPSED)' >> $@
+
+# ========= INSTALL TARGETS =========
+
+install: install-hdr install-man1 install-man5 install-man8
+
+# install the include file for man pages
+install-hdr:
+       $(top_srcdir)/mkinstalldirs $(etcdir)
+       $(INSTALL_DATA) tmac.h $(etcdir)/tmac.h
+
+# install the man pages in man1
+install-man1:
+       $(top_srcdir)/mkinstalldirs $(mandir)/man$(manext1)
+       for file in $(MAN1); do \
+         $(INSTALL_DATA) $$file $(mandir)/man$(manext1) ; \
+       done
+
+# install the man pages in man5
+install-man5:
+       $(top_srcdir)/mkinstalldirs $(mandir)/man$(manext5)
+       for file in $(MAN5); do \
+         $(INSTALL_DATA) $$file $(mandir)/man$(manext5) ; \
+       done
+
+# install the man pages in man8
+install-man8:
+       $(top_srcdir)/mkinstalldirs $(mandir)/man$(manext8)
+       for file in $(MAN8); do \
+         $(INSTALL_DATA) $$file $(mandir)/man$(manext8) ; \
+       done
+
+# ========= UNINSTALL TARGETS =========
+
+uninstall: uninstall-hdr uninstall-man1 uninstall-man5 uninstall-man8
+
+# uninstall the include file for man pages
+uninstall-hdr:
+       rm -f $(etcdir)/tmac.h
+
+# uninstall the man pages in man1
+uninstall-man1:
+       for file in $(MAN1); do \
+         rm -f $(mandir)/man$(manext1)/$$file; \
+       done
+
+# uninstall the man pages in man5
+uninstall-man5:
+       for file in $(MAN5); do \
+         rm -f $(mandir)/man$(manext5)/$$file; \
+       done
+
+# uninstall the man pages in man8
+uninstall-man8:
+       for file in $(MAN8); do \
+         rm -f $(mandir)/man$(manext8)/$$file; \
+       done
+
+# ========== DEPENDENCIES FOR CLEANUP ==========
+
+mostlyclean:
+       rm -f *~
+
+clean: mostlyclean
+       rm -f man.sed tmac.h *.$(manext1) *.$(manext5) *.$(manext8)
+
+distclean: clean
+       rm -f Makefile
+
+realclean: distclean
+
+superclean: realclean
+
+# ========== DEPENDENCIES FOR MAINTENANCE ==========
+
+subdir = man
+
+Makefile: Makefile.in ../config.status
+       cd .. && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+distdir = ../`cat ../distname`/$(subdir)
+nmhdist: $(DIST)
+       @echo "Copying distribution files in $(subdir)"
+       @for file in $(DIST); do \
+         cp -p $(srcdir)/$$file $(distdir); \
+       done
+
diff --git a/man/ali.man b/man/ali.man
new file mode 100644 (file)
index 0000000..7e0c181
--- /dev/null
@@ -0,0 +1,69 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH ALI %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+ali \- list mail aliases
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+ali
+\%[\-alias\ aliasfile]
+\%[\-list] \%[\-nolist]
+\%[\-normalize]
+.br
+\%[\-nonormalize]
+\%[\-user] \%[\-nouser]
+\%[aliases\ ...]
+.br
+\%[\-version]
+\%[\-help] 
+.in -.5i
+.SH DESCRIPTION
+\fIAli\fR searches the named mail alias files for each of the given
+\fIaliases\fR.  It creates a list of addresses for those \fIaliases\fR,
+and writes that list on standard output.  If no arguments are given,
+\fIali\fR outputs all alias entries.
+
+By default, when an aliases expands to multiple addresses, the addresses
+are separated by commas and printed on as few lines as possible.  If the
+`\-list' option is specified, then when an address expands to multiple
+addresses, each address will appear on a separate line.
+
+The switch `\-user' directs \fIali\fR to perform its processing in
+an inverted fashion: instead of listing the addresses that each given
+alias expands to, \fIali\fR will list the aliases that expand to each
+given address.  If the `\-normalize' switch is given, \fIali\fR will
+try to track down the official hostname of the address.
+
+The files specified by the profile entry \*(lqAliasfile:\*(rq and any
+additional alias files given by the `\-alias aliasfile' switch will be
+read.  Each \fIalias\fR is processed as described in \fImh\-alias\fR\0(5).
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+^/etc/passwd~^List of users
+^/etc/group~^List of groups
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Aliasfile:~^For a default alias file
+.Sa
+mh\-alias(5)
+.De
+`\-alias %etcdir%/MailAliases'
+.Ds
+`\-nolist'
+.Ds
+`\-nonormalize'
+.Ds
+`\-nouser'
+.Co
+None
+.Bu
+The `\-user' option with `\-nonormalize' is not entirely accurate, as it
+does not replace local nicknames for hosts with their official site names.
+.En
diff --git a/man/anno.man b/man/anno.man
new file mode 100644 (file)
index 0000000..5cf5831
--- /dev/null
@@ -0,0 +1,73 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH ANNO %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+anno \- annotate messages
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+anno
+\%[+folder] \%[msgs]
+\%[\-component\ field]
+\%[\-inplace]
+.br
+\%[\-noinplace]
+\%[\-date] \%[\-nodate]
+\%[\-text\ body]
+.br
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fIAnno\fR annotates the specified messages in the named folder using
+the field and body.
+
+Usually, annotation is performed by the commands \fIdist\fR, \fIforw\fR,
+and \fIrepl\fR, if they are given the `\-anno' switch.  This allows you
+to keep track of your distribution of, forwarding of, and replies to
+a message.
+
+By using \fIanno\fR, you can perform arbitrary annotations of your own.
+Each message selected will be annotated with the lines
+
+    field:\ date
+    field:\ body
+
+The `\-nodate' switch inhibits the date annotation, leaving only the
+body annotation.
+
+If a `\-component\ field' is not specified when \fIanno\fR is invoked,
+\fIanno\fR will prompt the user for the name of field for the annotation.
+
+The field specified should be a valid 822-style message field name,
+which means that it should consist of alphanumerics (or dashes) only.
+The body specified is arbitrary text.
+
+Normally \fIanno\fR does the annotation inplace in order to preserve
+any links to the message.  You may change this by using the `\-noinplace'
+switch.
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Current\-Folder:~^To find the default current folder
+.Sa
+dist (1), forw (1), repl (1)
+.De
+`+folder' defaults to the current folder
+.Ds
+`msgs' defaults to cur
+.Ds
+`\-inplace'
+.Ds
+`\-date'
+.Co
+If a folder is given, it will become the current folder.  The first
+message annotated will become the current message.
+.En
diff --git a/man/ap.man b/man/ap.man
new file mode 100644 (file)
index 0000000..d4c24a9
--- /dev/null
@@ -0,0 +1,84 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH AP %manext8% MH.6.8 [%nmhversion%]
+.SH NAME
+ap \- parse addresses 822-style
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+%libdir%/ap
+\%[\-form\ formatfile]
+.br
+\%[\-format\ string]
+\%[\-normalize] \%[\-nonormalize]
+.br
+\%[\-width\ columns]
+addrs\ ...
+.br
+\%[\-version]
+\%[\-help] 
+.in -.5i
+.SH DESCRIPTION
+\fIAp\fR is a program that parses addresses according to the ARPA
+Internet standard.  It also understands many non\-standard formats.
+It is useful for seeing how \fInmh\fR will interpret an address.
+
+The \fIap\fR program treats each argument as one or more addresses, and
+prints those addresses out in the official 822\-format.  Hence, it is
+usually best to enclose each argument in double\-quotes for the shell.
+
+To override the output format used by \fIap\fR, the `\-format\ string' or
+`\-format\ file' switches are used.  This permits individual fields of
+the address to be extracted with ease.  The string is simply a format
+string, and the file is simply a format file.  See \fImh\-format\fR\0(5)
+for the details.
+
+In addition to the standard escapes,
+\fIap\fR also recognizes the following additional escape:
+.sp 1
+.nf
+.ta \w'Escape  'u +\w'Returns  'u
+\fIEscape\fR   \fIReturns\fR   \fIDescription\fR
+error  string  A diagnostic if the parse failed
+.re
+.fi
+
+If the `\-normalize' switch is given, \fIap\fR will try to track down
+the official hostname of the address.
+
+Here is the default format string used by \fIap\fR:
+
+.ti +.5i
+%<{error}%{error}: %{text}%|%(putstr(proper{text}))%>
+
+which says that if an error was detected, print the error, a `:', and
+the address in error.  Otherwise, output the 822\-proper format of
+the address.
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+^%etcdir%/mts.conf~^nmh mts configuration file
+.Pr
+None
+.Sa
+dp(8),
+.br
+\fIStandard for the Format of ARPA Internet Text Messages\fR (RFC\-822)
+.De
+`\-format' defaults as described above
+.Ds
+`\-normalize'
+.Ds
+`\-width' defaults to the width of the terminal
+.Co
+None
+.Bu
+The argument to the `\-format' switch must be interpreted as a single token
+by the shell that invokes \fIap\fR.
+Therefore,
+one must usually place the argument to this switch inside double\-quotes.
+.En
diff --git a/man/burst.man b/man/burst.man
new file mode 100644 (file)
index 0000000..6b41d66
--- /dev/null
@@ -0,0 +1,99 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH BURST %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+burst \- explode digests into messages
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+burst
+\%[+folder] \%[msgs]
+\%[\-inplace] \%[\-noinplace]
+\%[\-quiet]
+.br
+\%[\-noquiet]
+\%[\-verbose] \%[\-noverbose]
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fIBurst\fR considers the specified messages in the named folder to be
+Internet digests, and explodes them in that folder.
+
+If `\-inplace' is given, each digest is replaced by the \*(lqtable
+of contents\*(rq for the digest (the original digest is removed).
+\fIBurst\fR then renumbers all of the messages following the digest in the
+folder to make room for each of the messages contained within the digest.
+These messages are placed immediately after the digest.
+
+If `\-noinplace' is given, each digest is preserved, no table of contents
+is produced, and the messages contained within the digest are placed at
+the end of the folder.  Other messages are not tampered with in any way.
+
+The `\-quiet' switch directs \fIburst\fR to be silent about reporting
+messages that are not in digest format.
+
+The `\-verbose' switch directs \fIburst\fR to tell the user the general
+actions that it is taking to explode the digest.
+
+It turns out that \fIburst\fR works equally well on forwarded messages
+and blind\-carbon\-copies as on Internet digests, provided that the
+former two were generated by \fIforw\fR or \fIsend\fR.
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Current\-Folder:~^To find the default current folder
+.Ps
+^Msg\-Protect:~^To set mode when creating a new message
+.Sa
+\fIProposed Standard for Message Encapsulation\fR (RFC\-934),
+.br
+inc(1), msh(1), pack(1)
+.De
+`+folder' defaults to the current folder
+.Ds
+`msgs' defaults to cur
+.Ds
+`\-noinplace'
+.Ds
+`\-noquiet'
+.Ds
+`\-noverbose'
+.Co
+If a folder is given, it will become the current folder.  If `\-inplace'
+is given, then the first message burst becomes the current message.
+This leaves the context ready for a \fIshow\fR of the table of contents
+of the digest, and a \fInext\fR to see the first message of the digest.
+If `\-noinplace' is given, then the first message extracted from the
+first digest burst becomes the current message.  This leaves the context
+in a similar, but not identical, state to the context achieved when using
+`\-inplace'.
+.Bu
+The \fIburst\fR program enforces a limit on the number of messages which
+may be \fIburst\fR from a single message.  This number is on the order
+of 1000 messages.  There is usually no limit on the number of messages
+which may reside in the folder after the \fIburst\fRing.
+
+Although \fIburst\fR uses a sophisticated algorithm to determine where
+one encapsulated message ends and another begins, not all digestifying
+programs use an encapsulation algorithm.  In degenerate cases, this
+usually results in \fIburst\fR finding an encapsulation boundary
+prematurely and splitting a single encapsulated message into two or
+more messages.  These erroneous digestifying programs should be fixed.
+
+Furthermore, any text which appears after the last encapsulated message
+is not placed in a separate message by \fIburst\fR.  In the case of
+digestified messages, this text is usually an \*(lqEnd of digest\*(rq
+string.  As a result of this possibly un\-friendly behavior on the
+part of \fIburst\fR, note that when the `\-inplace' option is used,
+this trailing information is lost.  In practice, this is not a problem
+since correspondents usually place remarks in text prior to the first
+encapsulated message, and this information is not lost.
+.En
diff --git a/man/comp.man b/man/comp.man
new file mode 100644 (file)
index 0000000..aebcb00
--- /dev/null
@@ -0,0 +1,124 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH COMP %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+comp \- compose a message
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+comp 
+\%[+folder] \%[msg]
+.br
+.br
+\%[\-form\ formfile]
+\%[\-use] \%[\-nouse]
+\%[\-file\ file]
+.br
+\%[\-draftfolder\ +folder]
+\%[\-draftmessage\ msg]
+.br
+\%[\-nodraftfolder]
+\%[\-editor\ editor]
+\%[\-noedit]
+.br
+\%[\-whatnowproc\ program] \%[\-nowhatnowproc]
+.br
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fIComp\fR is used to create a new message to be mailed.  It copies a
+message form to the draft being composed and then invokes an editor on
+the draft (unless `\-noedit' is given, in which case the initial edit
+is suppressed).
+
+The default message form contains the following elements:
+
+.nf
+.in +.5i
+.ne 10
+.eo
+.so %etcdir%/components
+.ec
+.in -.5i
+.fi
+
+If a file named \*(lqcomponents\*(rq exists in the user's nmh directory,
+it will be used instead of this form.  You may specify an alternate
+forms file with the switch `\-form\ formfile'.
+
+You may also start \fIcomp\fR using the contents of an existing message
+as the form.  If you supply either a `+folder' or `msg' argument, that
+message will be used as the message form.  You may not supply both a
+`\-form\ formfile' and a `+folder' or \&`msg' argument.  The line of
+dashes or a blank line must be left between the header and the body of
+the message for the message to be identified properly when it is sent
+(see \fIsend\fR(1)).
+
+The switch `\-use' directs \fIcomp\fR to continue editing an already
+started message.  That is, if a \fIcomp\fR (or \fIdist\fR, \fIrepl\fR,
+or \fIforw\fR\0) is terminated without sending the draft, the draft can
+be edited again via \*(lqcomp\ \-use\*(rq.
+
+The `\-file\ file' switch says to use the named file as the message draft.
+
+If the draft already exists, \fIcomp\fR will ask you as to the disposition
+of the draft.  A reply of \fBquit\fR will abort \fIcomp\fR, leaving
+the draft intact; \fBreplace\fR will replace the existing draft with
+the appropriate form; \fBlist\fR will display the draft; \fBuse\fR will
+use the draft for further composition; and \fBrefile\ +folder\fR will
+file the draft in the given folder, and give you a new draft with the
+appropriate form.  (The `+folder' argument to \fBrefile\fR is required.)
+
+The `\-draftfolder\ +folder' and `\-draftmessage\ msg' switches invoke
+the \fInmh\fR draft folder facility.  This is an advanced (and highly
+useful) feature.  Consult the \fImh-draft\fR(5) man page for more
+information.
+
+The `\-editor\ editor' switch indicates the editor to use for the
+initial edit.  Upon exiting from the editor, \fIcomp\fR will invoke
+the \fIwhatnow\fR program.  See \fIwhatnow\fR\0(1) for a discussion of
+available options.  The invocation of this program can be inhibited
+by using the `\-nowhatnowproc' switch.  (In truth of fact, it is
+the \fIwhatnow\fR program which starts the initial edit.  Hence,
+`\-nowhatnowproc' will prevent any edit from occurring.)
+.Fi
+^%etcdir%/components~^The standard message skeleton
+^or <mh\-dir>/components~^Rather than the standard skeleton
+^$HOME/\&.mh\(ruprofile~^The user profile
+^<mh\-dir>/draft~^The draft file
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Draft\-Folder:~^To find the default draft\-folder
+.Ps
+^Editor:~^To override the default editor
+.Ps
+^Msg\-Protect:~^To set mode when creating a new message (draft)
+.Ps
+^fileproc:~^Program to refile the message
+.Ps
+^whatnowproc:~^Program to ask the \*(lqWhat now?\*(rq questions
+.Sa
+dist(1), forw(1), repl(1), send(1), whatnow(1), mh-profile(5)
+.De
+`+folder' defaults to the current folder
+.Ds
+`msg' defaults to the current message
+.Ds
+`\-nodraftfolder'
+.Ds
+`\-nouse'
+.Co
+None
+.Bu
+If \fIwhatnowproc\fR is \fIwhatnow\fR, then \fIcomp\fR uses a built\-in
+\fIwhatnow\fR, it does not actually run the \fIwhatnow\fR program.
+Hence, if you define your own \fIwhatnowproc\fR, don't call it
+\fIwhatnow\fR since \fIcomp\fR won't run it.
+.En
diff --git a/man/conflict.man b/man/conflict.man
new file mode 100644 (file)
index 0000000..01f7936
--- /dev/null
@@ -0,0 +1,58 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH CONFLICT %manext8% MH.6.8 [%nmhversion%]
+.SH NAME
+conflict \- search for alias/password conflicts
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+%libdir%/conflict
+\%[\-search\ directory]
+.br
+\%[\-mail\ name]
+\%[aliasfiles...]
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fIConflict\fR is a program that checks to see if the interface between
+\fInmh\fR and transport system is in good shape
+
+\fIConflict\fR also checks for maildrops in %mailspool% which do not
+belong to a valid user.  It assumes that no user name will start with
+`.', and thus ignores files in %mailspool% which begin with `.'.  It also
+checks for entries in the \fIgroup\fR\0(5) file which do not belong
+to a valid user, and for users who do not have a valid group number.
+In addition duplicate users and groups are noted.
+
+If the `\-mail\ name' switch is used, then the results will be sent
+to the specified \fIname\fR.  Otherwise, the results are sent to the
+standard output.
+
+The `\-search\ directory' switch can be used to search directories
+other than %mailspool% and to report anomalies in those directories.
+The `\-search\ directory' switch can appear more than one time in an
+invocation to \fIconflict\fR.
+
+\fIConflict\fR should be run under  \fIcron\fR\0(8), or whenever system
+accounting takes place.
+.Fi
+^%etcdir%/mts.conf~^nmh mts configuration file
+^/etc/passwd~^List of users
+^/etc/group~^List of groups
+^%bindir%/mhmail~^Program to send mail
+^%mailspool%/~^Directory of mail drop
+.Pr
+None
+.Sa
+mh\-alias(5)
+.De
+`aliasfiles' defaults to %etcdir%/MailAliases
+.Co
+None
+.En
diff --git a/man/dist.man b/man/dist.man
new file mode 100644 (file)
index 0000000..f0c16aa
--- /dev/null
@@ -0,0 +1,150 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH DIST %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+dist \- redistribute a message to additional addresses
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+dist
+\%[+folder] \%[msg] 
+\%[\-annotate] \%[\-noannotate] 
+.br
+\%[\-inplace] \%[\-noinplace] 
+\%[\-form\ formfile] 
+.br
+\%[\-draftfolder\ +folder] \%[\-draftmessage\ msg]
+.br
+\%[\-nodraftfolder]
+\%[\-editor\ editor] \%[\-noedit]
+.br
+\%[\-whatnowproc\ program] \%[\-nowhatnowproc]
+.br
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fIDist\fR is similar to \fIforw\fR.  It prepares the specified message
+for redistribution to addresses that (presumably) are not on the original
+address list.
+
+The default message form contains the following elements:
+
+.nf
+.in +.5i
+.ne 10
+.eo
+.so %etcdir%/distcomps
+.ec
+.in -.5i
+.fi
+
+If a file named \*(lqdistcomps\*(rq exists in the user's nmh directory, it
+will be used instead of this default form.  You may specify an alternate
+forms file with the switch `\-form\ formfile'.  The form used will be
+prepended to the message being resent.
+
+If the draft already exists, \fIdist\fR will ask you as to the disposition
+of the draft.  A reply of \fBquit\fR will abort \fIdist\fR, leaving the
+draft intact; \fBreplace\fR will replace the existing draft with a blank
+skeleton; and \fBlist\fR will display the draft.
+
+Only those addresses in \*(lqResent\-To:\*(rq, \*(lqResent\-cc:\*(rq,
+and \*(lqResent\-Bcc:\*(rq will be sent.  Also, a
+\*(lqResent\-Fcc:\ folder\*(rq will be honored (see \fIsend\fR\0(1)).
+Note that with \fIdist\fR, the draft should contain only
+\*(lqResent\-xxx:\*(rq fields and no body.  The headers and the body of
+the original message are copied to the draft when the message is sent.
+Use care in constructing the headers for the redistribution.
+
+If the `\-annotate' switch is given, the  message being distributed will
+be annotated with the lines:
+
+     Resent:\ date
+     Resent:\ addrs
+
+where each address list contains as many lines as required.  This
+annotation will be done only if the message is sent directly from
+\fIdist\fR.  If the message is not sent immediately from \fIdist\fR,
+\*(lqcomp \-use\*(rq may be used to re\-edit and send the constructed
+message, but the annotations won't take place.  Normally annotations are
+done inplace in order to preserve any links to the message.  You may use
+the '\-noinplace' switch to change this.
+
+See \fIcomp\fR\0(1) for a description of the `\-editor' and `\-noedit'
+switches.  Note that while in the editor, the message being resent
+is available through a link named \*(lq@\*(rq (assuming the default
+\fIwhatnowproc\fR\0).  In addition, the actual pathname of the message is
+stored in the environment variable \fB$editalt\fR, and the pathname of
+the folder containing the message is stored in the environment variable
+\fB$mhfolder\fR.
+
+The `\-draftfolder\ +folder' and `\-draftmessage\ msg' switches invoke
+the \fInmh\fR draft folder facility.  This is an advanced (and highly
+useful) feature.  Consult the \fImh-draft\fR(5) man page for more
+information.
+
+Upon exiting from the editor, \fIdist\fR will invoke the \fIwhatnow\fR
+program.  See \fIwhatnow\fR\0(1) for a discussion of available
+options.  The invocation of this program can be inhibited by using the
+`\-nowhatnowproc' switch.  (In truth of fact, it is the \fIwhatnow\fR
+program which starts the initial edit.  Hence, `\-nowhatnowproc' will
+prevent any edit from occurring.)
+.Fi
+^%etcdir%/distcomps~^The standard message skeleton
+^or <mh\-dir>/distcomps~^Rather than the standard skeleton
+^$HOME/\&.mh\(ruprofile~^The user profile
+^<mh\-dir>/draft~^The draft file
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Current\-Folder:~^To find the default current folder
+.Ps
+^Draft\-Folder:~^To find the default draft\-folder
+.Ps
+^Editor:~^To override the default editor
+.Ps
+^fileproc:~^Program to refile the message
+.Ps
+^whatnowproc:~^Program to ask the \*(lqWhat now?\*(rq questions
+.Sa
+comp(1), forw(1), repl(1), send(1), whatnow(1)
+.De
+`+folder' defaults to the current folder
+.Ds
+`msg' defaults to cur
+.Ds
+`\-noannotate'
+.Ds
+`\-nodraftfolder'
+.Ds
+`\-inplace'
+.Co
+
+If a folder is given, it will become the current folder.  The message
+distributed will become the current message.
+.Hi
+\fIDist\fR originally used headers of the form \*(lqDistribute\-xxx:\*(rq
+instead of \*(lqResent\-xxx:\*(rq.  In order to conform with the ARPA
+Internet standard, RFC\-822, the \*(lqResent\-xxx:\*(rq form is now used.
+\fIDist\fR will recognize \*(lqDistribute\-xxx:\*(rq type headers and
+automatically convert them to \*(lqResent\-xxx:\*(rq.
+.Bu
+\fIDist\fR does not \fIrigorously\fR check the message being distributed
+for adherence to the transport standard, but \fIpost\fR called by
+\fIsend\fR does.  The \fIpost\fR program will balk (and rightly so) at
+poorly formatted messages, and \fIdist\fR won't correct things for you.
+
+If \fIwhatnowproc\fR is \fIwhatnow\fR, then \fIdist\fR uses a built\-in
+\fIwhatnow\fR, it does not actually run the \fIwhatnow\fR program.
+Hence, if you define your own \fIwhatnowproc\fR, don't call it
+\fIwhatnow\fR since \fIdist\fR won't run it.
+
+If your current working directory is not writable, the link named
+\*(lq@\*(rq is not available.
+.En
diff --git a/man/dp.man b/man/dp.man
new file mode 100644 (file)
index 0000000..f3fe6bf
--- /dev/null
@@ -0,0 +1,72 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH DP %manext8% MH.6.8 [%nmhversion%]
+.SH NAME
+dp \- parse dates 822-style
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+%libdir%/dp
+\%[\-form\ formatfile]
+.br
+\%[\-format\ string]
+\%[\-width\ columns]
+dates\ ...
+.br
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fIDp\fR is a program that parses dates according to the ARPA Internet
+standard.
+It also understands many non\-standard formats,
+such as those produced by TOPS\-20 sites and some UNIX sites using
+\fIctime\fR(3).
+It is useful for seeing how \fInmh\fR will interpret a date.
+
+The \fIdp\fR program treats each argument as a single date,
+and prints the date out in the official 822\-format.
+Hence, it is usually best to enclose each argument in double\-quotes for the
+shell.
+
+To override the output format used by \fIdp\fR,
+the `\-format\ string' or `\-format\ file' switches are used.
+This permits individual fields of the address to be extracted with ease.
+The string is simply a format string and the file is simply a format file.
+See \fImh\-format\fR(5) for the details.
+
+Here is the default format string used by \fIdp\fR:
+
+.nf
+.ti +.5i
+%<(nodate{text})error: %{text}%|%(putstr(pretty{text}))%>
+.fi
+
+which says that if an error was detected, print the error, a `:',
+and the date in error.
+Otherwise, output the 822\-proper format of the date.
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+.Pr
+None
+.Sa
+ap(8)
+.br
+\fIStandard for the Format of ARPA Internet Text Messages\fR (RFC\-822)
+.De
+`\-format' default as described above
+.Ds
+`\-width' default to the width of the terminal
+.Co
+None
+.Bu
+The argument to the `\-format' switch must be interpreted as a single token
+by the shell that invokes \fIdp\fR.
+Therefore,
+one must usually place the argument to this switch inside double\-quotes.
+.En
diff --git a/man/flist.man b/man/flist.man
new file mode 100644 (file)
index 0000000..177ad1d
--- /dev/null
@@ -0,0 +1,146 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH FLIST %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+flist, flists \- list the number of messages in given sequence(s)
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+flist
+\%[+folder1 [+folder2 ...]]
+.br
+\%[\-sequence\ name1 [\-sequence\ name2 ...]]
+.br
+\%[\-all] \%[\-noall]
+\%[\-showzero] \%[\-noshowzero]
+.br
+\%[\-recurse] \%[\-norecurse]
+\%[\-alpha] \%[\-noalpha]
+.br
+\%[\-fast] \%[\-nofast]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+flists is equivalent to flist -all
+.in -.5i
+.SH DESCRIPTION
+This program is used to search a list of folders and display the number
+of messages in these folders that are in a given sequence or set of
+sequences (for example the \*(lqunseen\*(rq sequence). This is especially
+useful if you use some mechanism such as \fIslocal\fP or \fIprocmail\fP
+(typically in conjunction with \fIrcvstore\fP) to pre-sort your mail into
+different folders before you view it.
+
+By default, the command \fIflist\fR will search the current folder for
+the given sequence or sequences (usually \*(lqunseen\*(rq).  If (possibly
+multiple) folders are specified on the command line with `+folder', then
+all these folders are searched for the given sequence(s).  \fIFlist\fR will
+display for each folder searched, the number of messages in each of the
+specified sequences, and the total number of messages.
+
+The option `\-sequence' is used to specify the name of a sequence in
+which to search for.  This option may be used multiple times to specify
+multiple sequences.  If this is not given, then the default is to search
+for all the sequences specified by the "Unseen-Sequence" profile component.
+For more details about sequences, read the mh\-sequence(5) man page.
+
+Typically, \fIflist\fR will produce a line for each sequence, for every
+folder that is searched, even those which do not contain any messages in
+the given sequence.  Specifying `\-noshowzero' will cause \fIflist\fR to
+print only those folder/sequence combinations such the folder has a non-zero
+number of messages in the given specified sequence.
+
+If `\-recurse' is given, then for each folder that is search, \fIflist\fR
+will also recursively descend into those folders to search subfolders
+for the given sequence.
+
+If `\-fast' is given, only the names of the folders searched will
+be displayed, and \fIflist\fR will suppress all other output.  If this
+option is used in conjunction with `\-noshowzero', then \fIflist\fR will
+only print the names of those folders searched that contain messages in
+in at least one of the specified sequences.
+
+.Uh "Multiple Folders"
+If the option `\-all' is given (and no folders are specified with
+`+folder'), then \fIflist\fR will search all the folders in the top
+level of the users nmh directory.  These folders are all preceded by
+the read\-only folders, which occur as \*(lqatr\-cur\-\*(rq entries
+in the user's \fInmh\fR context.
+
+An example of the output of `flist \-all' is:
+
+.nf
+.if t .in +.5i
+.ta \w'/rnd/phyl/Mail/EP 'u +\w'ddd 'u +\w'new out of 'u +\w'ddd 'u
+/work/Mail  has  5 in sequence unseen (private); out of  46
+inbox+      has 10 in sequence unseen          ; out of 153
+junklist    has  0 in sequence unseen          ; out of  63
+postmaster  has  1 in sequence unseen          ; out of   3
+.re
+.if t .in -.5i
+.fi
+
+The \*(lq+\*(rq after inbox indicates that it is the current folder.
+
+The \*(lqprivate\*(rq flag indicates that the given sequence for
+that folder is private.  See the mh\-sequence(5) man page for details
+about private sequences.
+
+If the option `\-all' and `+folder' are both specified, then \fIflist\fR
+will search this folder, and all its first level subfolders for the
+given sequence.  You may specify multiple folders in this way.
+
+If \fIflist\fR is invoked by a name ending with \*(lqs\*(rq
+(e.g., \fIflists\fR\0), then the switch `\-all' is assumed by
+default.
+
+The sorting order for the listing is alphabetical (with '\-alpha'),
+or in a priority order defined by the Flist-Order profile entry (with
+'\-noalpha').  Each item in the Flist-Order is a folder name or a
+folder name pattern that uses * to match zero or more characters.
+Longer matching patterns have precedence over shorter matching patterns.
+For example:
+
+.nf
+Flist-Order: personal petproject mh* * admin *junk
+.fi
+
+This order puts a few interesting folders first, such as those with mail
+addressed to you personally, those about a pet project, and those about
+mh-related things.  It places uninteresting folders at the end, and it
+puts everything else in the middle in alphabetical order.
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^mh-sequences:~^File that contains public sequences
+.Ps
+^Unseen-Sequence:~^The name of the unseen message sequence
+.Ps
+^Flist-Order:~^To sort folders by priority
+.Sa
+folder(1), rcvstore(1), slocal(1), mh\-sequence(5)
+.De
+`-sequence' defaults to Unseen-Sequence profile entry
+.Ds
+`\-showzero'
+.Ds
+`\-noall'
+.Ds
+`\-norecurse'
+.Ds
+`\-noalpha'
+.Ds
+`\-nofast'
+.Co
+If `+folder' is given, it will become the current folder.
+If multiple folders are given, the last one specified will
+become the current folder.
+.En
diff --git a/man/fmtdump.man b/man/fmtdump.man
new file mode 100644 (file)
index 0000000..0046377
--- /dev/null
@@ -0,0 +1,42 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH FMTDUMP %manext8% MH.6.8 [%nmhversion%]
+.SH NAME
+fmtdump \- decode nmh format files
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+%libdir%/fmtdump
+\%[\-form\ formatfile]
+.br
+\%[\-format\ string]
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fIFmtdump\fR is a program that parses an \fInmh\fP format file and
+produces a pseudo-language listing of the how \fInmh\fP interprets
+the file.  This is useful when debugging a complicated format file.
+
+The `\-format\ string' and `\-form\ formatfile' switches may be
+used to specify a format string or format file to read.  The string
+is simply a format string and the file is simply a format file.
+See \fImh-format\fR\|(5) for the details.
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+^%etcdir%/scan.default~^The default format file
+.Pr
+^Path:~^To determine the user's nmh directory
+.Sa
+mh-format(5), mh-sequences(8)
+.Co
+None
+.Bu
+The output may not be useful unless you are familiar
+with the internals of the mh-format subroutines.
+.En
diff --git a/man/folder.man b/man/folder.man
new file mode 100644 (file)
index 0000000..33b918b
--- /dev/null
@@ -0,0 +1,199 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH FOLDER %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+folder, folders \- set/list current folder/message
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+folder
+\%[+folder] \%[msg]
+\%[\-all] \%[\-noall]
+.br
+\%[\-create] \%[\-nocreate]
+\%[\-fast] \%[\-nofast]
+.br
+\%[\-header] \%[\-noheader]
+\%[\-recurse]
+\%[\-norecurse]
+.br
+\%[\-total] \%[\-nototal]
+\%[\-list] \%[\-nolist]
+.br
+\%[\-push] \%[\-pop]
+\%[\-pack] \%[\-nopack]
+\%[\-print]
+.br
+\%[\-verbose]
+\%[\-noverbose]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+folders is equivalent to folder -all
+.in -.5i
+.SH DESCRIPTION
+
+Since the \fInmh\fR environment is the shell, it is easy to lose track
+of the current folder from day to day.  When \fIfolder\fR is given the
+`\-print' switch (the default), \fIfolder\fR will list the current folder,
+the number of messages in it, the range of the messages (low\-high),
+and the current message within the folder, and will flag extra files if
+they exist.  An example of this summary is:
+
+.nf
+.if t .in +.5i
+.ta \w'/rnd/phyl/Mail/EP 'u +\w'has ddd messages 'u +\w'(ddd\-ddd); 'u
+inbox+ has \016 messages       (\0\03\-\022);  cur=\0\05.
+.re
+.if t .in -.5i
+.fi
+
+If a `+folder' and/or `msg' are specified, they will become the current
+folder and/or message.  By comparison, when a `+folder' argument is given,
+this corresponds to a \*(lqcd\*(rq operation in the \fIshell\fR; when no
+`+folder' argument is given, this corresponds roughly to a \*(lqpwd\*(rq
+operation in the \fIshell\fR.
+
+If the specified (or default) folder doesn't exist, the default action
+is to query the user as to whether the folder should be created; when
+standard input is not a tty, the answer to the query is assumed to be
+\*(lqyes\*(rq.
+
+Specifying `\-create' will cause \fIfolder\fP to create new folders
+without any query.  (This is the easy way to create an empty folder for
+use later.)  Specifying `\-nocreate' will cause \fIfolder\fP to exit
+without creating a non-existant folder.
+.\"
+.\" note - this doesn't work at present
+.\" If `\-noprint' is specified, 
+.\" a `+folder' and/or `msg' may still be specified
+.\" to set the current folder and/or message,
+.\" but the folder summary will not be printed.
+.Uh "Multiple Folders"
+Specifying `\-all' will produce a summary line for each top-level folder
+in the user's nmh directory, sorted alphabetically.  (If \fIfolder\fR
+is invoked by a name ending with \*(lqs\*(rq (e.g., \fIfolders\fR\0),
+`\-all' is assumed).  Specifying `\-recurse' with `\-all' will also
+produce a line for all sub-folders.  These folders are all preceded by
+the read\-only folders, which occur as \*(lqatr\-cur\-\*(rq entries in
+the user's \fInmh\fR context.  For example,
+.ne 9
+.nf
+.if t .in +.5i
+.ta \w'/rnd/phyl/Mail/EP 'u +\w'has ddd messages 'u +\w'(ddd\-ddd); 'u
+FOLDER \0\0\0\0\0\0# MESSAGES  RANGE   CUR     (OTHERS)
+/var/work/folder       has \035 messages       (\01\-\035);    cur=23.
+/usr/bugs/Mail has \082 messages       (\01\-108);     cur=82.
+ff     has \0no messages.
+inbox+ has \016 messages       (\03\-\022);    cur=\05.
+mh     has \076 messages       (15\-\076);     cur=70.
+notes  has \0\02 messages      (\01\-\0\02);   cur=\01.
+ucom   has 124 messages        (\01\-124);     cur=\06; (others).
+.ta \w'/rnd/phyl/Mail/EP has 'u
+
+TOTAL = 339 messages in 7 folders
+.re
+.if t .in -.5i
+.fi
+
+The \*(lq+\*(rq after inbox indicates that it is the current folder.
+The \*(lq(others)\*(rq indicates that the folder `ucom' has files which
+aren't messages.  These files may either be sub\-folders, or files that
+don't belong under the nmh file naming scheme.
+
+The header is output if either a `\-all' or a `\-header' switch is
+specified.  It is suppressed by `\-noheader'.
+
+The folder and message totals are output if either a `\-all' or a
+`\-total' switch is specified.  It is suppressed by `\-nototal'.
+
+If `\-fast' is given, only the folder name (or names in the case of
+`\-all') will be listed.  (This is faster because the folders need not
+be read.)
+
+If a `+folder' is given along with the `\-all' switch, \fIfolder\fR will,
+in addition to setting the current folder, list the top\-level subfolders
+for the current folder (with `\-norecurse') or list all sub-folders under
+the current folder recursively (with `\-recurse').  In this case, if a
+`msg' is also supplied, it will become the current message of `+folder'.
+
+The `\-recurse' switch lists each folder recursively, so use of this
+option effectively defeats the speed enhancement of the `\-fast' option,
+since each folder must be searched for subfolders.  Nevertheless, the
+combination of these options is useful.
+
+.Uh "Compacting a Folder"
+The `\-pack' switch will compress the message names in the designated
+folders, removing holes in message numbering.  The `\-verbose' switch
+directs \fIfolder\fR to tell the user the general actions that it is
+taking to compress the folder.
+
+.Uh "The Folder Stack"
+The `\-push' switch directs \fIfolder\fR to push the current folder
+onto the \fIfolder\-stack\fR, and make the `+folder' argument the
+current folder.  If `+folder' is not given, the current folder and the
+top of the \fIfolder\-stack\fR are exchanged.  This corresponds to the
+\*(lqpushd\*(rq operation in the \fICShell\fR.
+
+The `\-pop' switch directs \fIfolder\fR to discard the top of the
+\fIfolder\-stack\fR, after setting the current folder to that value.
+No `+folder' argument is allowed.  This corresponds to the \*(lqpopd\*(rq
+operation in the \fICShell\fR.  The `\-push' switch and the `\-pop' switch
+are mutually exclusive: the last occurrence of either one overrides
+any previous occurrence of the other.  Both of these switches also set
+`\-list' by default.
+
+The `\-list' switch directs \fIfolder\fR to list the contents of
+the \fIfolder\-stack\fR.  No `+folder' argument is allowed.  After a
+successful `\-push' or `\-pop', the `\-list' action is taken, unless a
+`\-nolist' switch follows them on the command line.  This corresponds
+to the \*(lqdirs\*(rq operation in the \fICShell\fR.  The `\-push',
+`\-pop', and `\-list' switches turn off `\-print'.
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Current\-Folder:~^To find the default current folder
+.Ps
+^Folder\-Protect:~^To set mode when creating a new folder
+.Ps
+^Folder\-Stack:~^To determine the folder stack
+.\" .Ps
+.\" ^lsproc:~^Program to list the contents of a folder
+.Sa
+refile(1), mhpath(1)
+.De
+`+folder' defaults to the current folder
+.Ds
+`msg' defaults to none
+.Ds
+`\-nofast'
+.Ds
+`\-noheader'
+.Ds
+`\-nototal'
+.Ds
+`\-nopack'
+.Ds
+`\-norecurse'
+.Ds
+`\-noverbose'
+.Ds
+`\-print' is the default if no `\-list', `\-push', or `\-pop' is specified
+.Ds
+`\-list' is the default if `\-push', or `\-pop' is specified
+.Co
+If `+folder' and/or `msg' are given, they will become the
+current folder and/or message.
+.Bu
+There is no way to restore the default behavior 
+(to ask the user whether to create a non-existant folder)
+after `\-create' or `\-nocreate' is given.
+.En
diff --git a/man/forw.man b/man/forw.man
new file mode 100644 (file)
index 0000000..7d95613
--- /dev/null
@@ -0,0 +1,240 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH FORW %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+forw \- forward messages
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+forw
+\%[+folder] \%[msgs]
+.br
+\%[\-annotate] \%[\-noannotate]
+\%[\-form\ formfile]
+.br
+\%[\-format] \%[\-noformat]
+\%[\-filter\ filterfile]
+.br
+\%[\-inplace] \%[\-noinplace]
+\%[\-mime] \%[\-nomime]
+.br
+\%[\-draftfolder\ +folder] \%[\-draftmessage\ msg]
+.br
+\%[\-nodraftfolder]
+\%[\-editor\ editor] \%[\-noedit]
+.br
+\%[\-whatnowproc\ program] \%[\-nowhatnowproc]
+.br
+\%[\-dashstuffing] \%[\-nodashstuffing]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+forw
+\%[+folder] \%[msgs]
+\%[\-digest\ list] \%[\-issue\ number]
+.br
+\%[\-volume\ number]
+\%[other\ switches\ for\ \fIforw\fR]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fIForw\fR may be used to prepare a message containing other messages.
+
+It constructs the new message from a forms (components) file, with a
+body composed of the message(s) to be forwarded.  An editor is invoked
+as in \fIcomp\fR, and after editing is complete, the user is prompted
+before the message is sent.
+
+The default message form contains the following elements:
+
+.nf
+.in +.5i
+.ne 10
+.eo
+.so %etcdir%/forwcomps
+.ec
+.in -.5i
+.fi
+
+If a file named \*(lqforwcomps\*(rq exists in the user's nmh directory,
+it will be used instead of this default form.  You may also specify an
+alternate forms file with the switch `\-form\ formfile'.
+
+When If the draft already exists, \fIforw\fR will ask you as to the disposition
+of the draft.  A reply of \fBquit\fR will abort \fIforw\fR, leaving the
+draft intact; \fBreplace\fR will replace the existing draft with a blank
+skeleton; and \fBlist\fR will display the draft.
+
+If the `\-annotate' switch is given, each message being forwarded will
+be annotated with the lines
+
+     Forwarded:\ date
+     Forwarded:\ addrs
+
+where each address list contains as many lines as required.  This
+annotation will be done only if the message is sent directly from
+\fIforw\fR.  If the message is not sent immediately from \fIforw\fR,
+\*(lqcomp\ \-use\*(rq may be used to re\-edit and send the constructed
+message, but the annotations won't take place.  Normally annotations
+are done inplace in order to preserve any links to the message.  You may
+change this by using the '\-noinplace' switch.
+
+See \fIcomp\fR\0(1) for a description of the `\-editor' and `\-noedit'
+switches.
+
+Although \fIforw\fR uses a forms (components) file to direct it how to
+construct the beginning of the draft, it uses a message filter file to
+direct it as to how each forwarded message should be formatted in the
+body of the draft.  The filter file for \fIforw\fR should be a standard
+form file for \fImhl\fR, as \fIforw\fR will invoke \fImhl\fR to filter
+(re\-format) the forwarded messages prior to being output to the body
+of the draft.
+
+The switches `\-noformat', `\-format', and `\-filter\ filterfile' specify
+which message filter file to use.
+
+If `\-noformat' is specified (this is the default), then each forwarded
+message is output into the draft exactly as it appears with no \fImhl\fR
+filtering.
+
+If `\-format' is specified, then a default message filter file is used.
+This default message filter should be adequate for most users.
+This default filter \*(lqmhl.forward\*(rq is:
+
+.nf
+.in +.5i
+.ne 10
+.eo
+.so %etcdir%/mhl.forward
+.ec
+.in -.5i
+.fi
+
+If a file named \*(lqmhl.forward\*(rq exists in the user's nmh
+directory, it will be used instead of this form.  You may specify an
+alternate message filter file with the switch `\-filter\ filterfile'.
+
+Each forwarded message is separated with an encapsulation delimiter.
+By default, any dashes in the first column of the forwarded messages
+will be prepended with `\-\ ' so that when received, the message is
+suitable for bursting by \fIburst\fR\0(1).  This follows the Internet
+RFC\-934 guidelines.  You may use the flag `\-nodashstuffing' in order
+to suppress this form of quoting to the forwarded messages.
+
+For users of \fIprompter\fR\0(1), by specifying prompter's `-prepend'
+switch in the \&.mh\(ruprofile file, any commentary text is entered
+before the forwarded messages.  (A major win!)
+
+To use the MIME rules for encapsulation, specify the `\-mime' switch.
+This directs \fIforw\fR to generate an \fImhbuild\fR composition file.
+Note that nmh will not invoke \fImhbuild\fR automatically, unless you
+add this line to your \&.mh\(ruprofile file:
+.sp
+.in +.5i
+automimeproc: 1
+.in -.5i
+.sp
+Otherwise,
+you must specifically give the command
+.sp
+.in +.5i
+What now? mime
+.in -.5i
+.sp
+prior to sending the draft.
+
+The `\-draftfolder\ +folder' and `\-draftmessage\ msg' switches invoke
+the \fInmh\fR draft folder facility.  This is an advanced (and highly
+useful) feature.  Consult the \fImh-draft\fR(5) man page for more
+information.
+
+Upon exiting from the editor, \fIforw\fR will invoke the \fIwhatnow\fR
+program.  See \fIwhatnow\fR\0(1) for a discussion of available
+options.  The invocation of this program can be inhibited by using the
+`\-nowhatnowproc' switch.  (In truth of fact, it is the \fIwhatnow\fR
+program which starts the initial edit.  Hence, `\-nowhatnowproc' will
+prevent any edit from occurring.)
+
+The `\-digest\ list', `\-issue\ number', and `\-volume\ number' switches
+implement a digest facility for \fInmh\fR.  Specifying these switches
+enables and/or overloads the following escapes:
+
+.sp 1
+.nf
+.ta \w'Component  'u +\w'Escape  'u +\w'Returns  'u
+\fIType\fR     \fIEscape\fR    \fIReturns\fR   \fIDescription\fR
+\fIcomponent\fR        \fIdigest\fR    string  Argument to `\-digest'
+\fIfunction\fR \fIcur\fR       integer Argument to `\-volume'
+\fIfunction\fR \fImsg\fR       integer Argument to `\-issue'
+.re
+.fi
+
+Consult the \fBAdvanced Features\fR section of 
+the \fInmh\fR User's Manual for more information on making digests.
+.Fi
+^%etcdir%/forwcomps~^The standard message skeleton
+^or <mh\-dir>/forwcomps~^Rather than the standard skeleton
+^%etcdir%/digestcomps~^The message skeleton if `\-digest' is given
+^or <mh\-dir>/digestcomps~^Rather than the standard skeleton
+^%etcdir%/mhl.forward~^The standard message filter
+^or <mh\-dir>/mhl.forward~^Rather than the standard filter
+^$HOME/\&.mh\(ruprofile~^The user profile
+^<mh\-dir>/draft~^The draft file
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Current\-Folder:~^To find the default current folder
+.Ps
+^Draft\-Folder:~^To find the default draft\-folder
+.Ps
+^Editor:~^To override the default editor
+.Ps
+^Msg\-Protect:~^To set mode when creating a new message (draft)
+.Ps
+^fileproc:~^Program to refile the message
+.Ps
+^mhlproc:~^Program to filter messages being forwarded
+.Ps
+^whatnowproc:~^Program to ask the \*(lqWhat now?\*(rq questions
+.Sa
+\fIProposed Standard for Message Encapsulation\fR (RFC\-934),
+.br
+mhbuild(1), comp(1), repl(1), send(1), whatnow(1), mh\-format(5)
+.De
+`+folder' defaults to the current folder
+`msgs' defaults to cur
+.Ds
+`\-noannotate'
+.Ds
+`\-nodraftfolder'
+.Ds
+`\-noformat'
+.Ds
+`\-inplace'
+.Ds
+`\-dashstuffing'
+.Ds
+`\-nomime'
+.Co
+If a folder is given, it will become the current folder.
+The first message forwarded will become the current message.
+.Bu
+
+If \fIwhatnowproc\fR is \fIwhatnow\fR, then \fIforw\fR uses a built\-in
+\fIwhatnow\fR, it does not actually run the \fIwhatnow\fR program.
+Hence, if you define your own \fIwhatnowproc\fR, don't call it
+\fIwhatnow\fR since \fIforw\fR won't run it.
+
+When \fIforw\fR is told to annotate the messages it forwards, it
+doesn't actually annotate them until the draft is successfully sent.
+If from the \fIwhatnowproc\fR, you \fIpush\fR instead of \fIsend\fR,
+it's possible to confuse \fIforw\fR by re\-ordering the file (e.g.,
+by using `folder\0\-pack') before the message is successfully sent.
+\fIDist\fR and \fIrepl\fR don't have this problem.
+.En
diff --git a/man/inc.man b/man/inc.man
new file mode 100644 (file)
index 0000000..b2e5c7b
--- /dev/null
@@ -0,0 +1,194 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH INC %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+inc \- incorporate new mail
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+inc
+\%[+folder]
+\%[\-audit\ audit\-file] \%[\-noaudit]
+\%[\-changecur]
+.br
+\%[\-nochangecur]
+\%[\-form\ formatfile]
+\%[\-format\ string]
+.br
+\%[\-file\ name]
+\%[\-silent] \%[\-nosilent]
+\%[\-truncate]
+.br
+\%[\-notruncate]
+\%[\-width\ columns]
+%nmhbeginpop%
+\%[\-host\ hostname]
+.br
+\%[\-user\ username]
+\%[\-pack\ file]
+\%[\-nopack]
+.br
+%nmhendpop%
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fIInc\fR incorporates mail from the user's incoming mail drop into
+an \fInmh\fR folder.
+
+You may specify which folder to use with `+folder'.  If no folder
+is specified, then \fIinc\fR will use either the folder given by a
+(non\-empty) \*(lqInbox:\*(rq entry in the user's profile, or the folder
+named \*(lqinbox\*(rq.  If the specified (or default) folder doesn't
+exist, the user will be queried prior to its creation.
+
+When the new messages are incorporated into the folder, they are assigned
+numbers starting with the next highest number for the folder.  As the
+messages are processed, a \fIscan\fR listing of the new mail is produced.
+
+If the user's profile contains a \*(lqMsg\-Protect: nnn\*(rq entry, it
+will be used as the protection on the newly created messages, otherwise
+the \fInmh\fR default of 0644 will be used.  For all subsequent operations
+on these messages, this initially assigned protection will be preserved.
+
+If the switch `\-audit\ audit\-file' is specified (usually as a default
+switch in the profile), then \fIinc\fR will append a header line and a
+line per message to the end of the specified audit\-file with the format:
+
+.nf
+.ti 1i
+\*(<<inc\*(>> date
+.ti 1.5i
+<scan line for first message>
+.ti 1.5i
+<scan line for second message>
+.ti 2.5i
+<etc.>
+.fi
+
+This is useful for keeping track of volume and source of incoming mail.
+Eventually, \fIrepl\fR, \fIforw\fR, \fIcomp\fR, and \fIdist\fR
+may also produce audits to this (or another) file, perhaps with
+\*(lqMessage\-Id:\*(rq information to keep an exact correspondence
+history.  \*(lqAudit\-file\*(rq will be in the user's nmh directory unless
+a full path is specified.
+
+\fIInc\fR will incorporate even improperly formatted messages into the
+user's nmh folder, inserting a blank line prior to the offending component
+and printing a comment identifying the bad message.
+
+In all cases, the user's mail drop will be zeroed, unless the
+`\-notruncate' switch is given.
+
+If the profile entry \*(lqUnseen\-Sequence\*(rq is present and non\-empty,
+then \fIinc\fR will add each of the newly incorporated messages to
+each sequence named by the profile entry.  \fIInc\fR will not zero each
+sequence prior to adding messages.
+
+The interpretation of the `\-form\ formatfile', `\-format\ string', and
+`\-width\ columns' switches is the same as in \fIscan\fR\0(1).
+
+By using the `\-file\ name' switch, one can direct \fIinc\fR to
+incorporate messages from a file other than the user's maildrop.
+Note that the name file will NOT be zeroed, unless the `\-truncate'
+switch is given.
+
+If the environment variable \fB$MAILDROP\fR is set, then \fIinc\fR
+uses it as the location of the user's maildrop instead of the default
+(the `-file\ name' switch still overrides this, however).  If this
+environment variable is not set, then \fIinc\fR will consult the profile
+entry \*(lqMailDrop\*(rq for this information.  If the value found is
+not absolute, then it is interpreted relative to the user's \fInmh\fR
+directory.  If the value is not found, then \fIinc\fR will look in the
+standard system location for the user's maildrop.
+
+The `\-silent' switch directs \fIinc\fR to be quiet and not ask any
+questions at all.  This is useful for putting \fIinc\fR in the background
+and going on to other things.
+%nmhbeginpop%
+
+.Uh "Using POP"
+\fIinc\fR will normally check local mail drops for mail, as given
+above.  But if the option \*(lqpophost:\*(rq is set in the mts
+configuration file \*(lqmts.conf\*(rq, or if the `\-host\ hostname'
+switch is given, then \fIinc\fR will query this POP service host
+for mail to incorporate.
+
+The default is for \fIinc\fR to assume that your account name on
+the POP server is the same as your current username.  To specify
+a different username, use the `\-user\ username' switch.
+
+When using POP, you will normally need to type the password for
+your account on the POP server, in order to retrieve your messages.
+It is possible to automate this process by creating a \*(lq.netrc\*(rq
+file containing your login account information for this POP server.
+For each POP server, this file should have a line of the following
+form.  Replace the words mypopserver, mylogin, and mypassword with
+your own account information.
+
+machine mypopserver login mylogin password mypassword
+
+This \*(lq.netrc\*(rq file should be owned and readable only by
+you.
+
+If \fIinc\fR uses POP, then the `\-pack\ file' switch is considered.
+If given, then \fIinc\fR simply uses the POP to \fIpackf\fR\0(1) the
+user's maildrop from the POP service host to the named file.  This switch
+is provided for those users who prefer to use \fImsh\fR to read their
+maildrops.
+
+For debugging purposes, you may give the switch `\-snoop', which will
+allow you to watch the POP transaction take place between you and the
+POP server.
+%nmhendpop%
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+^%etcdir%/mts.conf~^nmh mts configuration file
+^%mailspool%/$USER~^Location of mail drop
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Alternate\-Mailboxes:~^To determine the user's mailboxes
+.Ps
+^Inbox:~^To determine the inbox, default \*(lqinbox\*(rq
+.Ps
+^Folder\-Protect:~^To set mode when creating a new folder
+.Ps
+^Msg\-Protect:~^To set mode when creating a new message and audit\-file
+.Ps
+^Unseen\-Sequence:~^To name sequences denoting unseen messages
+.Sa
+mhmail(1), scan(1), mh\-mail(5), post(8)
+.De
+`+folder' defaulted by \*(lqInbox\*(rq above
+.Ds
+`\-noaudit'
+.Ds
+`\-changecur'
+.Ds
+`\-format' defaulted as described above
+.Ds
+`\-nosilent'
+.Ds
+`\-truncate' if `\-file\ name' not given, `\-notruncate' otherwise
+.Ds
+`\-width' defaulted to the width of the terminal
+%nmhbeginpop%
+.Ds
+`\-nopack'
+%nmhendpop%
+.Co
+The folder into which messages are being incorporated will become the
+current folder.  The first message incorporated will become the current
+message, unless the `\-nochangecur' option is specified.  This leaves
+the context ready for a \fIshow\fR of the first new message.
+.Bu
+The argument to the `\-format' switch must be interpreted as a single
+token by the shell that invokes \fIinc\fR.  Therefore, one must usually
+place the argument to this switch inside double\-quotes.
+.En
diff --git a/man/install-mh.man b/man/install-mh.man
new file mode 100644 (file)
index 0000000..c00f678
--- /dev/null
@@ -0,0 +1,49 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH INSTALL-MH %manext8% MH.6.8 [%nmhversion%]
+.SH NAME
+install-mh \- initialize the nmh environment
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+%libdir%/install\-mh
+\%[\-auto]
+.br
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fIInstall\-mh\fR is the \fInmh\fR program to create the initial setup
+for a first\-time \fInmh\fR user.  It is typically invoked by other
+\fInmh\fR commands, and should NOT be directly invoked by the user.
+
+When a user runs any \fInmh\fR program for the first time, the program
+will invoke \fIinstall\-mh\fR (with the `\-auto' switch) to query
+the user for the initial \fInmh\fR environment.  The user is asked
+for the name of the directory that will be designated as the user's
+\fInmh\fR directory.  If this directory does not exist, the user is
+asked if it should be created.  Normally, this directory should be
+under the user's home directory, and has the default name of Mail/.
+After \fIinstall\-mh\fR has written the initial \&.mh\(ruprofile for
+the user, control returns to the original \fInmh\fR program.
+
+As with all \fInmh\fR commands, \fIinstall\-mh\fR first consults the
+\fB$HOME\fR environment variable to determine the user's home directory.
+If \fB$HOME\fR is not set, then the \fI/etc/passwd\fR file is consulted.
+
+When creating the users initial \&.mh\(ruprofile, \fIinstall\-mh\fR will
+check for the existence of a global profile %etcdir%/mh.profile.  If
+found, this will be used to initialize the new \&.mh\(ruprofile.
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+^%etcdir%/mh.profile~^Used to initialize user profile
+.Pr
+^Path:~^To set the user's nmh directory
+.Co
+With `\-auto', the current folder is changed to \*(lqinbox\*(rq.
+.En
diff --git a/man/mark.man b/man/mark.man
new file mode 100644 (file)
index 0000000..426ea29
--- /dev/null
@@ -0,0 +1,128 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH MARK %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+mark \- manipulate message sequences
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+mark
+\%[+folder] \%[msgs]
+\%[\-sequence\ name\ ...]
+\%[\-add]
+.br
+\%[\-delete] \%[\-list] 
+\%[\-public] \%[\-nopublic]
+\%[\-zero]
+.br
+\%[\-nozero]
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+The \fImark\fR command manipulates message sequences by adding or deleting
+message numbers from folder\-specific message sequences, or by listing
+those sequences and messages.
+
+A message sequence is a keyword, just like one of the \*(lqreserved\*(rq
+message names, such as \*(lqfirst\*(rq or \*(lqnext\*(rq.  Unlike the
+\*(lqreserved\*(rq message names, which have a fixed semantics on
+a per\-folder basis, the semantics of a message sequence may be
+defined, modified, and removed by the user.  Message sequences are
+folder\-specific, e.g., the sequence name \*(lqseen\*(rq in the context
+of folder \*(lq+inbox\*(rq need not have any relation whatsoever to the
+sequence of the same name in a folder of a different name.
+
+Three action switches direct the operation of \fImark\fR.  These switches
+are mutually exclusive: the last occurrence of any of them overrides
+any previous occurrence of the other two.
+
+The `\-add' switch tells \fImark\fR to add messages to sequences or to
+create a new sequence.  For each sequence named via the `\-sequence\ name'
+argument (which must occur at least once) the messages named via `msgs'
+(which defaults to \*(lqcur\*(rq if no `msgs' are given), are added to the
+sequence.  The messages to be added need not be absent from the sequence.
+If the `\-zero' switch is specified, the sequence will be emptied prior
+to adding the messages.  Hence, `\-add\ \-zero' means that each sequence
+should be initialized to the indicated messages, while `\-add\ \-nozero'
+means that each sequence should be appended to by the indicated messages.
+
+The `\-delete' switch tells \fImark\fR to delete messages from sequences,
+and is the dual of `\-add'.  For each of the named sequences, the
+named messages are removed from the sequence.  These messages need
+not be already present in the sequence.  If the `\-zero' switch is
+specified, then all messages in the folder are added to the sequence
+(first creating the sequence, if necessary) before removing the messages.
+Hence, `\-delete\ \-zero' means that each sequence should contain
+all messages except those indicated, while `\-delete\ \-nozero' means
+that only the indicated messages should be removed from each sequence.
+As expected, the command `mark\0\-sequence\0foo\0\-delete\0all' deletes
+the sequence \*(lqfoo\*(rq from the current folder.
+
+When creating or modifying sequences, you can specify the switches
+`\-public' or `\-nopublic' to force the new or modified sequences to be
+\*(lqpublic\*(rq or \*(lqprivate\*(rq.  The switch `\-public' indicates
+that the sequences should be made \*(lqpublic\*(rq.  These sequences
+will then be readable by all \fInmh\fR users with permission to read the
+relevant folders.  In contrast, the `\-nopublic' switch indicates that the
+sequences should be made \*(lqprivate\*(rq, and will only be accessible by
+you.  If neither of these switches is specified, then existing sequences
+will maintain their current status, and new sequences will default to
+\*(lqpublic\*(rq if you have write permission for the relevant folder.
+Check the mh\-sequence(5) man page for more details about the difference
+between \*(lqpublic\*(rq and \*(lqprivate\*(rq sequences.
+
+The `\-list' switch tells \fImark\fR to list both the sequences defined
+for the folder and the messages associated with those sequences.
+\fIMark\fR will list the name of each sequence given by
+`\-sequence\ name' and the messages associated with that sequence.  If the
+sequence is private, this will also be indicated.  If no sequence is
+specified by the `\-sequence' switch, then all sequences for this folder
+will be listed.  The `\-zero' switch does not affect the operation of
+`\-list'.
+
+The current restrictions on sequences are:
+
+.in +.25i
+The name used to denote a message sequence must consist of an alphabetic
+character followed by zero or more alphanumeric characters, and cannot
+be one of the (reserved) message names \*(lqnew\*(rq, \*(lqfirst\*(rq,
+\*(lqlast\*(rq, \*(lqall\*(rq, \*(lqnext\*(rq, or \*(lqprev\*(rq.
+
+Only a certain number of sequences may be defined for a given folder.
+This number is usually limited to 26 (10 on small systems).
+
+Message ranges with user\-defined sequence names are restricted to the
+form \*(lqname:n\*(rq, \*(lqname:+n\*(rq, or \*(lqname:-n\*(rq, and refer
+to the first or last `n' messages of the sequence `name', respectively.
+Constructs of the form \*(lqname1\-name2\*(rq are forbidden for user
+defined sequences.
+.in -.25i
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Current\-Folder:~^To find the default current folder
+.Sa
+flist(1), pick(1), mh-sequence(5)
+.De
+`+folder' defaults to the current folder
+.Ds
+`\-add' if `\-sequence' is specified, `\-list' otherwise
+.Ds
+`msgs' defaults to cur (or all if `\-list' is specified)
+.Ds
+`\-nozero'
+.Co
+If a folder is given, it will become the current folder.
+.Hh
+Use \*(lqflist\*(rq to find folders with a given sequence, and
+\*(lqpick sequence \-list\*(rq to enumerate those messages in
+the sequence (such as for use by a shell script).
+.En
diff --git a/man/mh-alias.man b/man/mh-alias.man
new file mode 100644 (file)
index 0000000..451ea63
--- /dev/null
@@ -0,0 +1,198 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH MH-ALIAS %manext5% MH.6.8 [%nmhversion%]
+.SH NAME
+mh-alias \- alias file for nmh message system
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+any \fInmh\fR command
+.in -.5i
+.SH DESCRIPTION
+This describes both \fInmh\fR personal alias files and
+the global alias file for nmh mail delivery, the file
+
+       %etcdir%/MailAliases
+
+It does \fBnot\fR describe aliases files used by the message transport system.
+Each line of the alias file has the format:
+
+       alias : address\-group
+.br
+or
+.br
+       alias ; address\-group
+.br
+or
+.br
+       < alias\-file
+.br
+or
+.br
+       ; comment
+.br
+
+where:
+
+       address\-group  :=  address\-list
+.br
+                      |   \*(lq<\*(rq file
+.br
+                      |   \*(lq=\*(rq UNIX\-group
+.br
+                      |   \*(lq+\*(rq UNIX\-group
+.br
+                      |   \*(lq*\*(rq
+
+.br
+       address\-list   :=  address
+.br
+                      |   address\-list, address
+.br
+
+Continuation lines in alias files end with `\\' followed by the newline
+character.
+
+Alias\-file and file are UNIX file names.  UNIX\-group is a group name
+(or number) from \fI/etc/group\fR.  An address is a \*(lqsimple\*(rq
+Internet\-style address.  Througout this file, case is ignored, except
+for alias\-file names.
+
+If the line starts with a `<', then the file named after the `<' is
+read for more alias definitions.  The reading is done recursively, so a
+`<' may occur in the beginning of an alias file with the expected results.
+
+If the address\-group starts with a `<', then the file named after the
+`<' is read and its contents are added to the address\-list for the alias.
+
+If the address\-group starts with an `=', then the file \fI/etc/group\fR
+is consulted for the UNIX\-group named after the `='.  Each login name
+occurring as a member of the group is added to the address\-list for
+the alias.
+
+In contrast, if the address\-group starts with a `+', then the file
+\fI/etc/group\fR is consulted to determine the group\-id of the
+UNIX\-group named after the `+'.  Each login name occurring in the
+\fI/etc/passwd\fR file whose group\-id is indicated by this group is
+added to the address\-list for the alias.
+
+If the address\-group is simply `*', then the file \fI/etc/passwd\fR is
+consulted and all login names with a userid greater than some magic number
+(usually 200) are added to the address\-list for the alias.
+
+In match, a trailing * on an alias will match just about anything
+appropriate.  (See example below.)
+
+An approximation of the way aliases are resolved at posting time is
+(it's not really done this way):
+
+.in +.5i
+1) Build a list of all addresses from the message to be delivered,
+eliminating duplicate addresses.
+
+2) If this draft originated on the local host, then for those addresses in
+the message that have no host specified, perform alias resolution.
+
+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
+from the address list, and add each new address in the address\-group to
+the address list if it is not already on the list.  The alias itself is
+not usually output, rather the address\-group that the alias maps to is
+output instead.  If \*(lqalias\*(rq is terminated with a `;' instead of
+a `:', then both the \*(lqalias\*(rq and the address are output in the
+correct format.  (This makes replies possible since \fInmh\fR aliases
+and personal aliases are unknown to the mail transport system.)
+.in -.5i
+
+Since the alias file is read line by line, forward references work, but
+backward references are not recognized, thus, there is no recursion.
+
+.ne 10
+\fBExample:\fR
+.nf
+.in +.5i
+<%etcdir%/BBoardAliases
+sgroup: fred, fear, freida
+b-people: Blind List: bill, betty;
+fred: frated@UCI
+UNIX\-committee: <unix.aliases
+staff: =staff
+wheels: +wheel
+everyone: *
+news.*: news
+.in -.5i
+.fi
+
+The first line says that more aliases should immediately be read from
+the file \fI%etcdir%/BBoardAliases\fR.  Following this, \*(lqfred\*(rq
+is defined as an alias for \*(lqfrated@UCI\*(rq, and \*(lqsgroup\*(rq
+is defined as an alias for the three names \*(lqfrated@UCI\*(rq,
+\*(rqfear\*(rq, and \*(rqfreida\*(rq.
+.sp
+The alias \*(lqb-people\*(rq is a blind list which includes the addresses
+\*(lqbill\*(rq and \*(lqbetty\*(rq; the message will be delieved to those
+addresses, but the message header will  show only \*(lqBlind List: ;\*(rq
+(not the addresses).
+.sp
+Next, the definition of \*(lqUNIX\-committee\*(rq is given by
+reading the file \fIunix.aliases\fR in the users \fInmh\fR directory,
+\*(lqstaff\*(rq is defined as all users who are listed as members of the
+group \*(lqstaff\*(rq in the \fI/etc/group\fR file, and \*(lqwheels\*(rq
+is defined as all users whose group\-id in \fI/etc/passwd\fR is equivalent
+to the \*(lqwheel\*(rq group.
+.sp
+Finally, \*(lqeveryone\*(rq is defined as all users with a user\-id
+in \fI/etc/passwd\fR greater than 200, and all aliases of the form
+\*(lqnews.<anything>\*(rq are defined to be \*(lqnews\*(rq.
+
+The key thing to understand about aliasing in \fInmh\fR is that aliases
+in \fInmh\fR alias files are expanded into the headers of messages posted.
+This aliasing occurs first, at posting time, without the knowledge of the
+message transport system.  In contrast, once the message transport system
+is given a message to deliver to a list of addresses, for each address
+that appears to be local, a system\-wide alias file is consulted.  These
+aliases are \fBNOT\fR expanded into the headers of messages delivered.
+.Hh
+To use aliasing in \fInmh\fR quickly, do the following:
+
+.in +.5i
+First, in your \fI\&.mh\(ruprofile\fR, choose a name for your alias
+file, say \*(lqaliases\*(rq, and add the line:
+
+.nf
+.in +.5i
+Aliasfile: aliases
+.\" ali: \-alias aliases
+.\" send: \-alias aliases
+.\" whom: \-alias aliases
+.in -.5i
+.fi
+
+Second, create the file \*(lqaliases\*(rq in your \fInmh\fR directory.
+
+Third, start adding aliases to your \*(lqaliases\*(rq file as appropriate.
+.in -.5i
+.Fi
+^%etcdir%/MailAliases~^global nmh alias file
+.Pr
+^Aliasfile:~^For a default alias file
+.Sa
+ali(1), send(1), whom(1), group(5), passwd(5), conflict(8), post(8)
+.De
+None
+.Co
+None
+.Bu
+Although the forward-referencing semantics of \fImh\-alias\fR files
+prevent recursion, the \*(lq<\ alias\-file\*(rq command may defeat this.
+Since the number of file descriptors is finite (and very limited), such
+infinite recursion will terminate with a meaningless diagnostic when
+all the fds are used up.
+.sp
+Forward references do not work correctly inside blind lists.
+.En
diff --git a/man/mh-chart.man b/man/mh-chart.man
new file mode 100644 (file)
index 0000000..9f95ee8
--- /dev/null
@@ -0,0 +1,585 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.if '\*(ZZ'-man' \{\
+.TH MH-CHART %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+mh-chart \- Chart of nmh Commands
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+.\}
+.in 1i
+.na
+.ti .5i
+.ne 7
+ali
+\%[\-alias\ aliasfile]
+\%[\-list] \%[\-nolist]
+\%[\-normalize] \%[\-nonormalize]
+\%[\-user] \%[\-nouser]
+aliases\ ...
+\%[\-version]
+\%[\-help] 
+
+.ti .5i
+.ne 6
+anno
+\%[+folder] \%[msgs]
+\%[\-component\ field]
+\%[\-inplace] \%[\-noinplace]
+\%[\-date] \%[\-nodate]
+\%[\-text\ body]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 6
+burst
+\%[+folder] \%[msgs]
+\%[\-inplace] \%[\-noinplace]
+\%[\-quiet] \%[\-noquiet]
+\%[\-verbose] \%[\-noverbose]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 9
+comp
+\%[+folder] \%[msg]
+\%[\-draftfolder\ +folder] \%[\-draftmessage\ msg] \%[\-nodraftfolder]
+\%[\-editor\ editor] \%[\-noedit]
+\%[\-file\ file]
+\%[\-form\ formfile]
+\%[\-use] \%[\-nouse]
+\%[\-whatnowproc\ program] \%[\-nowhatnowproc]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 9
+dist
+\%[+folder] \%[msg] 
+\%[\-annotate] \%[\-noannotate] 
+\%[\-draftfolder\ +folder] \%[\-draftmessage\ msg] \%[\-nodraftfolder]
+\%[\-editor\ editor] \%[\-noedit]
+\%[\-form\ formfile] 
+\%[\-inplace] \%[\-noinplace] 
+\%[\-whatnowproc\ program] \%[\-nowhatnowproc]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 9
+flist
+\%[+folder1 [+folder2 ...]]
+\%[\-sequence\ name1 [\-sequence\ name2 ...]]
+\%[\-all] \%[\-noall]
+\%[\-showzero] \%[\-noshowzero]
+\%[\-alpha] \%[\-noalpha]
+\%[\-recurse] \%[\-norecurse]
+\%[\-fast] \%[\-nofast]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 4
+%libdir%/fmtdump
+\%[\-form\ formatfile]
+\%[\-format\ string]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 12
+folder
+\%[+folder] \%[msg]
+\%[\-all] \%[\-noall]
+\%[\-fast] \%[\-nofast]
+\%[\-header] \%[\-noheader]
+\%[\-pack] \%[\-nopack]
+\%[\-recurse] \%[\-norecurse]
+\%[\-total] \%[\-nototal]
+\%[\-print] \%[\-noprint]
+\%[\-list] \%[\-nolist]
+\%[\-push] \%[\-pop]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+folders
+
+.ti .5i
+.ne 11
+forw
+\%[+folder] \%[msgs]
+\%[\-annotate] \%[\-noannotate]
+\%[\-draftfolder\ +folder] \%[\-draftmessage\ msg] \%[\-nodraftfolder]
+\%[\-editor\ editor] \%[\-noedit]
+\%[\-filter\ filterfile]
+\%[\-form\ formfile]
+\%[\-format] \%[\-noformat]
+\%[\-inplace] \%[\-noinplace]
+\%[\-mime] \%[\-nomime]
+\%[\-whatnowproc\ program] \%[\-nowhatnowproc]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 5
+forw
+\%[+folder] \%[msgs]
+\%[\-digest\ list] \%[\-issue\ number] \%[\-volume\ number]
+\%[other\ switches\ for\ \fIforw\fR]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 11
+inc
+\%[+folder]
+\%[\-audit\ audit\-file] \%[\-noaudit]
+\%[\-changecur] \%[\-nochangecur]
+\%[\-file\ name]
+\%[\-form\ formatfile]
+\%[\-format\ string]
+\%[\-silent] \%[\-nosilent]
+\%[\-truncate] \%[\-notruncate]
+\%[\-width\ columns]
+%nmhbeginpop%
+\%[\-host\ hostname]
+\%[\-user\ username]
+\%[\-pack\ file]
+\%[\-nopack]
+%nmhendpop%
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 7
+mark
+\%[+folder] \%[msgs]
+\%[\-sequence\ name\ ...]
+\%[\-add] \%[\-delete] \%[\-list] 
+\%[\-public] \%[\-nopublic]
+\%[\-zero] \%[\-nozero]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 9
+mhbuild
+file
+\%[\-list] \%[-nolist]
+\%[\-realsize] \%[\-norealsize]
+\%[\-headers] \%[\-noheaders]
+\%[\-ebcdicsafe] \%[\-noebcdicsafe]
+\%[\-rfc934mode] \%[\-norfc934mode]
+\%[\-verbose] \%[\-noverbose]
+\%[\-check] \%[\-nocheck]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 9
+%libdir%/mhl 
+\%[\-bell] \%[\-nobell]
+\%[\-clear] \%[\-noclear]
+\%[\-folder\ +folder]
+\%[\-form\ formfile]
+\%[\-length\ lines] \%[\-width\ columns] 
+\%[\-moreproc\ program] \%[\-nomoreproc]
+\%[files\ ...]
+\%[\-version]
+\%[\-help] 
+
+.ti .5i
+.ne 8
+mhmail
+\%[
+addrs\ ... 
+\%[\-body\ text]
+\%[\-cc\ addrs\ ...]
+\%[\-from\ addr]
+\%[\-subject subject]]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 16
+mhn
+\%[+folder] \%[msgs] \%[\-file file]
+\%[\-part number]... \%[\-type content]...
+\%[\-show] \%[\-noshow]
+\%[\-list] \%[-nolist]
+\%[\-store] \%[\-nostore]
+\%[\-cache] \%[\-nocache]
+\%[\-headers] \%[\-noheaders]
+\%[\-realsize] \%[\-norealsize]
+\%[\-serialonly] \%[\-noserialonly]
+\%[\-form formfile]
+\%[\-pause] \%[\-nopause]
+\%[\-auto] \%[\-noauto]
+\%[\-rcache policy] \%[\-wcache policy]
+\%[\-check] \%[\-nocheck]
+\%[\-verbose] \%[\-noverbose]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 5
+mhparam
+\%[profile-components]
+\%[\-components] \%[\-nocomponents]
+\%[\-all]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 3
+mhpath
+\%[+folder] \%[msgs]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 5
+msgchk
+\%[\-date] \%[\-nodate]
+\%[\-notify\ all/mail/nomail] \%[\-nonotify\ all/mail/nomail]
+%nmhbeginpop%
+\%[\-host\ hostname]
+\%[\-user\ username]
+%nmhendpop%
+\%[users\ ...]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 5
+msh
+\%[\-prompt\ string]
+\%[\-scan] \%[\-noscan]
+\%[\-topcur] \%[\-notopcur]
+\%[file]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 6
+next 
+\%[+folder]
+\%[\-showproc\ program]
+\%[\-showmimeproc\ program]
+.br
+\%[\-header] \%[\-noheader]
+\%[\-checkmime] \%[\-nocheckmime]
+.br
+\%[switches\ for\ \fIshowproc\fR or\ \fIshowmimeproc\fR]
+.br
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 5
+packf
+\%[+folder] \%[msgs]
+\%[-file\ name]
+\%[-mbox] \%[-mmdf]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 10
+pick
+\%[+folder] \%[msgs]
+\%[\-and\ ...] \%[\-or\ ...] \%[\-not\ ...] \%[\-lbrace\ ...\ \-rbrace]
+\%[\-\|\-component\ pattern]
+\%[\-after\ date] \%[\-before\ date] \%[\-datefield\ field]
+\%[\-sequence\ name\ ...]
+\%[\-public] \%[\-nopublic]
+\%[\-zero] \%[\-nozero]
+\%[\-list] \%[\-nolist]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 6
+prev 
+\%[+folder]
+\%[\-showproc\ program]
+\%[\-showmimeproc\ program]
+.br
+\%[\-header] \%[\-noheader]
+\%[\-checkmime] \%[\-nocheckmime]
+.br
+\%[\-switches\ for\ \fIshowproc\fR or\ \fIshowmimeproc\fR]
+.br
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 7
+prompter
+\%[\-erase\ chr]
+\%[\-kill\ chr]
+\%[\-prepend] \%[\-noprepend]
+\%[\-rapid] \%[\-norapid]
+\%[\-doteof] \%[\-nodoteof]
+file
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 5
+%libdir%/rcvdist
+\%[\-form\ formfile]
+\%[switches\ for\ \fIpostproc\fR]
+address1\ ...
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 4
+%libdir%/rcvpack
+file
+\%[-mbox] \%[-mmdf]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 7
+%libdir%/rcvstore
+\%[+folder]
+\%[\-create] \%[\-nocreate]
+\%[\-unseen] \%[\-nounseen]
+\%[\-sequence\ name\ ...]
+\%[\-public] \%[\-nopublic]
+\%[\-zero] \%[\-nozero]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 10
+%libdir%/rcvtty
+\%[command]
+\%[\-form\ formatfile]
+\%[\-format\ string]
+\%[\-width\ columns]
+\%[\-bell] \%[\-nobell]
+\%[\-newline]
+\%[\-nonewline]
+\%[\-biff]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 9
+refile 
+\%[msgs] 
+\%[\-draft]
+\%[\-link] \%[\-nolink] 
+\%[\-preserve] \%[\-nopreserve]
+\%[\-unlink] \%[\-nounlink]
+\%[\-src\ +folder] 
+\%[\-file\ file] 
++folder ...
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 15
+repl
+\%[+folder] \%[msg]
+\%[\-group] \%[\-nogroup]
+\%[\-annotate] \%[\-noannotate]
+\%[\-cc\ all/to/cc/me] \%[\-nocc\ all/to/cc/me]
+\%[\-draftfolder\ +folder] \%[\-draftmessage\ msg] \%[\-nodraftfolder]
+\%[\-editor\ editor] \%[\-noedit]
+\%[\-fcc\ +folder]
+\%[\-filter\ filterfile]
+\%[\-form\ formfile]
+\%[\-format] \%[\-noformat]
+\%[\-inplace] \%[\-noinplace]
+\%[\-query] \%[\-noquery]
+\%[\-whatnowproc\ program] \%[\-nowhatnowproc]
+\%[\-width\ columns]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 4
+rmf 
+\%[+folder]
+\%[\-interactive] \%[\-nointeractive]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 3
+rmm
+\%[+folder] \%[msgs]
+\%[\-unlink] \%[\-nounlink]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 8
+scan
+\%[+folder] \%[msgs]
+\%[\-clear] \%[\-noclear]
+\%[\-form\ formatfile]
+\%[\-format\ string]
+\%[\-header] \%[\-noheader]
+\%[\-width\ columns]
+\%[\-reverse] \%[\-noreverse]
+\%[\-file filename]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 15
+send
+\%[\-alias\ aliasfile]
+\%[\-draft] 
+\%[\-draftfolder\ +folder] \%[\-draftmessage\ msg] \%[\-nodraftfolder]
+\%[\-filter\ filterfile] \%[\-nofilter]
+\%[\-format] \%[\-noformat]
+\%[\-forward] \%[\-noforward]
+\%[\-mime] \%[\-nomime]
+\%[\-msgid] \%[\-nomsgid]
+\%[\-push] \%[\-nopush]
+\%[\-split\ seconds]
+\%[\-verbose] \%[\-noverbose]
+\%[\-watch] \%[\-nowatch]
+\%[\-width\ columns]
+\%[file\ ...] 
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 7
+show
+\%[+folder] \%[msgs]
+\%[\-showproc\ program]
+.br
+\%[\-showmimeproc\ program]
+\%[\-header] \%[\-noheader]
+.br
+\%[\-draft]
+\%[\-checkmime] \%[\-nocheckmime]
+.br
+\%[switches\ for\ \fIshowproc\fR or \fIshowmimeproc\fR]
+.br
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 5
+sortm
+\%[+folder] \%[msgs]
+\%[\-datefield\ field]
+\%[\-textfield\ field] \%[\-notextfield]
+\%[\-limit days] \%[\-nolimit]
+\%[\-verbose] \%[\-noverbose]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 6
+whatnow
+\%[\-draftfolder\ +folder] \%[\-draftmessage\ msg] \%[\-nodraftfolder]
+\%[\-editor\ editor] \%[\-noedit]
+\%[\-prompt\ string]
+\%[file]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 7
+whom
+\%[\-alias\ aliasfile]
+\%[\-check] \%[\-nocheck]
+\%[\-draft]
+\%[\-draftfolder\ +folder] \%[\-draftmessage\ msg] \%[\-nodraftfolder]
+\%[file]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 7
+%libdir%/ap
+\%[\-form\ formatfile]
+\%[\-format\ string]
+\%[\-normalize] \%[\-nonormalize]
+\%[\-width\ columns]
+addrs\ ...
+\%[\-version]
+\%[\-help] 
+
+.ti .5i
+.ne 5
+%libdir%/conflict
+\%[\-mail\ name]
+\%[\-search\ directory]
+\%[aliasfiles\ ...]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 5
+%libdir%/dp
+\%[\-form\ formatfile]
+\%[\-format\ string]
+\%[\-width\ columns]
+dates\ ...
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 3
+%libdir%/install\-mh
+\%[\-auto]
+
+.ti .5i
+.ne 11
+%libdir%/post 
+\%[\-alias\ aliasfile]
+\%[\-filter\ filterfile] \%[\-nofilter]
+\%[\-format] \%[\-noformat]
+\%[\-mime] \%[\-nomime]
+\%[\-msgid] \%[\-nomsgid]
+\%[\-verbose] \%[\-noverbose]
+\%[\-watch] \%[\-nowatch]
+\%[\-width\ columns]
+file
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+.ne 10
+%libdir%/slocal \%[address\ info\ sender]
+.br
+\%[\-addr\ address]
+\%[\-info\ data]
+\%[\-sender\ sender]
+.br
+\%[\-user\ username]
+\%[\-mailbox\ mbox]
+\%[\-file\ file]
+.br
+\%[\-maildelivery\ deliveryfile]
+\%[\-suppressdup]
+.br
+\%[\-nosuppressdup]
+\%[\-verbose] \%[\-noverbose]
+\%[\-debug]
+.br
+\%[\-version]
+\%[\-help]
+.ad
+.in 0
diff --git a/man/mh-draft.man b/man/mh-draft.man
new file mode 100644 (file)
index 0000000..0b71fd7
--- /dev/null
@@ -0,0 +1,192 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH MH-DRAFT %manext5% MH.6.8 [%nmhversion%]
+.SH NAME
+mh-draft \- draft folder facility for nmh message system
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+any \fInmh\fR command
+.in -.5i
+.SH DESCRIPTION
+
+There are a number of interesting advanced facilities for the composition of
+outgoing mail.
+
+.Uh "The Draft Folder"
+The \fIcomp\fR, \fIdist\fR, \fIforw\fR, and \fIrepl\fR commands have
+two additional switches, `\-draftfolder\ +folder' and `\-draftmessage\ msg'
+which allow you to manipulate the various draft messages you are composing.
+
+If `\-draftfolder\ +folder' is used, these commands are
+directed to construct a draft message in the indicated folder.
+(The \*(lqDraft\-Folder:\*(rq profile entry may be used to declare a
+default draft folder for use with \fIcomp\fR, \fIdist\fR, \fIforw\fR,
+and \fIrepl\fR).
+
+If the swith `\-draftmessage\ msg' is given, the specified draft is used
+to compose the message.  If `\-draftmessage\ msg' is not used, then the
+draft defaults to `new' (create a new draft) unless the user invokes
+\fIcomp\fR with `\-use', in which case the default is `cur'.
+
+Hence, the user may have several message compositions in progress
+simultaneously.  Now, all of the \fInmh\fR tools are available on each of
+the user's message drafts (e.g., \fIshow\fR, \fIscan\fR, \fIpick\fR, and
+so on).  If the folder does not exist, the user is asked if it should be
+created (just like with \fIrefile\fR).  Also, the last draft message
+the user was composing is known as `cur' in the draft folder.
+
+Furthermore, the \fIsend\fR command has these switches as well.  Hence,
+from the shell, the user can send off whatever drafts desired using the
+standard \fInmh\fR `msgs' convention with `\-draftmessage msgs'.  If no
+`msgs' are given, it defaults to `cur'.
+
+In addition, all five programs have a `\-nodraftfolder' switch, which
+undoes the last occurrence of `\-draftfolder\ folder' (useful if the
+latter occurs in the user's \fInmh\fR profile).
+
+If the user does not give the `\-draftfolder\ +folder' switch, then
+all these commands act ``normally''.  Note that the `\-draft' switch
+to \fIsend\fR and \fIshow\fR still refers to the file called `draft'
+in the user's \fInmh\fR directory.  In the interests of economy of
+expression, when using \fIcomp\fR or \fIsend\fR, the user needn't
+prefix the draft `msg' or `msgs' with `\-draftmessage'.  Both of these
+commands accept a `file' or `files' argument, and they will, if given
+`\-draftfolder\ +folder' treat these arguments as `msg' or `msgs'.
+(This may appear to be inconsistent, at first, but it saves a lot of
+typing) Hence,
+
+.ti +.5i
+send -draftf +drafts first
+
+is the same as
+
+.ti +.5i
+send -draftf +drafts -draftm first
+
+To make all this a bit more clear, here are some examples.  Let's assume
+that the following entries are in the \fInmh\fR profile:
+
+.in +.5i
+.nf
+Draft\-Folder: +drafts
+sendf: -draftfolder +drafts
+.fi
+.in -.5i
+
+Furthermore, let's assume that the program \fIsendf\fR is a (symbolic)
+link in the user's \fB$HOME/bin/\fR directory to \fIsend\fR.  Then, any
+of the commands
+
+.in +.5i
+.nf
+comp
+dist
+forw
+repl
+.fi
+.in -.5i
+
+constructs the message draft in the `draft' folder using the `new'
+message number.  Furthermore, they each define `cur' in this folder to
+be that message draft.  If the user were to use the \fIquit\fR option
+at `What now?' level, then later on, if no other draft composition was
+done, the draft could be sent with simply
+
+.ti +.5i
+sendf
+
+Or, if more editing was required, the draft could be edited with
+
+.ti +.5i
+comp -use
+
+Instead, if other drafts had been composed in the meantime, so that this
+message draft was no longer known as `cur' in the `draft' folder, then
+the user could \fIscan\fR the folder to see which message draft in the
+folder should be used for editing or sending.  Clever users could even
+employ a back-quoted \fIpick\fR to do the work:
+
+.ti +.5i
+comp -use `pick +drafts -to nmh-workers`
+
+or
+
+.ti +.5i
+sendf `pick +drafts -to nmh-workers`
+
+Note that in the \fIcomp\fR example, the output from \fIpick\fR must
+resolve to a single message draft (it makes no sense to talk about
+composing two or more drafts with one invocation of \fIcomp\fR).
+In contrast, in the \fIsend\fR example, as many message drafts as desired
+can appear, since \fIsend\fR doesn't mind sending more than one draft
+at a time.
+
+Note that the argument `\-draftfolder\ +folder' is not included in the
+profile entry for \fIsend\fR, since when \fIcomp\fR, et. al., invoke
+\fIsend\fR directly, they supply \fIsend\fR with the UNIX pathname
+of the message draft, and \fBnot\fR a `draftmessage\ msg' argument.
+As far as \fIsend\fR is concerned, a \fIdraft folder\fR is not being used.
+
+It is important to realize that \fInmh\fR treats the draft folder
+like a standard \fInmh\fR folder in nearly all respects.  There are
+two exceptions:
+
+First, under no circumstancs will the `\-draftfolder\ folder' switch cause the
+named folder to become the current folder.
+
+Obviously, if the folder appeared in the context of a standard `+folder'
+argument to an \fInmh\fR program, as in
+
+.ti +.5i
+scan +drafts
+
+it might become the current folder, depending on the context changes of
+the \fInmh\fR program in question.
+
+Second, although conceptually \fIsend\fR deletes the `msgs' named in
+the draft folder, it does not call `delete-prog' to perform the deletion.
+
+.Uh "What Happens if the Draft Exists"
+When the \fIcomp\fR, \fIdist\fR, \fIforw\fR, and \fIrepl\fR commands
+are invoked and the draft you indicated already exists, these programs
+will prompt the user for a reponse directing the program's action.
+The prompt is
+
+.ti +.5i
+Draft ``/home/foobar/nmhbox/draft'' exists (xx bytes).
+.ti +.5i
+Disposition?
+
+The appropriate responses and their meanings are:
+
+.nf
+^replace - deletes the draft and starts afresh
+^list - lists the draft
+^refile - files the draft into a folder and starts afresh
+^quit - leaves the draft intact and exits
+.fi
+
+In addition, if you specified `\-draftfolder\ folder' to the command,
+then one other response will be accepted:
+
+.nf
+^new - finds a new draft,
+.fi
+
+just as if `\-draftmessage\ new' had been given.
+Finally, the \fIcomp\fR command will accept one more response:
+
+.nf
+^use - re-uses the draft
+.fi
+
+just as if `\-use' had been given.
+.Co
+None
+.En
diff --git a/man/mh-format.man b/man/mh-format.man
new file mode 100644 (file)
index 0000000..bc40c88
--- /dev/null
@@ -0,0 +1,482 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH MH-FORMAT %manext5% MH.6.8 [%nmhversion%]
+.SH NAME
+mh-format \- format file for nmh message system
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+some \fInmh\fR commands
+.in -.5i
+.SH DESCRIPTION
+Several \fInmh\fR commands utilize either a \fIformat\fR string or a
+\fIformat\fR file during their execution.  For example, \fIscan\fR\0(1)
+uses a format string which directs it how to generate the scan listing
+for each message; \fIrepl\fR\0(1) uses a format file which directs it
+how to generate the reply to a message, and so on.
+
+Format strings are designed to be efficiently parsed by \fInmh\fR
+which means they are not necessarily simple to write and understand.
+This means that novice, casual, or even advanced users of \fInmh\fR
+should not have to deal with them.
+
+There are a few alternate scan listing formats available
+in %etcdir%/scan.time, %etcdir%/scan.size, and %etcdir%/scan.timely.
+Look in %etcdir% for other \fIscan\fR and \fIrepl\fR format files which
+may have been written at your site.
+
+It suffices to have your local \fInmh\fR expert actually write new format
+commands or modify existing ones.  This manual section explains how to
+do that.  Note: familiarity with the C \fIprintf\fR routine is assumed.
+
+A format string consists of ordinary text, and special multi-character
+\fIescape\fR sequences which begin with `%'.  When specifying a format
+string, the usual C backslash characters are honored: `\\b', `\\f',
+`\\n', `\\r', and `\\t'.  Continuation lines in format files end with
+`\\' followed by the newline character.
+
+.\" TALK ABOUT SYNTAX FIRST, THEN SEMANTICS
+There are three types of \fIescape\fR sequences: header
+\fIcomponents\fR, built-in \fIfunctions\fR, and flow \fIcontrol\fR.
+
+A \fIcomponent\fR escape is specified as `%{\fIcomponent\fR\^}', and
+exists for each header found in the message being processed.  For example
+`%{date}' refers to the \*(lqDate:\*(rq field of the appropriate message.
+All component escapes have a string value.  Normally, component values are
+compressed by converting any control characters (tab and newline included)
+to spaces, then eliding any leading or multiple spaces.  However, commands
+may give different interpretations to some component escapes; be sure
+to refer to each command's manual entry for complete details.
+
+A \fIfunction\fR escape is specified as `%(\fIfunction\fR\^)'.
+All functions are built-in, and most have a string or numeric value.
+
+.ne 12
+.Uh "Control-flow escapes"
+A \fIcontrol\fR escape is one of: `%<', `%?', `%|', or `%>'. 
+These are combined into the conditional execution construct:
+.sp
+.nf
+       %<condition
+               \fIformat text 1\fP
+       %?condition2
+               \fIformat text 2\fP
+       %?condition3
+               \fIformat text 3\fP
+       \.\.\.
+       %|
+               \fIformat text N\fP
+       %>
+.fi
+.sp
+Extra white space is shown here only for clarity.  These
+constructs may be nested without ambiguity.  They form a general
+\fBif\-elseif\-else\-endif\fP block where only one of the \fIformat
+text\fP segments is interpreted.
+
+The `%<' and `%?' control escapes causes a condition to be evaluated.  
+This condition
+may be either a \fIcomponent\fP or a \fIfunction\fP.
+The four constructs have the following syntax:
+.sp 1
+.nf
+       %<{component}
+       %<(function)
+       %?{component}
+       %?(function)
+.fi
+.sp
+These control escapes test whether the function or component value is
+non-zero (for integer-valued escapes), or non-empty (for string-valued
+escapes).
+
+If this test evaulates true, then the format text up to the next
+corresponding control escape (one of `%|', `%?', or `%>') is interpreted
+normally.  Next, all format text (if any) up to the corresponding `%>'
+control escape is skipped.  The `%>' control escape is not interpreted;
+normal interpretation resumes after the `%>' escape.
+
+If the test evaluates false, however, then the format text up to
+the next corresponding control escape (again, one of `%|', `%?', or
+`%>') is skipped, instead of being interpreted.  If the control escape
+encountered was `%?', then the condition associated with that control
+escape is evaluated, and interpretation proceeds after that test as
+described in the previous paragraph.  If the control escape encountered
+was `%|', then the format text up to the corresponding `%>' escape is
+interpreted normally.  As above, the `%>' escape is not interpreted and
+normal interpretation resumes after the `%>' escape.
+
+The `%?' control escape and its following format text is optional, and may
+be included zero or more times.  The `%|' control escape and its following
+format text is also optional, and may be included zero or one times.
+
+.Uh "Function escapes"
+.ne 10
+Most functions expect an argument of a particular type:
+.sp 1
+.nf
+.ta +\w'Argument 'u +\w'An optional component, 'u
+\fIArgument\fR \fIDescription\fR       \fIExample Syntax\fR
+literal        A literal number,       %(\fIfunc\fR 1234)
+       or string       %(\fIfunc\fR text string)
+comp   Any header component    %(\fIfunc\fR\^{\fIin-reply-to\fR\^})
+date   A date component        %(\fIfunc\fR\^{\fIdate\fR\^})
+addr   An address component    %(\fIfunc\fR\^{\fIfrom\fR\^})
+expr   An optional component,  %(\fIfunc\fR\^(\fIfunc2\fR\^))
+       function or control,    %(\fIfunc\fR %<{\fIreply-to\fR\^}%|%{\fIfrom\fR\^}%>)
+       perhaps nested  %(\fIfunc\fR\^(\fIfunc2\fR\^{\fIcomp\fR\^}))
+.re
+.fi
+
+The types \fIdate\fR and \fIaddr\fR have the same syntax as \fIcomp\fR,
+but require that the header component be a date string, or address
+string, respectively.
+
+All arguments except those of type \fIexpr\fR are required.  For the
+\fIexpr\fR argument type, the leading `%' must be omitted for component
+and function escape arguments, and must be present (with a leading space)
+for control escape arguments.
+
+The evaluation of format strings is based on a simple virtual machine
+with an integer register \fInum\fR, and a text string register \fIstr\fR.
+When a function escape is processed, if it accepts an optional \fIexpr\fR
+argument which is not present, it reads the current value of either
+\fInum\fR or \fIstr\fR as appropriate.
+
+.Uh "Return values"
+Component escapes write the value of their message header in \fIstr\fR.
+Function escapes write their return value in \fInum\fR for functions
+returning \fIinteger\fR or \fIboolean\fR values, and in \fIstr\fR for
+functions returning string values.  (The \fIboolean\fR type is a subset
+of integers with usual values 0=false and 1=true.)  Control escapes
+return a \fIboolean\fP value, and set \fInum\fP.
+
+All component escapes, and those function escapes which return an
+\fIinteger\fR or \fIstring\fR value, pass this value back to their caller
+in addition to setting \fIstr\fR or \fInum\fR.  These escapes will print
+out this value unless called as part of an argument to another escape
+sequence.  Escapes which return a \fIboolean\fR value do pass this value
+back to their caller in \fInum\fP, but will never print out the value.
+
+.nf
+.ta \w'Formataddr 'u +\w'Argument 'u +\w'Rboolean 'u
+\fIFunction\fR \fIArgument\fR  \fIReturn\fR    \fIDescription\fR
+msg            integer message number
+cur            integer message is current
+unseen         integer message is unseen
+size           integer size of message
+strlen         integer length of \fIstr\fR
+width          integer output buffer size in bytes
+charleft               integer bytes left in output buffer
+timenow                integer seconds since the UNIX epoch
+me             string  the user's mailbox
+eq     literal boolean \fInum\fR == \fIarg\fR
+ne     literal boolean \fInum\fR != \fIarg\fR
+gt     literal boolean \fInum\fR > \fIarg\fR
+match  literal boolean \fIstr\fR contains \fIarg\fR
+amatch literal boolean \fIstr\fR starts with \fIarg\fR
+plus   literal integer \fIarg\fR plus \fInum\fR
+minus  literal integer \fIarg\fR minus \fInum\fR
+divide literal integer \fInum\fR divided by \fIarg\fR
+modulo literal integer \fInum\fR modulo \fIarg\fR
+num    literal integer Set \fInum\fR to \fIarg\fR
+lit    literal string  Set \fIstr\fR to \fIarg\fR
+getenv         literal string  Set \fIstr\fR to environment value of \fIarg\fR
+profile        literal string  Set \fIstr\fR to profile component \fIarg\fR value
+.\" dat        literal int     return value of dat[arg]
+nonzero        expr    boolean \fInum\fR is non-zero
+zero   expr    boolean \fInum\fR is zero
+null   expr    boolean \fIstr\fR is empty
+nonnull        expr    boolean \fIstr\fR is non-empty
+void   expr            Set \fIstr\fR or \fInum\fR
+comp   comp    string  Set \fIstr\fR to component text
+compval        comp    integer Set \fInum\fR to \*(lq\fBatoi\fR(\fIcomp\fR\^)\*(rq
+.\" compflag   comp    integer Set \fInum\fR to component flags bits (internal)
+.\" decodecomp comp    string  Set \fIstr\fR to RFC-2047 decoded component text
+decode expr    string  decode \fIstr\fR as RFC-2047 component
+trim   expr            trim trailing white-space from \fIstr\fR
+putstr expr            print \fIstr\fR
+putstrf        expr            print \fIstr\fR in a fixed width
+putnum expr            print \fInum\fR
+putnumf        expr            print \fInum\fR in a fixed width
+.\" addtoseq literal    add msg to sequence (LBL option)
+.re    
+.fi
+
+These functions require a date component as an argument:
+.sp 1
+.nf
+.ta \w'Formataddr 'u +\w'Argument 'u +\w'Rboolean 'u
+\fIFunction\fR \fIArgument\fR  \fIReturn\fR    \fIDescription\fR
+sec    date    integer seconds of the minute
+min    date    integer minutes of the hour
+hour   date    integer hours of the day (0-23)
+wday   date    integer day of the week (Sun=0)
+day    date    string  day of the week (abbrev.)
+weekday        date    string  day of the week
+sday   date    integer day of the week known?
+                       (0=implicit,\-1=unknown)
+mday   date    integer day of the month
+yday   date    integer day of the year
+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
+tzone  date    string  timezone string
+szone  date    integer timezone explicit?
+                       (0=implicit,\-1=unknown)
+date2local     date            coerce date to local timezone
+date2gmt       date            coerce date to GMT
+dst    date    integer daylight savings in effect?
+clock  date    integer seconds since the UNIX epoch
+rclock date    integer seconds prior to current time
+tws    date    string  official 822 rendering
+pretty date    string  user-friendly rendering
+nodate date    integer \fIstr\fR not a date string
+.re    
+.fi
+
+.ne 12
+These functions require an address component as an argument.  
+The return value of functions noted with `*' pertain only to
+the first address present in the header component.
+.sp 1
+.nf
+.ta \w'Formataddr 'u +\w'Argument 'u +\w'Rboolean 'u
+\fIFunction\fR \fIArgument\fR  \fIReturn\fR    \fIDescription\fR
+proper addr    string  official 822 rendering
+friendly       addr    string  user-friendly rendering
+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 the user's addresses? (0=no,1=yes)
+host   addr    string  the host domain*
+nohost addr    integer no host was present*
+type   addr    integer host type* (0=local,1=network,
+                       \-1=uucp,2=unknown)
+path   addr    string  any leading host route*
+ingrp  addr    integer address was inside a group*
+gname  addr    string  name of group*
+formataddr     expr            append \fIarg\fR to \fIstr\fR as a
+                       (comma separated) address list
+putaddr        literal         print \fIstr\fR address list with
+                       \fIarg\fR as optional label;
+                       get line width from \fInum\fR
+.re    
+.fi
+
+When escapes are nested, evaluation is done from inner-most to outer-most.
+The outer-most escape must begin with `%'; the inner escapes must not.
+For example,
+
+.ti +.5i
+%<(mymbox{from}) To: %{to}%>
+
+writes the value of the header component \*(lqFrom:\*(rq to \fIstr\fR\^;
+then (\fImymbox\fR\^) reads \fIstr\fR and writes its result to \fInum\fR;
+then the control escape evaluates \fInum\fR.  If \fInum\fR is non-zero,
+the string \*(lqTo: \*(rq is printed followed by the value of the header
+component \*(lqTo:\*(rq.
+
+A minor explanation of (\fImymbox\fR\^{\fIcomp\fR\^}) is in order.
+In general, it checks each of the addresses in the header component
+\*(lq\fIcomp\fR\*(rq against the user's mailbox name and any
+\fIAlternate-Mailboxes\fR.  It returns true if any address matches,
+however, it also returns true if the \*(lq\fIcomp\fR\*(rq header is not
+present in the message.  If needed, the (\fInull\fR\^) function can be
+used to explicitly test for this condition.
+
+When a function or component escape is interpreted and the result will
+be immediately printed, an optional field width can be specified to
+print the field in exactly a given number of characters.  For example, a
+numeric escape like %4(\fIsize\fR\^) will print at most 4 digits of the
+message size; overflow will be indicated by a `?' in the first position
+(like `?234').  A string escape like %4(\fIme\fR\^) will print the first 4
+characters and truncate at the end.  Short fields are padded at the right
+with the fill character (normally, a blank).  If the field width argument
+begins with a leading zero, then the fill character is set to a zero.
+
+As above, the functions (\fIputnumf\fR\^) and (\fIputstrf\fR\^)
+print their result in exactly the number of characters
+specified by their leading field width argument.  For example,
+%06(\fIputnumf\fR\^(\fIsize\fR\^)) will print the message
+size in a field six characters wide filled with leading zeros;
+%14(\fIputstrf\^\fR{\fIfrom\^\fR}) will print the \*(lqFrom:\*(rq header
+component in fourteen characters with trailing spaces added as needed.
+For \fIputstrf\fR, using a negative value for the field width causes
+right-justification of the string within the field, with padding on
+the left up to the field width.  The functions (\fIputnum\fR\^) and
+(\fIputstr\fR\^) print their result in the minimum number of characters
+required, and ignore any leading field width argument.
+
+The available output width is kept in an internal register; any output
+past this width will be truncated.
+
+Comments may be inserted in most places where a function argument is
+not expected.  A comment begins with `%;' and ends with a (non-escaped)
+newline.
+
+With all this in mind,
+here's the default format string for \fIscan\fR.
+It's been divided into several pieces for readability.
+The first part is:
+
+.ti +.5i
+%4(msg)%<(cur)+%| %>%<{replied}\-%?{encrypted}E%| %>
+
+which says that the message number should be printed in four digits,
+if the message is the current message then a `+' else a space should
+be printed, and if a \*(lqReplied:\*(rq field is present then a `\-'
+else if an \*(lqEncrypted:\*(rq field is present then an `E' otherwise
+a space should be printed.  Next:
+
+.ti +.5i
+%02(mon{date})/%02(mday{date})
+
+the month and date are printed in two digits (zero filled) separated by
+a slash.
+Next,
+
+.ti +.5i
+%<{date} %|*>
+
+If a \*(lqDate:\*(rq field was present,
+then a space is printed, otherwise a `*'.
+Next,
+
+.ti +.5i
+%<(mymbox{from})%<{to}To:%14(friendly{to})%>%>
+
+if the message is from me,
+and there is a \*(lqTo:\*(rq header,
+print `To:' followed by a \*(lquser-friendly\*(rq rendering of the 
+first address in the \*(lqTo:\*(rq field.
+Continuing,
+
+.ti +.5i
+%<(zero)%17(friendly{from})%>
+
+if either of the above two tests failed,
+then the \*(lqFrom:\*(rq address is printed
+in a \*(lquser-friendly\*(rq format.
+And finally,
+
+.ti +.5i
+%{subject}%<{body}<<%{body}%>
+
+the subject and initial body (if any) are printed.
+
+For a more complicated example, next consider
+the default \fIreplcomps\fR format file.
+
+.ti +.5i
+%(lit)%(formataddr %<{reply-to}
+
+This clears \fIstr\fR and formats the \*(lqReply-To:\*(rq header 
+if present.  If not present, the else-if clause is executed.
+
+.ti +.5i
+%?{from}%?{sender}%?{return-path}%>)\\
+
+This formats the 
+\*(lqFrom:\*(rq, \*(lqSender:\*(rq and \*(lqReturn-Path:\*(rq
+headers, stopping as soon as one of them is present.  Next:
+
+.ti +.5i
+%<(nonnull)%(void(width))%(putaddr To: )\\n%>\\
+
+If the \fIformataddr\fR result is non-null, it is printed as
+an address (with line folding if needed) in a field \fIwidth\fR
+wide with a leading label of \*(lqTo: \*(rq.
+
+.ti +.5i
+%(lit)%(formataddr{to})%(formataddr{cc})%(formataddr(me))\\
+
+\fIstr\fR is cleared, and the 
+\*(lqTo:\*(rq and \*(lqCc:\*(rq headers, along with the user's
+address 
+(depending on what was specified with
+the \*(lq\-cc\*(rq switch to \fIrepl\fR\^) are formatted.
+
+.ti +.5i
+%<(nonnull)%(void(width))%(putaddr cc: )\\n%>\\
+
+If the result is non-null, it is printed as above with a
+leading label of \*(lqcc: \*(rq.
+
+.ti +.5i
+%<{fcc}Fcc: %{fcc}\\n%>\\
+
+If a \*(lq\-fcc\ folder\*(rq switch was given to \fIrepl\fR
+(see \fIrepl\fR\0(1) for more details about %{\fIfcc\fR\^}),
+an \*(lqFcc:\*(rq header is output.
+
+.ti +.5i
+%<{subject}Subject: Re: %{subject}\\n%>\\
+
+If a subject component was present,
+a suitable reply subject is output.
+
+.nf
+.ti +.5i
+%<{date}In-reply-to: Your message of "\\
+.ti +.5i
+%<(nodate{date})%{date}%|%(pretty{date})%>."%<{message-id}
+.ti +.5i
+             %{message-id}%>\\n%>\\
+.ti +.5i
+\-\-\-\-\-\-\-\-
+.fi
+
+If a date component was present, an \*(lqIn-Reply-To:\*(rq header is
+output with the preface \*(lqYour message of \*(rq.  If the date was
+parseable, it is output in a user-friendly format, otherwise it is
+output as-is.  The message-id is included if present.  As with all
+plain-text, the row of dashes are output as-is.
+
+This last part is a good example for a little more elaboration.
+Here's that part again in pseudo-code:
+.sp 1
+.nf
+.in +.5i
+.ta .5i 1i 1.5i 2i
+if (comp_exists(date))  then
+       print (\*(lqIn-reply-to: Your message of \\\*(lq\*(rq)
+       if (not_date_string(date.value) then
+               print (date.value)
+       else
+               print (pretty(date.value))
+       endif
+       print (\*(lq\\\*(rq\*(rq)
+       if (comp_exists(message-id)) then
+               print (\*(lq\\n\\t\*(rq)
+               print (message-id.value)
+       endif
+       print (\*(lq\\n\*(rq)
+endif
+.re
+.in -.5i
+.fi
+.sp 1
+Although this seems complicated,
+in point of fact,
+this method is flexible enough to extract individual fields and print them in
+any format the user desires.
+.Fi
+None
+.Pr
+None
+.Sa
+scan(1), repl(1), ap(8), dp(8)
+.De
+None
+.Co
+None
+.En
diff --git a/man/mh-mail.man b/man/mh-mail.man
new file mode 100644 (file)
index 0000000..df39b13
--- /dev/null
@@ -0,0 +1,235 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH MH-MAIL %manext5% MH.6.8 [%nmhversion%]
+.SH NAME
+mh-mail \- message format for nmh message system
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+any \fInmh\fR command
+.in -.5i
+.SH DESCRIPTION
+\fInmh\fR processes messages in a particular format.  It should be noted
+that although neither Bell nor Berkeley mailers produce message files
+in the format that \fInmh\fR prefers, \fInmh\fR can read message files in
+that antiquated format.
+
+Each user possesses a mail drop box which initially receives all messages
+processed by \fIpost\fR\0(8).  \fIInc\fR\0(1) will read from that drop
+box and incorporate the new messages found there into the user's own
+mail folders (typically `+inbox').  The mail drop box consists of one
+or more messages.
+
+Messages are expected to consist of lines of text.  Graphics and binary
+data are not handled.  No data compression is accepted.  All text is
+clear ASCII 7-bit data.
+
+The general \*(lqmemo\*(rq framework of RFC\-822 is used.  A message
+consists of a block of information in a rigid format, followed by
+general text with no specified format.  The rigidly formatted first
+part of a message is called the header, and the free-format portion is
+called the body.  The header must always exist, but the body is optional.
+These parts are separated by an empty line, i.e., two consecutive newline
+characters.  Within \fInmh\fR, the header and body may be separated by
+a line consisting of dashes:
+
+.nf
+.in +.5i
+.ne 10
+.eo
+.so %etcdir%/components
+.ec
+.in -.5i
+.fi
+
+The header is composed of one or more header items.  Each header item can
+be viewed as a single logical line of ASCII characters.  If the text of
+a header item extends across several real lines, the continuation lines
+are indicated by leading spaces or tabs.
+
+Each header item is called a component and is composed of a keyword or
+name, along with associated text.  The keyword begins at the left margin,
+may NOT contain spaces or tabs, may not exceed 63 characters (as specified
+by RFC\-822), and is terminated by a colon (`:').  Certain components
+(as identified by their keywords) must follow rigidly defined formats
+in their text portions.
+
+The text for most formatted components (e.g., \*(lqDate:\*(rq and
+\*(lqMessage\-Id:\*(rq) is produced automatically.  The only ones entered
+by the user are address fields such as \*(lqTo:\*(rq, \*(lqcc:\*(rq,
+etc.  Internet addresses are assigned mailbox names and host computer
+specifications.  The rough format is \*(lqlocal@domain\*(rq, such as
+\*(lqMH@UCI\*(rq, or \*(lqMH@UCI\-ICSA.ARPA\*(rq.  Multiple addresses
+are separated by commas.  A missing host/domain is assumed to be the
+local host/domain.
+
+As mentioned above, a blank line (or a line of dashes) signals that all
+following text up to the end of the file is the body.  No formatting is
+expected or enforced within the body.
+
+Following is a list of header components that are considered
+meaningful to various nmh programs.
+
+.in +.5i
+.ti -.5i
+Date:
+.br
+Added by \fIpost\fR\0(8), contains date and time of the message's entry
+into the mail transport system.
+
+.ti -.5i
+From:
+.br
+Added by \fIpost\fR\0(8), contains the address of the author or authors
+(may be more than one if a \*(lqSender:\*(rq field is present).  For a
+standard reply (using \fIrepl\fR), the reply address is constructed by
+checking the following headers (in this order): \*(lqMail-Reply\-To:\*(rq,
+\*(lqReply\-To:\*(rq, \*(lqFrom:\*(rq, \*(lqSender:\*(rq.
+
+.ti -.5i
+Mail\-Reply\-To:
+.br
+For a standard reply (using \fIrepl\fR), the reply address is
+constructed by checking the following headers (in this order):
+\*(lqMail-Reply\-To:\*(rq, \*(lqReply\-To:\*(rq, \*(lqFrom:\*(rq,
+\*(lqSender:\*(rq.
+
+.ti -.5i
+Mail\-Followup\-To:
+.br
+When making a \*(lqgroup\*(rq reply (using \fIrepl\fR -group), any
+addresses in this field will take precedence, and no other reply address
+will be added to the draft.  If this header is not available, then the
+return addresses will be constructed from the \*(lqMail-Reply\-To:\*(rq,
+or \*(lqReply\-To:\*(rq, or \*(lqFrom:\*(rq, along with adding the
+addresses from the headers \*(lqTo:\*(rq, \*(lqcc:\*(rq, as well as
+adding your personal address.
+
+.ti -.5i
+Reply\-To:
+.br
+For a standard reply (using \fIrepl\fR), the reply address is
+constructed by checking the following headers (in this order):
+\*(lqMail-Reply\-To:\*(rq, \*(lqReply\-To:\*(rq, \*(lqFrom:\*(rq,
+\*(lqSender:\*(rq.
+
+.ti -.5i
+Sender:
+.br
+Added by \fIpost\fR\0(8) in the event that the message already has a
+\*(lqFrom:\*(rq line.  This line contains the address of the actual
+sender.
+
+.ti -.5i
+To:
+.br
+Contains addresses of primary recipients.
+
+.ti -.5i
+cc:
+.br
+Contains addresses of secondary recipients.
+
+.ti -.5i
+Bcc:
+.br
+Still more recipients.  However, the \*(lqBcc:\*(rq line is not
+copied onto the message as delivered, so these recipients are not
+listed.  \fInmh\fR uses an encapsulation method for blind copies, see
+\fIsend\fR\0(1).
+
+.ti -.5i
+Fcc:
+.br
+Causes \fIpost\fR\0(8) to copy the message into the specified folder for the
+sender,
+if the message was successfully given to the transport system.
+
+.ti -.5i
+Message\-ID:
+.br
+A unique message identifier added by \fIpost\fR\0(8) if the `\-msgid' flag
+is set.
+
+.ti -.5i
+Subject:
+.br
+Sender's commentary.  It is displayed by \fIscan\fR\0(1).
+
+.ti -.5i
+In\-Reply\-To:
+.br
+A commentary line added by \fIrepl\fR\0(1) when replying to a message.
+
+.ti -.5i
+Resent\-Date:
+.br
+Added when redistributing a message by \fIpost\fR\0(8).
+
+.ti -.5i
+Resent\-From:
+.br
+Added when redistributing a message by \fIpost\fR\0(8).
+
+.ti -.5i
+Resent\-To:
+.br
+New recipients for a message resent by \fIdist\fR\0(1).
+
+.ti -.5i
+Resent\-cc:
+.br
+Still more recipients.
+See \*(lqcc:\*(rq and \*(lqResent\-To:\*(rq.
+
+.ti -.5i
+Resent\-Bcc:
+.br
+Even more recipients.
+See \*(lqBcc:\*(rq and \*(lqResent\-To:\*(rq.
+
+.ti -.5i
+Resent\-Fcc:
+.br
+Copy resent message into a folder.
+See \*(lqFcc:\*(rq and \*(lqResent\-To:\*(rq.
+
+.ti -.5i
+Resent\-Message\-Id:
+.br
+A unique identifier glued on by \fIpost\fR\0(8) if the `\-msgid' flag
+is set.
+See \*(lqMessage\-Id:\*(rq and \*(lqResent\-To:\*(rq.
+
+.ti -.5i
+Resent:
+.br
+Annotation for \fIdist\fR\0(1) under the `\-annotate' option.
+
+.ti -.5i
+Forwarded:
+.br
+Annotation for \fIforw\fR\0(1) under the `\-annotate' option.
+
+.ti -.5i
+Replied:
+.br
+Annotation for \fIrepl\fR\0(1) under the `\-annotate' option.
+.in -.5i
+.sp
+.Fi
+^%mailspool%/$USER~^Location of mail drop
+.Pr
+None
+.Sa
+RFC\-822:\fIStandard for the Format of ARPA Internet Text Messages\fR
+.De
+None
+.Co
+None
+.En
diff --git a/man/mh-mts.man b/man/mh-mts.man
new file mode 100644 (file)
index 0000000..1829ae6
--- /dev/null
@@ -0,0 +1,96 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH MH-MTS %manext8% MH.6.8 [%nmhversion%]
+.SH NAME
+mh-mts \- the nmh interface to the message transport system
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+SendMail
+
+.ti .5i
+Zmailer
+
+.ti .5i
+MMDF (any release)
+
+.ti .5i
+stand\-alone
+.in -.5i
+.SH DESCRIPTION
+THIS IS OUT OF DATE AND NEEDS REWORKING.
+
+\fInmh\fR can use a wide range of message transport systems to deliver
+mail.  Although the \fInmh\fR administrator usually doesn't get to choose
+which MTS to use (since it's already in place), this document briefly
+describes the interfaces.
+
+When communicating with \fISendMail\fR, \fInmh\fR always uses the SMTP to
+post mail.  Depending on the \fInmh\fR configuration, \fISendMail\fR may
+be invoked directly (via a \fIfork\fR and an \fIexec\fR), or \fInmh\fR
+may open a TCP/IP connection to the SMTP server on the localhost.
+
+When communicating with \fIzmailer\fP, the \fISendMail\fP compatibility
+program is required to be installed in /usr/lib.  \fInmh\fP communicates
+with \fIzmailer\fP by using the SMTP.  It does this by invoking the
+\fB/usr/lib/sendmail\fP compatibility program directly, with the
+`\-bs' option.
+
+When communicating with \fIMMDF\fR, normally \fInmh\fR uses the
+\*(lqmm\(ru\*(rq routines to post mail.  However, depending on the
+\fInmh\fR configuration, \fInmh\fR instead may open a TCP/IP connection
+to the SMTP server on the localhost.
+
+If you are running a UNIX system with TCP/IP networking, then it is
+felt that the best interface is achieved by using either \fISendMail\fR
+or \fIMMDF\fR with the SMTP option.  This gives greater flexibility.
+To enable this option you append the /smtp suffix to the mts option
+in the \fInmh\fR configuration.  This yields two primary advantages:
+First, you don't have to know where \fIsubmit\fR or \fISendMail\fR live.
+This means that \fInmh\fR binaries (e.g., \fIpost\fR\0) don't have to have
+this information hard\-coded, or can run different programs altogether;
+and, second, you can post mail with the server on different systems, so
+you don't need either \fIMMDF\fR or \fISendMail\fR on your local host.
+Big win in conserving cycles and disk space.  Since \fInmh\fR supports
+the notion of a server search\-list in this respect, this approach can
+be tolerant of faults.  Be sure to set \*(lqservers:\*(rq as described
+in mh\-tailor(8) if you use this option.
+
+There are four disadvantages to using the SMTP option: First, only UNIX
+systems with TCP/IP are supported.  Second, you need to have an SMTP
+server running somewhere on any network your local host can reach.
+Third, this bypasses any authentication mechanisms in \fIMMDF\fR
+or \fISendMail\fR.  Fourth, the file \fB/etc/hosts\fR is used for
+hostname lookups (although there is an exception file).  In response
+to these disadvantages though: First, there's got to be an SMTP server
+somewhere around if you're in the Internet or have a local network.
+Since the server search\-list is very general, a wide\-range of options
+are possible.  Second, SMTP should be fixed to have authentication
+mechanisms in it, like POP.  Third, \fInmh\fR won't choke on mail to
+hosts whose official names it can't verify, it'll just plug along (and
+besides if you enable the DUMB configuration options, \fInmh\fR
+ignores the hosts file altogether).
+.Fi
+^%etcdir%/mts.conf~^nmh mts configuration file
+.Pr
+None
+.Sa
+\fIMMDF\-II: A Technical Review\fR,
+Proceedings, Usenix Summer '84 Conference
+.br
+\fISENDMAIL \-\- An Internetwork Mail Router\fR
+.br
+mh\-tailor(8), post(8)
+.De
+None
+.Co
+None
+.Bu
+The %etcdir%/mts.conf file ignores the information in the \fIMMDF\-II\fR
+tailoring file.
+.En
diff --git a/man/mh-profile.man b/man/mh-profile.man
new file mode 100644 (file)
index 0000000..e1326ed
--- /dev/null
@@ -0,0 +1,587 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH MH-PROFILE %manext5% MH.6.8 [%nmhversion%]
+.SH NAME
+mh-profile \- user profile customization for nmh message handler
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+\&\fI.mh\(ruprofile\fP
+.in -.5i
+.SH DESCRIPTION
+Each user of \fInmh\fR is expected to have a file named
+\fI\&.mh\(ruprofile\fR in his or her home directory.  This file contains
+a set of user parameters used by some or all of the \fInmh\fR family
+of programs.  Each entry in the file is of the format
+
+    \fIprofile\-component\fR: \fIvalue\fR
+
+If the text of profile entry is long, you may extend it across several
+real lines by indenting the continuation lines with leading spaces
+or tabs.
+
+.Uh "Standard Profile Entries"
+The possible profile components are exemplified below.  The only mandatory
+entry is `Path:'.  The others are optional; some have default values if
+they are not present.  In the notation used below, (profile, default)
+indicates whether the information is kept in the user's \fInmh\fR profile
+or \fInmh\fR context, and indicates what the default value is.
+
+.in +1i
+.ti -1i
+Path: Mail
+.br
+Locates \fInmh\fR transactions in directory \*(lqMail\*(rq.  This is the
+only mandatory profile entry.  (profile, no default)
+
+.ti -1i
+context: context
+.br
+Declares the location of the \fInmh\fR context file.  This is
+overridden by the environment variable \fBMHCONTEXT\fR.
+See the \fBHISTORY\fR section below.
+(profile, default: <nmh\-dir>/context)
+
+.ti -1i
+Current\-Folder:\ inbox
+.br
+Keeps track of the current open folder.
+(context, default: folder specified by \*(lqInbox\*(rq)
+
+.ti -1i
+Inbox: inbox
+.br
+Defines the name of your default inbox.
+(profile, default: inbox)
+
+.ti -1i
+Previous\-Sequence:\ pseq
+.br
+Names the sequence or sequences which should be defined as the `msgs' or
+`msg' argument given to any \fInmh\fR command.  If not present or empty,
+no such sequences are defined.  Otherwise, for each name given, the
+sequence is first zero'd and then each message is added to the sequence.
+Read the mh\-sequence(5) man page for the details about this sequence.
+(profile, no default)
+
+.ti -1i
+Sequence\-Negation:\ not
+.br
+Defines the string which, when prefixed to a sequence name, negates
+that sequence.  Hence, \*(lqnotseen\*(rq means all those messages that
+are not a member of the sequence \*(lqseen\*(rq.  Read the mh\-sequence(5)
+man page for the details.  (profile, no default)
+
+.ti -1i
+Unseen\-Sequence:\ unseen
+.br
+Names the sequence or sequences which should be defined as those
+messages which are unread.  The commands \fIinc\fR, \fIrcvstore\fR,
+\fImhshow\fR, and \fIshow\fR will add or remove messages from these
+sequences when they are incorporated or read.  If not present or
+empty, no such sequences are defined.  Otherwise, each message is
+added to, or removed from, each sequence name given.  Read the
+mh\-sequence(5) man page for the details about this sequence.
+(profile, no default)
+
+.ti -1i
+mh\-sequences:\ \&.mh\(rusequences
+.br
+The name of the file in each folder which defines public sequences.
+To disable the use of public sequences, leave the value portion of this
+entry blank.  (profile, default: \&.mh\(rusequences)
+
+.ti -1i
+atr\-\fIseq\fR\-\fIfolder\fR:\ 172\0178\-181\0212
+.br
+Keeps track of the private sequence called \fIseq\fR in the specified
+folder.  Private sequences are generally used for read\-only folders.
+See the mh\-sequence(5) man page for details about private sequences.
+(context, no default)
+
+.ti -1i
+Editor:\ /usr/bin/vi
+.br
+Defines the editor to be used by the commands \fIcomp\fR\0(1),
+\fIdist\fR\0(1), \fIforw\fR\0(1), and \fIrepl\fR\0(1).  (profile, default:
+%default_editor%)
+
+.ti -1i
+automimeproc:
+.br
+If defined and set to 1, then the \fIwhatnow\fR program will automatically
+invoke the buildmimeproc (discussed below) to process each message as a MIME
+composition draft before it is sent.
+(profile, no default)
+
+.ti -1i
+Msg\-Protect:\ 644
+.br
+An octal number which defines the permission bits for new message files.
+See \fIchmod\fR\0(1) for an explanation of the octal number.
+(profile, default: 0644)
+
+.ti -1i
+Folder\-Protect:\ 700
+.br
+An octal number which defines the permission bits for new folder
+directories.  See \fIchmod\fR\0(1) for an explanation of the octal number.
+(profile, default: 0700)
+
+.ti -1i
+\fIprogram\fR:\ default switches
+.br
+Sets default switches to be used whenever the mh program \fIprogram\fR
+is invoked.  For example, one could override the \fIEditor\fR: profile
+component when replying to messages by adding a component such as:
+.br
+       repl: \-editor /bin/ed
+.br
+(profile, no defaults)
+
+.ti -1i
+\fIlasteditor\fR\-next:\ nexteditor
+.br
+Names \*(lqnexteditor\*(rq to be the default editor after using
+\*(lqlasteditor\*(rq.  This takes effect at \*(lqWhat now?\*(rq prompt
+in \fIcomp\fR, \fIdist\fR, \fIforw\fR, and \fIrepl\fR.  After editing
+the draft with \*(lqlasteditor\*(rq, the default editor is set to be
+\*(lqnexteditor\*(rq.  If the user types \*(lqedit\*(rq without any
+arguments to \*(lqWhat now?\*(rq, then \*(lqnexteditor\*(rq is used.
+(profile, no default)
+
+.ti -1i
+bboards: system
+.br
+Tells \fIbbc\fR which BBoards you are interested in.  (profile, default:
+system)
+
+.ti -1i
+Folder\-Stack: \fIfolders\fR
+.br
+The contents of the folder-stack for the \fIfolder\fR command.
+(context, no default)
+
+.ti -1i
+mhe:
+.br
+If present, tells \fIinc\fR to compose an \fIMHE\fR auditfile in addition
+to its other tasks.  \fIMHE\fR is Brian Reid's \fIEmacs\fR front-end
+for \fInmh\fR.  (profile, no default)
+
+.ti -1i
+Alternate\-Mailboxes: mh@uci\-750a, bug-mh*
+.br
+Tells \fIrepl\fR and \fIscan\fR which addresses are really yours.
+In this way, \fIrepl\fR knows which addresses should be included in the
+reply, and \fIscan\fR knows if the message really originated from you.
+Addresses must be separated by a comma, and the hostnames listed should
+be the \*(lqofficial\*(rq hostnames for the mailboxes you indicate, as
+local nicknames for hosts are not replaced with their official site names.
+For each address, if a host is not given, then that address on any host is
+considered to be you.  In addition, an asterisk (`*') may appear at either
+or both ends of the mailbox and host to indicate wild-card matching.
+(profile, default: your user-id)
+
+.ti -1i
+Aliasfile: aliases other-alias
+.br
+Indicates aliases files for \fIali\fR, \fIwhom\fR, and \fIsend\fR.
+This may be used instead of the `\-alias file' switch.  (profile, no
+default)
+
+.ti -1i
+Draft\-Folder: drafts
+.br
+Indicates a default draft folder for \fIcomp\fR, \fIdist\fR, \fIforw\fR,
+and \fIrepl\fR.  Read the mh\-draft (5) man page for details.
+(profile, no default)
+
+.ti -1i
+digest\-issue\-\fIlist\fR:\ 1
+.br
+Tells \fIforw\fR the last issue of the last volume sent for the digest
+\fIlist\fR.  (context, no default)
+
+.ti -1i
+digest\-volume\-\fIlist\fR:\ 1
+.br
+Tells \fIforw\fR the last volume sent for the digest \fIlist\fR.
+(context, no default)
+
+.ti -1i
+MailDrop: .mail
+.br
+Tells \fIinc\fR your maildrop, if different from the default.  This is
+superseded by the environment variable \fBMAILDROP\fR.  (profile, default:
+%mailspool%/$USER)
+
+.ti -1i
+Signature: RAND MH System (agent: Marshall Rose)
+.br
+Tells \fIsend\fR your mail signature.  This is superseded by the
+environment variable \fBSIGNATURE\fR.  If \fBSIGNATURE\fR is not set and
+this profile entry is not present, the \*(lqgcos\*(rq field of
+the \fI/etc/passwd\fP file will be used; otherwise, on hosts where
+\fInmh\fR was configured with the UCI option, the file $HOME/.signature
+is consulted.  Your signature will be added to the address \fIsend\fP
+puts in the \*(lqFrom:\*(rq header; do not include an address in the
+signature text.  (profile, no default)
+.in -1i
+
+.Uh "Process Profile Entries"
+The following profile elements are used whenever an \fInmh\fR
+program invokes some other program such as \fImore\fR\0(1).  The
+\fI\&.mh\(ruprofile\fR can be used to select alternate programs if the
+user wishes.  The default values are given in the examples.
+
+.in +1i
+.ti -1i
+buildmimeproc: %bindir%/mhbuild
+.br
+This is the program used by \fIwhatnow\fR to process drafts which
+are MIME composition files.
+
+.ti -1i
+fileproc: %bindir%/refile
+.br
+This program is used to refile or link a message to another folder.
+It is used by \fIpost\fR to file a copy of a message into a folder given
+by a \*(lqFcc:\*(rq field.  It is used by the draft folder facility in
+\fIcomp\fR, \fIdist\fR, \fIforw\fR, and \fIrepl\fR to refile a draft
+message into another folder.  It is used to refile a draft message in
+response to the `refile' directive at the \*(lqWhat now?\*(rq prompt.
+
+.ti -1i
+incproc: %bindir%/inc
+.br
+Program called by \fImhmail\fR to incorporate new mail when it
+is invoked with no arguments.
+
+.ti -1i
+installproc: %libdir%/install\-mh
+.br
+This program is called to initialize the environment for
+new users of nmh.
+
+.ti -1i
+lproc: %default_pager%
+.br
+This program is used to list the contents of a message in response
+to the `list' directive at the \*(lqWhat now?\*(rq prompt.  It is
+also used by the draft folder facility in \fIcomp\fR, \fIdist\fR,
+\fIforw\fR, and \fIrepl\fR to display the draft message.
+
+.ti -1i
+mailproc: %bindir%/mhmail
+.br
+This is the program used to automatically mail various messages
+and notifications.  It is used by \fIconflict\fR when using the
+`-mail' option.  It is used by \fIsend\fR to post failure notices.
+It is used to retrieve an external-body with access-type `mail-server'
+(such as when storing the body with \fImhstore\fR).
+
+.ti -1i
+mhlproc: %libdir%/mhl
+.br
+This is the program used to filter messages in various ways.  It
+is used by \fImhshow\fR to filter and display the message headers
+of MIME messages.  When the `-format' or `-filter' option is used
+by \fIforw\fR or \fIrepl\fR, the mhlproc is used to filter the
+message that you are forwarding, or to which you are replying.
+When the `-filter' option is given to \fIsend\fR or \fIpost\fR,
+the mhlproc is used by \fIpost\fR to filter the copy of the message
+that is sent to \*(lqBcc:\*(rq recipients.
+
+.ti -1i
+moreproc: %default_pager%
+.br
+This is the program used by \fImhl\fR to page the \fImhl\fR formatted
+message when displaying to a terminal.  It is also the default
+program used by \fImhshow\fR to display message bodies (or message
+parts) of type text/plain.
+
+.ti -1i
+mshproc: %bindir%/msh
+.br
+Currently not used.
+
+.ti -1i
+packproc: %bindir%/packf
+.br
+Currently not used.
+
+.ti -1i
+postproc: %libdir%/post
+.br
+This is the program used by \fIsend\fR, \fImhmail\fR, \fIrcvdist\fR,
+and \fIviamail\fR (used by the \fIsendfiles\fR shell script) to
+post a message to the mail transport system.  It is also called by
+\fIwhom\fR (called with the switches `-whom' and `-library') to do
+address verification.
+
+.ti -1i
+rmmproc: none
+.br
+This is the program used by \fIrmm\fR and \fIrefile\fR to delete
+a message from a folder.
+
+.ti -1i
+rmfproc: %bindir%/rmf
+.br
+Currently not used.
+
+.ti -1i
+sendproc: %bindir%/send
+.br
+This is the program to use by \fIwhatnow\fR to actually
+send the message
+
+.ti -1i
+showmimeproc: %bindir%/mhshow
+.br
+This is the program used by \fIshow\fR to process and display
+non-text (MIME) messages.
+
+.ti -1i
+showproc: %libdir%/mhl
+.br
+This is the program used by \fIshow\fR to filter and display text
+(non-MIME) messages.
+
+.ti -1i
+whatnowproc: %bindir%/whatnow
+.br
+This is the program invoked by \fIcomp\fR, \fIforw\fR, \fIdist\fR, and
+\fIrepl\fR to query about the disposition of a composed draft message.
+
+.ti -1i
+whomproc: %bindir%/whom
+.br
+This is the program used by \fIwhatnow\fR to determine to whom a
+message would be sent.
+
+.Uh "Environment Variables"
+The operation of nmh and its commands it also controlled by the
+presence of certain environment variables.
+
+Many of these environment variables are used internally by the
+\*(lqWhat now?\*(rq interface.  It's amazing all the information
+that has to get passed via environment variables to make the
+\*(lqWhat now?\*(rq interface look squeaky clean to the \fInmh\fR
+user, isn't it?  The reason for all this is that the \fInmh\fR user
+can select \fIany\fR program as the \fIwhatnowproc\fR, including
+one of the standard shells.  As a result, it's not possible to pass
+information via an argument list.
+
+If the WHATNOW option was set during \fInmh\fR configuration, and
+if this environment variable is set, then if the commands \fIrefile\fR,
+\fIsend\fR, \fIshow\fR, or \fIwhom\fR are not given any `msgs'
+arguments, then they will default to using the file indicated by
+\fBmhdraft\fR.  This is useful for getting the default behavior
+supplied by the default \fIwhatnowproc\fR.
+
+.in +.5i
+.ti -.5i
+\fBMH\fR\0: With this environment variable, you can specify a profile
+other than \fI\&.mh\(ruprofile\fR to be read by the \fInmh\fR programs
+that you invoke.  If the value of \fBMH\fR is not absolute, (i.e., does
+not begin with a \fB/\fR\0), it will be presumed to start from the current
+working directory.  This is one of the very few exceptions in \fInmh\fR
+where non-absolute pathnames are not considered relative to the user's
+\fInmh\fR directory.
+
+.ti -.5i
+\fBMHCONTEXT\fR\0: With this environment variable, you can specify a
+context other than the normal context file (as specified in
+the \fInmh\fR profile).  As always, unless the value of \fBMHCONTEXT\fR
+is absolute, it will be presumed to start from your \fInmh\fR directory.
+
+.ti -.5i
+\fBMM_CHARSET\fR\0: With this environment variable, you can specify
+the native character set you are using.  You must be able to display
+this character set on your terminal.
+
+This variable is checked to see if a RFC-2047 header field should be
+decoded (in \fIinc\fR, \fIscan\fR, \fImhl\fR).  This variable is
+checked by \fIshow\fR to see if the showproc or showmimeproc should
+be called, since showmimeproc will be called if a text message uses
+a character set that doesn't match MM_CHARSET.  This variable is
+checked by \fImhshow\fR for matches against the charset parameter
+of text contents to decide it the text content can be displayed
+without modifications to your terminal.  This variable is checked by
+\fImhbuild\fR to decide what character set to specify in the charset
+parameter of text contents containing 8bit characters.
+
+When decoding text in such an alternate character set, \fInmh\fR
+must be able to determine which characters are alphabetic, which
+are control characters, etc.  For many operating systems, this
+will require enabling the support for locales (such as setting
+the environment variable LC_CTYPE to iso_8859_1).
+
+.ti -.5i
+\fBMAILDROP\fR\0: tells \fIinc\fR the default maildrop
+.br
+This supersedes the \*(lqMailDrop:\*(rq profile entry.
+
+.ti -.5i
+\fBSIGNATURE\fR\0: tells \fIsend\fR and \fIpost\fR your mail signature
+.br
+This supersedes the \*(lqSignature:\*(rq profile entry.
+
+.ti -.5i
+\fBHOME\fR\0: tells all \fInmh\fR programs your home directory
+
+.ti -.5i
+\fBSHELL\fR\0: tells \fIbbl\fR the default shell to run
+
+.ti -.5i
+\fBTERM\fR\0: tells \fInmh\fR your terminal type
+.br
+The environment variable \fBTERMCAP\fR is also consulted.  In particular,
+these tell \fIscan\fR and \fImhl\fR how to clear your terminal, and how
+many columns wide your terminal is.  They also tell \fImhl\fR how many
+lines long your terminal screen is.
+
+.ti -.5i
+\fBeditalt\fR\0: the alternate message
+.br
+This is set by \fIdist\fR and \fIrepl\fR during edit sessions so you can
+peruse the message being distributed or replied to.  The message is also
+available through a link called \*(lq@\*(rq in the current directory if
+your current working directory and the folder the message lives in are
+on the same UNIX filesystem.
+
+.ti -.5i
+\fBmhdraft\fR\0: the path to the working draft
+.br
+This is set by \fIcomp\fR, \fIdist\fR, \fIforw\fR, and \fIrepl\fR
+to tell the \fIwhatnowproc\fR which file to ask \*(lqWhat now?\*(rq
+questions about.
+
+.ti -.5i
+\fBmhfolder\fR\0:
+.br
+This is set by \fIdist\fR, \fIforw\fR, and \fIrepl\fR,
+if appropriate.
+
+.ti -.5i
+\fBmhaltmsg\fR\0:
+.br
+\fIdist\fR and \fIrepl\fR set \fBmhaltmsg\fR to tell the
+\fIwhatnowproc\fR about an alternate message associated with the
+draft (the message being distributed or replied to).
+
+.ti -.5i
+\fBmhdist\fR\0:
+.br
+\fIdist\fR sets \fBmhdist\fR to tell the \fIwhatnowproc\fR that
+message re-distribution is occurring.
+
+.ti -.5i
+\fBmheditor\fR\0:
+.br
+This is set to tell the \fIwhatnowproc\fR the user's choice of
+editor (unless overridden by `\-noedit').
+
+.ti -.5i
+\fBmhuse\fR\0:
+.br
+This may be set by \fIcomp\fR.
+
+.ti -.5i
+\fBmhmessages\fR\0:
+.br
+This is set by \fIdist\fR, \fIforw\fR, and \fIrepl\fR if annotations
+are to occur.
+
+.ti -.5i
+\fBmhannotate\fR\0:
+.br
+This is set by \fIdist\fR, \fIforw\fR, and \fIrepl\fR if annotations
+are to occur.
+
+.ti -.5i
+\fBmhinplace\fR\0:
+.br
+This is set by \fIdist\fR, \fIforw\fR, and \fIrepl\fR if annotations
+are to occur.
+
+.ti -.5i
+\fBmhfolder\fR\0: the folder containing the alternate message
+.br
+This is set by \fIdist\fR and \fIrepl\fR during edit sessions so you
+can peruse other messages in the current folder besides the one being
+distributed or replied to.  The environment variable \fBmhfolder\fR is
+also set by \fIshow\fR, \fIprev\fR, and \fInext\fR for use by \fImhl\fR.
+.in -.5i
+
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+^or $MH~^Rather than the standard profile
+^<mh\-dir>/context~^The user context
+^or $MHCONTEXT~^Rather than the standard context
+^<folder>/\&.mh\(rusequences~^Public sequences for <folder>
+.Pr
+All
+.Sa
+mh(1), environ(5), mh-sequence(5)
+.De
+None
+.Co
+All
+.Hi
+The \fI\&.mh\(ruprofile\fR contains only static information, which
+\fInmh\fR programs will \fBNOT\fR update.  Changes in context are
+made to the \fIcontext\fR file kept in the users nmh \fIdirectory\fR.
+This includes, but is not limited to: the \*(lqCurrent\-Folder\*(rq entry
+and all private sequence information.  Public sequence information is
+kept in each folder in the file determined by the \*(lqmh\-sequences\*(rq
+profile entry (default is \fI\&.mh\(rusequences\fR).
+
+The \fI\&.mh\(ruprofile\fR may override the path of the \fIcontext\fR
+file, by specifying a \*(lqcontext\*(rq entry (this must be in
+lower-case).  If the entry is not absolute (does not start with a
+\fB/\fR\0), then it is interpreted relative to the user's \fInmh\fR
+directory.  As a result, you can actually have more than one set of
+private sequences by using different context files.
+.Bu
+The shell quoting conventions are not available in the \&.mh\(ruprofile.
+Each token is separated by whitespace.
+
+There is some question as to what kind of arguments should be placed
+in the profile as options.  In order to provide a clear answer, recall
+command line semantics of all \fInmh\fR programs: conflicting switches
+(e.g., `\-header and `\-noheader') may occur more than one time on the
+command line, with the last switch taking effect.  Other arguments, such
+as message sequences, filenames and folders, are always remembered on
+the invocation line and are not superseded by following arguments of
+the same type.  Hence, it is safe to place only switches (and their
+arguments) in the profile.
+
+If one finds that an \fInmh\fR program is being invoked again and again
+with the same arguments, and those arguments aren't switches, then there
+are a few possible solutions to this problem.  The first is to create a
+(soft) link in your \fI$HOME/bin\fR directory to the \fInmh\fR program
+of your choice.  By giving this link a different name, you can create
+a new entry in your profile and use an alternate set of defaults for
+the \fInmh\fR command.  Similarly, you could create a small shell script
+which called the \fInmh\fR program of your choice with an alternate set
+of invocation line switches (using links and an alternate profile entry
+is preferable to this solution).
+
+Finally, the \fIcsh\fR user could create an alias for the command of the form:
+
+.ti +.5i
+alias cmd 'cmd arg1 arg2 ...'
+
+In this way, the user can avoid lengthy type-in to the shell, and still
+give \fInmh\fR commands safely.  (Recall that some \fInmh\fR commands
+invoke others, and that in all cases, the profile is read, meaning that
+aliases are disregarded beyond an initial command invocation)
+.En
diff --git a/man/mh-sequence.man b/man/mh-sequence.man
new file mode 100644 (file)
index 0000000..7d2c72b
--- /dev/null
@@ -0,0 +1,209 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH MH-SEQUENCE %manext5% MH.6.8 [%nmhversion%]
+.SH NAME
+mh-sequence \- sequence specification for nmh message system
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+most \fInmh\fR commands
+.in -.5i
+.SH DESCRIPTION
+A sequence (or sequence set) is a symbolic name representing a
+message or collection of messages.  \fInmh\fP has several internally
+defined sequences, as well as allowing users to define their own
+sequences.
+
+.Uh "Message Specification and Pre\-Defined Message Sequences"
+Most \fInmh\fP commands accept a `msg' or `msgs' specification, where
+`msg' indicates one message and `msgs' indicates one or more messages.
+To designate a message, you may use either its number (e.g., 1, 10, 234)
+or one of these \*(lqreserved\*(rq message names:
+.in +.5i
+.sp 1
+.nf
+.ta +\w'\fIName\fP      'u
+\fIName\fP     \fIDescription\fR
+first  the first message in the folder
+last   the last message in the folder
+cur    the most recently accessed message
+prev   the message numerically preceding \*(lqcur\*(rq
+next   the message numerically following \*(lqcur\*(rq
+.re
+.fi
+.in -.5i
+
+In commands that take a `msg' argument, the default is \*(lqcur\*(rq.
+As a shorthand, \*(lq\&.\*(rq is equivalent to \*(lqcur\*(rq.
+
+For example: In a folder containing five messages numbered 5, 10, 94, 177
+and 325, \*(lqfirst\*(rq is 5 and \*(lqlast\*(rq is 325.  If \*(lqcur\*(rq
+is 94, then \*(lqprev\*(rq is 10 and \*(lqnext\*(rq is 177.
+
+The word `msgs' indicates that one or more messages may be specified.
+Such a specification consists of one message designation or of several
+message designations separated by spaces.  A message designation consists
+either of a message name as defined above, or a message range.
+
+A message range is specified as \*(lqname1\-name2\*(rq or
+\*(lqname:n\*(rq, where `name', `name1' and `name2' are message names,
+and `n' is an integer.
+
+The specification \*(lqname1\-name2\*(rq designates all currently existing
+messages from `name1' to `name2' inclusive.  The \*(lqreserved\*(rq
+message name \*(lqall\*(rq is a shorthand for the message range
+\*(lqfirst\-last\*(rq.
+
+The specification \*(lqname:n\*(rq designates up to `n' messages.
+These messages start with `name' if `name' is a message number or one of
+the reserved names \*(lqfirst\*(rq \*(lqcur\*(rq, or \*(lqnext\*(rq, The
+messages end with `name' if `name' is \*(lqprev\*(rq or \*(lqlast\*(rq.
+The interpretation of `n' may be overridden by preceding `n' with a
+plus or minus sign; `+n' always means up to `n' messages starting with
+`name', and `\-n' always means up to `n' messages ending with `name'.
+
+In commands which accept a `msgs' argument, the default is either
+\*(lqcur\*(rq or \*(lqall\*(rq, depending on which makes more sense
+for each command (see the individual man pages for details).  Repeated
+specifications of the same message have the same effect as a single
+specification of the message.
+
+There is also a special \*(lqreserved\*(rq message name \*(lqnew\*(rq
+which is used by the \fImhpath\fR command.
+
+.Uh "User\-Defined Message Sequences"
+In addition to the \*(lqreserved\*(rq (pre-defined) message names given
+above, \fInmh\fP supports user-defined sequence names.  User-defined
+sequences allow the \fInmh\fR user a tremendous amount of power in dealing
+with groups of messages in the same folder by allowing the user to bind
+a group of messages to a meaningful symbolic name.
+
+The name used to denote a message sequence must consist of an alphabetic
+character followed by zero or more alphanumeric characters, and can not
+be one of the \*(lqreserved\*(rq message names above.  After defining a
+sequence, it can be used wherever an \fInmh\fR command expects a `msg' or
+`msgs' argument.
+
+Some forms of message ranges are allowed with user-defined sequences.
+The specification \*(lqname:n\*(rq may be used, and it designates up
+to the first `n' messages (or last `n' messages for `\-n') which are
+elements of the user-defined sequence `name'.
+
+The specifications \*(lqname:next\*(rq and \*(lqname:prev\*(rq may also
+be used, and they designate the next or previous message (relative to the
+current message) which is an element of the user-defined sequence `name'.
+The specifications \*(lqname:first\*(rq and \*(lqname:last\*(rq are
+equivalent to \*(lqname:1\*(rq and \*(lqname:\-1\*(rq, respectively.  The
+specification \*(lqname:cur\*(rq is not allowed (use just \*(lqcur\*(rq
+instead).  The syntax of these message range specifications is subject
+to change in the future.
+
+User-defined sequence names are specific to each folder.  They are
+defined using the \fIpick\fP and \fImark\fP commands.
+
+.Uh "Public and Private User-Defined Sequences"
+There are two varieties of user-defined sequences: \fIpublic\fR and
+\fIprivate\fR.  \fIPublic\fR sequences of a folder are accessible to any
+\fInmh\fR user that can read that folder.  They are kept in each folder
+in the file determined by the \*(lqmh\-sequences\*(rq profile entry
+(default is \&.mh\(rusequences).  \fIPrivate\fR sequences are accessible
+only to the \fInmh\fR user that defined those sequences and are kept in
+the user's \fInmh\fR context file.
+
+In general, the commands that create sequences (such as \fIpick\fR and
+\fImark\fR) will create \fIpublic\fR sequences if the folder for which
+the sequences are being defined is writable by the \fInmh\fR user.
+For most commands, this can be overridden by using the switches
+`\-public' and `\-private'.  But if the folder is read\-only, or if
+the \*(lqmh\-sequences\*(rq profile entry is defined but empty, then
+\fIprivate\fR sequences will be created instead.
+
+.Uh "Sequence Negation"
+\fInmh\fP provides the ability to select all messages not elements of a
+user-defined sequence.  To do this, the user should define the entry
+\*(lqSequence\-Negation\*(rq in the \fInmh\fR profile file; its value
+may be any string.  This string is then used to preface an existing
+user-defined sequence name.  This specification then refers to those
+messages not elements of the specified sequence name.  For example, if
+the profile entry is:
+
+.ti +.5i
+Sequence\-Negation:\^ not
+
+then anytime an \fInmh\fR command is given \*(lqnotfoo\*(rq as a `msg' or
+`msgs' argument, it would substitute all messages that are not elements
+of the sequence \*(lqfoo\*(rq.
+
+Obviously, the user should beware of defining sequences with names that
+begin with the value of the \*(lqSequence\-Negation\*(rq profile entry.
+
+.Uh "The Previous Sequence"
+\fInmh\fR provides the ability to remember the `msgs' or `msg' argument
+last given to an \fInmh\fR command.  The entry \*(lqPrevious\-Sequence\*(rq
+should be defined in the \fInmh\fR profile; its value should be a sequence
+name or multiple sequence names separated by spaces.  If this entry
+is defined, when when an \fInmh\fP command finishes, it will define the
+sequence(s) named in the value of this entry to be those messages that
+were specified to the command.  Hence, a profile entry of
+
+.ti +.5i
+Previous\-Sequence:\^ pseq
+
+directs any \fInmh\fR command that accepts a `msg' or `msgs' argument to
+define the sequence \*(lqpseq\*(rq as those messages when it finishes.
+
+\fBNote:\fP there can be a performance penalty in using the
+\*(lqPrevious\-Sequence\*(rq facility.  If it is used, \fBall\fP
+\fInmh\fR programs have to write the sequence information to the
+\&.mh\(rusequences file for the folder each time they run.  If the
+\*(lqPrevious\-Sequence\*(rq profile entry is not included, only
+\fIpick\fR and \fImark\fR will write to the \&.mh\(rusequences file.
+
+.Uh "The Unseen Sequence"
+Finally, many users like to indicate which messages have not been
+previously seen by them.  The commands \fIinc\fR, \fIrcvstore\fR,
+\fIshow\fR, \fImhshow\fR, and \fIflist\fR honor the profile entry
+\*(lqUnseen\-Sequence\*(rq to support this activity.  This entry
+in the \&.mh\(ruprofile should be defined as one or more sequence
+names separated by spaces.  If there is a value for
+\*(lqUnseen\-Sequence\*(rq in the profile, then whenever new messages
+are placed in a folder (using \fIinc\fR or \fIrcvstore\fR), the
+new messages will also be added to all the sequences named in this
+profile entry.  For example, a profile entry of
+
+.ti +.5i
+Unseen\-Sequence:\^ unseen
+
+directs \fIinc\fR to add new messages to the sequence \*(lqunseen\*(rq.
+Unlike the behavior of the \*(lqPrevious\-Sequence\*(rq entry in the
+profile, however, the sequence(s) will \fBnot\fR be zeroed by \fIinc\fP.
+
+Similarly, whenever \fIshow\fR, \fImhshow\fR, \fInext\fR, or
+\fIprev\fR\^ displays a message, that message will be removed from
+any sequences named by the \*(lqUnseen\-Sequence\*(rq entry in the
+profile.
+
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+^<mh\-dir>/context~^The user context
+^<folder>/\&.mh\(rusequences~^File for public sequences
+.Pr
+^mh-sequences:~^Name of file to store public sequences
+.Ps
+^Sequence\-Negation:~^To designate messages not in a sequence
+.Ps
+^Previous\-Sequence:~^The last message specification given
+.Ps
+^Unseen\-Sequence:~^Those messages not yet seen by the user
+.Sa
+flist(1), mark(1), pick(1), mh-profile(5)
+.De
+None
+.Co
+All
+.En
diff --git a/man/mh-tailor.man b/man/mh-tailor.man
new file mode 100644 (file)
index 0000000..71cf0bc
--- /dev/null
@@ -0,0 +1,287 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH MH-TAILOR %manext5% MH.6.8 [%nmhversion%]
+.SH NAME
+mh-tailor, mts.conf \- mail transport customization for nmh message handler
+
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+\fI%etcdir%/mts.conf\fP
+.in -.5i
+.SH DESCRIPTION
+The file %etcdir%/mts.conf defines run-time options for those \fInmh\fR
+programs which interact (in some form) with the message transport system.
+At present, these (user) programs are: \fIap\fR, \fIconflict\fR,
+\fIinc\fR, \fImsgchk\fR, \fImsh\fR, \fIpost\fR, \fIrcvdist\fR, and
+\fIrcvpack\fR.
+
+Each option should be given on a single line.  Blank lines and lines
+which begin with `#' are ignored.  The options available along with
+default values and a description of their meanings are listed below:
+
+.in +.5i
+.ti -.5i
+localname:
+.br
+The hostname \fInmh\fR considers local.  It should typically be a fully
+qualified hostname.  If this is not set, depending on the version of
+UNIX you're running, \fInmh\fR will query the system for this value
+(e.g., uname, gethostname, etc.), and attempt to fully qualify this
+value.
+
+If you are using POP to retrieve new messages, you may want to set this
+value to the name of the POP server, so that outgoing message appear to
+have originated on the POP server.
+
+.ti -.5i
+localdomain:
+.br
+If this is set, a `.' followed by this string will be appended to your
+hostname.
+
+This should only be needed, if for some reason \fInmh\fR is not able to
+fully qualify the hostname returned by the system (e.g., uname,
+gethostname, etc.).
+
+.ti -.5i
+clientname:
+.br
+This option specifies the host name that \fInmh\fP will give in the
+SMTP \fBHELO\fP (and \fBEHLO\fP) command, when posting mail.  If not
+set, the default is to use the host name that \fInmh\fR considers local
+(see \*(lqlocalname\*(rq above).  If this option is set, but empty, no
+\fBHELO\fP command will be given.
+
+.sp
+Although the \fBHELO\fP command is required by RFC\-821, many SMTP servers
+do not require it.  Early versions of SendMail will fail if the hostname
+given in the \fBHELO\fP command is the local host.  Later versions of
+SendMail will complain if you omit the \fBHELO\fP command.  If you run
+SendMail, find out what your system expects and set this field if needed.
+
+.ti -.5i
+systemname:
+.br
+This option is only used for UUCP mail.  It specifies the name of the
+local host in the \fIUUCP\fR \*(lqdomain\*(rq.  If not set, depending
+on the version of UNIX you're running, \fInmh\fR will query the system
+for this value.  This has no equivalent in the \fInmh\fR configuration
+file.
+
+.ti -.5i
+mmdfldir: %mailspool%
+.br
+The directory where maildrops are kept.  If this option is set, but empty,
+the user's home directory is used.  This overrides the default value
+chosen at the time of compilation.
+
+.ti -.5i
+mmdflfil: 
+.br
+The name of the maildrop file in the directory where maildrops are kept.
+If this is empty, the user's login name is used.  This overrides the default
+value (which is empty).
+
+.ti -.5i
+mmdelim1: \\001\\001\\001\\001\\n
+.br
+The beginning-of-message delimiter for maildrops.
+
+.ti -.5i
+mmdelim2: \\001\\001\\001\\001\\n
+.br
+The end-of-message delimiter for maildrops.
+
+.ti -.5i
+mmailid: 0
+.br
+If this is non-zero, then activate support for MMailids (username
+masquerading).  When this is activated, \fInmh\fR will check if the
+pw_gecos field in the password file has the form
+
+.ti +.5i
+Full Name <fakeusername>
+
+If the pw_gecos field has this form, then the internal \fInmh\fR
+routines that find the username and full name of a user will return
+\*(lqfakeusername\*(rq and \*(lqFull Name\*(rq respectively.  If
+the pw_gecos field for a user is not of this form, there will be
+no username masquerading for that user.
+
+This facility is useful if you are using POP, and wish for messages
+that are sent by users to appear to originate from the username of
+their POP account, rather than their username on the local machine.
+
+.ti -.5i
+maildelivery: %libdir%/maildelivery
+.br
+The name of the system-wide default \fI\&.maildelivery\fR file.
+See \fIslocal\fR\0(1) for the details.
+
+.ti -.5i
+everyone: 200
+.br
+The highest user-id which should NOT receive mail addressed to
+\*(lqeveryone\*(rq.
+
+.ti -.5i
+noshell: 
+.br
+If set, then each user-id greater than \*(lqeveryone\*(rq that has a
+login shell equivalent to the given value (e.g., \*(lq/bin/csh\*(rq)
+indicates that mail for \*(lqeveryone\*(rq should not be sent to them.
+This is useful for handling admin, dummy, and guest logins.
+
+.in -.5i
+.Uh "SMTP support"
+These options are only available if you compiled \fInmh\fP with the
+\*(lq/smtp\*(rq support.
+
+.in +.5i
+.ti -.5i
+hostable: %etcdir%/hosts
+.br
+The exceptions file for /etc/hosts used by \fIpost\fR to try to find
+official names.  The format of this file is quite simple:
+
+.in +.5i
+1. Comments are surrounded by sharp (`#') and newline.
+.br
+2. Words are surrounded by white space.
+.br
+3. The first word on the line is the official name of a host.
+.br
+4. All words following the official names are aliases for that host.
+.in -.5i
+
+.ti -.5i
+servers: localhost \\01localnet
+.br
+A lists of hosts and networks which to look for SMTP servers when
+posting local mail.  It turns out this is a major win for hosts which
+don't run an message transport system.  The value of \*(lqservers\*(rq
+should be one or more items.  Each item is the name of either a host
+or a net (in the latter case, precede the name of the net by a \\01).
+This list is searched when looking for a smtp server to post mail.
+If a host is present, the SMTP port on that host is tried.  If a net
+is present, the SMTP port on each host in that net is tried.  Note that
+if you are running with the BIND code, then any networks specified are
+ignored (sorry, the interface went away under BIND).
+
+.in -.5i
+.Uh "SendMail"
+This option is only available if you compiled \fInmh\fP to use
+\fISendMail\fP as your delivery agent.
+
+.in +.5i
+.ti -.5i
+sendmail: %sendmailpath%
+.br
+The pathname to the \fIsendmail\fR program.
+
+.in -.5i
+.Uh "Post Office Protocol"
+This option is only available if you have compiled \fInmh\fP with POP
+support enabled (i.e., \*(lq--enable-nmh-pop\*(rq).
+
+.in +.5i
+.ti -.5i
+pophost:
+.br
+The name of the default POP service host.  If this is not set, then
+\fInmh\fR looks in the standard maildrop areas for waiting mail, otherwise
+the named POP service host is consulted.
+
+.in -.5i
+.Uh "BBoards Delivery"
+This option is only available if you compiled \fInmh\fP with
+\*(lqbbdelivery:\ on\*(rq.
+
+.in +.5i
+.ti -.5i
+bbdomain:
+.br
+The local BBoards domain (a UCI hack).
+
+.in -.5i
+.Uh "BBoards & The POP"
+These options are only available if you compiled \fInmh\fP with
+\*(lqbboards:\ pop\*(rq and \*(lqpop:\ on\*(rq.
+
+.in +.5i
+.ti -.5i
+popbbhost:
+.br
+The POP service host which also acts as a BBoard server.  This variable
+should be set on the POP BBoards client host.
+
+.ti -.5i
+popbbuser:
+.br
+The guest account on the POP/BB service host.  This should be a different
+login ID than either the POP user or the BBoards user.  (The user-id
+\*(lqftp\*(rq is highly recommended.)  This variable should be set on
+both the POP BBoards client and service hosts.
+
+.ti -.5i
+popbblist: %etcdir%/hosts.popbb
+.br
+A file containing of lists of hosts that are allowed to use the POP
+facility to access BBoards using the guest account.  If this file is not
+present, then no check is made.  This variable should be set on the POP
+BBoards service host.
+
+.in -.5i
+.if n .ne 8
+.Uh "BBoards & The NNTP"
+This option is only available if you compiled \fInmh\fP with
+\*(lqbboards:\ nntp\*(rq and \*(lqpop:\ on\*(rq.
+
+.in +.5i
+.ti -.5i
+nntphost:
+.br
+The host which provides the NNTP service.  This variable should be set
+on the NNTP BBoards client host.
+
+.in -.5i
+.Uh "File Locking"
+A few words on locking: \fInmh\fR has several methods for creating locks
+on files.  When configuring \fInmh\fR, you will need to decide on the
+locking style and locking directory (if any).  The first controls the
+method of locking, the second says where lock files should be created.
+
+To configure \fInmh\fR for kernel locking, define \fBFLOCK_LOCKING\fP if
+you want to use the \fIflock\fP system call; define \fBLOCKF_LOCKING\fP if
+you want to use the \fIlockf\fP system call; or define \fBFCNTL_LOCKING\fP
+if you want to use the \fIfcntl\fP system call for kernel-level locking.
+
+Instead of kernel locking, you can configure \fInmh\fR to use dot
+locking by defining \fBDOT_LOCKING\fP.  Dot locking specifies that
+a file should be created whose existence means \*(lqlocked\*(rq and
+whose non-existence means \*(lqunlocked\*(rq.  The name of this file is
+constructed by appending \*(lq.lock\*(rq to the name of the file being
+locked.  If \fBLOCKDIR\fP is not specified, lock files will be created
+in the directory where the file being locked resides.  Otherwise, lock
+files will be created in the directory specified by \fBLOCKDIR\fP.
+
+Prior to installing \fInmh\fR, you should see how locking is done at
+your site, and set the appropriate values.
+
+.Fi
+^%etcdir%/mts.conf~^nmh mts configuration file
+.Pr
+None
+.Sa
+mh\-mts(8)
+.De
+As listed above
+.Co
+None
+.En
diff --git a/man/mhbuild.man b/man/mhbuild.man
new file mode 100644 (file)
index 0000000..497ff3d
--- /dev/null
@@ -0,0 +1,580 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH MHBUILD %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+mhbuild \- translate MIME composition draft
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+mhbuild file
+.br
+\%[\-list] \%[-nolist]
+\%[\-realsize] \%[\-norealsize]
+.br
+\%[\-headers] \%[\-noheaders]
+\%[\-ebcdicsafe] \%[\-noebcdicsafe]
+.br
+\%[\-rfc934mode] \%[\-norfc934mode]
+\%[\-verbose] \%[\-noverbose]
+.br
+\%[\-check] \%[\-nocheck]
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+The \fImhbuild\fR command will translate a MIME composition draft into
+a valid MIME message.
+
+\fImhbuild\fR creates multi-media messages as specified in RFC\-2045
+thru RFC\-2049.  Currently \fImhbuild\fR only supports encodings in
+message bodies, and does not support the encoding of message headers as
+specified in RFC\-2047.
+
+If you specify the name of the composition file as \*(lq-\*(rq,
+then \fImhbuild\fR will accept the composition draft on the standard
+input.  If the translation of this input is successful, \fImhbuild\fR
+will output the new MIME message to the standard output.  This argument
+must be the last argument on the command line.
+
+Otherwise if the file argument to \fImhbuild\fR is the name of a valid
+composition file, and the translation is successful, \fImhbuild\fR will
+replace the original file with the new MIME message.  It will rename
+the original file to start with the \*(lq,\*(rq character and end with the
+string \*(lq.orig\*(rq, e.g., if you are editing the file \*(lqdraft\*(rq,
+it will be renamed to \*(lq,draft.orig\*(rq.  This allows you to easily
+recover the \fImhbuild\fR input file.
+
+.Uh "Listing the Contents"
+The `\-list' switch tells \fImhbuild\fR to list the table of contents
+associated with the MIME message that is created.
+
+The `\-headers' switch indicates
+that a one-line banner should be displayed above the listing.  The
+`\-realsize' switch tells \fImhbuild\fR to evaluate the \*(lqnative\*(rq
+(decoded) format of each content prior to listing.  This provides an
+accurate count at the expense of a small delay.  If the `\-verbose' switch
+is present, then the listing will show any \*(lqextra\*(rq information
+that is present in the message, such as comments in the Content-Type header.
+
+.Uh "Translating the Composition File"
+\fImhbuild\fR is essentially a filter to aid in the composition of MIME
+messages.  \fImhbuild\fR will convert an
+\fImhbuild\fR \*(lqcomposition file\*(rq
+into a valid MIME message.  A \fImhbuild\fR \*(lqcomposition file\*(rq
+is just a file containing plain text that is interspersed
+with various \fImhbuild\fR directives.  When this file is processed
+by \fImhbuild\fR, the various directives will be expanded to the
+appropriate content, and will be encoded according to the MIME standards.
+The resulting MIME message can then be sent by electronic mail.
+
+The formal syntax for a \fImhbuild\fR composition file is defined at the
+end of this document, but the ideas behind this format are not complex.
+Basically, the body contains one or more contents.  A content consists of
+either a directive, indicated with a \*(lq#\*(rq as the first character
+of a line; or, plaintext (one or more lines of text).  The continuation
+character, \*(lq\\\*(lq, may be used to enter a single directive on more
+than one line, e.g.,
+.sp
+.nf
+.in +.5i
+#image/png \\
+    /home/foobar/junk/picture.png
+.in -.5i
+.fi
+.sp
+There are four kinds of directives: \*(lqtype\*(rq directives, which
+name the type and subtype of the content; \*(lqexternal-type\*(rq
+directives, which also name the type and subtype of the content; the
+\*(lqmessage\*(rq directive (#forw), which is used to forward one or
+more messages; and, the \*(lqbegin\*(rq directive (#begin), which is
+used to create a multipart content.
+
+The \*(lqtype\*(rq directive is used to directly specify the type and
+subtype of a content.  You may only specify discrete types in this manner
+(can't specify the types multipart or message with this directive).
+You may optionally specify the name of a file containing the contents
+in \*(lqnative\*(rq (decoded) format.  If this filename starts with the
+\*(lq|\*(rq character, then it represents a command to execute whose
+output is captured accordingly.
+For example,
+.sp
+.nf
+.in +.5i
+#audio/basic |raw2audio -F < /usr/lib/sound/giggle.au
+.in -.5i
+.fi
+.sp
+If a filename is not given, \fImhbuild\fR will look for information in the
+user's profile to determine how the different contents should be composed.
+This is accomplished by consulting a composition string, and executing
+it under \fB/bin/sh\fR, with the standard output set to the content.
+If the `\-verbose' switch is given, \fImhbuild\fR will echo any commands
+that are used to create contents in this way.
+.ne 13
+The composition string may contain the following escapes:
+.sp
+.nf
+.in +.5i
+.ta \w'%P  'u
+%a     Insert parameters from directive
+%f     Insert filename containing content
+%F     %f, and stdout is not re-directed
+%s     Insert content subtype
+%%     Insert character %
+.re
+.in -.5i
+.fi
+.sp
+
+First,
+\fImhbuild\fR will look for an entry of the form:
+.sp
+.in +.5i
+mhbuild-compose-<type>/<subtype>
+.in -.5i
+.sp
+to determine the command to use to compose the content.  If this isn't
+found, \fImhbuild\fR will look for an entry of the form:
+.sp
+.in +.5i
+mhbuild-compose-<type>
+.in -.5i
+.sp
+to determine the composition command.
+
+If this isn't found, \fImhbuild\fR
+will complain.
+
+An example entry might be:
+.sp
+.in +.5i
+mhbuild-compose-audio/basic: record | raw2audio -F
+.in -.5i
+.sp
+Because commands like these will vary, depending on the display
+environment used for login, composition strings for different
+contents should probably be put in the file specified by the
+\fB$MHBUILD\fR environment variable, instead of directly in your
+user profile.
+
+The \*(lqexternal-type\*(rq directives are used to provide a MIME
+reference to a content, rather than enclosing the contents itself
+(for instance, by specifying an ftp site).  Hence, instead of
+providing a filename as with the type directives, external-parameters
+are supplied.  These look like regular parameters, so they must be
+separated accordingly.  For example,
+.sp
+.nf
+.in +.5i
+#@application/octet-stream; \\
+    type=tar; \\
+    conversions=compress \\
+    [this is the nmh distribution] \\
+    name="nmh.tar.gz"; \\
+    directory="/pub/nmh"; \\
+    site="ftp.math.gatech.edu"; \\
+    access-type=anon-ftp; \\
+    mode="image"
+.in -.5i
+.fi
+.sp
+You must give a description string to separate the content parameters
+from the external-parameters (although this string may be empty).
+This description string is specified by enclosing it within
+\*(lq[]\*(rq.
+.ne 19
+These parameters are of the form:
+.sp
+.nf
+.in +.5i
+.ta \w'access-type=  'u
+access-type=   usually \fIanon-ftp\fR or \fImail-server\fR
+name=  filename
+permission=    read-only or read-write
+site=  hostname
+directory=     directoryname (optional)
+mode=  usually \fIascii\fR or \fIimage\fR (optional)
+size=  number of octets
+server=        mailbox
+subject=       subject to send
+body=  command to send for retrieval
+.re
+.in -.5i
+.fi
+.sp
+
+The \*(lqmessage\*(rq directive (#forw) is used to specify a message or
+group of messages to include.  You may optionally specify the name of
+the folder and which messages are to be forwarded.  If a folder is not
+given, it defaults to the current folder.  Similarly, if a message is not
+given, it defaults to the current message.  Hence, the message directive
+is similar to the \fIforw\fR\0(1) command, except that the former uses
+the MIME rules for encapsulation rather than those specified in RFC\-934.
+For example,
+.sp
+.nf
+.in +.5i
+#forw +inbox 42 43 99
+.in -.5i
+.fi
+.sp
+If you include a single message, it will be included directly as a content
+of type \*(lqmessage/rfc822\*(rq.  If you include more than one message,
+then \fImhbuild\fR will add a content of type \*(lqmultipart/digest\*(rq
+and include each message as a subpart of this content.
+
+If you are using this directive to include more than one message, you
+may use the `\-rfc934mode' switch.  This switch will indicate that
+\fImhbuild\fR should attempt to utilize the MIME encapsulation rules
+in such a way that the \*(lqmultipart/digest\*(rq that is created
+is (mostly) compatible with the encapsulation specified in RFC\-934.
+If given, then RFC\-934 compliant user-agents should be able to burst the
+message on reception\0--\0providing that the messages being encapsulated
+do not contain encapsulated messages themselves.  The drawback of this
+approach is that the encapsulations are generated by placing an extra
+newline at the end of the body of each message.
+
+The \*(lqbegin\*(rq directive is used to create a multipart content.
+When using the \*(lqbegin\*(rq directive, you must specify at least one
+content between the begin and end pairs.
+.sp
+.nf
+.in +.5i
+#begin
+This will be a multipart with only one part.
+#end
+.in -.5i
+.fi
+.sp
+If you use multiple directives in a composition draft, \fImhbuild\fR will
+automatically encapsulate them inside a multipart content.  Therefore the
+\*(lqbegin\*(rq directive is only necessary if you wish to use nested
+multiparts, or create a multipart message containing only one part.
+
+For all of these directives, the user may include a brief description
+of the content between the \*(lq[\*(rq character and the \*(lq]\*(rq
+character.  This description will be copied into the
+\*(lqContent-Description\*(rq header when the directive is processed.
+.sp
+.nf
+.in +.5i
+#forw [important mail from Bob] +bob 1 2 3 4 5
+.in -.5i
+.fi
+.sp
+By default, \fImhbuild\fR will generate a unique \*(lqContent-ID:\*(rq for
+each directive; however, the user may override this by defining the ID
+using the \*(lq<\*(rq and \*(lq>\*(rq characters.
+
+In addition to the various directives, plaintext can be present.
+Plaintext is gathered, until a directive is found or the draft is
+exhausted, and this is made to form a text content.  If the plaintext
+must contain a \*(lq#\*(rq at the beginning of a line, simply double it,
+.ne 6
+e.g.,
+.sp
+.in +.5i
+##when sent, this line will start with only one #
+.in -.5i
+.sp
+If you want to end the plaintext prior to a directive, e.g., to have two
+plaintext contents adjacent, simply insert a line containing a single
+\*(lq#\*(rq character,
+.ne 10
+e.g.,
+.sp
+.nf
+.in +.5i
+this is the first content
+#
+and this is the second
+.in -.5i
+.fi
+.sp
+Finally,
+if the plaintext starts with a line of the form:
+.sp
+.in +.5i
+Content-Description: text
+.in -.5i
+.sp
+then this will be used to describe the plaintext content.
+You MUST follow this line with a blank line before starting
+your text.
+
+By default, plaintext is captured as a text/plain content.  You can
+override this by starting the plaintext with \*(lq#<\*(rq followed by
+a content-type specification.  For example,
+.ne 11
+e.g.,
+.sp
+.nf
+.in +.5i
+#<text/enriched
+this content will be tagged as text/enriched
+#
+and this content will be tagged as text/plain
+#
+#<application/x-patch [this is a patch]
+and this content will be tagged as application/x-patch
+.in -.5i
+.fi
+.sp
+Note that if you use the \*(lq#<\*(rq plaintext-form, then the
+content-description must be on the same line which identifies the content
+type of the plaintext.
+
+When composing a text content, you may indicate the relevant character
+set by adding the \*(lqcharset\*(rq parameter to the directive.
+.sp
+.in +.5i
+#<text/plain; charset=iso-8859-5
+.in -.5i
+.sp
+If a text content contains any 8bit characters (characters with the
+high bit set) and the character set is not specified as above, then
+\fImhbuild\fR will assume the character set is of the type given by the
+environment variable MM_CHARSET.  If this environment variable is not
+set, then the character set will be labeled as \*(lqx-unknown\*(rq.
+
+If a text content contains only 7bit characters and the character set
+is not specified as above, then the character set will be labeled as
+\*(lqus-ascii\*(rq
+
+Putting this all together,
+.ne 15
+here is an example of a more complicated message draft.  The
+following draft will expand into a multipart/mixed message
+containing five parts:
+.sp
+.nf
+.in +.5i
+To: nobody@nowhere.org
+cc:
+Subject: Look and listen to me!
+--------
+The first part will be text/plain
+#<text/enriched
+The second part will be text/enriched
+#
+This third part will be text/plain
+#audio/basic [silly giggle]  \\
+    |raw2audio -F < /usr/lib/sounds/giggle.au
+#image/gif   [photo of foobar] \\
+                    /home/foobar/lib/picture.gif
+.in -.5i
+.fi
+.sp
+.Uh "Integrity Check"
+If \fImhbuild\fR is given the `-check' switch, then it will also associate
+an integrity check with each \*(lqleaf\*(rq content.  This will add a
+Content-MD5 header field to the content, along with the md5 sum of the
+unencoded contents.  This may be used by the receiver of the message to
+verify that the contents of the message were not changed in transport.
+
+.Uh "Transfer Encodings"
+After \fImhbuild\fR constructs the new MIME message by parsing directives,
+including files, etc., it scans the contents of the message to determine
+which transfer encoding to use.  It will check for 8bit data, long lines,
+spaces at the end of lines, and clashes with multipart boundaries.  It will
+then choose a transfer encoding appropriate for each content type.
+
+If an integrity check is being associated with each content by using
+the `\-check' switch, then \fImhbuild\fR will encode each content with
+a transfer encoding, even it the content contains only 7bit data.  This
+is to increase the likelihood that the content is not changed while in
+transport.
+
+The switch `\-ebcdicsafe' will cause \fImhbuild\fR to slightly change
+the way in which it performs the \*(lqquoted-printable\*(rq transfer
+encoding.  Along with encoding 8bit characters, it will now also encode
+certain common punctuation characters as well.  This slightly reduces the
+readability of the message, but allows the message to pass more reliably
+through mail gateways which involve the EBCDIC character encoding.
+
+.Uh "Invoking mhbuild"
+Typically, \fImhbuild\fR is invoked by the \fIwhatnow\fR program.  This
+command will expect the body of the draft to be formatted as an
+\fImhbuild\fR composition file.  Once you have composed this input file
+using a command such as \fIcomp\fR, \fIrepl\fR, or \fIforw\fR, you invoke
+\fImhbuild\fR at the \*(lqWhat now\*(rq prompt with
+.sp
+.in +.5i
+What now? mime
+.in -.5i
+.sp
+prior to sending the draft.  This will cause \fIwhatnow\fR to execute
+\fImhbuild\fR to translate the composition file into MIME format.
+
+It is also possible to have the \fIwhatnow\fR program invoke \fImhbuild\fR 
+automatically when a message is sent.  To do this, you must add the line
+.sp
+.in +.5i
+automimeproc: 1
+.in -.5i
+.sp
+to your \&.mh\(ruprofile file.
+
+Finally, you should consider adding this line to your profile:
+.sp
+.in +.5i
+lproc: show
+.in -.5i
+.sp
+This way, if you decide to \fBlist\fR after invoking \fImime\fR,
+the command
+.sp
+.in +.5i
+What now? list
+.in -.5i
+.sp
+will work as you expect.
+
+.Uh "User Environment"
+Because the environment in which \fImhbuild\fR operates may vary for a
+user, \fImhbuild\fR will look for the environment variable \fB$MHBUILD\fR.
+If present, this specifies the name of an additional user profile which
+should be read.  Hence, when a user logs in on a particular machine,
+this environment variable should be set to refer to a file containing
+definitions useful for that machine.
+
+Finally, \fImhbuild\fR will attempt to consult a global \fImhbuild\fR
+user profile,
+.ne 6
+e.g.,
+.sp
+.in +.5i
+%etcdir%/mhn.defaults
+.in -.5i
+.sp
+if it exists.
+
+.Uh "Syntax of Composition Files"
+.ne 59
+The following is the formal syntax of a \fImhbuild\fR
+\*(lqcomposition file\*(rq.
+.sp
+.nf
+.in +.5i
+   body         ::=     1*(content | EOL)
+
+   content      ::=     directive | plaintext
+
+   directive    ::=     "#" type "/" subtype
+                            0*(";" attribute "=" value)
+                            [ "(" comment ")" ]
+                            [ "<" id ">" ]
+                            [ "[" description "]" ]
+                            [ filename ]
+                            EOL
+
+                      | "#@" type "/" subtype
+                            0*(";" attribute "=" value)
+                            [ "(" comment ")" ]
+                            [ "<" id ">" ]
+                            [ "[" description "]" ]
+                            external-parameters
+                            EOL
+
+                      | "#forw"
+                            [ "<" id ">" ]
+                            [ "[" description "]" ]
+                            [ "+"folder ] [ 0*msg ]
+                            EOL
+
+                      | "#begin"
+                              [ "<" id ">" ]
+                              [ "[" description "]" ]
+                              [   "alternative"
+                                | "parallel"
+                                | something-else    ]
+                              EOL
+                            1*body
+                        "#end" EOL
+
+   plaintext    ::=     [ "Content-Description:"
+                              description EOL EOL ]
+                            1*line
+                        [ "#" EOL ]
+
+                      | "#<" type "/" subtype
+                            0*(";" attribute "=" value)
+                            [ "(" comment ")" ]
+                            [ "[" description "]" ]
+                            EOL
+                            1*line
+                        [ "#" EOL ]
+
+   line         ::=     "##" text EOL
+                        -- interpreted as "#"text EOL
+                      | text EOL
+.in -.5i
+.fi
+.sp
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+^$MHBUILD~^Additional profile entries
+^%etcdir%/mhn.defaults~^System default MIME profile entries
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Current\-Folder:~^To find the default current folder
+.Ps
+^mhbuild-compose-<type>*~^Template for composing contents
+.Sa
+mhlist(1), mhshow(1), mhstore(1)
+.br
+RFC\-934:
+.br
+   \fIProposed Standard for Message Encapsulation\fR,
+.br
+RFC\-2045:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part One:
+.br
+   Format of Internet Message Bodies\fR,
+.br
+RFC\-2046:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part Two:
+.br
+   Media Types\fR,
+.br
+RFC\-2047:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part Three:
+.br
+   Message Header Extensions for Non-ASCII Text\fR,
+.br
+RFC\-2048:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part Four:
+.br
+   Registration Procedures\fR,
+.br
+RFC\-2049:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part Five:
+.br
+   Conformance Criteria and Examples\fR.
+.De
+`\-headers'
+.Ds
+`\-realsize'
+.Ds
+`\-norfc934mode'
+.Ds
+`\-nocheck'
+.Ds
+`\-noebcdicsafe'
+.Ds
+`\-noverbose'
+.Co
+If a folder is given, it will become the current folder.  The last
+message selected will become the current message.
+.En
diff --git a/man/mhl.man b/man/mhl.man
new file mode 100644 (file)
index 0000000..b81415c
--- /dev/null
@@ -0,0 +1,246 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH MHL %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+mhl \- produce formatted listings of nmh messages
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+%libdir%/mhl
+\%[\-bell] \%[\-nobell]
+\%[\-clear]
+.br
+\%[\-noclear]
+\%[\-folder\ +folder]
+\%[\-form\ formfile]
+.br
+\%[\-length\ lines] \%[\-width\ columns] 
+\%[\-moreproc\ program]
+.br
+\%[\-nomoreproc]
+\%[files\ ...]
+\%[\-version]
+\%[\-help] 
+.in -.5i
+.SH DESCRIPTION
+\fIMhl\fR is a \fInmh\fR command for filtering and/or displaying text
+messages.  It is the default method of displaying text messages for
+\fInmh\fR (it is the default \fIshowproc\fR).
+
+As with \fImore\fR, each of the messages specified as arguments (or
+the standard input) will be output.  If more than one message file is
+specified, the user will be prompted prior to each one, and a <RETURN>
+or <EOT> will begin the output, with <RETURN> clearing the screen (if
+appropriate), and <EOT> (usually CTRL\-D) suppressing the screen clear.
+An <INTERRUPT> (usually CTRL\-C) will abort the current message output,
+prompting for the next message (if there is one), and a <QUIT> (usually
+CTRL-\\) will terminate the program (without core dump).
+
+The `\-bell' option tells \fImhl\fR to ring the terminal's bell at the
+end of each page, while the `\-clear' option tells \fImhl\fR to clear the
+scree at the end of each page (or output a formfeed after each message).
+Both of these switches (and their inverse counterparts) take effect only
+if the profile entry \fImoreproc\fR is defined but empty, and \fImhl\fR
+is outputting to a terminal.  If the \fImoreproc\fR entry is defined and
+non-empty, and \fImhl\fR is outputting to a terminal, then \fImhl\fR will
+cause the \fImoreproc\fR to be placed between the terminal and \fImhl\fR
+and the switches are ignored.  Furthermore, if the `\-clear' switch is
+used and \fImhl's\fR output is directed to a terminal, then \fImhl\fR
+will consult the \fB$TERM\fR and \fB$TERMCAP\fR environment variables
+to determine the user's terminal type in order to find out how to clear
+the screen.  If the `\-clear' switch is used and \fImhl's\fR output is
+not directed to a terminal (e.g., a pipe or a file), then \fImhl\fR will
+send a formfeed after each message.
+
+To override the default \fImoreproc\fR and the profile entry, use the
+`\-moreproc\ program' switch.  Note that \fImhl\fR will never start a
+\fImoreproc\fR if invoked on a hardcopy terminal.
+
+The `\-length\ length' and `\-width\ width' switches set the screen
+length and width, respectively.  These default to the values indicated
+by \fB$TERMCAP\fR, if appropriate, otherwise they default to 40 and
+80, respectively.
+
+The default format file used by \fImhl\fR is called \*(lqmhl.format\*(rq.
+\fImhl\fR will first search for this file in the user's \fInmh\fR
+directory, and will then search in the directory %etcdir%.  This default
+can be changed by using the `\-form\ formatfile' switch.
+
+Finally, the `\-folder\ +folder' switch sets the \fInmh\fR folder name,
+which is used for the \*(lqmessagename:\*(rq field described below.  The
+environment variable \fB$mhfolder\fR is consulted for the default value,
+which \fIshow\fR, \fInext\fR, and \fIprev\fR initialize appropriately.
+
+\fIMhl\fR operates in two phases: 1) read and parse the format file, and
+2) process each message (file).  During phase 1, an internal description
+of the format is produced as a structured list.  In phase 2, this list
+is walked for each message, outputting message information under the
+format constraints from the format file.
+
+The format file can contain information controlling screen clearing,
+screen size, wrap\-around control, transparent text, component ordering,
+and component formatting.  Also, a list of components to ignore may be
+specified, and a couple of \*(lqspecial\*(rq components are defined
+to provide added functionality.  Message output will be in the order
+specified by the order in the format file.
+
+Each line of a format file has one of the following forms:
+
+     ;comment
+     :cleartext
+     variable[,variable...]
+     component:[variable,...]
+
+A line beginning with a `;' is a comment, and is ignored.
+A line beginning with a `:' is clear text,
+and is output exactly as is.
+A line containing only a `:' produces a blank line in the output.
+A line beginning with \*(lqcomponent:\*(rq defines the format for the specified
+component,
+and finally, remaining lines define the global environment.
+
+For example, the line:
+
+.ti +.5i
+width=80,length=40,clearscreen,overflowtext="***",overflowoffset=5
+
+defines the screen size to be 80 columns by 40 rows, specifies that the
+screen should be cleared prior to each page, that the overflow indentation
+is 5, and that overflow text should be flagged with \*(lq***\*(rq.
+
+Following are all of the current variables and their arguments.  If they
+follow a component, they apply only to that component, otherwise, their
+affect is global.  Since the whole format is parsed before any output
+processing, the last global switch setting for a variable applies to
+the whole message if that variable is used in a global context (i.e.,
+bell, clearscreen, width, length).
+
+.nf
+.in +.5i
+.ta \w'noclearscreen  'u +\w'integer/G  'u
+\fIvariable\fR \fItype\fR      \fIsemantics\fR
+width  integer screen width or component width
+length integer screen length or component length
+offset integer positions to indent \*(lqcomponent: \*(rq
+overflowtext   string  text to use at the beginning of an
+               overflow line
+overflowoffset integer positions to indent overflow lines
+compwidth      integer positions to indent component text
+               after the first line is output
+uppercase      flag    output text of this component in all
+               upper case
+nouppercase    flag    don't uppercase
+clearscreen    flag/G  clear the screen prior to each page
+noclearscreen  flag/G  don't clearscreen
+bell   flag/G  ring the bell at the end of each page
+nobell flag/G  don't bell
+component      string/L        name to use instead of \*(lqcomponent\*(rq for
+               this component
+nocomponent    flag    don't output \*(lqcomponent: \*(rq for this
+               component
+center flag    center component on line (works for
+               one\-line components only)
+nocenter       flag    don't center
+leftadjust     flag    strip off leading whitespace on each
+               line of text
+noleftadjust   flag    don't leftadjust
+compress       flag    change newlines in text to spaces
+nocompress     flag    don't compress
+split  flag    don't combine multiple fields into
+               a single field
+nosplit        flag    combine multiple fields into
+               a single field
+newline        flag    print newline at end of components
+               (this is the default)
+nonewline      flag    don't print newline at end of components
+formatfield    string  format string for this component
+               (see below)
+decode flag    decode text as RFC-2047 encoded
+               header field
+addrfield      flag    field contains addresses
+datefield      flag    field contains dates
+.re
+.in -.5i
+.fi
+
+To specify the value of integer\-valued and string\-valued variables,
+follow their name with an equals\-sign and the value.  Integer\-valued
+variables are given decimal values, while string\-valued variables
+are given arbitrary text bracketed by double\-quotes.  If a value is
+suffixed by \*(lq/G\*(rq or \*(lq/L\*(rq, then its value is useful in
+a global\-only or local\-only context (respectively).
+
+A line of the form:
+
+    ignores=component,...
+
+specifies a list of components which are never output.
+
+The component \*(lqMessageName\*(rq (case\-insensitive) will output the
+actual message name (file name) preceded by the folder name if one is
+specified or found in the environment.  The format is identical to that
+produced by the `\-header' option to \fIshow\fR.
+
+The component \*(lqExtras\*(rq will output all of the components of the
+message which were not matched by explicit components, or included in
+the ignore list.  If this component is not specified, an ignore list is
+not needed since all non\-specified components will be ignored.
+
+If \*(lqnocomponent\*(rq is NOT specified, then the component name will
+be output as it appears in the format file.
+
+The default format file is:
+
+.nf
+.in +.5i
+.ne 15
+.eo
+.so %etcdir%/mhl.format
+.ec
+.in -.5i
+.fi
+
+The variable \*(lqformatfield\*(rq specifies a format string (see
+\fImh\-format\fR\0(5)).  The flag variables \*(lqaddrfield\*(rq and
+\*(lqdatefield\*(rq (which are mutually exclusive), tell \fImhl\fR
+to interpret the escapes in the format string as either addresses or
+dates, respectively.
+
+By default, \fImhl\fR does not apply any formatting string to fields
+containing address or dates (see \fImh\-mail\fR\0(5) for a list of these
+fields).  Note that this results in faster operation since \fImhl\fR
+must parse both addresses and dates in order to apply a format string
+to them.  If desired, \fImhl\fR can be given a default format string for
+either address or date fields (but not both).  To do this, on a global
+line specify: either the flag addrfield or datefield, along with the
+appropriate formatfield variable string.
+.Fi
+^%etcdir%/mhl.format~^The message template
+^or <mh\-dir>/mhl.format~^Rather than the standard template
+^$HOME/\&.mh\(ruprofile~^The user profile
+.Pr
+^moreproc:~^Program to use as interactive front\-end
+.Sa
+show(1), ap(8), dp(8)
+.De
+`\-bell'
+.Ds
+`\-noclear'
+.Ds
+`\-length 40'
+.Ds
+`\-width 80'
+.Co
+None
+.Bu
+There should be some way to pass `bell' and `clear' information to the 
+front\-end.
+
+The \*(lqnonewline\*(rq option interacts badly with \*(lqcompress\*(rq
+and \*(lqsplit\*(rq.
+.En
diff --git a/man/mhlist.man b/man/mhlist.man
new file mode 100644 (file)
index 0000000..1cd7d0e
--- /dev/null
@@ -0,0 +1,167 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH MHLIST %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+mhlist \- list information about MIME messages
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+mhlist \%[+folder] \%[msgs] \%[\-file file]
+.br
+\%[\-part number]... \%[\-type content]...
+.br
+\%[\-headers] \%[\-noheaders]
+\%[\-realsize] \%[\-norealsize]
+.br
+\%[\-rcache policy] \%[\-wcache policy]
+\%[\-check] \%[\-nocheck]
+.br
+\%[\-verbose] \%[\-noverbose]
+\%[\-version]
+\%[\-help]
+.in -.5i
+
+.SH DESCRIPTION
+The \fImhlist\fR command allows you to list information (essentially
+a table of contents) about the various parts of a collection of
+MIME (multi-media) messages.
+
+\fImhlist\fR manipulates MIME (multi-media messages) as specified
+in RFC\-2045 thru RFC\-2049.
+
+The `\-headers' switch indicates that a one-line banner should be
+displayed above the listing.
+
+The `\-realsize' switch tells \fImhlist\fR to evaluate the
+\*(lqnative\*(rq (decoded) format of each content prior to listing.
+This provides an accurate count at the expense of a small delay.
+
+If the `\-verbose' switch is present, then the listing will show
+any \*(lqextra\*(rq information that is present in the message,
+such as comments in the Content-Type header.
+
+The option `\-file\ file' directs \fImhlist\fR to use the specified
+file as the source message, rather than a message from a folder.
+If you specify this file as \*(lq-\*(rq, then \fImhlist\fR will
+accept the source message on the standard input.  Note that the
+file, or input from standard input should be a validly formatted
+message, just like any other \fInmh\fR message.  It should \fBNOT\fR
+be in mail drop format (to convert a file in mail drop format to
+a folder of \fInmh\fR messages, see \fIinc\fR\0(1)).
+
+By default, \fImhlist\fR will list information about the entire
+message (all of its parts).  By using the `\-part' and `\-type'
+switches, you may limit the scope of this command to particular
+subparts (of a multipart content) and/or particular content types.
+
+A part specification consists of a series of numbers separated by dots.
+For example, in a multipart content containing three parts, these
+would be named as 1, 2, and 3, respectively.  If part 2 was also a
+multipart content containing two parts, these would be named as 2.1 and
+2.2, respectively.  Note that the `\-part' switch is effective for only
+messages containing a multipart content.  If a message has some other
+kind of content, or if the part is itself another multipart content, the
+`\-part' switch will not prevent the content from being acted upon.
+
+A content specification consists of a content type and a subtype.
+The initial list of \*(lqstandard\*(rq content types and subtypes can
+be found in RFC\-2046.
+.ne 18
+A list of commonly used contents is briefly reproduced here:
+.sp
+.nf
+.in +.5i
+.ta \w'application  'u
+Type   Subtypes
+----   --------
+text   plain, enriched
+multipart      mixed, alternative, digest, parallel
+message        rfc822, partial, external-body
+application    octet-stream, postscript
+image  jpeg, gif, png
+audio  basic
+video  mpeg
+.re
+.in -.5i
+.fi
+.sp
+A legal MIME message must contain a subtype specification.
+.PP
+To specify a content, regardless of its subtype, just use the
+name of the content, e.g., \*(lqaudio\*(rq.  To specify a specific
+subtype, separate the two with a slash, e.g., \*(lqaudio/basic\*(rq.
+Note that regardless of the values given to the `\-type' switch, a
+multipart content (of any subtype listed above) is always acted upon.
+Further note that if the `\-type' switch is used, and it is desirable to
+act on a message/external-body content, then the `\-type' switch must
+be used twice: once for message/external-body and once for the content
+externally referenced.
+
+.Uh "Checking the Contents"
+The `\-check' switch tells \fImhlist\fR to check each content for an
+integrity checksum.  If a content has such a checksum (specified as a
+Content-MD5 header field), then \fImhlist\fR will attempt to verify the
+integrity of the content.
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Current\-Folder:~^To find the default current folder
+.Sa
+mhbuild(1), mhshow(1), mhstore(1), sendfiles(1)
+.br
+RFC\-2045:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part One:
+.br
+   Format of Internet Message Bodies\fR,
+.br
+RFC\-2046:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part Two:
+.br
+   Media Types\fR,
+.br
+RFC\-2047:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part Three:
+.br
+   Message Header Extensions for Non-ASCII Text\fR,
+.br
+RFC\-2048:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part Four:
+.br
+   Registration Procedures\fR,
+.br
+RFC\-2049:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part Five:
+.br
+   Conformance Criteria and Examples\fR.
+.De
+`+folder' defaults to the current folder
+.Ds
+`msgs' defaults to cur
+.Ds
+`\-nocheck'
+.Ds
+`\-headers'
+.Ds
+`\-realsize'
+.Ds
+`\-rcache ask'
+.Ds
+`\-wcache ask'
+.Ds
+`\-noverbose'
+.Co
+If a folder is given, it will become the current folder.  The last
+message selected will become the current message.
+.En
diff --git a/man/mhmail.man b/man/mhmail.man
new file mode 100644 (file)
index 0000000..5902b7c
--- /dev/null
@@ -0,0 +1,71 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH MHMAIL %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+mhmail \- send or read mail
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+mhmail
+\%[
+addrs\ ... 
+\%[\-body\ text]
+\%[\-cc\ addrs\ ...]
+.br
+\%[\-from\ addr]
+\%[\-subject subject]]
+.br
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fImhmail\fR is intended as a replacement for the standard Berkeley
+mail program (\fImail\fR(1) or \fImailx\fR(1)), which is compatible
+with \fInmh\fR.  This program is intended for the use of programs such
+as \fIcron\fR(1), which expect to send mail automatically to various
+users.  It is also used by various \fInmh\fR commands to mail various
+error notifications.  Although \fImhmail\fR can be used interactively,
+it is recommended that \fIcomp\fR(1) and \fIsend\fR(1) be used instead
+to send messages.
+
+When invoked without arguments, it simply invokes \fIinc\fR(1) to
+incorporate new messages from the user's maildrop.  When one or more users
+is specified, a message is read from the standard input and spooled to
+a temporary file.  \fImhmail\fR then invokes \fIpost\fR(8) with the
+name of the temporary file as its argument to deliver the message to
+the specified user.
+
+The `\-subject\ subject' switch can be used to specify the
+\*(lqSubject:\*(rq field of the message.
+
+By default, \fImhmail\fR will read the message to be sent from the
+standard input.  You can specify the text of the message at the command
+line with the `\-body\ text' switch.  If the standard input has zero
+length, \fImhmail\fR will not send the message.  You can use the switch
+`\-body\ ""' to force an empty message.
+
+Normally, addresses appearing as arguments are put in the \*(lqTo:\*(rq
+field.  If the `\-cc' switch is used, all addresses following it are
+placed in the \*(lqcc:\*(rq field.
+
+By using `\-from\ addr', you can specify the \*(lqFrom:\*(rq header of
+the draft.  Naturally, \fIpost\fR will fill\-in the \*(lqSender:\*(rq
+header correctly.
+.Fi
+^%bindir%/inc~^Program to incorporate maildrop into folder
+^%libdir%/post~^Program to deliver a message
+^/tmp/mhmail*~^Temporary copy of message
+.Pr
+None
+.Sa
+inc(1), post(8)
+.De
+None
+.Co
+If \fIinc\fR is invoked, then \fIinc\fR's context changes occur.
+.En
diff --git a/man/mhn.man b/man/mhn.man
new file mode 100644 (file)
index 0000000..e4719dc
--- /dev/null
@@ -0,0 +1,715 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH MHN %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+mhn \- display/list/store/cache MIME messages
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+mhn \%[+folder] \%[msgs] \%[\-file file]
+.br
+\%[\-part number]... \%[\-type content]...
+.br
+\%[\-show] \%[\-noshow]
+\%[\-list] \%[-nolist]
+.br
+\%[\-store] \%[\-nostore]
+\%[\-cache] \%[\-nocache]
+.br
+\%[\-headers] \%[\-noheaders]
+\%[\-realsize] \%[\-norealsize]
+.br
+\%[\-serialonly] \%[\-noserialonly]
+\%[\-form formfile]
+.br
+\%[\-pause] \%[\-nopause]
+\%[\-auto] \%[\-noauto]
+.br
+\%[\-rcache policy] \%[\-wcache policy]
+\%[\-check] \%[\-nocheck]
+.br
+\%[\-verbose] \%[\-noverbose]
+\%[\-version]
+\%[\-help]
+
+.ti .5i
+mhn \-build\ file
+.br
+\%[\-ebcdicsafe] \%[\-noebcdicsafe]
+.br
+\%[\-rfc934mode] \%[\-norfc934mode]
+.in -.5i
+
+.SH DESCRIPTION
+MHN SHOULD BE CONSIDERED DEPRECATED.  IT IS RETAINED FOR THE PURPOSE
+OF BACKWARD COMPATIBILITY, BUT EVERYONE SHOULD MIGRATE TO USING THE
+COMMANDS MHSHOW, MHSTORE, AND MHLIST.  CHECK THE INDIVIDUAL MAN PAGES
+FOR DETAILS.
+
+The \fImhn\fR command allows you to display, list, store, or cache the
+contents of a MIME (multi-media) messages.
+
+\fImhn\fR manipulates multi-media messages as specified in RFC\-2045
+thru RFC\-2049.  Currently \fImhn\fR only supports encodings in message
+bodies, and does not support the encoding of message headers as specified
+in RFC\-2047.
+
+The switches `\-list', `\-show', `\-store', and `-cache' direct
+the operation of \fImhn\fR.  Only one of these switches may be used
+at a time.  These switches are used to operate on the content of
+each of the named messages.  By using the `\-part' and `\-type'
+switches, you may limit the scope of the given operation to particular
+subparts (of a multipart content) and/or particular content types.
+
+The switch `\-build' is used to construct a MIME message.  It is
+for backward compatibility and instructs \fImhn\fR to execute the
+\fImhbuild\fR command.  It is preferred that you use the \fImhbuild\fR
+command directly.  See the \fImhbuild\fR(1) man page for details.
+
+The option `\-file\ file' directs \fImhn\fR to use the specified file as
+the source message, rather than a message from a folder.  If you specify
+this file as \*(lq-\*(rq, then \fImhn\fR will accept the source message
+on the standard input.  Note that the file, or input from standard input
+should be a validly formatted message, just like any other \fInmh\fR
+message.  It should \fBNOT\fR be in mail drop format (to convert a file in
+mail drop format to a folder of \fInmh\fR messages, see \fIinc\fR\0(1)).
+
+A part specification consists of a series of numbers separated by dots.
+For example, in a multipart content containing three parts, these
+would be named as 1, 2, and 3, respectively.  If part 2 was also a
+multipart content containing two parts, these would be named as 2.1 and
+2.2, respectively.  Note that the `\-part' switch is effective for only
+messages containing a multipart content.  If a message has some other
+kind of content, or if the part is itself another multipart content, the
+`\-part' switch will not prevent the content from being acted upon.
+
+A content specification consists of a content type and a subtype.
+The initial list of \*(lqstandard\*(rq content types and subtypes can
+be found in RFC\-2046.
+.ne 18
+A list of commonly used contents is briefly reproduced here:
+.sp
+.nf
+.in +.5i
+.ta \w'application  'u
+Type   Subtypes
+----   --------
+text   plain, enriched
+multipart      mixed, alternative, digest, parallel
+message        rfc822, partial, external-body
+application    octet-stream, postscript
+image  jpeg, gif, png
+audio  basic
+video  mpeg
+.re
+.in -.5i
+.fi
+.sp
+A legal MIME message must contain a subtype specification.
+.PP
+To specify a content, regardless of its subtype, just use the
+name of the content, e.g., \*(lqaudio\*(rq.  To specify a specific
+subtype, separate the two with a slash, e.g., \*(lqaudio/basic\*(rq.
+Note that regardless of the values given to the `\-type' switch, a
+multipart content (of any subtype listed above) is always acted upon.
+Further note that if the `\-type' switch is used, and it is desirable to
+act on a message/external-body content, then the `\-type' switch must
+be used twice: once for message/external-body and once for the content
+externally referenced.
+
+.Uh "Checking the Contents"
+The `\-check' switch tells \fImhn\fR to check each content for an
+integrity checksum.  If a content has such a checksum (specified as a
+Content-MD5 header field), then \fImhn\fR will attempt to verify the
+integrity of the content.
+
+.Uh "Listing the Contents"
+The `\-list' switch tells \fImhn\fR to list the table of contents
+associated with the named messages.
+
+The `\-headers' switch indicates that
+a one-line banner should be displayed above the listing.  The `\-realsize'
+switch tells \fImhn\fR to evaluate the \*(lqnative\*(rq (decoded) format
+of each content prior to listing.  This provides an accurate count at
+the expense of a small delay.  If the `\-verbose' switch is present, then
+the listing will show any \*(lqextra\*(rq information that is present in
+the message, such as comments in the Content-Type header.
+
+.Uh "Showing the Contents"
+The `\-show' switch tells \fImhn\fR to display the contents of the named
+messages.
+
+The headers of each message are displayed with the \fImhlproc\fR
+(usually \fImhl\fR), using the standard format file \fImhl.headers\fR.
+You may specify an alternate format file with the `\-form formfile'
+switch.  If the format file \fImhl.null\fR is specified, then the display
+of the message headers is suppressed.
+
+The method used to display the different contents in the messages bodies
+will be determined by a \*(lqdisplay string\*(rq.  To find the display
+string, \fImhn\fR will first search your profile for an entry of the form:
+.sp
+.in +.5i
+mhn-show-<type>/<subtype>
+.in -.5i
+.sp
+to determine the display string.  If this isn't found, \fImhn\fR
+will search for an entry of the form:
+.sp
+.in +.5i
+mhn-show-<type>
+.in -.5i
+.sp
+to determine the display string.
+
+If a display string is found, any escapes (given below) will be expanded.
+The result will be executed under \fB/bin/sh\fR, with the standard input
+set to the content.
+.ne 16
+The display string may contain the following escapes:
+.sp
+.nf
+.in +.5i
+.ta \w'%F  'u
+%a     Insert parameters from Content-Type field
+%e     exclusive execution
+%f     Insert filename containing content
+%F     %e, %f, and stdin is terminal not content
+%l     display listing prior to displaying content
+%p     %l, and ask for confirmation
+%s     Insert content subtype
+%d     Insert content description
+%%     Insert the character %
+.re
+.in -.5i
+.fi
+.sp
+.ne 10
+For those display strings containing the e- or F-escape, \fImhn\fR will
+execute at most one of these at any given time.  Although the F-escape
+expands to be the filename containing the content, the e-escape has no
+expansion as far as the shell is concerned.
+
+When the p-escape prompts for confirmation, typing INTR (usually
+control-C) will tell \fImhn\fR not to display that content.  The p-escape
+can be disabled by specifying the switch `\-nopause'.  Further, when
+\fImhn\fR is display a content, typing QUIT (usually control-\\) will
+tell \fImhn\fR to wrap things up immediately.
+
+Note that if the content being displayed is multipart, but not one of
+the subtypes listed above, then the f- and F-escapes expand to multiple
+filenames, one for each subordinate content.  Further, stdin is not
+redirected from the terminal to the content.
+
+If a display string is not found, \fImhn\fR has several default values:
+.sp
+.nf
+.in +.5i
+mhn-show-text/plain: %pmoreproc '%F'
+mhn-show-message/rfc822: %pshow -file '%F'
+.in -.5i
+.fi
+.sp
+If a subtype of type text doesn't have a profile entry, it will be
+treated as text/plain.
+
+\fImhn\fR has default methods for handling multipart messages of subtype
+mixed, alternative, parallel, and digest.  Any unknown subtype of type
+multipart (without a profile entry), will be treated as multipart/mixed.
+
+If none of these apply, then \fImhn\fR will check to see if the message
+has an application/octet-stream content with parameter \*(lqtype=tar\*(rq.
+If so, \fImhn\fR will use an appropriate command.  If not, \fImhn\fR
+will complain.
+
+.ne 10
+Example entries might be:
+.sp
+.nf
+.in +.5i
+mhn-show-audio/basic: raw2audio 2>/dev/null | play
+mhn-show-image: xv '%f'
+mhn-show-application/PostScript: lpr -Pps
+.in -.5i
+.fi
+.sp
+Note that when using the f- or F-escape, it's a good idea to use
+single-quotes around the escape.  This prevents misinterpretation by
+the shell of any funny characters that might be present in the filename.
+
+Finally, \fImhn\fR will process each message serially\0--\0it won't start
+showing the next message until all the commands executed to display the
+current message have terminated.  In the case of a multipart content
+(of any subtype listed above), the content contains advice indicating if
+the parts should be displayed serially or in parallel.  Because this may
+cause confusion, particularly on uni-window displays, the `\-serialonly'
+switch can be given to tell \fImhn\fR to never display parts in parallel.
+
+.Uh "Showing Alternate Character Sets"
+Because a content of type text might be in a non-ASCII character
+set, when \fImhn\fR encounters a \*(lqcharset\*(rq parameter for
+this content, it checks if your terminal can display this character
+set natively.  \fIMhn\fR checks this by examining the the environment
+variable MM_CHARSET.  If the value of this environment variable is equal
+to the value of the charset parameter, then \fImhn\fR assumes it can
+display this content without any additional setup.  If this environment
+variable is not set, \fImhn\fR will assume a value of \*(lqUS-ASCII\*(rq.
+If the character set cannot be displayed natively, then \fImhn\fR will
+look for an entry of the form:
+.sp
+.in +.5i
+mhn-charset-<charset>
+.in -.5i
+.sp
+which should contain a command creating an environment to render
+the character set.  This command string should containing a single
+\*(lq%s\*(rq, which will be filled-in with the command to display the
+content.
+
+Example entries might be:
+.sp
+.in +.5i
+mhn-charset-iso-8859-1: xterm -fn '-*-*-medium-r-normal-*-*-120-*-*-c-*-iso8859-*' -e %s
+.in -.5i
+or
+.in +.5i
+mhn-charset-iso-8859-1: '%s'
+.in -.5i
+.sp
+The first example tells \fImhn\fR to start \fIxterm\fR and load the
+appropriate character set for that message content.  The second example
+tells \fImhn\fR that your pager (or other program handling that content
+type) can handle that character set, and that no special processing is
+needed beforehand.
+.sp
+Note that many pagers strip off the high-order bit or have problems
+displaying text with the high-order bit set.  However, the pager
+\fIless\fR has support for single-octet character sets.  The source
+to \fIless\fR is available on many ftp sites carrying free software.
+In order to view messages sent in the ISO-8859-1 character set using
+\fIless\fR,
+.ne 9
+put these lines in your \&.login file:
+.sp
+.nf
+.in +.5i
+setenv LESSCHARSET latin1
+setenv LESS "-f"
+.in -.5i
+.fi
+.sp
+The first line tells \fIless\fR to use the ISO-8859-1 definition for
+determining whether a character is \*(lqnormal\*(rq, \*(lqcontrol\*(lq,
+or \*(lqbinary\*(rq.  The second line tells \fIless\fR not to warn you
+if it encounters a file that has non-ASCII characters.  Then, simply
+set the \fBmoreproc\fR profile entry to \fIless\fR, and it will get
+called automatically.  (To handle other single-octet character sets,
+look at the \fIless\fR\0(1) manual entry for information about the
+\fBLESSCHARDEF\fR environment variable.)
+
+.Uh "Storing the Contents"
+The `\-store' switch tells \fImhn\fR to store the contents of the
+named messages in \*(lqnative\*(rq (decoded) format.  Two things must
+be determined: the directory to store the content, and the filenames.
+Files are written in the directory given by the \fBnmh-storage\fR
+profile entry,
+.ne 6
+e.g.,
+.sp
+.in +.5i
+nmh-storage: /tmp
+.in -.5i
+.sp
+If this entry isn't present,
+the current working directory is used.
+
+If the `\-auto' switch is given, then \fImhn\fR will check if the
+message contains information indicating the filename that should be
+used to store the content.  This information should be specified as the
+attribute \*(lqname=filename\*(rq in the Content-Type header for the
+content you are storing.  For security reasons, this filename will be
+ignored if it begins with the character '/', '.', '|', or '!', or if it
+contains the character '%'.  For the sake of security, this switch is
+not the default, and it is recommended that you do NOT put the `\-auto'
+switch in your \&.mh\(ruprofile file.
+
+If the `\-auto' switch is not given (or is being ignored for
+security reasons) then \fImhn\fR will look in the user's profile for
+a \*(lqformatting string\*(rq to determine how the different contents
+should be stored.  First, \fImhn\fR will look for an entry of the form:
+.sp
+.in +.5i
+mhn-store-<type>/<subtype>
+.in -.5i
+.sp
+to determine the formatting string.  If this isn't found, \fImhn\fR will
+look for an entry of the form:
+.sp
+.in +.5i
+mhn-store-<type>
+.in -.5i
+.sp
+to determine the formatting string.
+
+If the formatting string starts with a \*(lq+\*(rq character, then
+content is stored in the named folder.  A formatting string consisting
+solely of a \*(lq+\*(rq character is interpreted to be the current folder.
+
+If the formatting string consists solely of a \*(lq-\*(rq character,
+then the content is sent to the standard output.
+
+If the formatting string starts with a '|', then the display string will
+represent a command for \fImhn\fR to execute which should ultimately
+store the content.  The content will be passed to the standard input of
+the command.  Before the command is executed, \fImhn\fR will change to
+the appropriate directory, and any escapes (given below) in the display
+string will be expanded.
+
+Otherwise the formatting string will represent a pathname in which to
+store the content.  If the formatting string starts with a '/', then the
+content will be stored in the full path given, else the file name will
+be relative to the value of \fBnmh-storage\fR or the current working
+directory.  Any escapes (given below) will be expanded, except for the
+a-escape.
+
+A command or pathname formatting string may contain the following escapes.
+If the content isn't part of a multipart (of any subtype listed above)
+content, the p-escapes are ignored.
+.sp
+.nf
+.in +.5i
+.ta \w'%P  'u
+%a     Parameters from Content-type  (only valid with command)
+%m     Insert message number
+%P     Insert part number with leading dot
+%p     Insert part number without leading dot
+%t     Insert content type
+%s     Insert content subtype
+%%     Insert character %
+.re
+.in -.5i
+.fi
+.sp
+If no formatting string is found, \fImhn\fR will check to see if the
+content is application/octet-stream with parameter \*(lqtype=tar\*(rq.
+If so, \fImhn\fR will choose an appropriate filename.  If the content
+is not application/octet-stream, then \fImhn\fR will check to see if the
+content is a message.  If so, \fImhn\fR will use the value \*(lq+\*(rq.
+As a last resort, \fImhn\fR will use the value \*(lq%m%P.%s\*(rq.
+
+.ne 10
+Example profile entries might be:
+.sp
+.nf
+.in +.5i
+mhn-store-text: %m%P.txt
+mhn-store-text: +inbox
+mhn-store-message/partial: +
+mhn-store-audio/basic: | raw2audio -e ulaw -s 8000 -c 1 > %m%P.au
+mhn-store-image/jpeg: %m%P.jpg
+mhn-store-application/PostScript: %m%P.ps
+.in -.5i
+.fi
+.sp
+.Uh "Reassembling Messages of Type message/partial"
+When asked to store a content containing a partial message, \fImhn\fR
+will try to locate all of the portions and combine them accordingly.
+The default is to store the combined parts as a new message in the
+current folder, although this can be changed using formatting
+strings as discussed above.  Thus, if someone has sent you a message
+in several parts (such as the output from \fIsendfiles\fR), you can
+easily reassemble them all into a single message in the following
+fashion:
+.sp
+.nf
+.in +.5i
+% mhn -list 5-8
+ msg part  type/subtype             size description
+   5       message/partial           47K part 1 of 4
+   6       message/partial           47K part 2 of 4
+   7       message/partial           47K part 3 of 4
+   8       message/partial           18K part 4 of 4
+% mhn -store 5-8
+reassembling partials 5,6,7,8 to folder inbox as message 9
+% mhn -list -verbose 9
+ msg part  type/subtype             size description
+   9       application/octet-stream 118K
+             (extract with uncompress | tar xvpf -)
+             type=tar
+             conversions=compress
+.in -.5i
+.fi
+.sp
+This will store exactly one message, containing the sum of the
+parts.  It doesn't matter whether the partials are specified in
+order, since \fImhn\fR will sort the partials, so that they are
+combined in the correct order.  But if \fImhn\fR can not locate
+every partial necessary to reassemble the message, it will not
+store anything.
+
+.Uh "External Access"
+For contents of type message/external-body,
+.ne 12
+\fImhn\fR supports these access-types:
+.sp
+.nf
+.in +.5i
+afs
+anon-ftp
+ftp
+local-file
+mail-server
+.in -.5i
+.fi
+.sp
+For the \*(lqanon-ftp\*(rq and \*(lqftp\*(rq access types,
+\fImhn\fR will look for the \fBnmh-access-ftp\fR
+profile entry,
+.ne 6
+e.g.,
+.sp
+.in +.5i
+nmh-access-ftp: myftp.sh
+.in -.5i
+.sp
+to determine the pathname of a program to perform the FTP retrieval.
+.ne 14
+This program is invoked with these arguments:
+.sp
+.nf
+.in +.5i
+domain name of FTP-site
+username
+password
+remote directory
+remote filename
+local filename
+\*(lqascii\*(rq or \*(lqbinary\*(rq
+.in -.5i
+.fi
+.sp
+The program should terminate with an exit status of zero if the
+retrieval is successful, and a non-zero exit status otherwise.
+
+If this entry is not provided, then \fImhn\fR will use a simple
+built-in FTP client to perform the retrieval.
+
+.Uh "The Content Cache"
+When \fImhn\fR encounters an external content containing a
+\*(lqContent-ID:\*(rq field, and if the content allows caching, then
+depending on the caching behavior of \fImhn\fR, the content might be
+read from or written to a cache.
+
+The caching behavior of \fImhn\fR is controlled with the `\-rcache'
+and `\-wcache' switches, which define the policy for reading from,
+and writing to, the cache, respectively.  One of four policies may be
+specified: \*(lqpublic\*(rq, indicating that \fImhn\fR should make use
+of a publically-accessible content cache; \*(lqprivate\*(rq, indicating
+that \fImhn\fR should make use of the user's private content cache;
+\*(lqnever\*(rq, indicating that \fImhn\fR should never make use of
+caching; and, \*(lqask\*(rq, indicating that \fImhn\fR should ask
+the user.
+
+There are two directories where contents may be cached: the profile entry
+\fBnmh-cache\fR names a directory containing world-readable contents, and,
+the profile entry \fBnmh-private-cache\fR names a directory containing
+private contents.  The former should be an absolute (rooted) directory
+name.
+.ne 6
+For example,
+.sp
+.in +.5i
+nmh-cache: /tmp
+.in -.5i
+.sp
+might be used if you didn't care that the cache got wiped after each
+reboot of the system.  The latter is interpreted relative to the user's
+nmh directory, if not rooted,
+.ne 6
+e.g.,
+.sp
+.in +.5i
+nmh-private-cache: .cache
+.in -.5i
+.sp
+(which is the default value).
+
+.Uh "Caching the Contents"
+When you encounter a content of type message/external-body with access
+type \*(lqmail-server\*(rq, \fImhn\fR will ask you if may send a message
+to a mail-server requesting the content,
+.ne 14
+e.g.,
+.sp
+.nf
+.in +.5i
+% show 1
+Retrieve content by asking mail-server@...
+
+SEND file
+
+? yes
+mhn: request sent
+.in -.5i
+.fi
+.sp
+Regardless of your decision,
+\fImhn\fR can't perform any other processing on the content.
+
+However, if \fImhn\fR is allowed to request the content, then when it
+arrives, there should be a top-level \*(lqContent-ID:\*(rq field which
+corresponds to the value in the original message/external-body content.
+You should now use the `-cache' switch to tell \fImhn\fR to enter the
+arriving content into the content cache,
+.ne 8
+e.g.,
+.sp
+.nf
+.in +.5i
+% mhn -cache 2
+caching message 2 as file ...
+.in -.5i
+.fi
+.sp
+You can then re-process the original message/external-body content, and
+\*(lqthe right thing should happen\*(rq,
+.ne 8
+e.g.,
+.sp
+.nf
+.in +.5i
+% show 1
+\0...
+.in -.5i
+.fi
+
+.Uh "User Environment"
+Because the display environment in which \fImhn\fR operates may vary for
+different machines, \fImhn\fR will look for the environment variable
+\fB$MHN\fR.  If present, this specifies the name of an additional
+user profile which should be read.  Hence, when a user logs in on a
+particular display device, this environment variable should be set to
+refer to a file containing definitions useful for the given display device.
+Normally, only entries that deal with the methods to display different
+content type and subtypes
+.sp
+.in +.5i
+mhn-show-<type>/<subtype>
+.br
+mhn-show-<type>
+.in -.5i
+.sp
+need be present in this additional profile.
+Finally,
+\fImhn\fR will attempt to consult one other additional user profile,
+.ne 6
+e.g.,
+.sp
+.in +.5i
+%etcdir%/mhn.defaults
+.in -.5i
+.sp
+which is created automatically during nmh installation.
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+^$MHN~^Additional profile entries
+^%etcdir%/mhn.defaults~^System default MIME profile entries
+^%etcdir%/mhl.headers~^The headers template
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Current\-Folder:~^To find the default current folder
+.Ps
+^mhlproc:~^Default program to display message headers
+.Ps
+^nmh-access-ftp:~^Program to retrieve contents via FTP
+.Ps
+^nmh-cache~^Public directory to store cached external contents
+.Ps
+^nmh-private-cache~^Personal directory to store cached external contents
+.Ps
+^mhn-charset-<charset>~^Template for environment to render character sets
+.Ps
+^mhn-show-<type>*~^Template for displaying contents
+.Ps
+^nmh-storage~^Directory to store contents
+.Ps
+^mhn-store-<type>*~^Template for storing contents
+.Ps
+^moreproc:~^Default program to display text/plain content
+.Sa
+mhbuild(1), mhl(1), sendfiles(1)
+.br
+RFC\-934:
+.br
+   \fIProposed Standard for Message Encapsulation\fR,
+.br
+RFC\-2045:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part One:
+.br
+   Format of Internet Message Bodies\fR,
+.br
+RFC\-2046:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part Two:
+.br
+   Media Types\fR,
+.br
+RFC\-2047:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part Three:
+.br
+   Message Header Extensions for Non-ASCII Text\fR,
+.br
+RFC\-2048:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part Four:
+.br
+   Registration Procedures\fR,
+.br
+RFC\-2049:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part Five:
+.br
+   Conformance Criteria and Examples\fR.
+.De
+`+folder' defaults to the current folder
+.Ds
+`msgs' defaults to cur
+.Ds
+`\-noauto'
+.Ds
+`\-nocache'
+.Ds
+`\-nocheck'
+.Ds
+`\-form mhl.headers'
+.Ds
+`\-headers'
+.Ds
+`\-pause'
+.Ds
+`\-rcache ask'
+.Ds
+`\-realsize'
+.Ds
+`\-noserialonly'
+.Ds
+`\-show'
+.Ds
+`\-noverbose'
+.Ds
+`\-wcache ask'
+.Co
+If a folder is given, it will become the current folder.  The last
+message selected will become the current message.
+.Bu
+Partial messages contained within a multipart content are not reassembled
+with the `\-store' switch.
+.En
diff --git a/man/mhparam.man b/man/mhparam.man
new file mode 100644 (file)
index 0000000..a4af2b5
--- /dev/null
@@ -0,0 +1,81 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH MHPARAM %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+mhparam \- print nmh profile components
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+mhparam
+\%[components]
+\%[-all]
+\%[-component] \%[-nocomponent]
+.br
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fIMhparam\fR writes the value of the specified profile component to the
+standard output separated by newlines.  If the profile component is not
+present, the default value (or nothing if there is no default) is printed.
+
+If the switch `\-component' is given, then the component name is displayed
+along with the profile components value.  This can be disabled with the
+switch `\-nocomponent'.
+
+If more than one component is specified in the `components' list, then
+the switch `\-component' is on by default.  If only one component is
+specified, then the switch `\-nocomponent' is on by default.
+
+If `\-all' is specified, then all components in the nmh profile are
+displayed and other arguments are ignored.
+
+Examples:
+
+.nf
+.ta \w'AliasFile:'u+2n
+.in +.5i
+% mhparam path
+Mail
+
+% mhparam mhlproc
+%libdir%/mhl
+
+% mhparam \-component path
+Path: Mail
+
+% mhparam AliasFile rmmproc
+AliasFile: aliases
+rmmproc: rmmproc
+
+% mhparam \-nocomponent AliasFile rmmproc
+aliases
+rmmproc
+.in -.5i
+.fi
+
+\fIMhparam\fR is also useful in back\-quoted operations:
+
+.nf
+.in +.5i
+% fgrep cornell.edu `mhpath +`/`mhparam aliasfile`
+
+.in -.5i
+.fi
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+.Sa
+mh-profile\|(5)
+.De
+`\-nocomponent' if only one component is specified
+`\-component' if more than one component is specified
+.Ds
+`components' defaults to none
+.Co
+None
+.En
diff --git a/man/mhpath.man b/man/mhpath.man
new file mode 100644 (file)
index 0000000..ff17b73
--- /dev/null
@@ -0,0 +1,136 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH MHPATH %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+mhpath \- print full pathnames of nmh messages and folders
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+mhpath
+\%[+folder] \%[msgs]
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fIMhpath\fR expands and sorts the message list `msgs' and writes the full
+pathnames of the messages to the standard output separated by newlines.
+If no `msgs' are specified, \fImhpath\fR outputs the folder pathname
+instead.  If the only argument is `+', your nmh \fIPath\fR is output;
+this can be useful is shell scripts.
+
+Contrasted with other nmh commands, a message argument to \fImhpath\fR
+may often be intended for \fIwriting\fR.  Because of this:
+.sp
+1) the name \*(lqnew\*(rq has been added to \fImhpath\fR's list of
+reserved message names (the others are \*(lqfirst\*(rq, \*(lqlast\*(rq,
+\*(lqprev\*(rq, \*(lqnext\*(rq, \*(lqcur\*(rq, and \*(lqall\*(rq).
+The new message is equivalent to the message after the last message
+in a folder (and equivalent to 1 in a folder without messages).
+The \*(lqnew\*(rq message may not be used as part of a message range.
+.sp
+2) Within a message list, the following designations may refer to messages
+that do not exist: a single numeric message name, the single message name
+\*(lqcur\*(rq, and (obviously) the single message name \*(lqnew\*(rq.
+All other message designations must refer to at least one existing
+message.
+.sp
+3) An empty folder is not in itself an error.
+
+Message numbers greater than the highest existing message in a folder
+as part of a range designation are replaced with the next free message
+number.
+
+Examples: The current folder foo contains messages 3 5 6.
+Cur is 4.
+
+.nf
+.in +.5i
+% mhpath
+/r/phyl/Mail/foo
+
+% mhpath all
+/r/phyl/Mail/foo/3
+/r/phyl/Mail/foo/5
+/r/phyl/Mail/foo/6
+
+% mhpath 2001
+/r/phyl/Mail/foo/7
+
+% mhpath 1\-2001
+/r/phyl/Mail/foo/3
+/r/phyl/Mail/foo/5
+/r/phyl/Mail/foo/6
+
+% mhpath new
+/r/phyl/Mail/foo/7
+
+% mhpath last new
+/r/phyl/Mail/foo/6
+/r/phyl/Mail/foo/7
+
+% mhpath last\-new
+bad message list \*(lqlast\-new\*(rq.
+
+% mhpath cur
+/r/phyl/Mail/foo/4
+
+% mhpath 1\-2
+no messages in range \*(lq1\-2\*(rq.
+
+% mhpath first:2
+/r/phyl/Mail/foo/3
+/r/phyl/Mail/foo/5
+
+% mhpath 1 2
+/r/phyl/Mail/foo/1
+/r/phyl/Mail/foo/2
+.in -.5i
+.fi
+
+\fImhpath\fR is also useful in back\-quoted operations:
+
+.nf
+.in +.5i
+% cd `mhpath +inbox`
+
+% echo `mhpath +`
+/r/phyl/Mail
+.in -.5i
+.fi
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Current\-Folder:~^To find the default current folder
+.Sa
+folder(1)
+.De
+`+folder' defaults to the current folder
+.Ds
+`msgs' defaults to none
+.Co
+None
+.Bu
+Like all nmh commands, \fImhpath\fR expands and sorts \%[msgs].  So don't
+expect
+
+.ti +.5i
+mv `mhpath 501 500`
+
+to move 501 to 500.
+Quite the reverse.  But
+
+.ti +.5i
+mv `mhpath 501` `mhpath 500`
+
+will do the trick.
+
+Out of range message 0 is treated far more severely than large out of
+range message numbers.
+.En
diff --git a/man/mhshow.man b/man/mhshow.man
new file mode 100644 (file)
index 0000000..30a925f
--- /dev/null
@@ -0,0 +1,480 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH MHSHOW %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+mhshow \- display MIME messages
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+mhshow \%[+folder] \%[msgs] \%[\-file file]
+.br
+\%[\-part number]... \%[\-type content]...
+.br
+\%[\-serialonly] \%[\-noserialonly]
+\%[\-pause] \%[\-nopause]
+.br
+\%[\-check] \%[\-nocheck]
+\%[\-form formfile]
+.br
+\%[\-rcache policy] \%[\-wcache policy]
+.br
+\%[\-verbose] \%[\-noverbose]
+\%[\-version] \%[\-help]
+.in -.5i
+
+.SH DESCRIPTION
+The \fImhshow\fR command display contents of a MIME (multi-media)
+message or collection of messages.
+
+\fImhshow\fR manipulates multi-media messages as specified in
+RFC\-2045 thru RFC\-2049.  Currently \fImhshow\fR only supports
+encodings in message bodies, and does not support the encoding of
+message headers as specified in RFC\-2047.
+
+By default \fImhshow\fR will display all parts of a multipart
+message.  By using the `\-part' and `\-type' switches, you may
+limit the scope of \fImhshow\fR to particular subparts (of a
+multipart content) and/or particular content types.
+
+The option `\-file\ file' directs \fImhshow\fR to use the specified file as
+the source message, rather than a message from a folder.  If you specify
+this file as \*(lq-\*(rq, then \fImhshow\fR will accept the source message
+on the standard input.  Note that the file, or input from standard input
+should be a validly formatted message, just like any other \fInmh\fR
+message.  It should \fBNOT\fR be in mail drop format (to convert a file in
+mail drop format to a folder of \fInmh\fR messages, see \fIinc\fR\0(1)).
+
+A part specification consists of a series of numbers separated by dots.
+For example, in a multipart content containing three parts, these
+would be named as 1, 2, and 3, respectively.  If part 2 was also a
+multipart content containing two parts, these would be named as 2.1 and
+2.2, respectively.  Note that the `\-part' switch is effective for only
+messages containing a multipart content.  If a message has some other
+kind of content, or if the part is itself another multipart content, the
+`\-part' switch will not prevent the content from being acted upon.
+
+A content specification consists of a content type and a subtype.
+The initial list of \*(lqstandard\*(rq content types and subtypes can
+be found in RFC\-2046.
+.ne 18
+A list of commonly used contents is briefly reproduced here:
+.sp
+.nf
+.in +.5i
+.ta \w'application  'u
+Type   Subtypes
+----   --------
+text   plain, enriched
+multipart      mixed, alternative, digest, parallel
+message        rfc822, partial, external-body
+application    octet-stream, postscript
+image  jpeg, gif, png
+audio  basic
+video  mpeg
+.re
+.in -.5i
+.fi
+.sp
+A legal MIME message must contain a subtype specification.
+.PP
+To specify a content, regardless of its subtype, just use the
+name of the content, e.g., \*(lqaudio\*(rq.  To specify a specific
+subtype, separate the two with a slash, e.g., \*(lqaudio/basic\*(rq.
+Note that regardless of the values given to the `\-type' switch, a
+multipart content (of any subtype listed above) is always acted upon.
+Further note that if the `\-type' switch is used, and it is desirable to
+act on a message/external-body content, then the `\-type' switch must
+be used twice: once for message/external-body and once for the content
+externally referenced.
+
+.Uh "Unseen Sequence"
+
+If the profile entry \*(lqUnseen\-Sequence\*(rq is present and
+non\-empty, then \fImhshow\fR will remove each of the messages shown
+from each sequence named by the profile entry.
+
+.Uh "Checking the Contents"
+The `\-check' switch tells \fImhshow\fR to check each content for an
+integrity checksum.  If a content has such a checksum (specified as a
+Content-MD5 header field), then \fImhshow\fR will attempt to verify the
+integrity of the content.
+
+.Uh "Showing the Contents"
+The headers of each message are displayed with the \fImhlproc\fR
+(usually \fImhl\fR), using the standard format file \fImhl.headers\fR.
+You may specify an alternate format file with the `\-form formfile'
+switch.  If the format file \fImhl.null\fR is specified, then the display
+of the message headers is suppressed.
+
+The method used to display the different contents in the messages bodies
+will be determined by a \*(lqdisplay string\*(rq.  To find the display
+string, \fImhshow\fR will first search your profile for an entry of the
+form:
+.sp
+.in +.5i
+mhshow-show-<type>/<subtype>
+.in -.5i
+.sp
+to determine the display string.  If this isn't found, \fImhshow\fR
+will search for an entry of the form:
+.sp
+.in +.5i
+mhshow-show-<type>
+.in -.5i
+.sp
+to determine the display string.
+
+If a display string is found, any escapes (given below) will be expanded.
+The result will be executed under \fB/bin/sh\fR, with the standard input
+set to the content.
+.ne 16
+The display string may contain the following escapes:
+.sp
+.nf
+.in +.5i
+.ta \w'%F  'u
+%a     Insert parameters from Content-Type field
+%e     exclusive execution
+%f     Insert filename containing content
+%F     %e, %f, and stdin is terminal not content
+%l     display listing prior to displaying content
+%p     %l, and ask for confirmation
+%s     Insert content subtype
+%d     Insert content description
+%%     Insert the character %
+.re
+.in -.5i
+.fi
+.sp
+.ne 10
+For those display strings containing the e- or F-escape, \fImhshow\fR will
+execute at most one of these at any given time.  Although the F-escape
+expands to be the filename containing the content, the e-escape has no
+expansion as far as the shell is concerned.
+
+When the p-escape prompts for confirmation, typing INTR (usually
+control-C) will tell \fImhshow\fR not to display that content.
+The p-escape can be disabled by specifying the switch `\-nopause'.
+Further, when \fImhshow\fR is display a content, typing QUIT (usually
+control-\\) will tell \fImhshow\fR to wrap things up immediately.
+
+Note that if the content being displayed is multipart, but not one of
+the subtypes listed above, then the f- and F-escapes expand to multiple
+filenames, one for each subordinate content.  Further, stdin is not
+redirected from the terminal to the content.
+
+If a display string is not found, \fImhshow\fR has several default values:
+.sp
+.nf
+.in +.5i
+mhshow-show-text/plain: %pmoreproc '%F'
+mhshow-show-message/rfc822: %pshow -file '%F'
+.in -.5i
+.fi
+.sp
+If a subtype of type text doesn't have a profile entry, it will be
+treated as text/plain.
+
+\fImhshow\fR has default methods for handling multipart messages of subtype
+mixed, alternative, parallel, and digest.  Any unknown subtype of type
+multipart (without a profile entry), will be treated as multipart/mixed.
+
+If none of these apply, then \fImhshow\fR will check to see if the message
+has an application/octet-stream content with parameter \*(lqtype=tar\*(rq.
+If so, \fImhshow\fR will use an appropriate command.  If not, \fImhshow\fR
+will complain.
+
+.ne 10
+Example entries might be:
+.sp
+.nf
+.in +.5i
+mhshow-show-audio/basic: raw2audio 2>/dev/null | play
+mhshow-show-image: xv '%f'
+mhshow-show-application/PostScript: lpr -Pps
+.in -.5i
+.fi
+.sp
+Note that when using the f- or F-escape, it's a good idea to use
+single-quotes around the escape.  This prevents misinterpretation by
+the shell of any funny characters that might be present in the filename.
+
+Finally, \fImhshow\fR will process each message serially\0--\0it won't start
+showing the next message until all the commands executed to display the
+current message have terminated.  In the case of a multipart content
+(of any subtype listed above), the content contains advice indicating if
+the parts should be displayed serially or in parallel.  Because this may
+cause confusion, particularly on uni-window displays, the `\-serialonly'
+switch can be given to tell \fImhshow\fR to never display parts in parallel.
+
+.Uh "Showing Alternate Character Sets"
+Because a content of type text might be in a non-ASCII character
+set, when \fImhshow\fR encounters a \*(lqcharset\*(rq parameter for
+this content, it checks if your terminal can display this character
+set natively.  \fIMhn\fR checks this by examining the the environment
+variable MM_CHARSET.  If the value of this environment variable is equal
+to the value of the charset parameter, then \fImhshow\fR assumes it can
+display this content without any additional setup.  If this environment
+variable is not set, \fImhshow\fR will assume a value of \*(lqUS-ASCII\*(rq.
+If the character set cannot be displayed natively, then \fImhshow\fR will
+look for an entry of the form:
+.sp
+.in +.5i
+mhshow-charset-<charset>
+.in -.5i
+.sp
+which should contain a command creating an environment to render
+the character set.  This command string should containing a single
+\*(lq%s\*(rq, which will be filled-in with the command to display the
+content.
+
+Example entries might be:
+.sp
+.in +.5i
+mhshow-charset-iso-8859-1: xterm -fn '-*-*-medium-r-normal-*-*-120-*-*-c-*-iso8859-*' -e %s
+.in -.5i
+or
+.in +.5i
+mhshow-charset-iso-8859-1: '%s'
+.in -.5i
+.sp
+The first example tells \fImhshow\fR to start \fIxterm\fR and load the
+appropriate character set for that message content.  The second example
+tells \fImhshow\fR that your pager (or other program handling that content
+type) can handle that character set, and that no special processing is
+needed beforehand.
+.sp
+Note that many pagers strip off the high-order bit or have problems
+displaying text with the high-order bit set.  However, the pager
+\fIless\fR has support for single-octet character sets.  The source
+to \fIless\fR is available on many ftp sites carrying free software.
+In order to view messages sent in the ISO-8859-1 character set using
+\fIless\fR,
+.ne 9
+put these lines in your \&.login file:
+.sp
+.nf
+.in +.5i
+setenv LESSCHARSET latin1
+setenv LESS "-f"
+.in -.5i
+.fi
+.sp
+The first line tells \fIless\fR to use the ISO-8859-1 definition for
+determining whether a character is \*(lqnormal\*(rq, \*(lqcontrol\*(lq,
+or \*(lqbinary\*(rq.  The second line tells \fIless\fR not to warn you
+if it encounters a file that has non-ASCII characters.  Then, simply
+set the \fBmoreproc\fR profile entry to \fIless\fR, and it will get
+called automatically.  (To handle other single-octet character sets,
+look at the \fIless\fR\0(1) manual entry for information about the
+\fBLESSCHARDEF\fR environment variable.)
+
+.Uh "Messages of Type message/partial"
+\fImhshow\fR cannot directly display messages of type partial.
+You must reassemble them first into a normal message using
+\fImhstore\fR.  Check the man page for \fImhstore\fR for details.
+
+.Uh "External Access"
+For contents of type message/external-body,
+.ne 12
+\fImhshow\fR supports these access-types:
+.sp
+.nf
+.in +.5i
+afs
+anon-ftp
+ftp
+local-file
+mail-server
+.in -.5i
+.fi
+.sp
+For the \*(lqanon-ftp\*(rq and \*(lqftp\*(rq access types,
+\fImhshow\fR will look for the \fBnmh-access-ftp\fR
+profile entry,
+.ne 6
+e.g.,
+.sp
+.in +.5i
+nmh-access-ftp: myftp.sh
+.in -.5i
+.sp
+to determine the pathname of a program to perform the FTP retrieval.
+.ne 14
+This program is invoked with these arguments:
+.sp
+.nf
+.in +.5i
+domain name of FTP-site
+username
+password
+remote directory
+remote filename
+local filename
+\*(lqascii\*(rq or \*(lqbinary\*(rq
+.in -.5i
+.fi
+.sp
+The program should terminate with an exit status of zero if the
+retrieval is successful, and a non-zero exit status otherwise.
+
+If this entry is not provided, then \fImhshow\fR will use a simple
+built-in FTP client to perform the retrieval.
+
+.Uh "The Content Cache"
+When \fImhshow\fR encounters an external content containing a
+\*(lqContent-ID:\*(rq field, and if the content allows caching, then
+depending on the caching behavior of \fImhshow\fR, the content might be
+read from or written to a cache.
+
+The caching behavior of \fImhshow\fR is controlled with the `\-rcache'
+and `\-wcache' switches, which define the policy for reading from,
+and writing to, the cache, respectively.  One of four policies may be
+specified: \*(lqpublic\*(rq, indicating that \fImhshow\fR should make use
+of a publically-accessible content cache; \*(lqprivate\*(rq, indicating
+that \fImhshow\fR should make use of the user's private content cache;
+\*(lqnever\*(rq, indicating that \fImhshow\fR should never make use of
+caching; and, \*(lqask\*(rq, indicating that \fImhshow\fR should ask
+the user.
+
+There are two directories where contents may be cached: the profile entry
+\fBnmh-cache\fR names a directory containing world-readable contents, and,
+the profile entry \fBnmh-private-cache\fR names a directory containing
+private contents.  The former should be an absolute (rooted) directory
+name.
+.ne 6
+For example,
+.sp
+.in +.5i
+nmh-cache: /tmp
+.in -.5i
+.sp
+might be used if you didn't care that the cache got wiped after each
+reboot of the system.  The latter is interpreted relative to the user's
+nmh directory, if not rooted,
+.ne 6
+e.g.,
+.sp
+.in +.5i
+nmh-private-cache: .cache
+.in -.5i
+.sp
+(which is the default value).
+
+.Uh "User Environment"
+Because the display environment in which \fImhshow\fR operates may vary for
+different machines, \fImhshow\fR will look for the environment variable
+\fB$MHSHOW\fR.  If present, this specifies the name of an additional
+user profile which should be read.  Hence, when a user logs in on a
+particular display device, this environment variable should be set to
+refer to a file containing definitions useful for the given display device.
+Normally, only entries that deal with the methods to display different
+content type and subtypes
+.sp
+.in +.5i
+mhshow-show-<type>/<subtype>
+.br
+mhshow-show-<type>
+.in -.5i
+.sp
+need be present in this additional profile.
+Finally,
+\fImhshow\fR will attempt to consult one other additional user profile,
+.ne 6
+e.g.,
+.sp
+.in +.5i
+%etcdir%/mhn.defaults
+.in -.5i
+.sp
+which is created automatically during nmh installation.
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+^$MHSHOW~^Additional profile entries
+^%etcdir%/mhn.defaults~^System default MIME profile entries
+^%etcdir%/mhl.headers~^The headers template
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Current\-Folder:~^To find the default current folder
+.Ps
+^Unseen\-Sequence:~^To name sequences denoting unseen messages
+.Ps
+^mhlproc:~^Default program to display message headers
+.Ps
+^nmh-access-ftp:~^Program to retrieve contents via FTP
+.Ps
+^nmh-cache~^Public directory to store cached external contents
+.Ps
+^nmh-private-cache~^Personal directory to store cached external contents
+.Ps
+^mhshow-charset-<charset>~^Template for environment to render character sets
+.Ps
+^mhshow-show-<type>*~^Template for displaying contents
+.Ps
+^moreproc:~^Default program to display text/plain content
+.Sa
+mhbuild(1), mhl(1), mhlist(1), mhstore(1), sendfiles(1)
+.br
+RFC\-934:
+.br
+   \fIProposed Standard for Message Encapsulation\fR,
+.br
+RFC\-2045:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part One:
+.br
+   Format of Internet Message Bodies\fR,
+.br
+RFC\-2046:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part Two:
+.br
+   Media Types\fR,
+.br
+RFC\-2047:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part Three:
+.br
+   Message Header Extensions for Non-ASCII Text\fR,
+.br
+RFC\-2048:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part Four:
+.br
+   Registration Procedures\fR,
+.br
+RFC\-2049:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part Five:
+.br
+   Conformance Criteria and Examples\fR.
+.De
+`+folder' defaults to the current folder
+.Ds
+`msgs' defaults to cur
+.Ds
+`\-nocheck'
+.Ds
+`\-form mhl.headers'
+.Ds
+`\-pause'
+.Ds
+`\-rcache ask'
+.Ds
+`\-realsize'
+.Ds
+`\-noserialonly'
+.Ds
+`\-noverbose'
+.Ds
+`\-wcache ask'
+.Co
+If a folder is given, it will become the current folder.  The last
+message selected will become the current message.
+.En
diff --git a/man/mhstore.man b/man/mhstore.man
new file mode 100644 (file)
index 0000000..7148dc8
--- /dev/null
@@ -0,0 +1,420 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH MHSTORE %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+mhstore \- store contents of MIME messages into files
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+mhstore \%[+folder] \%[msgs] \%[\-file file]
+.br
+\%[\-part number]... \%[\-type content]...
+.br
+\%[\-auto] \%[\-noauto]
+\%[\-check] \%[\-nocheck]
+.br
+\%[\-rcache policy] \%[\-wcache policy]
+.br
+\%[\-verbose] \%[\-noverbose]
+\%[\-version]
+\%[\-help]
+.in -.5i
+
+.SH DESCRIPTION
+The \fImhstore\fR command allows you to store the contents of a
+collection of MIME (multi-media) messages into files or other
+messages.
+
+\fImhstore\fR manipulates multi-media messages as specified in
+RFC\-2045 thru RFC\-2049.
+
+By default, \fImhstore\fR will store all the parts of each message.
+Each part will be store in a separate file.  The header fields of
+the message are not stored.  By using the `\-part' and `\-type'
+switches, you may limit the scope of \fImhstore\fR to particular
+subparts (of a multipart content) and/or particular content types.
+
+The option `\-file\ file' directs \fImhstore\fR to use the specified
+file as the source message, rather than a message from a folder.
+If you specify this file as \*(lq-\*(rq, then \fImhstore\fR will
+accept the source message on the standard input.  Note that the
+file, or input from standard input should be a validly formatted
+message, just like any other \fInmh\fR message.  It should \fBNOT\fR
+be in mail drop format (to convert a file in mail drop format to
+a folder of \fInmh\fR messages, see \fIinc\fR\0(1)).
+
+A part specification consists of a series of numbers separated by
+dots.  For example, in a multipart content containing three parts,
+these would be named as 1, 2, and 3, respectively.  If part 2 was
+also a multipart content containing two parts, these would be named
+as 2.1 and 2.2, respectively.  Note that the `\-part' switch is
+effective for only messages containing a multipart content.  If a
+message has some other kind of content, or if the part is itself
+another multipart content, the `\-part' switch will not prevent
+the content from being acted upon.
+
+A content specification consists of a content type and a subtype.
+The initial list of \*(lqstandard\*(rq content types and subtypes
+can be found in RFC\-2046.
+.ne 18
+A list of commonly used contents is briefly reproduced here:
+.sp
+.nf
+.in +.5i
+.ta \w'application  'u
+Type   Subtypes
+----   --------
+text   plain, enriched
+multipart      mixed, alternative, digest, parallel
+message        rfc822, partial, external-body
+application    octet-stream, postscript
+image  jpeg, gif, png
+audio  basic
+video  mpeg
+.re
+.in -.5i
+.fi
+.sp
+A legal MIME message must contain a subtype specification.
+.PP
+To specify a content, regardless of its subtype, just use the name
+of the content, e.g., \*(lqaudio\*(rq.  To specify a specific
+subtype, separate the two with a slash, e.g., \*(lqaudio/basic\*(rq.
+Note that regardless of the values given to the `\-type' switch,
+a multipart content (of any subtype listed above) is always acted
+upon.  Further note that if the `\-type' switch is used, and it is
+desirable to act on a message/external-body content, then the
+`\-type' switch must be used twice: once for message/external-body
+and once for the content externally referenced.
+
+.Uh "Checking the Contents"
+The `\-check' switch tells \fImhstore\fR to check each content for
+an integrity checksum.  If a content has such a checksum (specified
+as a Content-MD5 header field), then \fImhstore\fR will attempt to
+verify the integrity of the content.
+
+.Uh "Storing the Contents"
+The \fImhstore\fR will store the contents of the named messages in
+\*(lqnative\*(rq (decoded) format.  Two things must be determined:
+the directory to store the content, and the filenames.  Files are
+written in the directory given by the \fBnmh-storage\fR profile
+entry,
+.ne 6
+e.g.,
+.sp
+.in +.5i
+nmh-storage: /tmp
+.in -.5i
+.sp
+If this entry isn't present,
+the current working directory is used.
+
+If the `\-auto' switch is given, then \fImhstore\fR will check if
+the message contains information indicating the filename that should
+be used to store the content.  This information should be specified
+as the attribute \*(lqname=filename\*(rq in the Content-Type header
+for the content you are storing.  For security reasons, this filename
+will be ignored if it begins with the character '/', '.', '|', or
+'!', or if it contains the character '%'.  For the sake of security,
+this switch is not the default, and it is recommended that you do
+NOT put the `\-auto' switch in your \&.mh\(ruprofile file.
+
+If the `\-auto' switch is not given (or is being ignored for security
+reasons) then \fImhstore\fR will look in the user's profile for a
+\*(lqformatting string\*(rq to determine how the different contents
+should be stored.  First, \fImhstore\fR will look for an entry of
+the form:
+.sp
+.in +.5i
+mhstore-store-<type>/<subtype>
+.in -.5i
+.sp
+to determine the formatting string.  If this isn't found, \fImhstore\fR
+will look for an entry of the form:
+.sp
+.in +.5i
+mhstore-store-<type>
+.in -.5i
+.sp
+to determine the formatting string.
+
+If the formatting string starts with a \*(lq+\*(rq character, then
+content is stored in the named folder.  A formatting string consisting
+solely of a \*(lq+\*(rq character is interpreted to be the current
+folder.
+
+If the formatting string consists solely of a \*(lq-\*(rq character,
+then the content is sent to the standard output.
+
+If the formatting string starts with a '|', then the display string
+will represent a command for \fImhstore\fR to execute which should
+ultimately store the content.  The content will be passed to the
+standard input of the command.  Before the command is executed,
+\fImhstore\fR will change to the appropriate directory, and any
+escapes (given below) in the display string will be expanded.
+
+Otherwise the formatting string will represent a pathname in which
+to store the content.  If the formatting string starts with a '/',
+then the content will be stored in the full path given, else the
+file name will be relative to the value of \fBnmh-storage\fR or
+the current working directory.  Any escapes (given below) will be
+expanded, except for the a-escape.
+
+A command or pathname formatting string may contain the following
+escapes.  If the content isn't part of a multipart (of any subtype
+listed above) content, the p-escapes are ignored.
+.sp
+.nf
+.in +.5i
+.ta \w'%P  'u
+%a     Parameters from Content-type  (only valid with command)
+%m     Insert message number
+%P     Insert part number with leading dot
+%p     Insert part number without leading dot
+%t     Insert content type
+%s     Insert content subtype
+%%     Insert character %
+.re
+.in -.5i
+.fi
+.sp
+If no formatting string is found, \fImhstore\fR will check to see
+if the content is application/octet-stream with parameter
+\*(lqtype=tar\*(rq.  If so, \fImhstore\fR will choose an appropriate
+filename.  If the content is not application/octet-stream, then
+\fImhstore\fR will check to see if the content is a message.  If
+so, \fImhstore\fR will use the value \*(lq+\*(rq.  As a last resort,
+\fImhstore\fR will use the value \*(lq%m%P.%s\*(rq.
+
+.ne 10
+Example profile entries might be:
+.sp
+.nf
+.in +.5i
+mhstore-store-text: %m%P.txt
+mhstore-store-text: +inbox
+mhstore-store-message/partial: +
+mhstore-store-audio/basic: | raw2audio -e ulaw -s 8000 -c 1 > %m%P.au
+mhstore-store-image/jpeg: %m%P.jpg
+mhstore-store-application/PostScript: %m%P.ps
+.in -.5i
+.fi
+.sp
+.Uh "Reassembling Messages of Type message/partial"
+\fImhstore\fR is also able to reassemble messages that have been
+split into multiple messages of type \*(lqmessage/partial\*(rq.
+
+When asked to store a content containing a partial message,
+\fImhstore\fR will try to locate all of the portions and combine
+them accordingly.  The default is to store the combined parts as
+a new message in the current folder, although this can be changed
+using formatting strings as discussed above.  Thus, if someone has
+sent you a message in several parts (such as the output from
+\fIsendfiles\fR), you can easily reassemble them all into a single
+message in the following fashion:
+.sp
+.nf
+.in +.5i
+% mhlist 5-8
+ msg part  type/subtype             size description
+   5       message/partial           47K part 1 of 4
+   6       message/partial           47K part 2 of 4
+   7       message/partial           47K part 3 of 4
+   8       message/partial           18K part 4 of 4
+% mhstore 5-8
+reassembling partials 5,6,7,8 to folder inbox as message 9
+% mhlist -verbose 9
+ msg part  type/subtype             size description
+   9       application/octet-stream 118K
+             (extract with uncompress | tar xvpf -)
+             type=tar
+             conversions=compress
+.in -.5i
+.fi
+.sp
+This will store exactly one message, containing the sum of the
+parts.  It doesn't matter whether the partials are specified in
+order, since \fImhstore\fR will sort the partials, so that they
+are combined in the correct order.  But if \fImhstore\fR can not
+locate every partial necessary to reassemble the message, it will
+not store anything.
+
+.Uh "External Access"
+For contents of type message/external-body,
+.ne 12
+\fImhstore\fR supports these access-types:
+.sp
+.nf
+.in +.5i
+afs
+anon-ftp
+ftp
+local-file
+mail-server
+.in -.5i
+.fi
+.sp
+For the \*(lqanon-ftp\*(rq and \*(lqftp\*(rq access types,
+\fImhstore\fR will look for the \fBnmh-access-ftp\fR
+profile entry,
+.ne 6
+e.g.,
+.sp
+.in +.5i
+nmh-access-ftp: myftp.sh
+.in -.5i
+.sp
+to determine the pathname of a program to perform the FTP retrieval.
+.ne 14
+This program is invoked with these arguments:
+.sp
+.nf
+.in +.5i
+domain name of FTP-site
+username
+password
+remote directory
+remote filename
+local filename
+\*(lqascii\*(rq or \*(lqbinary\*(rq
+.in -.5i
+.fi
+.sp
+The program should terminate with an exit status of zero if the
+retrieval is successful, and a non-zero exit status otherwise.
+
+If this entry is not provided, then \fImhstore\fR will use a simple
+built-in FTP client to perform the retrieval.
+
+.Uh "The Content Cache"
+When \fImhstore\fR encounters an external content containing a
+\*(lqContent-ID:\*(rq field, and if the content allows caching, then
+depending on the caching behavior of \fImhstore\fR, the content might be
+read from or written to a cache.
+
+The caching behavior of \fImhstore\fR is controlled with the `\-rcache'
+and `\-wcache' switches, which define the policy for reading from,
+and writing to, the cache, respectively.  One of four policies may be
+specified: \*(lqpublic\*(rq, indicating that \fImhstore\fR should make use
+of a publically-accessible content cache; \*(lqprivate\*(rq, indicating
+that \fImhstore\fR should make use of the user's private content cache;
+\*(lqnever\*(rq, indicating that \fImhstore\fR should never make use of
+caching; and, \*(lqask\*(rq, indicating that \fImhstore\fR should ask
+the user.
+
+There are two directories where contents may be cached: the profile entry
+\fBnmh-cache\fR names a directory containing world-readable contents, and,
+the profile entry \fBnmh-private-cache\fR names a directory containing
+private contents.  The former should be an absolute (rooted) directory
+name.
+.ne 6
+For example,
+.sp
+.in +.5i
+nmh-cache: /tmp
+.in -.5i
+.sp
+might be used if you didn't care that the cache got wiped after each
+reboot of the system.  The latter is interpreted relative to the user's
+nmh directory, if not rooted,
+.ne 6
+e.g.,
+.sp
+.in +.5i
+nmh-private-cache: .cache
+.in -.5i
+.sp
+(which is the default value).
+
+.Uh "User Environment"
+Because the environment in which \fImhstore\fR operates may vary
+for different machines, \fImhstore\fR will look for the environment
+variable \fB$MHSTORE\fR.  If present, this specifies the name of
+an additional user profile which should be read.  Hence, when a
+user logs in on a machine, this environment variable should be set
+to refer to a file containing definitions useful for that machine.
+Finally, \fImhstore\fR will attempt to consult one other additional
+user profile,
+.ne 6
+e.g.,
+.sp
+.in +.5i
+%etcdir%/mhn.defaults
+.in -.5i
+.sp
+which is created automatically during nmh installation.
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+^$MHSTORE~^Additional profile entries
+^%etcdir%/mhn.defaults~^System default MIME profile entries
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Current\-Folder:~^To find the default current folder
+.Ps
+^nmh-access-ftp:~^Program to retrieve contents via FTP
+.Ps
+^nmh-cache~^Public directory to store cached external contents
+.Ps
+^nmh-private-cache~^Personal directory to store cached external contents
+.Ps
+^nmh-storage~^Directory to store contents
+.Ps
+^mhstore-store-<type>*~^Template for storing contents
+.Sa
+mhbuild(1), mhlist(1), mhshow(1), sendfiles(1)
+.br
+RFC\-2045:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part One:
+.br
+   Format of Internet Message Bodies\fR,
+.br
+RFC\-2046:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part Two:
+.br
+   Media Types\fR,
+.br
+RFC\-2047:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part Three:
+.br
+   Message Header Extensions for Non-ASCII Text\fR,
+.br
+RFC\-2048:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part Four:
+.br
+   Registration Procedures\fR,
+.br
+RFC\-2049:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part Five:
+.br
+   Conformance Criteria and Examples\fR.
+.De
+`+folder' defaults to the current folder
+.Ds
+`msgs' defaults to cur
+.Ds
+`\-noauto'
+.Ds
+`\-nocheck'
+.Ds
+`\-rcache ask'
+.Ds
+`\-wcache ask'
+.Ds
+`\-noverbose'
+.Co
+If a folder is given, it will become the current folder.  The last
+message selected will become the current message.
+.Bu
+Partial messages contained within a multipart content are not reassembled.
+.En
diff --git a/man/msgchk.man b/man/msgchk.man
new file mode 100644 (file)
index 0000000..e7624a0
--- /dev/null
@@ -0,0 +1,95 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH MSGCHK %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+msgchk \- check for messages
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+msgchk
+\%[\-date] \%[\-nodate]
+\%[\-notify\ all/mail/nomail]
+.br
+\%[\-nonotify\ all/mail/nomail]
+.br
+%nmhbeginpop%
+\%[\-host\ hostname]
+\%[\-user\ username]
+.br
+%nmhendpop%
+\%[users\ ...]
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+The \fImsgchk\fR program checks all known mail drops for mail waiting
+for you.  For those drops which have mail for you, \fImsgchk\fR will
+indicate if it believes that you have seen the mail in question before.
+
+The `\-notify\ type' switch indicates under what circumstances
+\fImsgchk\fR should produce a message.  The default is `\-notify\ all'
+which says that \fImsgchk\fR should always report the status of the
+users maildrop.  Other values for `type' include `mail' which says that
+\fImsgchk\fR should report the status of waiting mail; and, `nomail'
+which says that \fImsgchk\fR should report the status of empty maildrops.
+The `\-nonotify\ type' switch has the inverted sense, so
+`\-nonotify\ all' directs \fImsgchk\fR to never report the status of
+maildrops.  This is useful if the user wishes to check \fImsgchk\fR's
+exit status.  A non\-zero exit status indicates that mail was \fBnot\fR
+waiting for at least one of the indicated users.
+
+If \fImsgchk\fR produces output, then the `\-date' switch directs
+\fImsgchk\fR to print out the last date mail was read, if this can
+be determined.
+%nmhbeginpop%
+.Uh "Using POP"
+\fImsgchk\fR will normally check all the local mail drops, but if
+the option \*(lqpophost:\*(rq is set in the mts configuration file
+\*(lqmts.conf\*(rq, or if the `\-host\ hostname' switch is given,
+\fImsgchk\fR will query this POP service host as to the status of
+mail waiting.
+
+The default is for \fImsgchk\fR to assume that your account name
+on the POP server is the same as your current username.  To specify
+a different username, use the `\-user\ username' switch.
+
+When using POP, you will normally need to type the password for
+your account on the POP server, in order to retrieve your messages.
+It is possible to automate this process by creating a \*(lq.netrc\*(rq
+file containing your login account information for this POP server.
+For each POP server, this file should have a line of the following
+form.  Replace the words mypopserver, mylogin, and mypassword with
+your own account information.
+
+machine mypopserver login mylogin password mypassword
+
+This \*(lq.netrc\*(rq file should be owned and readable only by
+you.
+
+For debugging purposes, there is also a switch `\-snoop', which will
+allow you to watch the POP transaction take place between you and the
+POP server.
+%nmhendpop%
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+^%etcdir%/mts.conf~^nmh mts configuration file
+^%mailspool%/$USER~^Location of mail drop
+.Pr
+None
+.Sa
+inc(1)
+.De
+`user' defaults to the current user
+.Ds
+`\-date'
+.Ds
+`\-notify\ all'
+.Co
+None
+.En
diff --git a/man/msh.man b/man/msh.man
new file mode 100644 (file)
index 0000000..88ae414
--- /dev/null
@@ -0,0 +1,208 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH MSH %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+msh \- nmh shell (and BBoard reader)
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+msh
+\%[\-prompt\ string]
+\%[\-scan] \%[\-noscan]
+\%[\-topcur] \%[\-notopcur]
+\%[file]
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fImsh\fR is an interactive program that implements a subset of the normal
+\fInmh\fR commands operating on a single file in \fIpackf\fR'd format.
+That is, \fImsh\fR is used to read a file that contains a number
+of messages, as opposed to the standard \fInmh\fR style of reading
+a number of files, each file being a separate message in a folder.
+\fImsh\fR's chief advantage is that the normal \fInmh\fR style does not
+allow a file to have more than one message in it.  Hence, \fImsh\fR is
+ideal for reading \fIBBoards\fR, as these files are delivered by the
+transport system in this format.  In addition, \fImsh\fR can be used on
+other files, such as message archives which have been \fIpack\fRed (see
+\fIpackf\fR\0(1)).  Finally, \fImsh\fR is an excellent \fInmh\fR tutor.
+As the only commands available to the user are \fInmh\fR commands, this
+allows \fInmh\fR beginners to concentrate on how commands to \fInmh\fR
+are formed and (more or less) what they mean.
+
+When invoked, \fImsh\fR reads the named file, and enters a command loop.
+The user may type most of the normal \fInmh\fR commands.  The syntax and
+semantics of these commands typed to \fImsh\fR are identical to their
+\fInmh\fR counterparts.  In cases where the nature of \fImsh\fR would be
+inconsistent (e.g., specifying a `+folder' with some commands), \fImsh\fR
+will duly inform the user.  The commands that \fImsh\fR currently supports
+(in some slightly modified or restricted forms) are:
+.sp 1
+.in +.5i
+ali
+.br
+burst
+.br
+comp
+.br
+dist
+.br
+folder
+.br
+forw
+.br
+inc
+.br
+mark
+.br
+mhmail
+.br
+mhn
+.br
+msgchk
+.br
+next
+.br
+packf
+.br
+pick
+.br
+prev
+.br
+refile
+.br
+repl
+.br
+rmm
+.br
+scan
+.br
+send
+.br
+show
+.br
+sortm
+.br
+whatnow
+.br
+whom
+.in -.5i
+
+In addition, \fImsh\fR has a \*(lqhelp\*(rq command which gives a
+brief overview.  To terminate \fImsh\fR, type CTRL\-D, or use the
+\*(lqquit\*(rq command.  If \fImsh\fR is being invoked from \fIbbc\fR,
+then typing CTRL\-D will also tell \fIbbc\fR to exit as well, while
+using the \*(lqquit\*(rq command will return control to \fIbbc\fR, and
+\fIbbc\fR will continue examining the list of BBoards that it is scanning.
+
+If the file is writable and has been modified, then using \*(lqquit\*(rq
+will query the user if the file should be updated.
+
+The `\-prompt string' switch sets the prompting string for \fImsh\fR.
+
+You may wish to use an alternate \fInmh\fR profile for the commands that
+\fImsh\fR executes; see \fImh-profile\fR\0(5) for details about the
+\fB$MH\fR environment variable.
+
+When invoked from \fIbbc\fR, two special features are enabled:
+First, the `\-scan' switch directs \fImsh\fR to do a `scan\0unseen'
+on start\-up if new items are present in the BBoard.  This feature is
+best used from \fIbbc\fR, which correctly sets the stage.  Second, the
+\fImark\fR command in \fImsh\fR acts specially when you are reading a
+BBoard, since \fImsh\fR will consult the sequence \*(lqunseen\*(rq in
+determining what messages you have actually read.  When \fImsh\fR exits,
+it reports this information to \fIbbc\fR.  In addition, if you give the
+\fImark\fR command with no arguments, \fImsh\fR will interpret it as
+`mark\0\-sequence\0unseen\0\-delete\0\-nozero\0all' Hence, to discard
+all of the messages in the current BBoard you're reading, just use the
+\fImark\fR command with no arguments.
+
+Normally, the \*(lqexit\*(rq command is identical to the \*(lqquit\*(rq
+command in \fImsh\fR.  When run under \fIbbc\fR however, \*(lqexit\*(rq
+directs \fImsh\fR to mark all messages as seen and then \*(lqquit\*(rq.
+For speedy type\-in, this command is often abbreviated as just
+\*(lqe\*(rq.
+
+When invoked from \fIvmh\fR, another special feature is enabled:
+The `topcur' switch directs \fImsh\fR to have the current message
+\*(lqtrack\*(rq the top line of the \fIvmh\fR scan window.  Normally,
+\fImsh\fR has the current message \*(lqtrack\*(rq the center of the window
+(under `\-notopcur', which is the default).
+
+\fImsh\fR supports an output redirection facility.  Commands may be
+followed by one of
+
+.nf
+.in +.5i
+.ta \w'| \fIcommand\fR  'u
+^> \fIfile\fR~^write output to \fIfile\fR
+^>> \fIfile\fR~^append output to \fIfile\fR
+^| \fIcommand\fR~^pipe output to UNIX \fIcommand\fR
+.re
+.in -.5i
+.fi
+
+If \fIfile\fR starts with a `\~' (tilde), then a \fIcsh\fR-like expansion
+takes place.  Note that \fIcommand\fR is interpreted by \fIsh\fR\0(1).
+Also note that \fImsh\fR does NOT support history substitutions, variable
+substitutions, or alias substitutions.
+
+When parsing commands to the left of any redirection symbol, \fImsh\fR
+will honor `\\' (back\-slash) as the quote next\-character symbol, and
+`"' (double\-quote) as quote\-word delimiters.  All other input tokens
+are separated by whitespace (spaces and tabs).
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+^%etcdir%/mts.conf~^nmh mts configuration file
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Msg\-Protect:~^To set mode when creating a new `file'
+.Ps
+^fileproc:~^Program to file messages
+.Ps
+^showproc:~^Program to show messages
+.Sa
+bbc(1)
+.De
+`file' defaults to \*(lq./msgbox\*(rq
+.Ds
+`\-prompt\ (msh)\ '
+.Ds
+`\-noscan'
+.Ds
+`\-notopcur'
+.Co
+None
+.Bu
+The argument to the `\-prompt' switch must be interpreted as a single
+token by the shell that invokes \fImsh\fR.  Therefore, one must usually
+place the argument to this switch inside double\-quotes.
+
+There is a strict limit of messages per file in \fIpackf\fR'd format
+which \fImsh\fR can handle.  Usually, this limit is 1000 messages.
+
+Please remember that \fImsh\fR is not the \fICShell\fR, and that a lot of
+the nice facilities provided by the latter are not present in the former.
+
+In particular, \fImsh\fR does not understand back\-quoting, so the only
+effective way to use \fIpick\fR inside \fImsh\fR is to always use the
+`\-seq\0select' switch.  Clever users of \fInmh\fR will put the line
+
+.ti +.5i
+pick:\0\-seq\0select\0\-list
+
+in their \&.mh\(ruprofile file so that \fIpick\fR works equally well
+from both the shell and \fImsh\fR.
+
+\fIsortm\fR always uses \*(lq\-noverbose\*(rq and if
+\*(lq\-textfield\ field\*(lq is used, \*(lq\-limit 0\*(rq.
+
+The \fImsh\fR program inherits most (if not all) of the bugs from the
+\fInmh\fR commands it implements.
+.En
diff --git a/man/next.man b/man/next.man
new file mode 100644 (file)
index 0000000..1bc2545
--- /dev/null
@@ -0,0 +1,62 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH NEXT %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+next \- show the next message
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+next 
+\%[+folder] 
+\%[\-showproc\ program]
+\%[\-showmimeproc\ program]
+.br
+\%[\-header] \%[\-noheader]
+\%[\-checkmime] \%[\-nocheckmime]
+.br
+\%[switches\ for\ \fIshowproc\fR or\ \fIshowmimeproc\fR]
+.br
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fINext\fR performs a \fIshow\fR on the next message in the specified
+(or current) folder.  Like \fIshow\fR, it passes any switches on to
+the program \fIshowproc\fR or \fIshowmimeproc\fR, which is called to list
+the message.  This command is almost exactly equivalent to \*(lqshow
+next\*(rq.  Consult the manual entry for \fIshow\fR\0(1) for all the
+details.
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Current\-Folder:~^To find the default current folder
+.Ps
+^showproc:~^Program to show non-MIME messages
+.Ps
+^showmimeproc:~^Program to show MIME messages
+.Sa
+show(1), prev(1)
+.De
+`+folder' defaults to the current folder
+.Ds
+`\-checkmime'
+.Ds
+`\-header'
+.Co
+If a folder is specified, it will become the current folder.  The message
+that is shown (i.e., the next message in sequence) will become the
+current message.
+.Bu
+\fInext\fR is really a link to the \fIshow\fR program.  As a result, if
+you make a link to \fInext\fR and that link is not called \fInext\fR,
+your link will act like \fIshow\fR instead.  To circumvent this, add a
+profile\-entry for the link to your \fInmh\fR profile and add the argument
+\fInext\fR to the entry.
+.En
diff --git a/man/nmh.man b/man/nmh.man
new file mode 100644 (file)
index 0000000..7cc20fc
--- /dev/null
@@ -0,0 +1,235 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.if '\*(ZZ'-man' \{\
+.TH NMH %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+nmh \- new MH message system
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+any \fInmh\fR command
+.in -.5i
+.SH DESCRIPTION
+\fInmh\fR is the name of a powerful message handling system.  Rather than
+being a single comprehensive program, \fInmh\fR consists of a collection
+of fairly simple single-purpose programs to send, retrieve, save,
+and manipulate messages.
+
+Unlike most mail clients in UNIX, \fInmh\fR is not a closed system which
+must be explicitly run, then exited when you wish to return to the shell.
+You may freely intersperse \fInmh\fR commands with other shell commands,
+allowing you to read and answer your mail while you have (for example)
+a compilation running, or search for a file or run programs as needed
+to find the answer to someone's question before answering their mail.
+
+The rest of this manual entry is a quick tutorial which will teach you
+the basics of \fInmh\fR.  You should read the manual entries for the
+individual programs for complete documentation.
+
+To get started using \fInmh\fR, put the directory \fB%bindir%\fR on your
+\fB$PATH\fR.  This is best done in one of the files: \fB\&.profile\fR,
+\fB\&.login\fR, or \fB\&.cshrc\fR in your home directory.  (Check the
+manual entry for the shell you use, in case you don't know how to
+do this.)  Run the \fIinc\fR command.  If you've never used \fInmh\fR
+before, it will create the necessary default files and directories after
+asking you if you wish it to do so.
+
+\fIinc\fR moves mail from your system maildrop into your \fInmh\fR
+`+inbox' folder, breaking it up into separate files and converting it
+to \fInmh\fR format as it goes.  It prints one line for each message it
+processes, containing the from field, the subject field and as much of
+the first line of the message as will fit.  It leaves the first message
+it processes as your current message.  You'll need to run \fIinc\fR each
+time you wish to incorporate new mail into your \fInmh\fR file.
+
+\fIscan\fR prints a list of the messages in your current folder.
+
+The commands: \fIshow\fR, \fInext\fR, and \fIprev\fR are used to read
+specific messages from the current folder.  \fIshow\fR displays the
+current message, or a specific message, which may be specified by its
+number, which you pass as an argument to \fIshow\fR.  \fInext\fR and
+\fIprev\fR display, respectively, the message numerically after or before
+the current message.  In all cases, the message displayed becomes the
+current message.  If there is no current message, \fIshow\fR may be
+called with an argument, or \fInext\fR may be used to advance to the
+first message.
+
+\fIrmm\fR (remove message) deletes the current message.  It may be called
+with message numbers passed as arguments, to delete specific messages.
+
+\fIrepl\fR is used to respond to the current message (by default).
+It places you in the editor with a prototype response form.  While you're
+in the editor, you may peruse the item you're responding to by reading
+the file \fB@\fR.  After completing your response, type \fBl\fR to list
+(review) it, or \fBs\fR to send it.
+
+\fIcomp\fR allows you to compose a message by putting you in the editor
+on a prototype message form, and then lets you send it.
+
+All the \fInmh\fR commands may be run with the single argument: `\-help',
+which causes them to print a list of the arguments they may be invoked
+with and then exit.
+
+All the \fInmh\fR commands may be run with the single argument:
+`\-version', which cause them to print the version number of the \fInmh\fR
+distribution, and then exit.
+
+Commands which take a message number as an argument (\fIscan\fR,
+\fIshow\fR, \fIrepl\fR, ...)  also take one of the words: \fIfirst\fR,
+\fIprev\fR, \fIcur\fR, \fInext\fR, or \fIlast\fR to indicate
+(respectively) the first, previous, current, next, or last message in
+the current folder (assuming they are defined).
+
+Commands which take a range of message numbers (\fIrmm\fR, \fIscan\fR,
+\fIshow\fR, ...)  also take any of the abbreviations:
+.sp
+.in +5
+.ti -3
+.I <num1>-<num2>
+- Indicates all messages in the range <num1> to <num2>, inclusive. The range
+.B must
+be nonempty.
+.sp
+.ti -3
+.I <num>:+N
+.ti -3
+.I <num>:-N
+- Up to
+.I N
+messages beginning with (or ending with) message
+.I num.
+.I Num
+may be any of the pre-defined symbols:
+.I first, prev, cur, next
+or
+.I last.
+.sp
+.ti -3
+.I first:N
+.ti -3
+.I prev:N
+.ti -3
+.I next:N
+.ti -3
+.I last:N
+- The first, previous, next or last
+.I N
+messages, if they exist.
+.in -5
+
+There are many other possibilities such as creating multiple folders
+for different topics, and automatically refiling messages according to
+subject, source, destination, or content.  These are beyond the scope
+of this manual entry.
+
+Following is a list of all the \fInmh\fR commands:
+.\}
+
+.nf
+.in .5i
+.ta 1.5i
+^ali (1)~^\- list mail aliases
+^anno (1)~^\- annotate messages
+^burst (1)~^\- explode digests into messages
+^comp (1)~^\- compose a message 
+^dist (1)~^\- redistribute a message to additional addresses
+^flist (1)~^\- list folders that contain messages in given sequence(s)
+^flists (1)~^\- list all folders that contain messages in given sequence(s)
+^folder (1)~^\- set/list current folder/message
+^folders (1)~^\- list all folders
+^forw (1)~^\- forward messages
+^inc (1)~^\- incorporate new mail
+^mark (1)~^\- mark messages
+^mhbuild (1)~^\- translate MIME composition draft
+^mhl (1)~^\- produce formatted listings of nmh messages
+^mhlist (1)~^\- list information about content of MIME messages
+^mhmail (1)~^\- send or read mail
+^mhn (1)~^\- display/list/store/cache MIME messages
+^mhparam (1)~^\- print nmh profile components
+^mhpath (1)~^\- print full pathnames of nmh messages and folders
+^mhshow (1)~^\- display MIME messages
+^mhstore (1)~^\- store contents of MIME messages into files
+^msgchk (1)~^\- check for messages
+^msh (1)~^\- nmh shell (and BBoard reader)
+^next (1)~^\- show the next message
+^packf (1)~^\- compress a folder into a single file
+^pick (1)~^\- select messages by content
+^prev (1)~^\- show the previous message
+^prompter (1)~^\- prompting editor front end
+^rcvdist (1)~^\- asynchronously redistribute new mail
+^rcvpack (1)~^\- append message to file
+^rcvstore (1)~^\- asynchronously incorporate new mail
+^rcvtty  (1)~^\- report new mail
+^refile (1)~^\- file messages in other folders
+^repl (1)~^\- reply to a message
+^rmf (1)~^\- remove folder
+^rmm (1)~^\- remove messages
+^scan (1)~^\- produce a one line per message scan listing
+^send (1)~^\- send a message
+^sendfiles (1)~^\- send multiple files and directories in MIME message
+^show (1)~^\- show (display) messages
+^slocal (1)~^\- asynchronously filter and deliver new mail
+^sortm (1)~^\- sort messages
+^whatnow (1)~^\- prompting front\-end for send
+^whom (1)~^\- report to whom a message would go
+.if '\*(ZZ'-man' \{\
+.sp 1
+^mh\-alias (5)~^\- alias file for nmh message system
+^mh\-draft (5)~^\- draft folder facility
+^mh\-format (5)~^\- format file for nmh message system
+^mh\-mail (5)~^\- message format for nmh message system
+^mh\-profile (5)~^\- user customization for nmh message system
+^mh\-sequence (5)~^\- sequence specification for nmh message system
+.sp 1
+^ap (8)~^\- parse addresses 822\-style
+^conflict (8)~^\- search for alias/password conflicts
+^dp (8)~^\- parse dates 822\-style
+^fmtdump (8)~^\- decode \fInmh\fP format files
+^install\-mh (8)~^\- initialize the nmh environment
+^post (8)~^\- deliver a message
+.\}
+.fi
+.re
+
+.if '\*(ZZ'-man' \{\
+.Fi
+^%bindir%~^directory containing \fInmh\fR commands
+^%etcdir%~^directory containing \fInmh\fR format files
+^%libdir%~^\fInmh\fR library commands
+.Bu
+If problems are encountered with an \fInmh\fR program, the problems should
+be reported to the local maintainers of \fInmh\fR.  When doing this, the
+name of the program should be reported, along with the version information
+for the program.
+.br
+To find out what version of an \fInmh\fR program is being run, invoke
+the program with the `\-version' switch.  This information includes
+the version of \fInmh\fR, the host it was generated on, and the date the
+program was loaded.
+
+Send bug reports and suggestions to \fBnmh-workers@math.gatech.edu\fR.
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+.Pr
+^Path:~^To determine the user's nmh directory
+.\" .Ps
+.\" for each additional profile entry
+.\" .Sa
+.\" the see\-also's go here
+.\" .De
+.\" the first default goes here
+.\" .Ds
+.\" for each additional default
+.\" .Co
+.\" context changes go here
+.\" You can also have
+.\" .Hh \- the helpful hints section
+.\" .Hi \- the history section
+.\" .Bu \- the bugs section
+.En
+.\}
diff --git a/man/packf.man b/man/packf.man
new file mode 100644 (file)
index 0000000..a9f66cb
--- /dev/null
@@ -0,0 +1,67 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH PACKF %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+packf \- pack messages in nmh folder into a single file
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+packf
+\%[+folder] \%[msgs]
+\%[\-file\ name]
+\%[\-mbox] \%[-mmdf]
+.br
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fIPackf\fR will pack copies of messages from a folder, into a single
+file.
+
+If the `-mbox' switch is given (the default), then the messages are
+separated using mbox (uucp) style delimiters.  This is the format used
+by most mail clients (elm, mailx, etc.).
+
+If the `-mmdf' switch is given, then the messages are separated by
+mmdf style delimiters.  Each message in the file is separated by four
+CTRL\-A's and a newline.
+
+You may specify the name of the file in which to use with the
+`\-file\ name' switch.  If you do specify the name of the file, it
+will default to `msgbox'.
+
+If the given file name points to an existing file, then the specified
+messages will be appended to the end of the file, otherwise the file
+will be created and the messages appended.
+
+Messages that are packed by \fIpackf\fR can be unpacked using
+\fIinc\fR.
+
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+^\&.msgbox\&.map~^A binary index of the file
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Current\-Folder:~^To find the default current folder
+.Ps
+^Msg\-Protect:~^To set mode when creating a new `file'
+.Sa
+inc(1)
+.De
+`+folder' defaults to the current folder
+.Ds
+`msgs' defaults to all
+.Ds
+`\-mbox'
+.Ds
+`\-file ./msgbox' 
+.Co
+If a folder is given, it will become the current folder.  The first
+message packed will become the current message.
+.En
diff --git a/man/pick.man b/man/pick.man
new file mode 100644 (file)
index 0000000..a2a54c4
--- /dev/null
@@ -0,0 +1,264 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH PICK %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+pick \- search for messages by content
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+pick
+\%[+folder] \%[msgs]
+\%[\-and\ ...] \%[\-or\ ...] \%[\-not\ ...]
+.br
+\%[\-lbrace\ ...\ \-rbrace]
+\%[\-\|\-component\ pattern]
+.br
+\%[\-cc\ pattern]
+\%[\-date\ pattern]
+\%[\-from\ pattern]
+.br
+\%[\-search\ pattern]
+\%[\-subject\ pattern]
+\%[\-to\ pattern]
+.br
+\%[\-after\ date]
+\%[\-before\ date]
+\%[\-datefield\ field]
+.br
+\%[\-sequence\ name\ ...]
+\%[\-public] \%[\-nopublic]
+\%[\-zero]
+.br
+\%[\-nozero]
+\%[\-list] \%[\-nolist]
+\%[\-version]
+\%[\-help]
+.ti .5i
+
+typical usage:
+.br
+scan\0`pick\0\-from\0jones`
+.br
+pick\0\-to\0holloway\0\-sequence\0select
+.br
+show\0`pick\0\-before\0friday`
+.in -.5i
+.SH DESCRIPTION
+\fIPick\fR searches within a folder for messages with the specified
+contents, and then identifies those messages.  Two types of search
+primitives are available: pattern matching and date constraint
+operations.
+
+A modified \fIgrep\fR(1) is used to perform the matching, so the
+full regular expression (see \fIed\fR(1)) facility is available
+within `pattern'.  With `\-search', `pattern' is used directly,
+and with the others, the grep pattern constructed is:
+
+.ti +.5i
+\*(lqcomponent[ \\t]*:\&.*pattern\*(rq
+
+This means that the pattern specified for a `\-search' will be found
+everywhere in the message, including the header and the body, while
+the other pattern matching requests are limited to the single specified
+component.  The expression
+
+.ti +.5i
+`\-\|\-component\ pattern'
+
+is a shorthand for specifying
+
+.ti +.5i
+`\-search \*(lqcomponent[ \\t]*:\&.*pattern\*(rq\ '
+
+It is used to pick a component which is not one of \*(lqTo:\*(rq,
+\*(lqcc:\*(rq, \*(lqDate:\*(rq, \*(lqFrom:\*(rq, or \*(lqSubject:\*(rq.
+An example is `pick\0\-\|\-reply\-to\0pooh'.
+
+Pattern matching is performed on a per\-line basis.  Within the header
+of the message, each component is treated as one long line, but in the
+body, each line is separate.  Lower\-case letters in the search pattern
+will match either lower or upper case in the message, while upper case
+will match only upper case.
+
+Note that since the `\-date' switch is a pattern matching operation (as
+described above), to find messages sent on a certain date the pattern
+string must match the text of the \*(lqDate:\*(rq field of the message.
+
+Independent of any pattern matching operations requested, the switches
+`\-after date' or `\-before date' may also be used to introduce date/time
+constraints on all of the messages.  By default, the \*(lqDate:\*(rq
+field is consulted, but if another date yielding field (such as
+\*(lqBB\-Posted:\*(rq or \*(lqDelivery\-Date:\*(rq) should be used, the
+`\-datefield\ field' switch may be used.
+
+With `\-before' and `\-after', \fIpick\fR will actually parse the date
+fields in each of the messages specified in `msgs' and compare them
+to the date/time specified.  If `\-after' is given, then only those
+messages whose \*(lqDate:\*(rq field value is chronologically after the
+date specified will be considered.  The `\-before' switch specifies the
+complimentary action.
+
+Both the `\-after' and `\-before' switches take legal 822\-style date
+specifications as arguments.  \fIPick\fR will default certain missing
+fields so that the entire date need not be specified.  These fields
+are (in order of defaulting): timezone, time and timezone, date, date
+and timezone.  All defaults are taken from the current date, time,
+and timezone.
+
+In addition to 822\-style dates, \fIpick\fR will also recognize any of
+the days of the week (\*(lqsunday\*(rq, \*(lqmonday\*(rq, and so on),
+and the special dates \*(lqtoday\*(rq, \*(lqyesterday\*(rq (24 hours
+ago), and \*(lqtomorrow\*(rq (24 hours from now).  All days of the
+week are judged to refer to a day in the past (e.g., telling \fIpick\fR
+\*(lqsaturday\*(rq on a \*(lqtuesday\*(rq means \*(lqlast\ saturday\*(rq
+not \*(lqthis\ saturday\*(rq).
+
+Finally, in addition to these special specifications, \fIpick\fR will
+also honor a specification of the form \*(lq\-dd\*(rq, which means
+\*(lqdd days ago\*(rq.
+
+\fIPick\fR supports complex boolean operations on the searching primitives
+with the `\-and', `\-or', `\-not', and `\-lbrace\ ...\ \-rbrace' switches.
+For example,
+
+.ti +.5i
+.ie t \{\
+pick\0\-after\0yesterday\0\-and\0\-lbrace\0\-from\0freida\0\-or\0\-from\0fear\0\-rbrace
+.\}
+.el \{\
+pick\0\-after\0yesterday\0\-and
+.br
+.ti +1i
+\-lbrace\0\-from\0freida\0\-or\0\-from\0fear\0\-rbrace
+.\}
+
+identifies messages recently sent by \*(lqfrieda\*(rq or \*(lqfear\*(rq.
+
+The matching primitives take precedence over the `\-not' switch, which
+in turn takes precedence over `\-and' which in turn takes precedence
+over `\-or'.  To override the default precedence, the `\-lbrace' and
+`\-rbrace' switches are provided, which act just like opening and closing
+parentheses in logical expressions.
+
+If no search criteria are given, all the messages specified on the
+command line are selected (this defaults to \*(lqall\*(rq).
+
+Once the search has been performed, if the `\-list' switch is given, the
+message numbers of the selected messages are written to the standard
+output separated by newlines.  This is \fIextremely\fR useful for
+quickly generating arguments for other \fInmh\fR programs by using the
+\*(lqbackquoting\*(rq syntax of the shell.  For example, the command
+
+.ti +.5i
+scan\0`pick\0+todo\0\-after\0\*(lq31 Mar 83 0123 PST\*(rq`
+
+says to \fIscan\fR those messages in the indicated folder which meet the
+appropriate criterion.  Note that since \fIpick\fR\0's context changes
+are written out prior to \fIscan\fR\0's invocation, you need not give
+the folder argument to \fIscan\fR as well.
+
+Regardless of the operation of the `\-list' switch, the `\-sequence name'
+switch may be given once for each sequence the user wishes to define.
+For each sequence named, that sequence will be defined to mean exactly
+those messages selected by \fIpick\fR.  For example,
+
+.ti +.5i
+pick\0\-from\0frated\0\-seq\0fred
+
+defines a new message sequence for the current folder called
+\*(lqfred\*(rq which contains exactly those messages that were selected.
+
+Note that whenever \fIpick\fR processes a `\-sequence\ name' switch, it
+sets `\-nolist'.
+
+By default, \fIpick\fR will zero the sequence before adding it.  This
+action can be disabled with the `\-nozero' switch, which means that the
+messages selected by \fIpick\fR will be added to the sequence, if it
+already exists, and any messages already a part of that sequence will
+remain so.
+
+The `\-public' and `\-nopublic' switches are used by \fIpick\fR in the
+same way \fImark\fR uses them.
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Current\-Folder:~^To find the default current folder
+.Sa
+mark(1)
+.De
+`+folder' defaults to the current folder
+.Ds
+`msgs' defaults to all
+.Ds
+`\-datefield date'
+.Ds
+`\-zero'
+.Ds
+`\-list' is the default if no `\-sequence', `\-nolist' otherwise
+.Co
+If a folder is given, it will become the current folder.
+.Hi
+In previous versions of \fIMH\fR, the \fIpick\fR command would \fIshow\fR,
+\fIscan\fR, or \fIrefile\fR the selected messages.  This was rather
+\*(lqinverted logic\*(rq from the UNIX point of view, so \fIpick\fR was
+changed to define sequences and output those sequences.  Hence, \fIpick\fR
+can be used to generate the arguments for all other \fIMH\fR commands,
+instead of giving \fIpick\fR endless switches for invoking those commands
+itself.
+
+Also, previous versions of \fIpick\fR balked if you didn't specify
+a search string or a date/time constraint.  The current version does
+not, and merely matches the messages you specify.  This lets you type
+something like:
+
+.ti +.5i
+show\0`pick\0last:20\0\-seq\0fear`
+
+instead of typing
+
+.in +.5i
+.nf
+mark\0\-add\0\-nozero\0\-seq\0fear\0last:20
+show\0fear
+.fi
+.in -.5i
+
+Finally, timezones used to be ignored when comparing dates: they aren't
+any more.
+.Hh
+Use \*(lqpick sequence \-list\*(rq to enumerate the messages in a sequence
+(such as for use by a shell script).
+.Bu
+The argument to the `\-after' and `\-before' switches must be interpreted
+as a single token by the shell that invokes \fIpick\fR.  Therefore, one
+must usually place the argument to this switch inside double\-quotes.
+Furthermore, any occurrence of `\-datefield' must occur prior to the
+`\-after' or `\-before' switch it applies to.
+
+If \fIpick\fR is used in a back\-quoted operation, such as
+
+.ti +.5i
+scan\0`pick\0\-from\0jones`
+
+and \fIpick\fR selects no messages (e.g., no messages are from
+\*(lqjones\*(rq), then the shell will still run the outer command (e.g.,
+\*(lqscan\*(rq).  Since no messages were matched, \fIpick\fR produced
+no output, and the argument given to the outer command as a result of
+backquoting \fIpick\fR is empty.  In the case of \fInmh\fR programs,
+the outer command now acts as if the default `msg' or `msgs' should be
+used (e.g., \*(lqall\*(rq in the case of \fIscan\fR\0).  To prevent this
+unexpected behavior, if `\-list' was given, and if its standard output is
+not a tty, then \fIpick\fR outputs the illegal message number \*(lq0\*(rq
+when it fails.  This lets the outer command fail gracefully as well.
+
+.sp
+The pattern syntax \*(lq[l-r]\*(rq is not supported; each letter to be
+matched must be included within the square brackets.
+.En
diff --git a/man/post.man b/man/post.man
new file mode 100644 (file)
index 0000000..397901a
--- /dev/null
@@ -0,0 +1,112 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH POST %manext8% MH.6.8 [%nmhversion%]
+.SH NAME
+post \- deliver a message
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+%libdir%/post 
+\%[\-alias\ aliasfile]
+.br
+\%[\-filter\ filterfile] \%[\-nofilter]
+\%[\-format] \%[\-noformat]
+.br
+\%[\-mime] \%[\-nomime]
+\%[\-msgid] \%[\-nomsgid]
+\%[\-verbose]
+.br
+\%[\-noverbose]
+\%[\-watch] \%[\-nowatch]
+\%[\-width\ columns]
+.br
+file
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fIPost\fR is the default program called by \fIsend\fR\0(1) to deliver
+the message in \fIfile\fR to local and remote users.  In fact, most of
+the features attributed to \fIsend\fR in its manual page are performed by
+\fIpost\fR, with \fIsend\fR acting as a relatively simple preprocessor.
+Thus, it is \fIpost\fR which parses the various header fields, appends
+From: and Date: lines, and interacts with the mail transport system.
+\fIPost\fR will not normally be called directly by the user.
+
+\fIPost\fR searches the \*(lqTo:\*(rq, \*(lqcc:\*(rq, \*(lqBcc:\*(rq,
+\*(lqFcc:\*(rq, and \*(lqResent\-xxx:\*(rq header lines of the specified
+message for destination addresses, checks these addresses for validity,
+and formats them so as to conform to ARPAnet Internet Message Format
+protocol, unless the `\-noformat' flag is set.  This will normally cause
+\*(lq@\fIlocal\-site\fR\*(rq to be appended to each local destination
+address, as well as any local return addresses.  The `\-width\ columns'
+switch can be used to indicate the preferred length of the header
+components that contain addresses.
+
+If a \*(lqBcc:\*(rq field is encountered, its addresses will be used for
+delivery, and the \*(lqBcc:\*(rq field will be removed from the message
+sent to sighted recipients.  The blind recipients will receive an entirely
+new message with a minimal set of headers.  Included in the body of the
+message will be a copy of the message sent to the sighted recipients.
+If `\-filter\ filterfile' is specified, then this copy is filtered
+(re\-formatted) by \fImhl\fR prior to being sent to the blind recipients.
+Alternately, if the `\-mime' switch is given, then \fIpost\fR will use
+the MIME rules for encapsulation.
+
+The `\-alias\ aliasfile' switch can be used to specify a file that post
+should take aliases from.  More than one file can be specified, each
+being preceded with `\-alias'.  In any event, the primary alias file is
+read first.
+
+The `\-msgid' switch indicates that a \*(lqMessage\-ID:\*(rq or
+\*(lqResent\-Message\-ID:\*(rq field should be added to the header.
+
+The `\-verbose' switch indicates that the user should be informed of
+each step of the posting/filing process.
+
+The `\-watch' switch indicates that the user would like to watch the
+transport system's handling of the message (e.g., local and \*(lqfast\*(rq
+delivery).
+
+\fIPost\fR consults the environment variable \fB$SIGNATURE\fR to determine
+the sender's personal name in constructing the \*(lqFrom:\*(rq line of
+the message.
+
+.Fi
+^%etcdir%/mts.conf~^nmh mts configuration file
+^%etcdir%/MailAliases~^global nmh alias file
+^%bindir%/refile~^Program to process Fcc:s
+^%libdir%/mhl~^Program to process Bcc:s
+.Pr
+\fIpost\fR does \fBNOT\fR consult the user's \&.mh\(ruprofile
+.Sa
+\fIStandard for the Format of ARPA Internet Text Messages\fR (RFC\-822),
+.br
+mhmail(1), send(1), mh\-mail(5), mh\-alias(5)
+.De
+`\-alias %etcdir%/MailAliases'
+.Ds
+`\-format'
+.Ds
+`\-nomime'
+.Ds
+`\-nomsgid'
+.Ds
+`\-noverbose'
+.Ds
+`\-nowatch'
+.Ds
+`\-width\ 72'
+.Ds
+`\-nofilter'
+.Co
+None
+.Bu
+\*(lqReply\-To:\*(rq fields are allowed to have groups in them according
+to the 822 specification, but \fIpost\fR won't let you use them.
+.En
diff --git a/man/prev.man b/man/prev.man
new file mode 100644 (file)
index 0000000..5bb1619
--- /dev/null
@@ -0,0 +1,62 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH PREV %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+prev \- show the previous message
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+prev 
+\%[+folder] 
+\%[\-showproc\ program]
+\%[\-showmimeproc\ program]
+.br
+\%[\-header] \%[\-noheader]
+\%[\-checkmime] \%[\-nocheckmime]
+.br
+\%[\-switches\ for\ \fIshowproc\fR or\ \fIshowmimeproc\fR]
+.br
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fIPrev\fR performs a \fIshow\fR on the previous message in the specified
+(or current) folder.  Like \fIshow\fR, it passes any switches on to
+the program named by \fIshowproc\fR or \fIshowmimeproc\fR, which is called
+to list the message.  This command is almost exactly equivalent to
+\*(lqshow prev\*(rq.  Consult the manual entry for \fIshow\fR\0(1) for
+all the details.
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Current\-Folder:~^To find the default current folder
+.Ps
+^showproc:~^Program to show non-MIME messages
+.Ps
+^showmimeproc:~^Program to show MIME messages
+.Sa
+show(1), next(1)
+.De
+`+folder' defaults to the current folder
+.Ds
+`\-checkmime'
+.Ds
+`\-header'
+.Co
+If a folder is specified, it will become the current folder.  The message
+that is shown (i.e., the previous message in sequence) will become the
+current message.
+.Bu
+\fIprev\fR is really a link to the \fIshow\fR program.  As a result, if
+you make a link to \fIprev\fR and that link is not called \fIprev\fR,
+your link will act like \fIshow\fR instead.  To circumvent this, add a
+profile\-entry for the link to your \fInmh\fR profile and add the argument
+\fIprev\fR to the entry.
+.En
diff --git a/man/prompter.man b/man/prompter.man
new file mode 100644 (file)
index 0000000..d8a2ef0
--- /dev/null
@@ -0,0 +1,116 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH PROMPTER %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+prompter \- prompting editor front-end for nmh
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+prompter
+\%[\-erase\ chr]
+\%[\-kill\ chr]
+\%[\-prepend] \%[\-noprepend]
+\%[\-rapid] \%[\-norapid]
+\%[\-doteof] \%[\-nodoteof]
+file
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fIPrompter\fR is an editor front\-end for \fInmh\fR which allows rapid
+composition of messages.  This program is not normally invoked directly by
+users but takes the place of an editor and acts as an editor front\-end.
+It operates on an RFC\-822 style message draft skeleton specified by
+file, normally provided by the nmh commands \fIcomp\fR, \fIdist\fR,
+\fIforw\fR, or \fIrepl\fR.
+
+\fIPrompter\fR is particularly useful when composing messages over slow
+network or modem lines.  It is an \fInmh\fR program in that it can have
+its own profile entry with switches, but it is not invoked directly by
+the user.  The commands \fIcomp\fR, \fIdist\fR, \fIforw\fR, and \fIrepl\fR
+invoke \fIprompter\fR as an editor, either when invoked with
+`\-editor\ prompter', or by the profile entry \*(lqEditor:\ prompter\*(rq,
+or when given the command `edit\ prompter' at the \*(lqWhat now?\*(rq prompt.
+
+For each empty component \fIprompter\fR finds in the draft, the user
+is prompted for a response; A <RETURN> will cause the whole component
+to be left out.  Otherwise, a `\\' preceding a <RETURN> will continue
+the response on the next line, allowing for multiline components.
+Continuation lines \fBmust\fR begin with a space or tab.
+
+Each non\-empty component is copied to the draft and displayed on the
+terminal.
+
+The start of the message body is denoted by a blank line or a line
+of dashes.  If the body is non\-empty, the prompt, which isn't written
+to the file, is
+
+    \*(lq--------Enter additional text\*(rq,
+
+or (if `\-prepend' was given)
+
+    \*(lq--------Enter initial text\*(rq.
+
+Message\-body typing is terminated with an end\-of\-file (usually
+CTRL\-D).  With the `\-doteof' switch, a period on a line all by itself
+also signifies end\-of\-file.  At this point control is returned to
+the calling program, where the user is asked \*(lqWhat now?\*(rq.
+See \fIwhatnow\fR for the valid options to this query.
+
+By using the `\-prepend' switch, the user can add type\-in to the
+beginning of the message body and have the rest of the body follow.
+This is useful for the \fIforw\fR command.
+
+By using the `\-rapid' switch, if the draft already contains text in
+the message\-body, it is not displayed on the user's terminal.  This is
+useful for low\-speed terminals.
+
+The line editing characters for kill and erase may be specified by the
+user via the arguments `\-kill\ chr' and `\-erase\ chr', where chr may
+be a character; or `\\nnn', where \*(lqnnn\*(rq is the octal value for
+the character.
+
+An interrupt (usually CTRL\-C) during component typing will abort
+\fIprompter\fR and the \fInmh\fR command that invoked it.  An interrupt
+during message\-body typing is equivalent to CTRL\-D, for historical
+reasons.  This means that \fIprompter\fR should finish up and exit.
+
+The first non\-flag argument to \fIprompter\fR is taken as the name of
+the draft file, and subsequent non\-flag arguments are ignored.
+.\" (\fIRepl\fR invokes editors with two file arguments:
+.\" the draft file name and the replied\-to message file name.)
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+^/tmp/prompter*~^Temporary copy of message
+.Pr
+prompter\-next:        To name the editor to be used on exit from \fIprompter\fR
+.Ps
+^Msg\-Protect:~^To set mode when creating a new draft
+.Sa
+comp(1), dist(1), forw(1), repl(1), whatnow(1)
+.De
+`\-prepend'
+.Ds
+`\-norapid'
+.Ds
+`\-nodoteof'
+.Co
+None
+.Hh
+The `\-rapid' option is particularly useful with \fIforw\fP, and
+`\-noprepend' is useful with \fIcomp\ \-use\fP.
+
+The user may wish to link \fIprompter\fR under several names (e.g.,
+\*(lqrapid\*(rq) and give appropriate switches in the profile entries
+under these names (e.g., \*(lqrapid: -rapid\*(rq).  This facilitates
+invoking prompter differently for different \fInmh\fP commands (e.g.,
+\*(lqforw: -editor rapid\*(rq).
+.Bu
+\fIPrompter\fR uses \fIstdio\fR\0(3), so it will lose if you edit files
+with nulls in them.
+.En
diff --git a/man/rcvdist.man b/man/rcvdist.man
new file mode 100644 (file)
index 0000000..1a2cfe5
--- /dev/null
@@ -0,0 +1,60 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH RCVDIST %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+rcvdist \- asynchronously redistribute new mail
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+%libdir%/rcvdist
+\%[\-form\ formfile]
+.br
+\%[switches\ for\ \fIpostproc\fR]
+address1\ ...
+.br
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+The \fIrcvdist\fR program will accept a message on its standard input
+and resend a copy of this message to all of the addresses listed on its
+command line.
+
+When a message is redistributed with the \fIrcvdist\fR command, the
+format of the Resent-xxx header fields is controlled by the forms files
+"rcvdistcomps".  If a file named "rcvdistcomps" exists in the user's nmh
+directory, it will be used instead of the default one.  You may specify
+an alternate forms file with the switch `\-form\ formfile'.
+
+The "rcvdistcomps" file uses the format string facility described in
+\fImh\-format\fR(5).  In addition to the standard format escapes,
+\fIrcvdist\fP also recognizes the following additional \fIcomponent\fR
+escape:
+.sp 1
+.ne 5
+.nf
+.ta \w'Dtimenow  'u +\w'Returns  'u
+\fIEscape\fR     \fIReturns\fR   \fIDescription\fR
+addresses  string    the addresses to distribute to
+.re
+.fi
+
+By default, \fIrcvdist\fR uses the program \fIpost\fR(8) to do the actual
+delivery of the message, although this can be changed by defining the
+\fIpostproc\fR profile component.
+.Fi
+^%etcdir%/rcvdistcomps~^Default message skeleton
+^or <mh\-dir>/rcvdistcomps~^Rather than standard message skeleton
+^%etcdir%/mts.conf~^nmh mts configuration file
+^$HOME/\&.maildelivery~^The file controlling local delivery
+^%etcdir%/maildelivery~^Rather than the standard file
+.Sa
+rcvpack(1), rcvstore(1), rcvtty(1), mh\-format(5), slocal(1)
+.Bu
+Only two return codes are meaningful, others should be.
+.En
diff --git a/man/rcvpack.man b/man/rcvpack.man
new file mode 100644 (file)
index 0000000..ed95ad0
--- /dev/null
@@ -0,0 +1,45 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH RCVPACK %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+rcvpack \- append message to file
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+%libdir%/rcvpack
+file
+\%[-mbox] \%[-mmdf]
+.br
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+The \fIrcvpack\fR program will append a copy of the message to the file
+listed on its command line.
+
+If the `-mbox' switch is given (the default), then the messages are
+separated using mbox (uucp) style delimiters.  This is the format used
+by most mail clients (elm, mailx, etc.).
+If the `-mmdf' switch is given, then the messages are separated by
+mmdf style delimiters.  Each message in the file is separated by four
+CTRL\-A's and a newline.
+
+\fIrcvpack\fR will correctly lock and unlock the file to serialize
+access to the file, when running multiple copies of \fIrcvpack\fR.
+
+In general, its use is obsoleted by the \*(lqfile\*(rq action of
+\fIslocal\fR, although it might still have occasional uses in various
+shell scripts.
+.Fi
+^%etcdir%/mts.conf~^nmh mts configuration file
+.Sa
+rcvdist(1), rcvstore(1), rcvtty(1), slocal(1)
+.Bu
+Only two return codes are meaningful, others should be.
+.En
diff --git a/man/rcvstore.man b/man/rcvstore.man
new file mode 100644 (file)
index 0000000..eeb164d
--- /dev/null
@@ -0,0 +1,100 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH RCVSTORE %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+rcvstore \- asynchronously incorporate mail into a folder
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+%libdir%/rcvstore
+\%[+folder]
+.br
+\%[\-create] \%[\-nocreate]
+\%[\-unseen] \%[\-nounseen]
+.br
+\%[\-zero] \%[\-nozero]
+\%[\-public] \%[\-nopublic]
+.br
+\%[\-sequence\ name\ ...]
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fIRcvstore\fR incorporates a message from the standard input into an
+\fInmh\fR folder.  This command is typically used in conjunction with
+mail filtering programs such as \fIslocal\fR and \fIprocmail\fR, to
+filter your mail into different folders.
+
+You may specify which folder to use with `+folder'.  If no folder is
+specified, \fIrcvstore\fP will use the folder given by a non\-empty
+\*(lqInbox:\*(rq entry in the user's profile, else it will use the folder
+named \*(lqinbox\*(rq.
+
+If the switch `\-create' is given (it is the default) and if the specified
+(or default) folder does not exist, then it will be created.  You may
+disable this with the `\-nocreate' option.  In this case \fIrcvstore\fP
+will exit if the specified folder does not exist.
+
+When the new message is incorporated into the folder, it is assigned
+the next highest number for that folder.
+
+\fIRcvstore\fR will incorporate anything except zero length messages
+into the user's nmh folder.  It will not change the message in any
+way.
+
+If the user's profile contains a \*(lqMsg\-Protect: nnn\*(rq entry, it
+will be used as the protection on the newly created message, otherwise
+the \fInmh\fR default of 0644 will be used.  For all subsequent operations
+on this message, this initially assigned protection will be preserved.
+
+If the switch `\-unseen' is given (it is on by default), and if the
+profile entry \*(lqUnseen\-Sequence\*(rq is present and non\-empty, then
+\fIrcvstore\fR will add the newly incorporated message to each sequence
+named by this profile entry.  You may use the switch `\-nounseen' to
+disable this.  These sequences will not be zero'ed by \fIrcvstore\fR
+prior to adding the new message.
+
+Furthermore, the incoming message may be added to additional sequences
+as they arrive by the use of the `\-sequence' switch.  As with the
+commands \fIpick\fP and \fImark\fP, you may also use the switches
+`\-zero' and `\-nozero' to specify whether to zero old sequences or not.
+Similarly, use of the `\-public' and `\-nopublic switches may be used
+to force these sequences to be public or private sequences.
+
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Folder\-Protect:~^To set mode when creating a new folder
+.Ps
+^Inbox:~^To find the default inbox
+.Ps
+^Msg\-Protect:~^To set mode when creating a new message
+.Ps
+^Unseen\-Sequence:~^To name sequences denoting unseen messages
+.Sa
+rcvdist(1), rcvpack(1), rcvtty(1), mh\-sequence(5)
+.De
+`+folder' defaults to \*(lqInbox\*(rq profile entry
+.Ds
+`\-create'
+.Ds
+`\-unseen'
+.Ds
+`\-nozero'
+.Co
+No context changes will be attempted, with the exception of
+sequence manipulation.
+.Bu
+If you use the \*(lqUnseen\-Sequence\*(rq profile entry, \fIrcvstore\fP
+could try to update the context while another \fInmh\fP process
+is also trying to do so.  This can cause the context to become
+corrupted.  To avoid this, do not use \fIrcvstore\fP if you use the
+\*(lqUnseen\-Sequence\*(rq profile entry.
+.En
diff --git a/man/rcvtty.man b/man/rcvtty.man
new file mode 100644 (file)
index 0000000..a00d1b1
--- /dev/null
@@ -0,0 +1,84 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH RCVTTY %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+rcvtty  \- report new mail
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+%libdir%/rcvtty
+\%[command]
+.br
+\%[\-form\ formatfile]
+\%[\-format\ string]
+\%[\-width\ columns]
+.br
+\%[\-bell] \%[\-nobell]
+\%[\-newline]
+\%[\-nonewline]
+\%[\-biff]
+.br
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+The \fIrcvtty\fR can be used to report new mail.  It is used primarily
+in conjunction with mail filtering agents such as \fIslocal\fP or
+\fIprocmail\fP.
+
+The \fIrcvtty\fR program executes the named command with the message as
+its standard input, and writes the resulting output on your terminal.
+
+Alternately, if no command is specified (or is bogus), then \fIrcvtty\fR
+will instead write a one\-line scan listing.  The default output format
+of this scan listing may be overridden by using either the
+`\-form\ formatfile' or `\-format\ string' option, similar to the
+equivalent options for \fIscan\fP and \fIinc\fP.
+See \fImh\-format\fP(5) for details.
+
+A newline is output before the message output, and the terminal bell is
+rung after the output.  The `\-nonewline' and `\-nobell' options will
+inhibit these functions.
+
+The switch `\-width\ columns' may be given to specify the width of
+the scan line.  The default is to use the width of the terminal.
+
+In addition to the standard format escapes described in
+\fImh\-format\fR(5), \fIrcvtty\fR also recognizes the following additional
+\fIcomponent\fR escapes:
+.sp 1
+.ne 5
+.nf
+.ta \w'Dtimenow  'u +\w'Returns  'u
+\fIEscape\fR   \fIReturns\fR   \fIDescription\fR
+body   string  the (compressed) first part of the body
+dtimenow       date    the current date
+folder string  the name of the current folder
+.re
+.fi
+
+By default, \fIrcvtty\fP will send its output to every terminal on the
+local machine that is owned by current user, and that has given write
+permission as granted by the command \fImesg\fP\0(1).  If the option
+`\-biff' is given, then \fIrcvtty\fP will obey the notification status
+set by the command \fIbiff\fP\0(1) instead.
+.Fi
+^%etcdir%/mts.conf~^nmh mts configuration file
+^$HOME/\&.maildelivery~^The file controlling local delivery
+^%etcdir%/maildelivery~^Rather than the standard file
+.De
+`\-width' defaults to the width of the terminal
+.Ds
+`\-newline'
+.Ds
+`\-bell'
+.Sa
+rcvdist(1), rcvpack(1), rcvstore(1), mh\-format(5), slocal(1)
+.Bu
+Only two return codes are meaningful, others should be.
+.En
diff --git a/man/refile.man b/man/refile.man
new file mode 100644 (file)
index 0000000..778616e
--- /dev/null
@@ -0,0 +1,143 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH REFILE %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+refile \- file message in other folders
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+refile 
+\%[msgs] 
+\%[\-draft]
+\%[\-link] \%[\-nolink]
+.br
+\%[\-preserve] \%[\-nopreserve]
+\%[\-unlink] \%[\-nounlink]
+.br
+\%[\-src\ +folder] 
+\%[\-file\ file]
+\%[\-rmmproc program]
+.br
+\%[\-normmproc]
++folder1 ...
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fIRefile\fR moves (\fImv\fR\0(1)) or links (\fIln\fR\0(1)) messages
+from a source folder into one or more destination folders.
+
+If you think of a message as a sheet of paper, this operation is not
+unlike filing the sheet of paper (or copies) in file cabinet folders.
+When a message is filed, it is linked into the destination folder(s)
+if possible, and is copied otherwise.  As long as the destination
+folders are all on the same file system, multiple filing causes little
+storage overhead.  This facility provides a good way to cross\-file or
+multiply\-index messages.  For example, if a message is received from
+Jones about the ARPA Map Project, the command
+
+     refile\0cur\0+jones\0+Map
+
+would allow the message to be found in either of the two folders `jones'
+or `Map'.
+
+You may specify the source folder using `\-src\ +folder'.  If this is
+not given, the current folder is used by default.  If no message is
+specified, then `cur' is used by default.
+
+The option `\-file\ file' directs \fIrefile\fR to use the specified file
+as the source message to be filed, rather than a message from a folder.
+Note that the file should be a validly formatted message, just like
+any other \fInmh\fR message.  It should \fBNOT\fR be in mail drop format
+(to convert a file in mail drop format to a folder of \fInmh\fR messages,
+see \fIinc\fR\0(1)).
+
+If a destination folder doesn't exist, \fIrefile\fR will ask if you want
+to create it.  A negative response will abort the file operation.  If the
+standard input for \fIrefile\fR is \fInot\fR a tty, then \fIrefile\fR
+will not ask any questions and will proceed as if the user answered
+\*(lqyes\*(rq to all questions.
+
+The option `\-link' preserves the source folder copy of the message (i.e.,
+it does a \fIln\fR(1) rather than a \fImv\fR(1)), whereas, `\-nolink'
+(the default) deletes the filed messages from the source folder.
+
+Normally when a message is refiled, for each destination folder it
+is assigned the number which is one above the current highest message
+number in that folder.  Use of the `\-preserve' switch will override
+this message renaming, and try to preserve the number of the message.
+If a conflict for a particular folder occurs when using the `\-preserve'
+switch, then \fIrefile\fR will use the next available message number
+which is above the message number you wish to preserve.
+
+If `\-link' is not specified (or `\-nolink' is specified), the filed
+messages will be removed from the source folder.  The default is to
+remove these messages by renaming them with a site-dependent prefix
+(usually a comma).  Such files will then need to be removed in some
+manner after a certain amount of time.  Many sites arrange for
+\fIcron\fR\0(8) to remove these files once a day, so check with your
+system administrator.
+
+Alternately, if you wish for \fIrefile\fR to really remove the files
+representing these messages from the source folder, you can use the
+`-unlink' switch (not to be confused with the -link switch).  But
+messages removed by this method cannot be later recovered.
+
+.ne 4
+If you prefer a more sophisticated method of `removing' the messages
+from the source folder, you can define the \fIrmmproc\fR profile
+component.  For example, you can add a profile component such as
+
+       rmmproc:        /home/coleman/bin/rmm_msgs
+
+then \fIrefile\fR will instead call the named program or script to
+handle the message files.
+
+The user may specify `\-rmmproc program' on the command line to
+override this profile specification.  The `-normmproc' option forces
+the message files to be deleted by renaming or unlinking them as
+described above.
+
+The `\-draft' switch tells \fIrefile\fR to file the <mh\-dir>/draft.
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Current\-Folder:~^To find the default current folder
+.Ps
+^Folder\-Protect:~^To set mode when creating a new folder
+.Ps
+^rmmproc:~^Program to delete the message
+.Sa
+folder(1), rmf(1), rmm(1)
+.De
+`\-src\ +folder' defaults to the current folder
+.Ds
+`msgs' defaults to cur
+.Ds
+`\-nolink'
+.Ds
+`\-nounlink'
+.Ds
+`\-nopreserve'
+.Co
+If `\-src\ +folder' is given, it will become the current folder.
+If neither `\-link' nor `all' is specified, the current message in the
+source folder will be set to the last message specified; otherwise, the
+current message won't be changed.
+
+If the Previous\-Sequence profile entry is set, in addition to defining
+the named sequences from the source folder, \fIrefile\fR will also define
+those sequences for the destination folders.  See \fImh\-sequence\fR\0(5)
+for information concerning the previous sequence.
+.Bu
+Since \fIrefile\fR uses your \fIrmmproc\fP to delete the message,
+the \fIrmmproc\fP must \fBNOT\fP call \fIrefile\fP without specifying
+`\-normmproc', or you will create an infinite loop.
+.En
diff --git a/man/repl.man b/man/repl.man
new file mode 100644 (file)
index 0000000..a7e1165
--- /dev/null
@@ -0,0 +1,337 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH REPL %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+repl \- reply to a message
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+repl
+\%[+folder] \%[msg]
+.br
+\%[\-group] \%[\-nogroup]
+\%[\-annotate] \%[\-noannotate]
+.br
+\%[\-cc\ all/to/cc/me] \%[\-nocc\ all/to/cc/me]
+.br
+\%[\-query] \%[\-noquery]
+\%[\-form\ formfile]
+.br
+\%[\-format] \%[\-noformat]
+\%[\-filter\ filterfile]
+.br
+\%[\-inplace] \%[\-noinplace]
+\%[\-mime] \%[\-nomime]
+.br
+\%[\-fcc\ +folder]
+\%[\-width\ columns]
+.br
+\%[\-draftfolder\ +folder] \%[\-draftmessage\ msg]
+.br
+\%[\-nodraftfolder]
+\%[\-editor\ editor] \%[\-noedit]
+.br
+\%[\-whatnowproc\ program] \%[\-nowhatnowproc]
+.br
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fIRepl\fR may be used to produce a reply to an existing message.
+
+In its simplest form (with no arguments), \fIrepl\fR will set up a
+message\-form skeleton in reply to the current message in the current
+folder, and invoke the whatnow shell.
+
+In order to construct the message draft of the reply, \fIrepl\fR uses
+a reply template to guide its actions.  A reply template is simply a
+\fImhl\fR format file (see \fImh\-format\fR\0(5) for details).
+
+If the switch `\-nogroup' is given (it is on by default), then \fIrepl\fR
+will use the standard forms file \*(lqreplcomps\*(rq.  This will construct
+a draft message that is intended to be sent only to the author of the
+message to which you are replying.  If a file named \*(lqreplcomps\*(rq
+exists in the user's nmh directory, it will be used instead of this
+default forms file.
+
+The default reply template \*(lqreplcomps\*(rq will direct \fIrepl\fR
+to construct the reply message draft as follows:
+
+.nf
+.in 1i
+To: <Mail\-Reply\-To> or <Reply\-To> or <From>
+Subject: Re: <Subject>
+In\-Reply\-To: Your message of <Date>.
+.ti +\w'In\-Reply\-To: 'u
+<Message\-Id>
+.in .5i
+.fi
+
+where field names enclosed in angle brackets (<\ >) indicate the
+contents of the named field from the message to which the reply is
+being made.
+
+If the switch `\-group' is given, then \fIrepl\fR will use the the
+standard forms file \*(lqreplgroupcomps\*(rq.  This will construct a
+draft message that is intended as a group or followup reply.  If a file
+named \*(lqreplgroupcomps\*(rq exists in the user's nmh directory, it
+will be used instead of this default forms file.
+
+The default group reply template \*(lqreplgroupcomps\*(rq will direct
+\fIrepl\fR to construct the reply message draft as follows:
+
+.nf
+.in 1i
+To: <Mail\-Followup\-To>
+Subject: Re: <Subject>
+In\-Reply\-To: Message from <From> of <Date>.
+.ti +\w'In\-Reply\-To: 'u
+<Message\-Id>
+.in .5i
+.fi
+
+or if the field <Mail\-Followup\-To> is not available:
+
+.nf
+.in 1i
+To: <Mail\-Reply\-To> or <Reply\-To> or <From>
+cc: <To> and <cc> and <personal address>
+Subject: Re: <Subject>
+In\-Reply\-To: Message from <From> of <Date>.
+.ti +\w'In\-Reply\-To: 'u
+<Message\-Id>
+.in .5i
+.fi
+
+In any case, you may specify an alternate forms file with the switch
+`\-form\ formfile'.
+
+You may selectively remove addresses from this default with the
+`\-nocc\ type' switch.  This switch takes an argument (all/to/cc/me)
+which specifies who gets removed from the default \*(lqcc:\*(rq list of
+the reply.  You may give this switch multiple times (with different
+arguments) if you wish to remove multiple types of addresses.
+
+The `\-query' switch modifies the action of `\-nocc\ type' switch by
+interactively asking you if each address that normally would be placed in
+the \*(lqTo:\*(rq and \*(lqcc:\*(rq list should actually be sent a copy.
+This is useful for special\-purpose replies.  Note that the position of
+the `\-cc' and `\-nocc' switches, like all other switches which take a
+positive and negative form, is important.
+
+Lines beginning with the fields \*(lqTo:\*(rq, \*(lqcc:\*(rq, and
+\*(rqBcc:\*(rq will be standardized and have duplicate addresses removed.
+In addition, the `\-width\ columns' switch will guide \fIrepl\fR's
+formatting of these fields.
+
+If the draft already exists, \fIrepl\fR will ask you as to the disposition
+of the draft.  A reply of \fBquit\fR will abort \fIrepl\fR, leaving the
+draft intact; \fBreplace\fR will replace the existing draft with a blank
+skeleton; and \fBlist\fR will display the draft.
+
+See \fIcomp\fR\0(1) for a description of the `\-editor' and `\-noedit'
+switches.  Note that while in the editor, the message being replied
+to is available through a link named \*(lq@\*(rq (assuming the default
+\fIwhatnowproc\fR\0).  In addition, the actual pathname of the message is
+stored in the environment variable \fB$editalt\fR, and the pathname of
+the folder containing the message is stored in the environment variable
+\fB$mhfolder\fR.
+
+Although \fIrepl\fR uses a forms file to direct it how to construct
+the beginning of the draft, it uses a message filter file to direct
+it as to how the message to which you are replying should be filtered
+(re\-formatted) in the body of the draft.  The filter file for \fIrepl\fR
+should be a standard form file for \fImhl\fR, as \fIrepl\fR will invoke
+\fImhl\fR to format the message to which you are replying.
+
+The switches `\-noformat', `\-format', and `\-filter\ filterfile' specify
+which message filter file to use.
+
+If the switch `\-noformat' is given (it is the default), then the message
+to which you are replying is not included in the body of the draft.
+
+If the switch `\-format' is given, then a default message filter file
+is used.  This default message filter should be adequate for most users.
+This default filter \*(lqmhl.reply\*(rq is:
+
+.nf
+.in +.5i
+.ne 10
+.eo
+.so %etcdir%/mhl.reply
+.ec
+.in -.5i
+.fi
+
+which outputs each line of the body of the message prefaced with the
+\*(lq>\*(rq character and a space.
+
+If a file named \*(lqmhl.reply\*(rq exists in the user's nmh directory,
+it will be used instead of this form.  You may specify an alternate
+message filter file with the switch `\-filter\ filterfile'.
+
+Other reply filters are commonly used, such as:
+
+.nf
+.in +.5i
+:
+body:nocomponent,compwidth=9,offset=9
+.in -.5i
+.fi
+
+which says to output a blank line and then the body of the message
+being replied\-to, indented by one tab\-stop.  Another popular format
+is:
+
+.nf
+.in +.5i
+.ie n \{
+message-id:nocomponent,\|nonewline,\\
+formatfield=\*(lqIn message %{text},\ \*(rq \}
+.el message-id:nocomponent,\|nonewline,\|formatfield=\*(lqIn message %{text},\ \*(rq
+from:nocomponent,\|formatfield=\*(lq%(friendly{text}) writes:\*(rq
+body:component=\*(lq>\*(rq,\|overflowtext=\*(lq>\*(rq,\|overflowoffset=0
+.in -.5i
+.fi
+
+This message filter file cites the Message-ID and author of the message
+being replied\-to, and then outputs each line of the body prefaced with
+the \*(lq>\*(rq character.
+
+To use the MIME rules for encapsulation, specify the `\-mime' switch.
+This directs \fIreply\fR to generate an \fImhbuild\fR composition file.
+Note that nmh will not invoke \fImhbuild\fR automatically, unless you
+add this line to your \&.mh\(ruprofile file:
+.sp
+.in +.5i
+automimeproc: 1
+.in -.5i
+.sp
+Otherwise, you must specifically give the command
+.sp
+.in +.5i
+What now? mime
+.in -.5i
+.sp
+prior to sending the draft.
+
+If the `\-annotate' switch is given, the message being replied\-to will
+be annotated with the lines
+
+     Replied:\ date
+     Replied:\ addrs
+
+where the address list contains one line for each addressee.
+The annotation will be done only if the message is sent directly from
+\fIrepl\fR.  If the message is not sent immediately from \fIrepl\fR,
+\*(lqcomp\ \-use\*(rq may be used to re\-edit and send the constructed
+message, but the annotations won't take place.  Normally annotations are
+done inplace in order to preserve any links to the message.  You may use
+the `\-noinplace' switch to change this.
+
+The `\-fcc\ +folder' switch can be used to automatically specify a folder
+to receive Fcc:s.  More than one folder, each preceded by `\-fcc' can
+be named.
+
+In addition to the standard \fImh\-format\fR\0(5) escapes, \fIrepl\fR
+also recognizes the following additional \fIcomponent\fR escape:
+.sp 1
+.nf
+.ta \w'Escape  'u +\w'Returns  'u
+\fIEscape\fR   \fIReturns\fR   \fIDescription\fR
+\fIfcc\fR      string  Any folders specified with `\-fcc\ folder'
+.re
+.fi
+
+To avoid reiteration, \fIrepl\fR strips any leading `Re: ' strings from
+the \fIsubject\fR component.
+
+The `\-draftfolder\ +folder' and `\-draftmessage\ msg' switches invoke
+the \fInmh\fR draft folder facility.  This is an advanced (and highly
+useful) feature.  Consult the \fImh-draft\fR(5) man page for more
+information.
+
+Upon exiting from the editor, \fIrepl\fR will invoke the \fIwhatnow\fR
+program.  See \fIwhatnow\fR\0(1) for a discussion of available
+options.  The invocation of this program can be inhibited by using the
+`\-nowhatnowproc' switch.  (In truth of fact, it is the \fIwhatnow\fR
+program which starts the initial edit.  Hence, `\-nowhatnowproc' will
+prevent any edit from occurring.)
+
+.Fi
+^%etcdir%/replcomps~^The standard reply template
+^or <mh\-dir>/replcomps~^Rather than the standard template
+^%etcdir%/replgroupcomps~^The standard `reply -group' template
+^or <mh\-dir>/replgroupcomps~^Rather than the standard template
+^%etcdir%/mhl.reply~^The standard message filter
+^or <mh\-dir>/mhl.reply~^Rather than the standard filter
+^$HOME/\&.mh\(ruprofile~^The user profile
+^<mh\-dir>/draft~^The draft file
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Alternate\-Mailboxes:~^To determine the user's mailboxes
+.Ps
+^Current\-Folder:~^To find the default current folder
+.Ps
+^Draft\-Folder:~^To find the default draft\-folder
+.Ps
+^Editor:~^To override the default editor
+.Ps
+^Msg\-Protect:~^To set mode when creating a new message (draft)
+.Ps
+^fileproc:~^Program to refile the message
+.Ps
+^mhlproc:~^Program to filter message being replied\-to
+.Ps
+^whatnowproc:~^Program to ask the \*(lqWhat now?\*(rq questions
+.Sa
+mhbuild(1), comp(1), forw(1), send(1), whatnow(1), mh\-format(5)
+.De
+`+folder' defaults to the current folder
+.Ds
+`msg' defaults to cur
+.Ds
+`\-nogroup'
+.Ds
+`\-cc\ all'
+.Ds
+`\-noannotate'
+.Ds
+`\-nodraftfolder'
+.Ds
+`\-noformat'
+.Ds
+`\-inplace'
+.Ds
+`\-nomime'
+.Ds
+`\-noquery'
+.Ds
+`\-width\ 72'
+.Co
+If a folder is given, it will become the current folder.  The message
+replied\-to will become the current message.
+.Bu
+If any addresses occur in the reply template, addresses in the template
+that do not contain hosts are defaulted incorrectly.  Instead of using
+the localhost for the default, \fIrepl\fR uses the sender's host.
+Moral of the story: if you're going to include addresses in a reply
+template, include the host portion of the address.
+
+The `\-width columns' switch is only used to do address-folding; other
+headers are not line\-wrapped.
+
+If \fIwhatnowproc\fR is \fIwhatnow\fR, then \fIrepl\fR uses a built\-in
+\fIwhatnow\fR, it does not actually run the \fIwhatnow\fR program.
+Hence, if you define your own \fIwhatnowproc\fR, don't call it
+\fIwhatnow\fR since \fIrepl\fR won't run it.
+
+If your current working directory is not writable, the link named
+\*(lq@\*(rq is not available.
+.En
diff --git a/man/rmf.man b/man/rmf.man
new file mode 100644 (file)
index 0000000..de9cd8f
--- /dev/null
@@ -0,0 +1,70 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH RMF %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+rmf \- remove an nmh folder
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+rmf 
+\%[+folder]
+\%[\-interactive] \%[\-nointeractive]
+.br
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fIRmf\fR removes all of the messages (files) within the specified
+(or default) folder, and then removes the folder (directory) itself.
+
+If there are any files within the folder which are not a part of
+\fInmh\fR, they will \fInot\fR be removed, and an error will be produced.
+If the folder is given explicitly or the `\-nointeractive' option is
+given, then the folder will be removed without confirmation.  Otherwise,
+the user will be asked for confirmation.  If \fIrmf\fR can't find the
+current folder, for some reason, the folder to be removed defaults to
+`+inbox' (unless overridden by user's profile entry \*(lqInbox\*(rq)
+with confirmation.
+
+If the folder being removed is a subfolder, the parent folder will become
+the new current folder, and \fIrmf\fR will produce a message telling the
+user this has happened.  This provides an easy mechanism for selecting
+a set of messages, operating on the list, then removing the list and
+returning to the current folder from which the list was extracted.
+
+If \fIrmf\fR is used on a read\-only folder, it will delete all the
+(private) sequences
+(i.e., \*(lqatr\-\fIseq\fR\-\fIfolder\fR\*(rq entries) for this folder
+from your context without affecting the folder itself.
+
+\fIRmf\fR irreversibly deletes messages that don't have other links, so
+use it with caution.
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Current\-Folder:~^To find the default current folder
+.Ps
+^Inbox:~^To find the default inbox
+.Sa
+rmm(1)
+.De
+`+folder' defaults to the current folder, usually with confirmation
+.Ds
+`\-interactive' if +folder' not given, `\-nointeractive' otherwise
+.Co
+\fIRmf\fR will set the current folder to the parent folder if a
+subfolder is removed; or if the current folder is removed, it will make
+\*(lqinbox\*(rq current.  Otherwise, it doesn't change the current folder
+or message.
+.Bu
+Although intuitively one would suspect that \fIrmf\fR works recursively,
+it does not.  Hence if you have a sub\-folder within a folder, in order
+to \fIrmf\fR the parent, you must first \fIrmf\fR each of the children.
+.En
diff --git a/man/rmm.man b/man/rmm.man
new file mode 100644 (file)
index 0000000..8773853
--- /dev/null
@@ -0,0 +1,76 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH RMM %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+rmm \- remove messages
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+rmm
+\%[+folder] \%[msgs]
+\%[\-unlink] \%[\-nounlink]
+.br
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+By default, \fIrmm\fR will remove the specified messages by renaming
+the message files with preceding commas.  Such files will then need to
+be removed in some manner after a certain amount of time.  Many sites
+arrange for \fIcron\fR\0(8) to remove these files once a day, so check
+with your system administrator.
+
+Alternately, if you wish for \fIrmm\fR to really remove the files
+representing these messages, you can use the `-unlink' switch.  But
+messages removed by this method cannot be later recovered.
+
+If you prefer a more sophisticated method of `removing' messages, you
+can define the \fIrmmproc\fR profile component.  For example, you can
+add a profile component such as
+
+       rmmproc:        /home/coleman/bin/rmm_msgs
+
+then instead of simply renaming the message file, \fIrmm\fR will call
+the named program or script to handle the files that represent the
+messages to be deleted.
+
+Some users of csh prefer the following:
+
+       alias rmm 'refile +d'
+
+where folder +d is a folder for deleted messages, and
+
+       alias mexp 'rm `mhpath +d all`'
+
+is used to \*(lqexpunge\*(rq deleted messages.
+
+The current message is not changed by \fIrmm\fR, so a \fInext\fR  will
+advance to the next message in the folder as expected.
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Current\-Folder:~^To find the default current folder
+.Ps
+^rmmproc:~^Program to delete the message
+.Sa
+refile(1), rmf(1)
+.De
+`+folder' defaults to the current folder
+.Ds
+`msgs' defaults to cur
+.Ds
+`-nounlink'
+.Co
+If a folder is given, it will become the current folder.
+.Bu
+Since \fIrefile\fR uses your \fIrmmproc\fP to delete the message,
+the \fIrmmproc\fP must \fBNOT\fP call \fIrefile\fP without specifying
+`\-normmproc', or you will create an infinte loop.
+.En
diff --git a/man/scan.man b/man/scan.man
new file mode 100644 (file)
index 0000000..d4ad6a7
--- /dev/null
@@ -0,0 +1,162 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH SCAN %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+scan \- produce a one line per message scan listing
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+scan
+\%[+folder] \%[msgs]
+\%[\-clear] \%[\-noclear]
+\%[\-form\ formatfile]
+\%[\-format\ string]
+\%[\-header] \%[\-noheader]
+\%[\-width\ columns]
+\%[\-reverse] \%[\-noreverse]
+\%[\-file filename]
+.br
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fIScan\fR produces a one\-line\-per\-message listing of the specified
+folder or messages.  Each \fIscan\fR line contains the message number
+(name), the date, the \*(lqFrom:\*(rq field, the \*(lqSubject\*(rq field,
+and, if room allows, some of the body of the message.  For example:
+
+.nf
+.in +.5i
+.ta \w'15+- 'u +\w'07/\|05x 'u +\w'Dcrocker  'u
+15+    10/\|05 crocker nned\0\0\*(<<Last week I asked some of
+16\-   10/\|05 crocker message id format\0\0\*(<<I recommend
+18     10/\|06 brien   Re: Exit status from mkdir
+19     10/\|07*brien   \*(lqscan\*(rq listing format in nmh
+.re
+.in -.5i
+.fi
+
+The `+' on message 15 indicates that it is the current message.
+
+The `\-' on message 16 indicates that it has been replied to, as indicated
+by a \*(lqReplied:\*(rq component (produced by the `\-annotate' switch
+to the \fIrepl\fR command).
+
+The `*' on message 19 indicates that no \*(lqDate:\*(rq header was
+present.  The time of last modification of the message is given instead.
+
+If there is sufficient room left on the \fIscan\fR line after the
+subject, the line will be filled with text from the body, preceded by
+<<, and terminated by >> if the body is sufficiently short.  \fIScan\fR
+actually reads each of the specified messages and parses them to extract
+the desired fields.  During parsing, appropriate error messages will be
+produced if there are format errors in any of the messages.
+
+By default, \fIscan\fR will decode RFC-2047 (MIME) encoding in
+these scan listings.  \fIScan\fR will only decode these fields if your
+terminal can natively display the character set used in the encoding.
+You should set the MM_CHARSET environment variable to your native
+character set, if it is not US-ASCII.  See the mh-profile(5) man
+page for details about this environment variable.
+
+The switch `\-reverse', makes \fIscan\fR list the messages in reverse
+order.
+
+The `\-file filename' switch allows the user to obtain a \fIscan\fP
+listing of a maildrop file as produced by \fIpackf\fP.  This listing
+includes every message in the file (you can't scan individual messages).
+The switch `\-reverse' is ignored with this option.
+
+The switch `\-width\ columns' may be used to specify the width of
+the scan line.  The default is to use the width of the terminal.
+
+The `\-header' switch produces a header line prior to the \fIscan\fR
+listing.  Currently, the name of the folder and the current date and
+time are output (see the \fBHISTORY\fR section for more information).
+
+If the `\-clear' switch is used and \fIscan's\fR output is directed
+to a terminal, then \fIscan\fR will consult the environment variables
+\fB$TERM\fR and \fB$TERMCAP\fR to determine your terminal type in order
+to find out how to clear the screen prior to exiting.  If the `\-clear'
+switch is used and \fIscan's\fR output is not directed to a terminal
+(e.g., a pipe or a file), then \fIscan\fR will send a formfeed prior
+to exiting.
+
+For example, the command:
+
+.ti +.5i
+(scan \-clear \-header; show all \-show pr \-f) | lpr
+
+produces a scan listing of the current folder, followed by a formfeed,
+followed by a formatted listing of all messages in the folder, one
+per page.  Omitting `\-show\ pr\ \-f' will cause the messages to be
+concatenated, separated by a one\-line header and two blank lines.
+
+To override the output format used by \fIscan\fR, the `\-format\ string'
+or `\-form\ file' switches are used.  This permits individual fields of
+the scan listing to be extracted with ease.  The string is simply a format
+string and the file is simply a format file.  See \fImh\-format\fR(5)
+for the details.
+
+In addition to the standard \fImh\-format\fR(5) escapes, \fIscan\fR
+also recognizes the following additional \fIcomponent\fR escapes:
+.sp 1
+.nf
+.ta \w'Dtimenow  'u +\w'Returns  'u
+\fIEscape\fR   \fIReturns\fR   \fIDescription\fR
+body   string  the (compressed) first part of the body
+dtimenow       date    the current date
+folder string  the name of the current folder
+.re
+.fi
+
+If no date header is present in the message, the \fIfunction\fR escapes
+which operate on {\fIdate\fP\|} will return values for the date of last
+modification of the message file itself.  This feature is handy for
+scanning a \fIdraft folder\fR, as message drafts usually aren't allowed
+to have dates in them.
+
+\fIscan\fR will update the \fInmh\fR context prior to starting the listing,
+so interrupting a long \fIscan\fR listing preserves the new context.
+\fInmh\fR purists hate this idea.
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Alternate\-Mailboxes:~^To determine the user's mailboxes
+.Ps
+^Current\-Folder:~^To find the default current folder
+.Sa
+inc(1), pick(1), show(1), mh\-format(5)
+.De
+`+folder' defaults to the current folder
+.Ds
+`msgs' defaults to all
+.Ds
+`\-format' defaulted as described above
+.Ds
+`\-noheader'
+.Ds
+`\-width' defaulted to the width of the terminal
+.Co
+If a folder is given, it will become the current folder.
+.Hi
+Prior to using the format string mechanism, `\-header' used to generate
+a heading saying what each column in the listing was.  Format strings
+prevent this from happening.
+.Bu
+The argument to the `\-format' switch must be interpreted as a single
+token by the shell that invokes \fIscan\fR.  Therefore, one must usually
+place the argument to this switch inside double\-quotes.
+
+The value of each \fIcomponent\fR escape is set by \fIscan\fR to the
+contents of the first message header \fIscan\fR encounters with the
+corresponding component name; any following headers with the same
+component name are ignored.
+.En
diff --git a/man/send.man b/man/send.man
new file mode 100644 (file)
index 0000000..43d1ad1
--- /dev/null
@@ -0,0 +1,186 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH SEND %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+send \- send a message
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+send
+\%[\-alias\ aliasfile]
+\%[\-draft]
+\%[\-draftfolder\ +folder]
+.br
+\%[\-draftmessage\ msg] \%[\-nodraftfolder]
+.br
+\%[\-filter\ filterfile] \%[\-nofilter]
+\%[\-format] \%[\-noformat]
+.br
+\%[\-forward] \%[\-noforward]
+\%[\-mime] \%[\-nomime]
+\%[\-msgid]
+.br
+\%[\-nomsgid]
+\%[\-push] \%[\-nopush]
+\%[\-split\ seconds]
+.br
+\%[\-verbose] \%[\-noverbose]
+\%[\-watch] \%[\-nowatch]
+.br
+\%[\-width\ columns]
+\%[file\ ...] 
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fISend\fR will cause each of the specified files to be delivered
+to each of the destinations in the \*(lqTo:\*(rq, \*(lqcc:\*(rq,
+\*(lqBcc:\*(rq, and \*(lqFcc:\*(rq fields of the message.  If \fIsend\fR
+is re\-distributing a message, as invoked from \fIdist\fR, then the
+corresponding \*(lqResent\-xxx\*(rq fields are examined instead.
+
+By default, \fIsend\fR uses the program \fIpost\fR(8) to do the actual
+delivery of the messages, although this can be changed by defining the
+\fIpostproc\fR profile component.  Most of the features attributed to
+\fIsend\fR are actually performed by \fIpost\fR.
+
+If `\-push' is specified, \fIsend\fR will detach itself from the user's
+terminal and perform its actions in the background.  If \fIpush\fR\0'd
+and the draft can't be sent, then an error message will be sent (using
+the mailproc) back to the user.  If `\-forward' is given, then a copy
+of the draft will be attached to this failure notice.  Using `\-push'
+differs from putting \fIsend\fR in the background because the output is
+trapped and analyzed by \fInmh\fR.
+
+If `\-verbose' is specified, \fIsend\fR will indicate the interactions
+occurring with the transport system, prior to actual delivery.
+If `\-watch' is specified \fIsend\fR will monitor the delivery of local
+and network mail.  Hence, by specifying both switches, a large detail
+of information can be gathered about each step of the message's entry
+into the transport system.
+
+The `\-draftfolder\ +folder' and `\-draftmessage\ msg' switches invoke
+the \fInmh\fR draft folder facility.  This is an advanced (and highly
+useful) feature.  Consult the \fImh-draft\fR(5) man page for more
+information.
+
+If `\-split' is specified, \fIsend\fR will split the draft into one
+or more partial messages prior to sending.  This makes use of the
+MIME features in nmh.  Note however that if \fIsend\fR is
+invoked under \fIdist\fR\0(1), then this switch is ignored\0--\0it makes
+no sense to redistribute a message in this fashion.  Sometimes you want
+\fIsend\fR to pause after posting a partial message.  This is usually
+the case when you are running \fIsendmail\fR and expect to generate a
+lot of partial messages.  The argument to `\-split' tells it how long
+to pause between postings.
+
+\fISend\fR with no \fIfile\fR argument will query whether the draft
+is the intended file, whereas `\-draft' will suppress this question.
+Once the transport system has successfully accepted custody of the
+message, the file will be renamed with a leading comma, which allows
+it to be retrieved until the next draft message is sent.  If there are
+errors in the formatting of the message, \fIsend\fR will abort with a
+(hopefully) helpful error message.
+
+If a \*(lqBcc:\*(rq field is encountered, its addresses will be used for
+delivery, and the \*(lqBcc:\*(rq field will be removed from the message
+sent to sighted recipients.  The blind recipients will receive an entirely
+new message with a minimal set of headers.  Included in the body of the
+message will be a copy of the message sent to the sighted recipients.
+If `\-filter\ filterfile' is specified, then this copy is filtered
+(re\-formatted) by \fImhl\fR prior to being sent to the blind recipients.
+Alternately, if you specify the `-mime' switch, then \fIsend\fR will
+use the MIME rules for encapsulation.
+
+Prior to sending the message, the fields \*(lqFrom:\ user@local\*(rq,
+and \*(lqDate:\ now\*(rq will be appended to the headers in the message.
+If the environment variable \fB$SIGNATURE\fR is set, then its value
+is used as your personal name when constructing the \*(lqFrom:\*(rq
+line of the message.  If this environment variable is not set, then
+\fIsend\fR will consult the profile entry \*(lqSignature\*(rq for
+this information.  On hosts where \fInmh\fR was configured with the UCI
+option, if \fB$SIGNATURE\fR is not set and the \*(lqSignature\*(rq profile
+entry is not present, then the file \fB$HOME\fR/.signature is consulted.
+If `\-msgid' is specified, then a \*(lqMessage\-ID:\*(rq field will also
+be added to the message.
+
+If \fIsend\fR is re\-distributing a message (when invoked by
+\fIdist\fR\0), then \*(lqResent\-\*(rq will be prepended to each of these
+fields: \*(lqFrom:\*(rq, \*(lqDate:\*(rq, and \*(lqMessage\-ID:\*(rq.
+If the message already contains a \*(lqFrom:\*(rq field, then a
+\*(lqSender: user@local\*(rq field will be added as well.  (An already
+existing \*(lqSender:\*(rq field is an error!)
+
+By using the `\-format' switch, each of the entries in the \*(lqTo:\*(rq
+and \*(lqcc:\*(rq fields will be replaced with \*(lqstandard\*(rq
+format entries.  This standard format is designed to be usable by all
+of the message handlers on the various systems around the Internet.
+If `\-noformat' is given, then headers are output exactly as they appear
+in the message draft.
+
+If an \*(lqFcc:\ folder\*(rq is encountered, the message will be copied
+to the specified folder for the sender in the format in which it will
+appear to any non\-Bcc receivers of the message.  That is, it will have
+the appended fields and field reformatting.  The \*(lqFcc:\*(rq fields
+will be removed from all outgoing copies of the message.
+
+By using the `\-width\ columns' switch, the user can direct \fIsend\fR
+as to how long it should make header lines containing addresses.
+
+The files specified by the profile entry \*(lqAliasfile:\*(rq and any
+additional alias files given by the `\-alias aliasfile' switch will be
+read (more than one file, each preceded by `\-alias', can be named).
+See \fImh\-alias\fR\0(5) for more information.
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Draft\-Folder:~^To find the default draft\-folder
+.Ps
+^Aliasfile:~^For a default alias file
+.Ps
+^Signature:~^To determine the user's mail signature
+.Ps
+^mailproc:~^Program to post failure notices
+.Ps
+^postproc:~^Program to post the message
+.Sa
+comp(1), dist(1), forw(1), repl(1), mh\-alias(5), post(8)
+.De
+`file' defaults to <mh\-dir>/draft
+.Ds
+`\-alias %etcdir%/MailAliases'
+.Ds
+`\-nodraftfolder'
+.Ds
+`\-nofilter'
+.Ds
+`\-format'
+.Ds
+`\-forward'
+.Ds
+`\-nomime'
+.Ds
+`\-nomsgid'
+.Ds
+`\-nopush'
+.Ds
+`\-noverbose'
+.Ds
+`\-nowatch'
+.Ds
+`\-width\ 72'
+.Co
+None
+.Bu
+Under some configurations, it is not possible to monitor the mail delivery
+transaction; `\-watch' is a no-op on those systems.
+.sp
+Using `\-split\00' doesn't work correctly.
+.En
diff --git a/man/sendfiles.man b/man/sendfiles.man
new file mode 100644 (file)
index 0000000..72f843c
--- /dev/null
@@ -0,0 +1,150 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH SENDFILES %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+sendfiles \- send multiple files via a MIME message
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+sendfiles \%[delay] mailpath subject file1 \%[file2]...
+.in -.5i
+
+.SH DESCRIPTION
+The shell script \fIsendfiles\fR, is used to send a collection
+of files and directories via electronic mail.
+.sp
+.in +.5i
+%libdir%/sendfiles mailpath \*(lqsubject\*(rq files\0...
+.in -.5i
+.sp
+\fIsendfiles\fR will archive the files and directories you name
+with the \fItar\fR\0(1) command, and then mail the compressed
+archive to the `mailpath' with the given `subject'.  The archive
+will be automatically split up into as many messages as necessary
+in order to get past most mailers.
+
+Sometimes you want \fIsendfiles\fR to pause after posting a partial
+message.  This is usually the case when you are running \fIsendmail\fR
+and expect to generate a lot of partial messages.  If the first
+argument given to \fIsendfiles\fR starts with a dash, then it is
+interpreted as the number of seconds to pause in between postings,
+.ne 6
+e.g.,
+.sp
+.in +.5i
+%libdir%/sendfiles -30 mailpath \*(lqsubject\*(rq files\0...
+.in -.5i
+.sp
+will pause 30 seconds in between each posting.
+
+.Uh "Extracting the Received Files"
+When these messages are received, invoke \fImhstore\fR once for
+the list of messages.  The default is for \fImhstore\fR to store
+the combined parts as a new message in the current folder, although
+this can be changed using storage formatting strings.  You can then
+use \fImhlist\fR to find out what's inside; possibly followed by
+\fImhstore\fR again to write the archive to a file where you can
+subsequently uncompress and untar it.  For instance:
+.sp
+.nf
+.in +.5i
+% mhlist 5-8
+ msg part  type/subtype             size description
+   5       message/partial           47K part 1 of 4
+   6       message/partial           47K part 2 of 4
+   7       message/partial           47K part 3 of 4
+   8       message/partial           18K part 4 of 4
+% mhstore 5-8
+reassembling partials 5,6,7,8 to folder inbox as message 9
+% mhlist -verbose 9
+ msg part  type/subtype             size description
+   9       application/octet-stream 118K
+             (extract with uncompress | tar xvpf -)
+             type=tar
+             conversions=compress
+% mhstore 9
+% uncompress < 9.tar.Z | tar xvpf -
+.in -.5i
+.fi
+.sp
+Alternately, by using the `\-auto' switch, \fImhstore\fR will
+automatically do the extraction for you:
+.sp
+.nf
+.in +.5i
+% mhlist 5-8
+ msg part  type/subtype             size description
+   5       message/partial           47K part 1 of 4
+   6       message/partial           47K part 2 of 4
+   7       message/partial           47K part 3 of 4
+   8       message/partial           18K part 4 of 4
+% mhstore 5-8
+reassembling partials 5,6,7,8 to folder inbox as message 9
+% mhlist -verbose 9
+ msg part  type/subtype             size description
+   9       application/octet-stream 118K
+             (extract with uncompress | tar xvpf -)
+             type=tar
+             conversions=compress
+% mhstore -auto 9
+-- \fItar\fR listing appears here as files are extracted
+.in -.5i
+.fi
+.sp
+As the second \fItar\fR listing is generated, the files are extracted.
+A prudent user will never put `\-auto' in the \&.mh\(ruprofile
+file.  The correct procedure is to first use \fImhlist\fR to find
+out what will be extracted.  Then \fImhstore\fR can be invoked with
+`\-auto' to perform the extraction.
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Current\-Folder:~^To find the default current folder
+.Sa
+mhbuild(1), mhlist(1), mhshow(1), mhstore(1)
+.br
+RFC\-934:
+.br
+   \fIProposed Standard for Message Encapsulation\fR,
+.br
+RFC\-2045:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part One:
+.br
+   Format of Internet Message Bodies\fR,
+.br
+RFC\-2046:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part Two:
+.br
+   Media Types\fR,
+.br
+RFC\-2047:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part Three:
+.br
+   Message Header Extensions for Non-ASCII Text\fR,
+.br
+RFC\-2048:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part Four:
+.br
+   Registration Procedures\fR,
+.br
+RFC\-2049:
+.br
+   \fIMultipurpose Internet Mail Extensions (MIME) Part Five:
+.br
+   Conformance Criteria and Examples\fR.
+.De
+`\-noverbose'
+.Co
+None.
+.En
diff --git a/man/show.man b/man/show.man
new file mode 100644 (file)
index 0000000..a864048
--- /dev/null
@@ -0,0 +1,164 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH SHOW %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+show \- show (display) messages
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+show
+\%[+folder] \%[msgs]
+\%[\-showproc\ program]
+.br
+\%[\-showmimeproc\ program]
+\%[\-header] \%[\-noheader]
+.br
+\%[\-draft]
+\%[\-checkmime] \%[\-nocheckmime]
+.br
+\%[switches\ for\ \fIshowproc\fR or \fIshowmimeproc\fR]
+.br
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fIShow\fR lists each of the specified messages to the standard output
+(typically, the terminal).
+
+By default, text (non-MIME) messages are filtered and displayed by
+the \fInmh\fR command \fImhl\fR.  This command will display text
+messages in a nice, uniform format.  It also allows you to configure
+the format of the displayed messages and which headers fields are
+shown.  See the \fImhl\fR(1) manual page for the details about this
+command.  This default can be changed by defining the \fIshowproc\fR
+profile component.  Any switches not recognized by \fIshow\fR are
+passed along to that program.  To override the default and the
+\fIshowproc\fR profile component, use the `\-showproc\ program'
+switch.  For example, `\-showproc\ more' will cause the \fImore\fR(1)
+program to list the messages with no reformatting.  Normally, this
+program is specified as the \fIshowproc\fR in the user's
+\&.mh\(ruprofile, rather than using a command line switch.
+
+By default, non-text messages (MIME messages with multi-media
+contents) are processed and displayed by the \fInmh\fR command
+\fImhshow\fR.  See the \fImhshow\fR(1) manual page for details
+about this command.  This default can changed by defining the
+\fIshowmimeproc\fR profile component.  Any switches not recognized
+by \fIshow\fR are passed along to that program.  To override this
+default and the \fIshowmimeproc\fR profile component, use the
+`\-showmimeproc\ program' switch.
+
+Note that in some cases, \fIshow\fR may invoke the \fIshowmimeproc\fR
+even for textual contents.  This will happen for text messages that
+specify a transfer encoding (such as MIME quoted-printable or
+base64) or specify a character set that \fIshow\fR doesn't believe
+can be displayed natively.  The environment variable MM_CHARSET
+should be set to the terminal's native character set to avoid
+gratuitous invocations of the \fIshowmimeproc\fR.  See the
+mh-profile(5) man page for details about this environment variable.
+
+The option `\-checkmime' (set by default) instructs \fIshow\fR to
+test if any of the messages to be displayed are non-text (MIME)
+messages.  If any are non-text, they are displayed by the program
+\fIshowmimeproc\fR, else they are displayed by the program
+\fIshowproc\fR.  The option `-nocheckmime' disables this test and
+instructs \fIshow\fR to use \fIshowproc\fR, regardless of whether
+any of the messages are non-text (MIME) messages.
+
+The `\-noshowproc' switch will disable any formatting or paging of
+messages.  It is equivalent to `-nocheckmime\ -showproc\ cat'.  It
+is still accepted, but should be considered (somewhat) obsolete.
+
+If the environment variable \fBNOMHNPROC\fR is set, the test for
+non-text (MIME) messages will be disabled.  This method is obsolete.
+Use the `-nocheckmime' switch instead.
+
+The `\-header' switch tells \fIshow\fR to display a one\-line
+description of the message being shown.  This description includes
+the folder and the message number.
+
+If no `msgs' are specified, the current message is used.  Although
+it depends on the specific \fIshowproc\fR or \fIshowmimeproc\fR,
+in the default setup when more than one message is specified, you
+will be prompted for a <RETURN> prior to listing each message.
+Each message will be listed a page at a time, and when the end of
+page is reached, the program will wait for a <SPACE> or <RETURN>.
+If a <RETURN> is entered, it will print the next line, whereas
+<SPACE> will print the next screenful.
+
+If the standard output is not a terminal, no queries are made, and
+each file is listed with a one\-line header and two lines of
+separation.
+
+\*(lqshow \-draft\*(rq will list the file <mh\-dir>/draft if it
+exists.
+
+If the profile entry \*(lqUnseen\-Sequence\*(rq is present and
+non\-empty, then \fIshow\fR will remove each of the messages shown
+from each sequence named by the profile entry.
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Current\-Folder:~^To find the default current folder
+.Ps
+^Unseen\-Sequence:~^To name sequences denoting unseen messages
+.Ps
+^showproc:~^Program to show text (non-MIME) messages
+.Ps
+^showmimeproc:~^Program to show non-text (MIME) messages
+.Sa
+mhl(1), mhshow(1), more(1), next(1), prev(1), scan(1)
+.De
+`+folder' defaults to the current folder
+.Ds
+`msgs' defaults to cur
+.Ds
+`\-checkmime'
+.Ds
+`\-header'
+.Co
+If a folder is given, it will become the current folder.  The last
+message shown will become the current message.
+.Bu
+The `\-header' switch doesn't work when `msgs' expands to more than
+one message.  If the \fIshowproc\fR is \fImhl\fR, then is problem can
+be circumvented by referencing the \*(lqmessagename\*(rq field in the
+\fImhl\fR format file.
+
+\fIShow\fR updates the user's context before showing the message.
+Hence \fIshow\fR will mark messages as seen prior to the user actually
+seeing them.  This is generally not a problem, unless the user relies
+on the \*(lqunseen\*(rq messages mechanism, and interrupts \fIshow\fR
+while it is showing \*(lqunseen\*(rq messages.
+
+If your \fIshowproc\fR is \fImhl\fR (the default), then \fIshow\fR uses
+a built\-in \fImhl\fR: it does not actually run the \fImhl\fR program.
+Hence, if you define your own \fIshowproc\fR, don't call it \fImhl\fR
+since \fIshow\fR won't run it.
+
+If your \fIshowproc\fR is the pager \fImore\fR, then avoid running
+\fIshow\fR in the background with only its standard output piped to
+another process, as in
+
+.ti +.5i
+show | imprint &
+
+Due to a bug in \fImore\fR, show will go into a \*(lqtty input\*(rq state.
+To avoid this problem, re\-direct \fIshow\fR's diagnostic output as well.
+For users of \fIcsh\fR:
+
+.ti +.5i
+show |& imprint &
+
+For users of \fIsh\fR:
+
+.ti +.5i
+show 2>&1 | imprint &
+.En
diff --git a/man/slocal.man b/man/slocal.man
new file mode 100644 (file)
index 0000000..b54bc3f
--- /dev/null
@@ -0,0 +1,354 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH SLOCAL %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+slocal \- asynchronously filter and deliver new mail
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+%libdir%/slocal \%[address\ info\ sender]
+.na
+.br
+\%[\-addr\ address]
+\%[\-info\ data]
+\%[\-sender\ sender]
+.br
+\%[\-user\ username]
+\%[\-mailbox\ mbox]
+.\" \%[\-home\ homedir]
+\%[\-file\ file]
+.br
+\%[\-maildelivery\ deliveryfile]
+\%[\-verbose] \%[\-noverbose]
+.br
+\%[\-suppressdup] \%[\-nosuppressdup]
+\%[\-debug]
+.br
+\%[\-version]
+\%[\-help]
+.ad
+.in -.5i
+.SH DESCRIPTION
+\fISlocal\fP is a program designed to allow you to have your inbound
+mail processed according to a complex set of selection criteria.
+You do not normally invoke \fIslocal\fP yourself, rather \fIslocal\fP
+is invoked on your behalf by your system's Message Transfer Agent
+(such as sendmail) when the message arrives.
+
+The message selection criteria used by \fIslocal\fP is specified
+in the file \fI\&.maildelivery\fP in the user's home directory.
+You can specify an alternate file with the `\-maildelivery file'
+option.  The syntax of this file is specified below.
+
+The message delivery address and message sender are determined from
+the Message Transfer Agent envelope information, if possible.
+Under \fIsendmail\fP, the sender will obtained from the UUCP
+\*(lqFrom\ \*(rq line, if present.  The user may override these
+values with command line arguments, or arguments to the `\-addr'
+and `\-sender' switches.
+
+The message is normally read from the standard input.  The `\-file'
+switch sets the name of the file from which the message should be
+read, instead of reading stdin.  This is useful when debugging a
+\fI\&.maildelivery\fP file.
+
+The `\-user' switch tells \fIslocal\fP the name of the user for
+whom it is delivering mail.  The `\-mailbox' switch tells \fIslocal\fP
+the name of the user's maildrop file.
+
+\fIslocal\fR is able to detect and suppress duplicate messages.
+To enable this, use the option `\-suppressdup'.   \fIslocal\fR will
+keep a database containing the Message-ID's of incoming messages,
+in order to detect duplicates.  Depending on your configuration,
+this database will be in either ndbm or Berkeley db format.
+
+The `\-info' switch may be used to pass an arbitrary argument to
+sub-processes which \fIslocal\fP may invoke on your behalf.
+
+The `\-verbose' switch causes \fIslocal\fP to give information on
+stdout about its progress.  The `\-debug' switch produces more
+verbose debugging output on stderr.  These flags are useful when
+creating and debugging your \fI\&.maildelivery\fP file, as they
+allow you to see the decisions and actions that \fIslocal\fR is
+taking, as well as check for syntax errors in your \fI\&.maildelivery\fP
+file.
+
+.Uh "Message Transfer Agents"
+If your MTA is \fIsendmail\fP, you should include the line
+.sp
+.nf
+.in +.5i
+    \*(lq|\ %libdir%/slocal\ \-user\ username\*(rq
+.in -.5i
+.fi
+.sp
+in your \&.forward file in your home directory.  This will cause
+\fIsendmail\fP to invoke \fIslocal\fP on your behalf when a
+message arrives.
+
+If your MTA is \fIMMDF-I\fP, you should (symbolically) link
+%libdir%/slocal to the file bin/rcvmail in your home directory.  This will
+cause \fIMMDF-I\fP to invoke \fIslocal\fP on your behalf with the correct
+\*(lq\fIaddress\ info\ sender\fP\*(rq arguments.
+
+If your MTA is \fIMMDF-II\fP, then you should not use \fIslocal\fP.
+An equivalent functionality is already provided by \fIMMDF-II\fP; see
+maildelivery(5) for details.
+
+.Uh "The Maildelivery File"
+
+The \fI\&.maildelivery\fR file controls how slocal filters and delivers
+incoming mail.  Each line of this file consists of five fields, separated
+by white-space or comma.  Since double-quotes are honored, these
+characters may be included in a single argument by enclosing the entire
+argument in double-quotes.  A double-quote can be included by preceding it
+with a backslash.  Lines beginning with `#' and blank lines are ignored.
+
+The format of each line in the \fI\&.maildelivery\fR file is:
+
+       \fBheader       pattern action  result  string\fR
+.sp
+.in +.5i
+.ti -.5i
+\fBheader\fP:
+.br
+The name of a header field (such as To, Cc,  or From) that is to
+be searched for a pattern.  This is any field in the headers of
+the message that might be present.
+
+The following special fields are also defined:
+.sp
+.in +1i
+.ta +1i
+.ti -1i
+\fIsource\fR   the out-of-band sender information
+.ti -1i
+\fIaddr\fR     the address that was used to cause delivery to the recipient
+.ti -1i
+\fIdefault\fR  this matches \fIonly\fR if the message hasn't been delivered yet
+.ti -1i
+\fI*\fR        this always matches
+.in -1i
+
+.ti -.5i
+\fBpattern\fR:
+.br
+The sequence of characters to match in the specified header field.
+Matching is case-insensitive, but does not use regular expressions.
+
+.ti -.5i
+\fBaction\fR:
+.br
+The action to take to deliver the message.  When a message is delivered,
+a \*(lqDelivery\-Date:\ date\*(rq header is added which indicates the date
+and time that message was delivered.
+.sp
+.in +1i
+.ta +1i
+.ti -1i
+\fIdestroy\fR
+This action always succeeds.
+
+.ti -1i
+\fIfile\fR, \fImbox\fR, or >
+Append the message to the file named by \fBstring\fR.  The message is
+appended to the file in mbox (uucp) format.  This is the format used by most
+other mail clients (such as mailx, elm).  If the message can be appended to
+the file, then this action succeeds.
+
+.ti -1i
+\fImmdf\fR     Identical to \fIfile\fR, but always appends the message using
+the MMDF mailbox format.
+
+.ti -1i
+\fIpipe\fR or |
+Pipe the message as the standard input to the command named by
+\fBstring\fR, using the Bourne shell \fIsh\fR(1) to interpret the string.
+Prior to giving the string to the shell, it is expanded with the following
+built-in variables:
+.sp
+.in +1i
+.ta +1i
+.ti -1i
+$(sender)      the out-of-band sender information
+.ti -1i
+$(address)     the address that was used to cause delivery to the recipient
+.ti -1i
+$(size)        the size of the message in bytes
+.ti -1i
+$(reply\-to)   either the \*(lqReply\-To:\*(rq or \*(lqFrom:\*(rq field
+of the message
+.ti -1i
+$(info)        the out-of-band information specified
+.in -1i
+
+.ti -1i
+\fIqpipe\fR or <caret> Similar to \fIpipe\fR, but executes the command
+directly, after built-in variable expansion, without assistance from
+the shell.  This action can be used to avoid quoting special characters
+which your shell might interpret.
+
+.ti -1i
+\fIfolder\fR or \fI\+\fR       Store the message in the nmh folder named
+by \fBstring\fR.  Currently his is handled by piping the message to the nmh
+program `rcvstore', although this may change in the future.
+
+.in -1i
+.ti -.5i
+\fBresult\fR:
+.br
+Indicates how the action should be performed:
+
+.in +1i
+.ta +1i
+.ti -1i
+\fIA\fR        Perform the action.  If the action succeeds, then the message
+is considered delivered.
+
+.ti -1i
+\fIR\fR        Perform the action.
+Regardless of the outcome of the action, the message is not considered
+delivered.
+
+.ti -1i
+\fI?\fR        Perform the action only if the message has not been delivered.
+If the action succeeds, then the message is considered delivered.
+
+.ti -1i
+\fIN\fR        Perform the action only if the message has not been delivered
+and the previous action succeeded.  If this action succeeds, then the
+message is considered delivered.
+.sp
+.in -1i
+.in -.5i
+
+The delivery file is always read completely, so that several matches
+can be made and several actions can be taken.
+.fi
+
+.Uh "Security of Delivery Files"
+In order to prevent security problems, the \fI\&.maildelivery\fR
+file must be owned either by the user or by root, and must be
+writable only by the owner.  If this is not the case, the file is
+not read.
+
+If the \fI\&.maildelivery\fR file cannot be found, or does not
+perform an action which delivers the message, then \fIslocal\fP
+will check for a global delivery file at %etcdir%/maildelivery.
+This file is read according to the same rules.  This file must be
+owned by the root and must be writable only by the root.
+
+If a global delivery file cannot be found or does not perform an
+action which delivers the message, then standard delivery to the
+user's maildrop is performed.
+.fi
+
+.Uh "Example Delivery File"
+To summarize, here's an example delivery file:
+.sp
+.if t .in +.5i
+.nf
+.ta \w'default  'u +\w'mh-workersxx 'uC +\w'destroy 'uC +\w'result 'u
+#
+# .maildelivery file for nmh's slocal
+#
+# Blank lines and lines beginning with a '#' are ignored
+#
+# FIELD   PATTERN   ACTION  RESULT  STRING
+#
+
+# File mail with foobar in the \*(lqTo:\*(rq line into file foobar.log
+To        foobar    file    A       foobar.log
+
+# Pipe messages from coleman to the program message-archive
+From      coleman   pipe    A       /bin/message-archive
+
+# Anything to the \*(lqnmh-workers\*(rq mailing list is put in
+# its own folder, if not filed already
+To        nmh-workers  folder ?     nmh-workers
+
+# Anything with Unix in the subject is put into
+# the file unix-mail
+Subject   unix      file    A       unix-mail
+
+# I don't want to read mail from Steve, so destroy it
+From      steve     destroy A       \-
+
+# Put anything not matched yet into mailbox
+default   \-        file    ?       mailbox
+
+# always run rcvtty
+*         \-        pipe    R       /nmh/lib/rcvtty
+.re
+.fi
+
+.Uh "Sub-process environment"
+When a process is invoked, its environment is: the user/group-ids are
+set to recipient's ids; the working directory is the recipient's home
+directory; the umask is 0077; the process has no /dev/tty; the standard
+input is set to the message; the standard output and diagnostic output are
+set to /dev/null; all other file-descriptors are closed; the environment
+variables \fB$USER\fR, \fB$HOME\fR, \fB$SHELL\fR are set appropriately,
+and no other environment variables exist.
+
+The process is given a certain amount of time to execute.  If the process
+does not exit within this limit, the process will be terminated with
+extreme prejudice.  The amount of time is calculated as ((size / 60) +
+300) seconds, where size is the number of bytes in the message (with
+30 minutes the maximum time allowed).
+
+The exit status of the process is consulted in determining the success
+of the action.  An exit status of zero means that the action succeeded.
+Any other exit status (or abnormal termination) means that the action
+failed.
+
+In order to avoid any time limitations, you might implement a process
+that began by \fIforking\fR.  The parent would return the appropriate
+value immediately, and the child could continue on, doing whatever it
+wanted for as long as it wanted.  This approach is somewhat risky if
+the parent is going to return an exit status of zero.  If the parent is
+going to return a non-zero exit status, then this approach can lead to
+quicker delivery into your maildrop.
+.Fi
+^%etcdir%/mts.conf~^nmh mts configuration file
+^$HOME/\&.maildelivery~^The file controlling local delivery
+^%etcdir%/maildelivery~^Rather than the standard file
+^%mailspool%/$USER~^The default maildrop
+.Sa
+rcvdist(1), rcvpack(1), rcvstore(1), rcvtty(1), mh\-format(5)
+.De
+`\-noverbose'
+.Ds
+`\-nosuppressdup'
+.Ds
+`\-maildelivery \&.maildelivery'
+.Ds
+`\-mailbox %mailspool%/$USER'
+.Ds
+`\-file' defaults to stdin
+.Ds
+`\-user' defaults to the current user
+.Co
+None
+.Hi
+\fISlocal\fP was originally designed to be backward-compatible with
+the \fImaildelivery\fP facility provided by \fIMMDF-II\fP.  Thus, the
+\fI\&.maildelivery\fP file syntax is somewhat limited.  But \fIslocal\fP
+has been modified and extended, so that is it no longer compatible with
+\fIMMDF-II\fP.
+
+In addition to an exit status of zero, the \fIMMDF\fR values \fIRP_MOK\fR
+(32) and \fIRP_OK\fR (9) mean that the message has been fully delivered.
+Any other non-zero exit status, including abnormal termination, is
+interpreted as the \fIMMDF\fR value \fIRP_MECH\fR (200), which means
+\*(lquse an alternate route\*(rq (deliver the message to the maildrop).
+.Bu
+Only two return codes are meaningful, others should be.
+
+\fISlocal\fP was originally designed to be backwards-compatible with the
+\fImaildelivery\fP functionality provided by \fBMMDF-II\fP.
diff --git a/man/sortm.man b/man/sortm.man
new file mode 100644 (file)
index 0000000..eeaac1a
--- /dev/null
@@ -0,0 +1,101 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH SORTM %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+sortm \- sort messages
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+sortm
+\%[+folder] \%[msgs]
+\%[\-datefield\ field]
+\%[\-textfield\ field]
+.br
+\%[\-notextfield]
+\%[\-limit days] \%[\-nolimit]
+\%[\-verbose]
+.br
+\%[\-noverbose]
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fISortm\fR sorts the specified messages in the named folder according
+to the chronological order of the \*(lqDate:\*(rq field of each message.
+
+The `\-verbose' switch directs \fIsortm\fR to tell the user the general
+actions that it is taking to place the folder in sorted order.
+
+The `\-datefield\ field' switch tells \fIsortm\fR the name of the field to
+use when making the date comparison.  If the user has a special field in
+each message, such as \*(lqBB\-Posted:\*(rq or \*(lqDelivery\-Date:\*(rq,
+then the `\-datefield' switch can be used to direct \fIsortm\fR which
+field to examine.
+
+The `\-textfield\ field' switch causes \fIsortm\fR to sort messages
+by the specified text field.  If this field is \*(lqsubject\*(rq, any
+leading "re:" is stripped off.  In any case, all characters except
+letters and numbers are stripped and the resulting strings are sorted
+datefield\-major, textfield\-minor, using a case insensitive comparison.
+
+With `\-textfield\ field', if `\-limit\ days' is specified, messages
+with similar textfields that are dated within `days' of each other
+appear together.  Specifying `\-nolimit' makes the limit infinity.
+With `\-limit 0', the sort is instead made textfield\-major, date\-minor.
+
+.\"Ex
+For example, to order a folder by date-major, subject-minor, use:
+
+.ti +.5i
+sortm -textfield subject +folder
+
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Current\-Folder:~^To find the default current folder
+.Sa
+folder (1)
+.De
+`+folder' defaults to the current folder
+.Ds
+`msgs' defaults to all
+.Ds
+`\-datefield date'
+.Ds
+`\-notextfield'
+.Ds
+`\-noverbose'
+.Ds
+`\-nolimit'
+.Co
+If a folder is given, it will become the current folder.  If the current
+message is moved, \fIsortm\fR will preserve its status as current.
+.Hi
+Timezones used to be ignored when comparing dates: they aren't any more.
+
+Messages which were in the folder, but not specified by `msgs', used to
+be moved to the end of the folder; now such messages are left untouched.
+
+\fISortm\fP sometimes did not preserve the message numbering in a folder
+(e.g., messages 1, 3, and 5, might have been renumbered to 1, 2, 3 after
+sorting).  This was a bug, and has been fixed.  To compress the message
+numbering in a folder, use \*(lq\fIfolder\ \-pack\fR\|\*(rq as always.
+.Bu
+If \fIsortm\fR encounters a message without a date\-field, or if the
+message has a date\-field that \fIsortm\fR cannot parse, then \fIsortm\fR
+attempts to keep the message in the same relative position.  This does
+not always work.  For instance, if the first message encountered lacks
+a date which can be parsed, then it will usually be placed at the end
+of the messages being sorted.
+
+When \fIsortm\fR complains about a message which it can't temporally
+order, it complains about the message number \fIprior\fR to sorting.
+It should indicate what the message number will be \fIafter\fR sorting.
+.En
diff --git a/man/tmac.h.in b/man/tmac.h.in
new file mode 100644 (file)
index 0000000..7b49b79
--- /dev/null
@@ -0,0 +1,77 @@
+.\"
+.\" $Id$
+.\"    Try to keep only one copy of the documentation around
+.\"    by re-defining macros and so forth.
+.\"
+.fc ^ ~
+.\"    I pity the fool who tampers with the next line...
+.ds ZZ -man
+.de SC                                 \" Title section
+.TH \\$1 \\$2 MH.6.8 [%nmhversion%]
+..
+.de NA                                 \" Name section
+.SH NAME
+..
+.de SY                                 \" Synopsis section
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+..
+.de DE                                 \" Description section
+.in -.5i
+.SH DESCRIPTION
+..
+.de Fi                                 \" Files section
+.SH FILES
+.nf
+.ta \w'/usr/local/nmh/lib/ExtraBigFileName  'u
+..
+.de Pr                                 \" Profile section
+.SH "PROFILE\ COMPONENTS"
+.nf
+.ta 2.4i
+.ta \w'ExtraBigProfileName  'u
+..
+.de Ps                                 \" Profile next
+.br
+..
+.de Sa                                 \" See Also section
+.fi
+.SH "SEE\ ALSO"
+..
+.de De                                 \" Defaults section
+.SH "DEFAULTS"
+.nf
+..
+.de Ds                                 \" Defaults next
+.br
+..
+.de Co                                 \" Context section
+.fi
+.SH CONTEXT
+..
+.de Hh                                 \" Hints section
+.fi
+.SH "HELPFUL HINTS"
+..
+.de Hi                                 \" History section
+.fi
+.SH HISTORY
+..
+.de Bu                                 \" Bugs section
+.fi
+.SH BUGS
+..
+.de En
+..
+.de ip
+.IP "\\$1" \\$2
+..
+.de Uh
+.ne 4
+.SS "\\$1"
+..
+.\" a useful -me macro
+.de re
+.ta 0.5i +0.5i +0.5i +0.5i +0.5i +0.5i +0.5i +0.5i +0.5i +0.5i +0.5i +0.5i +0.5i +0.5i +0.5i
+..
diff --git a/man/vmh.man b/man/vmh.man
new file mode 100644 (file)
index 0000000..050cdf7
--- /dev/null
@@ -0,0 +1,105 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH VMH %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+vmh \- visual front-end to nmh
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+vmh
+\%[\-prompt\ string]
+\%[\-vmhproc\ program] \%[\-novmhproc]
+.br
+\%[switches\ for\ \fIvmhproc\fR]
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fIvmh\fR is a program which implements the server side of the \fInmh\fR
+window management protocol and uses \fIcurses\fR\0(3) routines to maintain
+a split\-screen interface to any program which implements the client
+side of the protocol.  This latter program, called the \fIvmhproc\fR,
+is specified using the `\-vmhproc\ program' switch.
+
+The upshot of all this is that one can run \fImsh\fR on a display terminal
+and get a nice visual interface.  To do this, for example, just add
+the line
+
+.ti +.5i
+mshproc: vmh
+
+to your \&.mh\(ruprofile.  (This takes advantage of the fact that
+\fImsh\fR is the default \fIvmhproc\fR for \fIvmh\fR.)
+
+In order to facilitate things, if the `\-novmhproc' switch is given,
+and \fIvmh\fR can't run on the user's terminal, the \fIvmhproc\fR is
+run directly without the window management protocol.
+
+After initializing the protocol, \fIvmh\fR prompts the user for a command
+to be given to the client.  Usually, this results in output being sent to
+one or more windows.  If a output to a window would cause it to scroll,
+\fIvmh\fR prompts the user for instructions, roughly permitting the
+capabilities of \fIless\fR or \fImore\fR (e.g., the ability to scroll
+backwards and forwards):
+
+.nf
+.in +.5i
+.ta \w'RETURN  'u +\w'*  'u
+SPACE          advance to the next windowful
+RETURN *       advance to the next line
+y      *       retreat to the previous line
+d      *       advance to the next ten lines
+u      *       retreat to the previous ten lines
+g      *       go to an arbitrary line
+               (preceed g with the line number)
+G      *       go to the end of the window
+               (if a line number is given, this acts like `g')
+CTRL\-L                refresh the entire screen
+h              print a help message
+q              abort the window
+.re
+.in -.5i
+.fi
+
+(A `*' indicates that a numeric prefix is meaningful for this command.)
+
+Note that if a command resulted in more than one window's worth of
+information being displayed, and you allow the command which is generating
+information for the window to gracefully finish (i.e., you don't use
+the `q' command to abort information being sent to the window), then
+\fIvmh\fR will give you one last change to peruse the window.  This is
+useful for scrolling back and forth.  Just type `q' when you're done.
+
+To abnormally terminate \fIvmh\fR (without core dump), use <QUIT>
+(usually CTRL\-\\).  For instance, this does the \*(lqright\*(rq thing
+with \fIbbc\fR and \fImsh\fR.
+
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+.Pr
+^Path:~^To determine the user's nmh directory
+.Sa
+msh(1)
+.De
+`\-prompt\ (vmh)\ '
+.Ds
+`\-vmhproc\ msh'
+.Co
+None
+.Bu
+The argument to the `\-prompt' switch must be interpreted as a single
+token by the shell that invokes \fIvmh\fR.  Therefore, one must usually
+place the argument to this switch inside double\-quotes.
+
+At present, there is no way to pass signals (e.g., interrupt, quit) to
+the client.  However, generating QUIT when \fIvmh\fR is reading a command
+from the terminal is sufficient to tell the client to go away quickly.
+
+Acts strangely (loses peer or botches window management protocol with
+peer) on random occasions.
+.En
diff --git a/man/whatnow.man b/man/whatnow.man
new file mode 100644 (file)
index 0000000..1a015bb
--- /dev/null
@@ -0,0 +1,141 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH WHATNOW %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+whatnow \- prompting front-end for sending messages
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+whatnow
+\%[\-draftfolder\ +folder] \%[\-draftmessage\ msg]
+.br
+\%[\-nodraftfolder]
+\%[\-editor\ editor] \%[\-noedit]
+.br
+\%[\-prompt\ string]
+\%[file]
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fIWhatnow\fR is the default program that queries the user about
+the disposition of a composed draft.  It is normally automatically
+invoked by one of the \fInmh\fR commands \fIcomp\fR, \fIdist\fR,
+\fIforw\fR, or \fIrepl\fR after the initial edit.
+
+When started, the editor is started on the draft (unless `\-noedit'
+is given, in which case the initial edit is suppressed).  Then,
+\fIwhatnow\fR repetitively prompts the user with \*(lqWhat now?\*(rq
+and awaits a response.  The valid responses are:
+
+.nf
+.in .5i
+.ta \w'\fBrefile +folder\fR  'u
+^\fBedit\fR~^re\-edit using the same editor that was used on the
+^~^preceding round unless a profile entry
+^~^\*(lq<lasteditor>\-next: <editor>\*(rq names an alternate editor
+^\fBedit <editor>\fR~^invoke <editor> for further editing
+^\fBrefile +folder\fR~^refile the draft into the given folder
+^\fBmime\fR~^process the draft as MIME composition file using
+^~^the "buildmimeproc" command (mhbuild by default)
+^\fBdisplay\fR~^list the message being distributed/replied\-to
+^~^on the terminal
+^\fBlist\fR~^list the draft on the terminal
+^\fBsend\fR~^send the message
+^\fBsend \-watch\fR~^send the message and monitor the delivery process
+^\fBpush\fR~^send the message in the background
+^\fBwhom\fR~^list the addresses that the message will go to
+^\fBwhom \-check\fR~^list the addresses and verify that they are
+^~^acceptable to the transport service
+^\fBquit\fR~^preserve the draft and exit
+^\fBquit \-delete\fR~^delete the draft and exit
+^\fBdelete\fR~^delete the draft and exit
+.fi
+.re
+
+When entering your response, you need only type enough characters
+to uniquely identify the response.
+
+For the \fBedit\fR response, any valid switch to the editor is valid.
+
+For the \fBsend\fR and \fBpush\fR responses, any valid switch to
+\fIsend\fR\0(1) are valid (as \fBpush\fR merely invokes \fIsend\fR
+with the `\-push' option).
+
+For the \fBwhom\fR response, any valid switch to \fIwhom\fR\0(1)
+is valid.
+
+For the \fBrefile\fR response, any valid switch to the \fIfileproc\fR
+is valid.
+
+For the \fBdisplay\fR and \fBlist\fR responses, any valid argument to
+the \fIlproc\fR is valid.  If any non\-switch arguments are present, then
+the pathname of the draft will be excluded from the argument list given
+to the \fIlproc\fR (this is useful for listing another \fInmh\fR message).
+
+See \fImh\-profile\fR\0(5) for further information about how editors
+are used by nmh.  It also discusses how environment variables can be
+used to direct \fIwhatnow\fR's actions in complex ways.
+
+The `\-prompt\ string' switch sets the prompting string for \fIwhatnow\fR.
+
+The `\-draftfolder\ +folder' and `\-draftmessage\ msg' switches invoke
+the \fInmh\fR draft folder facility.  This is an advanced (and highly
+useful) feature.  Consult the \fImh-draft\fR(5) man page for more
+information.
+
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+^<mh\-dir>/draft~^The draft file
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Draft\-Folder:~^To find the default draft\-folder
+.Ps
+^Editor:~^To override the default editor
+.Ps
+^<lasteditor>\-next:~^To name an editor to be used after exit
+^~^from <lasteditor>
+.Ps
+^automimeproc:~^If value is 1, and the draft is a MIME
+^~^composition file, then automatically call
+^~^buildmimeproc prior to sending.
+.Ps
+^buildmimeproc:~^Program to translate MIME composition files
+.Ps
+^fileproc:~^Program to refile the message
+.Ps
+^lproc:~^Program to list the contents of a message
+.Ps
+^sendproc:~^Program to use to send the message
+.Ps
+^whomproc:~^Program to determine who a message would go to
+.Sa
+send(1), whom(1)
+.De
+`\-prompt\ \*(lqWhat\ Now?\ \*(rq'
+.Co
+None
+.Bu
+The argument to the `\-prompt' switch must be interpreted as a single
+token by the shell that invokes \fIwhatnow\fR.  Therefore, one must
+usually place the argument to this switch inside double\-quotes.
+
+If the initial edit fails, \fIwhatnow\fR deletes your draft (by renaming
+it with a leading comma); failure of a later edit preserves the draft.
+
+If \fIwhatnowproc\fR is \fIwhatnow\fR, then \fIcomp\fR, \fIdist\fP,
+\fIforw\fP, and \fIrepl\fP use a built\-in \fIwhatnow\fR, and do not
+actually run the \fIwhatnow\fR program.  Hence, if you define your own
+\fIwhatnowproc\fR, don't call it \fIwhatnow\fR since it won't be run.
+
+If \fIsendproc\fR is \fIsend\fR, then \fIwhatnow\fR uses a built\-in
+\fIsend\fR, it does not actually run the \fIsend\fR program.  Hence, if
+you define your own \fIsendproc\fR, don't call it \fIsend\fR since
+\fIwhatnow\fR won't run it.
+.En
diff --git a/man/whom.man b/man/whom.man
new file mode 100644 (file)
index 0000000..9caf26c
--- /dev/null
@@ -0,0 +1,69 @@
+.\"
+.\" %nmhwarning%
+.\" $Id$
+.\"
+.\" include the -mh macro file
+.so %etcdir%/tmac.h
+.\"
+.TH WHOM %manext1% MH.6.8 [%nmhversion%]
+.SH NAME
+whom \- report to whom a message would go
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+whom
+\%[\-alias\ aliasfile]
+\%[\-check] \%[\-nocheck]
+\%[\-draft]
+.br
+\%[\-draftfolder\ +folder] \%[\-draftmessage\ msg]
+.br
+\%[\-nodraftfolder]
+\%[file]
+\%[\-version]
+\%[\-help]
+.in -.5i
+.SH DESCRIPTION
+\fIWhom\fR is used to expand the headers of a message into a set of
+addresses and optionally verify that those addresses are deliverable at
+that time (if `\-check' is given).
+
+The `\-draftfolder\ +folder' and `\-draftmessage\ msg' switches invoke
+the \fInmh\fR draft folder facility.  This is an advanced (and highly
+useful) feature.  Consult the \fImh-draft\fR(5) man page for more
+information.
+
+The files specified by the profile entry \*(lqAliasfile:\*(rq and any
+additional alias files given by the `\-alias aliasfile' switch will be
+read (more than one file, each preceded by `\-alias', can be named).
+See \fImh\-alias\fR\0(5) for more information.
+
+.Fi
+^$HOME/\&.mh\(ruprofile~^The user profile
+.Pr
+^Path:~^To determine the user's nmh directory
+.Ps
+^Draft\-Folder:~^To find the default draft\-folder
+.Ps
+^Aliasfile:~^For a default alias file
+.Ps
+^postproc:~^Program to post the message
+.Sa
+mh\-alias(5), post(8)
+.De
+`file' defaults to <mh\-dir>/draft
+.Ds
+`\-nocheck'
+.Ds
+`\-alias %etcdir%/MailAliases'
+.Co
+None
+.Bu
+With the `\-check' option, \fIwhom\fR makes no guarantees that the
+addresses listed as being ok are really deliverable, rather, an address
+being listed as ok means that at the time that \fIwhom\fR was run
+the address was thought to be deliverable by the transport service.
+For local addresses, this is absolute; for network addresses, it means
+that the host is known; for uucp addresses, it (often) means that the
+\fIUUCP\fR network is available for use.
+.En
diff --git a/mkinstalldirs b/mkinstalldirs
new file mode 100755 (executable)
index 0000000..0801ec2
--- /dev/null
@@ -0,0 +1,32 @@
+#! /bin/sh
+# mkinstalldirs --- make directory hierarchy
+# Author: Noah Friedman <friedman@prep.ai.mit.edu>
+# Created: 1993-05-16
+# Last modified: 1994-03-25
+# Public domain
+
+errstatus=0
+
+for file in ${1+"$@"} ; do 
+   set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
+   shift
+
+   pathcomp=
+   for d in ${1+"$@"} ; do
+     pathcomp="$pathcomp$d"
+     case "$pathcomp" in
+       -* ) pathcomp=./$pathcomp ;;
+     esac
+
+     if test ! -d "$pathcomp"; then
+        echo "mkdir $pathcomp" 1>&2
+        mkdir "$pathcomp" || errstatus=$?
+     fi
+
+     pathcomp="$pathcomp/"
+   done
+done
+
+exit $errstatus
+
+# mkinstalldirs ends here
diff --git a/mts/Makefile.in b/mts/Makefile.in
new file mode 100644 (file)
index 0000000..328a9ea
--- /dev/null
@@ -0,0 +1,83 @@
+#
+# Makefile for mts subdirectory
+#
+# $Id$
+#
+
+SHELL = /bin/sh
+
+srcdir = @srcdir@
+VPATH  = @srcdir@
+
+# flags passed to recursive makes in subdirectories
+MAKEDEFS = CC='$(CC)' CPPFLAGS='$(CPPFLAGS)' DEFS='$(DEFS)' \
+CFLAGS='$(CFLAGS)' LDFLAGS='$(LDFLAGS)' LIBS='$(LIBS)' \
+prefix='$(prefix)' exec_prefix='$(exec_prefix)' bindir='$(bindir)' \
+etcdir='$(etcdir)' libdir='$(libdir)' mandir='$(mandir)' \
+mailspool='$(mailspool)' sendmailpath='$(sendmailpath)' \
+default_editor='$(default_editor)' default_pager='$(default_pager)'
+
+# auxiliary files
+AUX = Makefile.in
+
+# all files in this directory included in the distribution
+DIST = $(AUX)
+
+# subdirectories
+SUBDIRS = smtp sendmail mmdf
+
+# mail transport agent we are using
+MTS = @MTS@
+
+# ========= DEPENDENCIES FOR BUILDING AND INSTALLING ==========
+
+all install uninstall:
+       for subdir in $(MTS); do \
+         (cd $$subdir && $(MAKE) $(MAKEDEFS) $@) || exit 1; \
+       done
+
+# ========== DEPENDENCIES FOR CLEANUP ==========
+
+mostlyclean: mostlyclean-recursive mostlyclean-local
+clean:       clean-recursive       clean-local
+distclean:   distclean-recursive   distclean-local
+realclean:   realclean-recursive   realclean-local
+superclean:  superclean-recursive  superclean-local
+
+mostlyclean-local:
+       rm -f *~
+
+clean-local: mostlyclean-local
+
+distclean-local: clean-local
+       rm -f Makefile
+
+realclean-local: distclean-local
+
+superclean-local: realclean-local
+
+mostlyclean-recursive clean-recursive distclean-recursive realclean-recursive superclean-recursive:
+       for subdir in $(SUBDIRS); do \
+         target=`echo $@ | sed 's/-recursive//'`; \
+         (cd $$subdir && $(MAKE) $(MAKEDEFS) $$target) || exit 1; \
+       done
+
+# ========== DEPENDENCIES FOR MAINTENANCE ==========
+
+subdir = mts
+
+Makefile: Makefile.in ../config.status
+       cd .. && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+distdir = ../`cat ../distname`/$(subdir)
+nmhdist: $(DIST)
+       @echo "Copying distribution files in $(subdir)"
+       @for file in $(DIST); do \
+         cp -p $(srcdir)/$$file $(distdir); \
+       done
+       @for subdir in $(SUBDIRS); do \
+         mkdir $(distdir)/$$subdir; \
+         chmod 755 $(distdir)/$$subdir; \
+         (cd $$subdir && $(MAKE) $@) || exit 1; \
+       done
+
diff --git a/mts/mmdf/Makefile.in b/mts/mmdf/Makefile.in
new file mode 100644 (file)
index 0000000..d076a13
--- /dev/null
@@ -0,0 +1,64 @@
+#
+# Makefile for mts/mmdf subdirectory
+#
+# $Id$
+#
+
+SHELL = /bin/sh
+
+srcdir = @srcdir@
+VPATH  = @srcdir@
+
+prefix      = @prefix@
+exec_prefix = @exec_prefix@
+bindir      = @bindir@
+libdir      = @libdir@
+etcdir      = @sysconfdir@
+
+.SUFFIXES:
+
+# source files
+SRC = hosts.c
+
+# auxiliary files
+AUX = Makefile.in
+
+# all files in this directory included in the distribution
+DIST = $(SRC) $(AUX)
+
+# ========= DEPENDENCIES FOR BUILDING ==========
+
+all:
+
+install:
+
+uninstall:
+
+# ========== DEPENDENCIES FOR CLEANUP ==========
+
+mostlyclean:
+       rm -f *~
+
+clean: mostlyclean
+
+distclean: clean
+       rm -f Makefile
+
+realclean: distclean
+
+superclean: realclean
+
+# ========== DEPENDENCIES FOR MAINTENANCE ==========
+
+subdir = mts/mmdf
+
+Makefile: Makefile.in ../../config.status
+       cd ../.. && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+distdir = ../../`cat ../../distname`/$(subdir)
+nmhdist: $(DIST)
+       @echo "Copying distribution files in $(subdir)"
+       @for file in $(DIST); do \
+         cp -p $(srcdir)/$$file $(distdir); \
+       done
+
diff --git a/mts/mmdf/hosts.c b/mts/mmdf/hosts.c
new file mode 100644 (file)
index 0000000..748d8d6
--- /dev/null
@@ -0,0 +1,72 @@
+
+/*
+ * hosts.c -- use MMDF to get hostname information
+ *
+ * $Id$
+ */
+
+#include "../../h/strings.h"
+#include "util.h"
+#include "mmdf.h"
+#include "ch.h"
+
+#ifdef MMDFII
+# include "dm.h"
+#endif /* MMDFII */
+
+#include "../../zotnet/mts.h"
+
+/* 
+ * We really should be careful with the number of fd's that this routine
+ * opens:  the ch_seq ch_table.c module likes to keep 6 (yes, SIX) fds around
+ * to speed-up host lookups in the channel table.  Unfortunately, after all
+ * of them get opened, six are always open (ch_table may close one to open
+ * another).  The bottom line is that if repl calls post, then we get 12
+ * (yes, TWELVE) fds open, with only six usable.
+ *
+ * send will close all fds >= 3 prior to invoking post.  It would be nice
+ * if one could control ch_seq's use of fds for table lookups, but such is
+ * life.
+ *
+ */
+
+#ifndef        MMDFII
+char *
+OfficialName (char *name)
+{
+    register Chan *ch;
+    static char buffer[BUFSIZ];
+
+    return ((ch = ch_h2chan (name, buffer)) == (Chan *) (-1) ? NULL
+           : ch == (Chan *) NULL ? LocalName ()
+           : buffer);
+}
+#else  /* MMDFII */
+
+extern char *invo_name;
+
+extern short ch_yloc;            /* ok to intercept local names */
+
+static int inited = 0;
+
+char *
+OfficialName (char *name)
+{
+    Dmn_route route;
+    static char buffer[BUFSIZ];
+
+    if (!inited) {
+       mmdf_init (invo_name);
+       inited = 1;
+    }
+    switch (dm_v2route (name, buffer, &route)) {
+       case NOTOK: 
+       case OK: 
+           return ((ch_yloc && lexequ (name, LocalName ())) ? LocalName ()
+                   : NULL);
+
+       default: 
+           return buffer;
+    }
+}
+#endif /* MMDFII */
diff --git a/mts/sendmail/Makefile.in b/mts/sendmail/Makefile.in
new file mode 100644 (file)
index 0000000..d5580a5
--- /dev/null
@@ -0,0 +1,89 @@
+#
+# Makefile for mts/sendmail subdirectory
+#
+# $Id$
+#
+
+SHELL = /bin/sh
+
+top_srcdir = @top_srcdir@
+srcdir     = @srcdir@
+VPATH      = @srcdir@
+
+prefix      = @prefix@
+exec_prefix = @exec_prefix@
+bindir      = @bindir@
+libdir      = @libdir@
+etcdir      = @sysconfdir@
+
+CC       = @CC@
+CFLAGS   = @CFLAGS@
+DEFS     = @DEFS@
+INCLUDES = -I../.. -I$(srcdir) -I$(top_srcdir)
+
+LORDER  = @LORDER@
+TSORT   = @TSORT@
+RANLIB  = @RANLIB@
+
+COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CFLAGS)
+
+.SUFFIXES:
+.SUFFIXES: .c .o
+
+.c.o:
+       $(COMPILE) $<
+
+# source
+SRCS = hosts.c sendmail.c
+
+# object files in libsend.a
+OBJS = hosts.o sendmail.o
+
+# auxiliary files
+AUX = Makefile.in
+
+# all files in this directory included in the distribution
+DIST = $(SRCS) $(AUX)
+
+# ========= DEPENDENCIES FOR BUILDING AND INSTALLING ==========
+
+all: libsend.a
+
+libsend.a: $(OBJS)
+       rm -f $@
+       ar cr $@ `$(LORDER) $(OBJS) | $(TSORT)`
+       $(RANLIB) $@
+
+install:
+
+uninstall:
+
+# ========== DEPENDENCIES FOR CLEANUP ==========
+
+mostlyclean:
+       rm -f *.o *~
+
+clean: mostlyclean
+       rm -f libsend.a
+
+distclean: clean
+       rm -f Makefile
+
+realclean: distclean
+
+superclean: realclean
+
+# ========== DEPENDENCIES FOR MAINTENANCE ==========
+
+subdir = mts/sendmail
+
+Makefile: Makefile.in ../../config.status
+       cd ../.. && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+distdir = ../../`cat ../../distname`/$(subdir)
+nmhdist: $(DIST)
+       @echo "Copying distribution files in $(subdir)"
+       @for file in $(DIST); do \
+         cp -p $(srcdir)/$$file $(distdir); \
+       done
+
diff --git a/mts/sendmail/hosts.c b/mts/sendmail/hosts.c
new file mode 100644 (file)
index 0000000..55f3393
--- /dev/null
@@ -0,0 +1,135 @@
+
+/*
+ * hosts.c -- find out the official name of a host
+ *
+ * $Id$
+ */
+
+/*
+ * In the SendMail world, we really don't know what the valid
+ * hosts are.  We could poke around in the sendmail.cf file, but
+ * that still isn't a guarantee.  As a result, we'll say that
+ * everything is a valid host, and let SendMail worry about it.
+ */
+
+#include <h/mh.h>
+#include <zotnet/mts/mts.h>
+#include <netdb.h>
+
+static struct host {
+    char *h_name;
+    char **h_aliases;
+    struct host *h_next;
+} hosts;
+
+
+/*
+ * static prototypes
+ */
+static int init_hs(void);
+
+
+char *
+OfficialName (char *name)
+{
+    char *p, *q, site[BUFSIZ];
+    struct hostent *hp;
+
+    static char buffer[BUFSIZ];
+    char **r;
+    struct host *h;
+
+    for (p = name, q = site; *p && (q - site < sizeof(site) - 1); p++, q++)
+       *q = isupper (*p) ? tolower (*p) : *p;
+    *q = '\0';
+    q = site;
+
+    if (!strcasecmp (LocalName(), site))
+       return LocalName();
+
+#ifndef        BIND
+    sethostent (1);
+#endif
+
+    if ((hp = gethostbyname (q))) {
+       strncpy (buffer, hp->h_name, sizeof(buffer));
+       return buffer;
+    }
+    if (hosts.h_name || init_hs ())
+       for (h = hosts.h_next; h; h = h->h_next)
+           if (!strcasecmp (h->h_name, q))
+               return h->h_name;
+           else
+               for (r = h->h_aliases; *r; r++)
+                   if (!strcasecmp (*r, q))
+                       return h->h_name;
+
+    strncpy (buffer, site, sizeof(buffer));
+    return buffer;
+}
+
+/*
+ * Use hostable as an exception file for those hosts that aren't
+ * on the Internet (listed in /etc/hosts).  These are usually
+ * PhoneNet and UUCP sites.
+ */
+
+#define        NALIASES 50
+
+static int
+init_hs (void)
+{
+    char  *cp, *dp, **q, **r;
+    char buffer[BUFSIZ], *aliases[NALIASES];
+    register struct host *h;
+    register FILE  *fp;
+
+    if ((fp = fopen (hostable, "r")) == NULL)
+       return 0;
+
+    h = &hosts;
+    while (fgets (buffer, sizeof(buffer), fp) != NULL) {
+       if ((cp = strchr(buffer, '#')))
+           *cp = 0;
+       if ((cp = strchr(buffer, '\n')))
+           *cp = 0;
+       for (cp = buffer; *cp; cp++)
+           if (isspace (*cp))
+               *cp = ' ';
+       for (cp = buffer; isspace (*cp); cp++)
+           continue;
+       if (*cp == 0)
+           continue;
+
+       q = aliases;
+       if ((cp = strchr(dp = cp, ' '))) {
+           *cp = 0;
+           for (cp++; *cp; cp++) {
+               while (isspace (*cp))
+                   cp++;
+               if (*cp == 0)
+                   break;
+               if ((cp = strchr(*q++ = cp, ' ')))
+                   *cp = 0;
+               else
+                   break;
+               if (q >= aliases + NALIASES)
+                   break;
+           }
+       }
+
+       *q = 0;
+
+       h->h_next = (struct host *) calloc (1, sizeof(*h));
+       h = h->h_next;
+       h->h_name = getcpy (dp);
+       r = h->h_aliases =
+               (char **) calloc ((size_t) (q - aliases + 1), sizeof(*q));
+       for (q = aliases; *q; q++)
+           *r++ = getcpy (*q);
+       *r = 0;
+    }
+
+    fclose (fp);
+    return 1;
+}
diff --git a/mts/sendmail/sendmail.c b/mts/sendmail/sendmail.c
new file mode 100644 (file)
index 0000000..7556503
--- /dev/null
@@ -0,0 +1,827 @@
+
+/*
+ * sendmail.c -- nmh sendmail interface
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <mts/smtp/smtp.h>
+#include <zotnet/mts/mts.h>
+#include <signal.h>
+
+/*
+ * This module implements an interface to SendMail very similar
+ * to the MMDF mm_(3) routines.  The sm_() routines herein talk
+ * SMTP to a sendmail process, mapping SMTP reply codes into
+ * RP_-style codes.
+ */
+
+/*
+ * On older 4.2BSD machines without the POSIX function `sigaction',
+ * the alarm handing stuff for time-outs will NOT work due to the way
+ * syscalls get restarted.  This is not really crucial, since SendMail
+ * is generally well-behaved in this area.
+ */
+
+#ifdef SENDMAILBUG
+/*
+ * It appears that some versions of Sendmail will return Code 451
+ * when they don't really want to indicate a failure.
+ * "Code 451 almost always means sendmail has deferred; we don't
+ * really want bomb out at this point since sendmail will rectify
+ * things later."  So, if you define SENDMAILBUG, Code 451 is
+ * considered the same as Code 250.  Yuck!
+ */
+#endif
+
+#define        TRUE    1
+#define        FALSE   0
+
+#define        NBITS ((sizeof (int)) * 8)
+
+/*
+ * these codes must all be different!
+ */
+#define        SM_OPEN  90      /* Changed from 30 in case of nameserver flakiness */
+#define        SM_HELO  20
+#define        SM_RSET  15
+#define        SM_MAIL  40
+#define        SM_RCPT 120
+#define        SM_DATA  20
+#define        SM_TEXT 150
+#define        SM_DOT  180
+#define        SM_QUIT  30
+#define        SM_CLOS  10
+
+static int sm_addrs = 0;
+static int sm_alarmed = 0;
+static int sm_child = NOTOK;
+static int sm_debug = 0;
+static int sm_nl = TRUE;
+static int sm_verbose = 0;
+
+static FILE *sm_rfp = NULL;
+static FILE *sm_wfp = NULL;
+
+#ifdef MPOP
+static int sm_ispool = 0;
+static char sm_tmpfil[BUFSIZ];
+#endif /* MPOP */
+
+static char *sm_noreply = "No reply text given";
+static char *sm_moreply = "; ";
+
+struct smtp sm_reply;          /* global... */
+
+#ifdef MPOP
+extern int errno;
+#endif
+
+static int doingEHLO;
+
+#define        MAXEHLO 20
+char *EHLOkeys[MAXEHLO + 1];
+
+/*
+ * static prototypes
+ */
+static int sm_ierror (char *fmt, ...);
+static int smtalk (int time, char *fmt, ...);
+static int sm_wrecord (char *, int);
+static int sm_wstream (char *, int);
+static int sm_werror (void);
+static int smhear (void);
+static int sm_rrecord (char *, int *);
+static int sm_rerror (void);
+static RETSIGTYPE alrmser (int);
+
+
+int
+sm_init (char *client, char *server, int watch, int verbose,
+         int debug, int onex, int queued)
+{
+    int i, result, vecp;
+    int pdi[2], pdo[2];
+    char *vec[15];
+
+    if (watch)
+       verbose = TRUE;
+
+    sm_verbose = verbose;
+    sm_debug = debug;
+    if (sm_rfp != NULL && sm_wfp != NULL)
+       return RP_OK;
+
+    if (client == NULL || *client == '\0')
+       if (clientname)
+           client = clientname;
+       else
+           client = LocalName();       /* no clientname -> LocalName */
+
+#ifdef ZMAILER
+    if (client == NULL || *client == '\0')
+       client = "localhost";
+#endif
+
+    if (pipe (pdi) == NOTOK)
+       return sm_ierror ("no pipes");
+    if (pipe (pdo) == NOTOK) {
+       close (pdi[0]);
+       close (pdi[1]);
+       return sm_ierror ("no pipes");
+    }
+
+    for (i = 0; (sm_child = fork ()) == NOTOK && i < 5; i++)
+       sleep (5);
+
+    switch (sm_child) {
+       case NOTOK: 
+           close (pdo[0]);
+           close (pdo[1]);
+           close (pdi[0]);
+           close (pdi[1]);
+           return sm_ierror ("unable to fork");
+
+       case OK: 
+           if (pdo[0] != fileno (stdin))
+               dup2 (pdo[0], fileno (stdin));
+           if (pdi[1] != fileno (stdout))
+               dup2 (pdi[1], fileno (stdout));
+           if (pdi[1] != fileno (stderr))
+               dup2 (pdi[1], fileno (stderr));
+           for (i = fileno (stderr) + 1; i < NBITS; i++)
+               close (i);
+
+           vecp = 0;
+           vec[vecp++] = r1bindex (sendmail, '/');
+           vec[vecp++] = "-bs";
+#ifndef ZMAILER
+           vec[vecp++] = watch ? "-odi" : queued ? "-odq" : "-odb";
+           vec[vecp++] = "-oem";
+           vec[vecp++] = "-om";
+# ifndef RAND
+           if (verbose)
+               vec[vecp++] = "-ov";
+# endif /* not RAND */
+#endif /* not ZMAILER */
+           vec[vecp++] = NULL;
+
+           setgid (getegid ());
+           setuid (geteuid ());
+           execvp (sendmail, vec);
+           fprintf (stderr, "unable to exec ");
+           perror (sendmail);
+           _exit (-1);         /* NOTREACHED */
+
+       default: 
+           SIGNAL (SIGALRM, alrmser);
+           SIGNAL (SIGPIPE, SIG_IGN);
+
+           close (pdi[1]);
+           close (pdo[0]);
+           if ((sm_rfp = fdopen (pdi[0], "r")) == NULL
+                   || (sm_wfp = fdopen (pdo[1], "w")) == NULL) {
+               close (pdi[0]);
+               close (pdo[1]);
+               sm_rfp = sm_wfp = NULL;
+               return sm_ierror ("unable to fdopen");
+           }
+           sm_alarmed = 0;
+           alarm (SM_OPEN);
+           result = smhear ();
+           alarm (0);
+           switch (result) {
+               case 220: 
+                   break;
+
+               default: 
+                   sm_end (NOTOK);
+                   return RP_RPLY;
+           }
+
+           if (client && *client) {
+               doingEHLO = 1;
+               result = smtalk (SM_HELO, "EHLO %s", client);
+               doingEHLO = 0;
+
+               if (500 <= result && result <= 599)
+                   result = smtalk (SM_HELO, "HELO %s", client);
+
+               switch (result) {
+                   case 250:
+                       break;
+
+                   default:
+                       sm_end (NOTOK);
+                       return RP_RPLY;
+               }
+           }
+
+#ifndef ZMAILER
+           if (onex)
+               smtalk (SM_HELO, "ONEX");
+#endif
+           if (watch)
+               smtalk (SM_HELO, "VERB on");
+
+           return RP_OK;
+    }
+}
+
+
+int
+sm_winit (int mode, char *from)
+{
+#ifdef MPOP
+    if (sm_ispool && !sm_wfp) {
+       strlen (strcpy (sm_reply.text, "unable to create new spool file"));
+       sm_reply.code = NOTOK;
+       return RP_BHST;
+    }
+#endif /* MPOP */
+
+    switch (smtalk (SM_MAIL, "%s FROM:<%s>",
+               mode == S_SEND ? "SEND" : mode == S_SOML ? "SOML"
+               : mode == S_SAML ? "SAML" : "MAIL", from)) {
+       case 250: 
+           sm_addrs = 0;
+           return RP_OK;
+
+       case 500: 
+       case 501: 
+       case 552: 
+           return RP_PARM;
+
+       default: 
+           return RP_RPLY;
+    }
+}
+
+
+int
+sm_wadr (char *mbox, char *host, char *path)
+{
+    switch (smtalk (SM_RCPT, host && *host ? "RCPT TO:<%s%s@%s>"
+                                          : "RCPT TO:<%s%s>",
+                            path ? path : "", mbox, host)) {
+       case 250: 
+       case 251: 
+           sm_addrs++;
+           return RP_OK;
+
+       case 451: 
+#ifdef SENDMAILBUG
+           sm_addrs++;
+           return RP_OK;
+#endif /* SENDMAILBUG */
+       case 421: 
+       case 450: 
+       case 452: 
+           return RP_NO;
+
+       case 500: 
+       case 501: 
+           return RP_PARM;
+
+       case 550: 
+       case 551: 
+       case 552: 
+       case 553: 
+           return RP_USER;
+
+       default: 
+           return RP_RPLY;
+    }
+}
+
+
+int
+sm_waend (void)
+{
+    switch (smtalk (SM_DATA, "DATA")) {
+       case 354: 
+           sm_nl = TRUE;
+           return RP_OK;
+
+       case 451: 
+#ifdef SENDMAILBUG
+           sm_nl = TRUE;
+           return RP_OK;
+#endif /* SENDMAILBUG */
+       case 421: 
+           return RP_NO;
+
+       case 500: 
+       case 501: 
+       case 503: 
+       case 554: 
+           return RP_NDEL;
+
+       default: 
+           return RP_RPLY;
+    }
+}
+
+
+int
+sm_wtxt (char *buffer, int len)
+{
+    int result;
+
+    sm_alarmed = 0;
+    alarm (SM_TEXT);
+    result = sm_wstream (buffer, len);
+    alarm (0);
+
+    return (result == NOTOK ? RP_BHST : RP_OK);
+}
+
+
+int
+sm_wtend (void)
+{
+    if (sm_wstream ((char *) NULL, 0) == NOTOK)
+       return RP_BHST;
+
+    switch (smtalk (SM_DOT + 3 * sm_addrs, ".")) {
+       case 250: 
+       case 251: 
+           return RP_OK;
+
+       case 451: 
+#ifdef SENDMAILBUG
+           return RP_OK;
+#endif /* SENDMAILBUG */
+       case 452: 
+       default: 
+           return RP_NO;
+
+       case 552: 
+       case 554: 
+           return RP_NDEL;
+    }
+}
+
+
+int
+sm_end (int type)
+{
+    int status;
+    struct smtp sm_note;
+
+    switch (sm_child) {
+       case NOTOK: 
+       case OK: 
+           return RP_OK;
+
+       default: 
+           break;
+    }
+
+    if (sm_rfp == NULL && sm_wfp == NULL)
+       return RP_OK;
+
+    switch (type) {
+       case OK: 
+           smtalk (SM_QUIT, "QUIT");
+           break;
+
+       case NOTOK: 
+           sm_note.code = sm_reply.code;
+           strncpy (sm_note.text, sm_reply.text, sm_note.length = sm_reply.length);/* fall */
+       case DONE: 
+           if (smtalk (SM_RSET, "RSET") == 250 && type == DONE)
+               return RP_OK;
+           kill (sm_child, SIGKILL);
+           discard (sm_rfp);
+           discard (sm_wfp);
+           if (type == NOTOK) {
+               sm_reply.code = sm_note.code;
+               strncpy (sm_reply.text, sm_note.text, sm_reply.length = sm_note.length);
+           }
+           break;
+    }
+    if (sm_rfp != NULL) {
+       alarm (SM_CLOS);
+       fclose (sm_rfp);
+       alarm (0);
+    }
+    if (sm_wfp != NULL) {
+       alarm (SM_CLOS);
+       fclose (sm_wfp);
+       alarm (0);
+    }
+
+    status = pidwait (sm_child, OK);
+
+    sm_child = NOTOK;
+    sm_rfp = sm_wfp = NULL;
+
+    return (status ? RP_BHST : RP_OK);
+}
+
+
+static int
+sm_ierror (char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    vsnprintf (sm_reply.text, sizeof(sm_reply.text), fmt, ap);
+    va_end(ap);
+
+    sm_reply.length = strlen (sm_reply.text);
+    sm_reply.code = NOTOK;
+
+    return RP_BHST;
+}
+
+
+static int
+smtalk (int time, char *fmt, ...)
+{
+    int result;
+    char buffer[BUFSIZ];
+    va_list ap;
+
+    va_start(ap, fmt);
+    vsnprintf (buffer, sizeof(buffer), fmt, ap);
+    va_end(ap);
+
+    if (sm_debug) {
+       printf ("=> %s\n", buffer);
+       fflush (stdout);
+    }
+
+#ifdef MPOP
+    if (sm_ispool) {
+       char file[BUFSIZ];
+
+       if (strcmp (buffer, ".") == 0)
+           time = SM_DOT;
+       fprintf (sm_wfp, "%s\r\n", buffer);
+       switch (time) {
+           case SM_DOT:
+               fflush (sm_wfp);
+               if (ferror (sm_wfp))
+                   return sm_werror ();
+               snprintf (file, sizeof(file), "%s%c.bulk", sm_tmpfil,
+                               (char) (sm_ispool + 'a' - 1));
+               if (rename (sm_tmpfil, file) == NOTOK) {
+                   int len;
+                   char *bp;
+
+                   snprintf (sm_reply.text, sizeof(sm_reply.text),
+                       "error renaming %s to %s: ", sm_tmpfil, file);
+                   bp = sm_reply.text;
+                   len = strlen (bp);
+                   bp += len;
+                   if ((s = strerror (errno)))
+                       strncpy (bp, s, sizeof(sm_reply.text) - len);
+                   else
+                       snprintf (bp, sizeof(sm_reply.text) - len,
+                               "unknown error %d", errno);
+                   sm_reply.length = strlen (sm_reply.text);
+                   sm_reply.code = NOTOK;
+                   return RP_BHST;
+               }
+               fclose (sm_wfp);
+               if (sm_wfp = fopen (sm_tmpfil, "w"))
+                   chmod (sm_tmpfil, 0600);
+               sm_ispool++;
+               /* and fall... */
+
+           case SM_MAIL:
+           case SM_RCPT:
+               result = 250;
+               break;
+
+           case SM_RSET:
+               fflush (sm_wfp);
+               ftruncate (fileno (sm_wfp), 0L);
+               fseek (sm_wfp, 0L, SEEK_SET);
+               result = 250;
+               break;
+
+           case SM_DATA:
+               result = 354;
+               break;
+
+           case SM_QUIT:
+               unlink (sm_tmpfil);
+               sm_ispool = 0;
+               result = 221;
+               break;
+
+           default:
+               result = 500;
+               break;
+       }
+       if (sm_debug) {
+           printf ("<= %d\n", result);
+           fflush (stdout);
+       }
+
+       sm_reply.text[sm_reply.length = 0] = NULL;
+       return (sm_reply.code = result);
+    }
+#endif /* MPOP */
+
+    sm_alarmed = 0;
+    alarm ((unsigned) time);
+    if ((result = sm_wrecord (buffer, strlen (buffer))) != NOTOK)
+       result = smhear ();
+    alarm (0);
+
+    return result;
+}
+
+
+static int
+sm_wrecord (char *buffer, int len)
+{
+    if (sm_wfp == NULL)
+       return sm_werror ();
+
+    fwrite (buffer, sizeof *buffer, len, sm_wfp);
+    fputs ("\r\n", sm_wfp);
+    fflush (sm_wfp);
+
+    return (ferror (sm_wfp) ? sm_werror () : OK);
+}
+
+
+static int
+sm_wstream (char *buffer, int len)
+{
+    char *bp;
+    static char lc = 0;
+
+    if (sm_wfp == NULL)
+       return sm_werror ();
+
+    if (buffer == NULL && len == 0) {
+       if (lc != '\n')
+           fputs ("\r\n", sm_wfp);
+       lc = 0;
+       return (ferror (sm_wfp) ? sm_werror () : OK);
+    }
+
+    for (bp = buffer; len > 0; bp++, len--) {
+       switch (*bp) {
+           case '\n': 
+               sm_nl = TRUE;
+               fputc ('\r', sm_wfp);
+               break;
+
+           case '.': 
+               if (sm_nl)
+                   fputc ('.', sm_wfp);/* FALL THROUGH */
+           default: 
+               sm_nl = FALSE;
+       }
+       fputc (*bp, sm_wfp);
+       if (ferror (sm_wfp))
+           return sm_werror ();
+    }
+
+    if (bp > buffer)
+       lc = *--bp;
+    return (ferror (sm_wfp) ? sm_werror () : OK);
+}
+
+
+#ifdef _AIX
+/*
+ * AIX by default will inline the strlen and strcpy commands by redefining
+ * them as __strlen and __strcpy respectively.  This causes compile problems
+ * with the #ifdef MPOP in the middle.  Should the #ifdef MPOP be removed,
+ * remove these #undefs.
+ */
+# undef strlen
+# undef strcpy
+#endif /* _AIX */
+
+static int
+sm_werror (void)
+{
+    sm_reply.length =
+       strlen (strcpy (sm_reply.text, sm_wfp == NULL ? "no pipe opened"
+           : sm_alarmed ? "write to pipe timed out"
+           : "error writing to pipe"));
+
+    return (sm_reply.code = NOTOK);
+}
+
+
+static int
+smhear (void)
+{
+    int i, code, cont, bc, rc, more;
+    char *bp, *rp;
+    char **ehlo, buffer[BUFSIZ];
+
+    if (doingEHLO) {
+       static int at_least_once = 0;
+
+       if (at_least_once) {
+           for (ehlo = EHLOkeys; *ehlo; ehlo++)
+               free (*ehlo);
+       } else {
+           at_least_once = 1;
+       }
+
+       *(ehlo = EHLOkeys) = NULL;
+    }
+
+again:
+
+    sm_reply.text[sm_reply.length = 0] = 0;
+
+    rp = sm_reply.text;
+    rc = sizeof(sm_reply.text) - 1;
+
+    for (more = FALSE; sm_rrecord (bp = buffer, &bc) != NOTOK;) {
+       if (sm_debug) {
+           printf ("<= %s\n", buffer);
+           fflush (stdout);
+       }
+
+       if (doingEHLO
+               && strncmp (buffer, "250", sizeof("250") - 1) == 0
+               && (buffer[3] == '-' || doingEHLO == 2)
+               && buffer[4]) {
+           if (doingEHLO == 2) {
+               if ((*ehlo = malloc ((size_t) (strlen (buffer + 4) + 1)))) {
+                   strcpy (*ehlo++, buffer + 4);
+                   *ehlo = NULL;
+                   if (ehlo >= EHLOkeys + MAXEHLO)
+                       doingEHLO = 0;
+               }
+               else
+                   doingEHLO = 0;
+           }
+           else
+               doingEHLO = 2;
+       }
+
+       for (; bc > 0 && (!isascii (*bp) || !isdigit (*bp)); bp++, bc--)
+           continue;
+
+       cont = FALSE;
+       code = atoi (bp);
+       bp += 3, bc -= 3;
+       for (; bc > 0 && isspace (*bp); bp++, bc--)
+           continue;
+       if (bc > 0 && *bp == '-') {
+           cont = TRUE;
+           bp++, bc--;
+           for (; bc > 0 && isspace (*bp); bp++, bc--)
+               continue;
+       }
+
+       if (more) {
+           if (code != sm_reply.code || cont)
+               continue;
+           more = FALSE;
+       } else {
+           sm_reply.code = code;
+           more = cont;
+           if (bc <= 0) {
+               strncpy (buffer, sm_noreply, sizeof(buffer));
+               bp = buffer;
+               bc = strlen (sm_noreply);
+           }
+       }
+       if ((i = min (bc, rc)) > 0) {
+           strncpy (rp, bp, i);
+           rp += i;
+           rc -= i;
+           if (more && rc > strlen (sm_moreply) + 1) {
+               strncpy (sm_reply.text + rc, sm_moreply, sizeof(sm_reply.text) - rc);
+               rc += strlen (sm_moreply);
+           }
+       }
+       if (more)
+           continue;
+       if (sm_reply.code < 100) {
+           if (sm_verbose) {
+               printf ("%s\n", sm_reply.text);
+               fflush (stdout);
+           }
+           goto again;
+       }
+
+       sm_reply.length = rp - sm_reply.text;
+
+       return sm_reply.code;
+    }
+
+    return NOTOK;
+}
+
+
+static int
+sm_rrecord (char *buffer, int *len)
+{
+    if (sm_rfp == NULL)
+       return sm_rerror ();
+
+    buffer[*len = 0] = 0;
+
+    fgets (buffer, BUFSIZ, sm_rfp);
+    *len = strlen (buffer);
+    if (ferror (sm_rfp) || feof (sm_rfp))
+       return sm_rerror ();
+    if (buffer[*len - 1] != '\n')
+       while (getc (sm_rfp) != '\n' && !ferror (sm_rfp) && !feof (sm_rfp))
+           continue;
+    else
+       if (buffer[*len - 2] == '\r')
+           *len -= 1;
+    buffer[*len - 1] = 0;
+
+    return OK;
+}
+
+
+static int
+sm_rerror (void)
+{
+    sm_reply.length =
+       strlen (strcpy (sm_reply.text, sm_rfp == NULL ? "no pipe opened"
+           : sm_alarmed ? "read from pipe timed out"
+           : feof (sm_rfp) ? "premature end-of-file on pipe"
+           : "error reading from pipe"));
+
+    return (sm_reply.code = NOTOK);
+}
+
+
+static RETSIGTYPE
+alrmser (int i)
+{
+#ifndef        RELIABLE_SIGNALS
+    SIGNAL (SIGALRM, alrmser);
+#endif
+
+    sm_alarmed++;
+    if (sm_debug) {
+       printf ("timed out...\n");
+       fflush (stdout);
+    }
+}
+
+
+char *
+rp_string (int code)
+{
+    char  *text;
+    static char buffer[BUFSIZ];
+
+    switch (sm_reply.code != NOTOK ? code : NOTOK) {
+       case RP_AOK:
+           text = "AOK";
+           break;
+
+       case RP_MOK:
+           text = "MOK";
+           break;
+
+       case RP_OK: 
+           text = "OK";
+           break;
+
+       case RP_RPLY: 
+           text = "RPLY";
+           break;
+
+       case RP_BHST: 
+       default: 
+           text = "BHST";
+           snprintf (buffer, sizeof(buffer), "[%s] %s", text, sm_reply.text);
+           return buffer;
+
+       case RP_PARM: 
+           text = "PARM";
+           break;
+
+       case RP_NO: 
+           text = "NO";
+           break;
+
+       case RP_USER: 
+           text = "USER";
+           break;
+
+       case RP_NDEL: 
+           text = "NDEL";
+           break;
+    }
+
+    snprintf (buffer, sizeof(buffer), "[%s] %3d %s",
+               text, sm_reply.code, sm_reply.text);
+    return buffer;
+}
+
diff --git a/mts/smtp/Makefile.in b/mts/smtp/Makefile.in
new file mode 100644 (file)
index 0000000..7d197d5
--- /dev/null
@@ -0,0 +1,92 @@
+#
+# Makefile for mts/smtp subdirectory
+#
+# $Id$
+#
+
+SHELL = /bin/sh
+
+top_srcdir = @top_srcdir@
+srcdir     = @srcdir@
+VPATH      = @srcdir@
+
+prefix      = @prefix@
+exec_prefix = @exec_prefix@
+bindir      = @bindir@
+libdir      = @libdir@
+etcdir      = @sysconfdir@
+
+CC       = @CC@
+CFLAGS   = @CFLAGS@
+DEFS     = @DEFS@
+INCLUDES = -I../.. -I$(srcdir) -I$(top_srcdir)
+
+LORDER  = @LORDER@
+TSORT   = @TSORT@
+RANLIB  = @RANLIB@
+
+COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CFLAGS)
+
+.SUFFIXES:
+.SUFFIXES: .c .o
+
+.c.o:
+       $(COMPILE) $<
+
+# header files
+HDRS = smtp.h
+
+# source
+SRCS = hosts.c smtp.c
+
+# object files in libsmtp.a
+OBJS = hosts.o smtp.o
+
+# auxiliary files
+AUX = Makefile.in
+
+# all files in this directory included in the distribution
+DIST = $(HDRS) $(SRCS) $(AUX)
+
+# ========= DEPENDENCIES FOR BUILDING AND INSTALLING ==========
+
+all: libsmtp.a
+
+libsmtp.a: $(OBJS)
+       rm -f $@
+       ar cr $@ `$(LORDER) $(OBJS) | $(TSORT)`
+       $(RANLIB) $@
+
+install:
+
+uninstall:
+
+# ========== DEPENDENCIES FOR CLEANUP ==========
+
+mostlyclean:
+       rm -f *.o *~
+
+clean: mostlyclean
+       rm -f libsmtp.a
+
+distclean: clean
+       rm -f Makefile
+
+realclean: distclean
+
+superclean: realclean
+
+# ========== DEPENDENCIES FOR MAINTENANCE ==========
+
+subdir = mts/smtp
+
+Makefile: Makefile.in ../../config.status
+       cd ../.. && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+distdir = ../../`cat ../../distname`/$(subdir)
+nmhdist: $(DIST)
+       @echo "Copying distribution files in $(subdir)"
+       @for file in $(DIST); do \
+         cp -p $(srcdir)/$$file $(distdir); \
+       done
+
diff --git a/mts/smtp/hosts.c b/mts/smtp/hosts.c
new file mode 100644 (file)
index 0000000..55f3393
--- /dev/null
@@ -0,0 +1,135 @@
+
+/*
+ * hosts.c -- find out the official name of a host
+ *
+ * $Id$
+ */
+
+/*
+ * In the SendMail world, we really don't know what the valid
+ * hosts are.  We could poke around in the sendmail.cf file, but
+ * that still isn't a guarantee.  As a result, we'll say that
+ * everything is a valid host, and let SendMail worry about it.
+ */
+
+#include <h/mh.h>
+#include <zotnet/mts/mts.h>
+#include <netdb.h>
+
+static struct host {
+    char *h_name;
+    char **h_aliases;
+    struct host *h_next;
+} hosts;
+
+
+/*
+ * static prototypes
+ */
+static int init_hs(void);
+
+
+char *
+OfficialName (char *name)
+{
+    char *p, *q, site[BUFSIZ];
+    struct hostent *hp;
+
+    static char buffer[BUFSIZ];
+    char **r;
+    struct host *h;
+
+    for (p = name, q = site; *p && (q - site < sizeof(site) - 1); p++, q++)
+       *q = isupper (*p) ? tolower (*p) : *p;
+    *q = '\0';
+    q = site;
+
+    if (!strcasecmp (LocalName(), site))
+       return LocalName();
+
+#ifndef        BIND
+    sethostent (1);
+#endif
+
+    if ((hp = gethostbyname (q))) {
+       strncpy (buffer, hp->h_name, sizeof(buffer));
+       return buffer;
+    }
+    if (hosts.h_name || init_hs ())
+       for (h = hosts.h_next; h; h = h->h_next)
+           if (!strcasecmp (h->h_name, q))
+               return h->h_name;
+           else
+               for (r = h->h_aliases; *r; r++)
+                   if (!strcasecmp (*r, q))
+                       return h->h_name;
+
+    strncpy (buffer, site, sizeof(buffer));
+    return buffer;
+}
+
+/*
+ * Use hostable as an exception file for those hosts that aren't
+ * on the Internet (listed in /etc/hosts).  These are usually
+ * PhoneNet and UUCP sites.
+ */
+
+#define        NALIASES 50
+
+static int
+init_hs (void)
+{
+    char  *cp, *dp, **q, **r;
+    char buffer[BUFSIZ], *aliases[NALIASES];
+    register struct host *h;
+    register FILE  *fp;
+
+    if ((fp = fopen (hostable, "r")) == NULL)
+       return 0;
+
+    h = &hosts;
+    while (fgets (buffer, sizeof(buffer), fp) != NULL) {
+       if ((cp = strchr(buffer, '#')))
+           *cp = 0;
+       if ((cp = strchr(buffer, '\n')))
+           *cp = 0;
+       for (cp = buffer; *cp; cp++)
+           if (isspace (*cp))
+               *cp = ' ';
+       for (cp = buffer; isspace (*cp); cp++)
+           continue;
+       if (*cp == 0)
+           continue;
+
+       q = aliases;
+       if ((cp = strchr(dp = cp, ' '))) {
+           *cp = 0;
+           for (cp++; *cp; cp++) {
+               while (isspace (*cp))
+                   cp++;
+               if (*cp == 0)
+                   break;
+               if ((cp = strchr(*q++ = cp, ' ')))
+                   *cp = 0;
+               else
+                   break;
+               if (q >= aliases + NALIASES)
+                   break;
+           }
+       }
+
+       *q = 0;
+
+       h->h_next = (struct host *) calloc (1, sizeof(*h));
+       h = h->h_next;
+       h->h_name = getcpy (dp);
+       r = h->h_aliases =
+               (char **) calloc ((size_t) (q - aliases + 1), sizeof(*q));
+       for (q = aliases; *q; q++)
+           *r++ = getcpy (*q);
+       *r = 0;
+    }
+
+    fclose (fp);
+    return 1;
+}
diff --git a/mts/smtp/smtp.c b/mts/smtp/smtp.c
new file mode 100644 (file)
index 0000000..f775638
--- /dev/null
@@ -0,0 +1,1325 @@
+
+/*
+ * smtp.c -- nmh SMTP interface
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include "smtp.h"
+#include <zotnet/mts/mts.h>
+#include <signal.h>
+
+/*
+ * This module implements an interface to SendMail very similar
+ * to the MMDF mm_(3) routines.  The sm_() routines herein talk
+ * SMTP to a sendmail process, mapping SMTP reply codes into
+ * RP_-style codes.
+ */
+
+/*
+ * On older 4.2BSD machines without the POSIX function `sigaction',
+ * the alarm handing stuff for time-outs will NOT work due to the way
+ * syscalls get restarted.  This is not really crucial, since SendMail
+ * is generally well-behaved in this area.
+ */
+
+#ifdef SENDMAILBUG
+/*
+ * It appears that some versions of Sendmail will return Code 451
+ * when they don't really want to indicate a failure.
+ * "Code 451 almost always means sendmail has deferred; we don't
+ * really want bomb out at this point since sendmail will rectify
+ * things later."  So, if you define SENDMAILBUG, Code 451 is
+ * considered the same as Code 250.  Yuck!
+ */
+#endif
+
+#define        TRUE    1
+#define        FALSE   0
+
+/*
+ * these codes must all be different!
+ */
+#define        SM_OPEN  90      /* Changed from 30 in case of nameserver flakiness */
+#define        SM_HELO  20
+#define        SM_RSET  15
+#define        SM_MAIL  40
+#define        SM_RCPT 120
+#define        SM_DATA  20
+#define        SM_TEXT 150
+#define        SM_DOT  180
+#define        SM_QUIT  30
+#define        SM_CLOS  10
+
+static int sm_addrs = 0;
+static int sm_alarmed = 0;
+static int sm_debug = 0;
+static int sm_nl = TRUE;
+static int sm_verbose = 0;
+
+static FILE *sm_rfp = NULL;
+static FILE *sm_wfp = NULL;
+
+#ifdef MPOP
+static int sm_ispool = 0;
+static char sm_tmpfil[BUFSIZ];
+#endif /* MPOP */
+
+static char *sm_noreply = "No reply text given";
+static char *sm_moreply = "; ";
+
+struct smtp sm_reply;          /* global... */
+
+#ifdef MPOP
+extern int errno;
+#endif
+
+
+#define        MAXEHLO 20
+
+static int doingEHLO;
+char *EHLOkeys[MAXEHLO + 1];
+
+/*
+ * static prototypes
+ */
+static int rclient (char *, char *, char *);
+static int sm_ierror (char *fmt, ...);
+static int smtalk (int time, char *fmt, ...);
+static int sm_wrecord (char *, int);
+static int sm_wstream (char *, int);
+static int sm_werror (void);
+static int smhear (void);
+static int sm_rrecord (char *, int *);
+static int sm_rerror (void);
+static RETSIGTYPE alrmser (int);
+static char *EHLOset (char *);
+
+#ifdef MPOP
+/*
+ * smtp.c's own static copy of several nmh library subroutines
+ */
+static char **smail_brkstring (char *, char *, char *);
+static int smail_brkany (char, char *);
+char **smail_copyip (char **, char **, int);
+#endif
+
+
+int
+sm_init (char *client, char *server, int watch, int verbose,
+         int debug, int onex, int queued)
+{
+    int result, sd1, sd2;
+
+    if (watch)
+       verbose = TRUE;
+
+    sm_verbose = verbose;
+    sm_debug = debug;
+
+#ifdef MPOP
+    if (sm_ispool)
+       goto all_done;
+#endif
+
+    if (sm_rfp != NULL && sm_wfp != NULL)
+       goto send_options;
+
+    if (client == NULL || *client == '\0')
+       if (clientname)
+           client = clientname;
+       else
+           client = LocalName();       /* no clientname -> LocalName */
+
+#ifdef ZMAILER
+    if (client == NULL || *client == '\0')
+       client = "localhost";
+#endif
+
+    if ((sd1 = rclient (server, "tcp", "smtp")) == NOTOK)
+       return RP_BHST;
+
+#ifdef MPOP
+    if (sm_ispool) {
+       if (sm_rfp) {
+           alarm (SM_CLOS);
+           fclose (sm_rfp);
+           alarm (0);
+           sm_rfp = NULL;
+       }
+       if ((sm_wfp = fdopen (sd1, "w")) == NULL) {
+           unlink (sm_tmpfil);
+           close (sd1);
+           return sm_ierror ("unable to fdopen");
+       }
+all_done: ;
+       sm_reply.text[sm_reply.length = 0] = NULL;
+       return (sm_reply.code = RP_OK);
+    }
+#endif /* MPOP */
+
+    if ((sd2 = dup (sd1)) == NOTOK) {
+       close (sd1);
+       return sm_ierror ("unable to dup");
+    }
+
+    SIGNAL (SIGALRM, alrmser);
+    SIGNAL (SIGPIPE, SIG_IGN);
+
+    if ((sm_rfp = fdopen (sd1, "r")) == NULL
+           || (sm_wfp = fdopen (sd2, "w")) == NULL) {
+       close (sd1);
+       close (sd2);
+       sm_rfp = sm_wfp = NULL;
+       return sm_ierror ("unable to fdopen");
+    }
+
+    sm_alarmed = 0;
+    alarm (SM_OPEN);
+    result = smhear ();
+    alarm (0);
+
+    switch (result) {
+       case 220: 
+           break;
+
+       default: 
+           sm_end (NOTOK);
+           return RP_RPLY;
+    }
+
+    /*
+     * Give EHLO or HELO command
+     */
+    if (client && *client) {
+       doingEHLO = 1;
+       result = smtalk (SM_HELO, "EHLO %s", client);
+       doingEHLO = 0;
+
+       if (result >= 500 && result <= 599)
+           result = smtalk (SM_HELO, "HELO %s", client);
+
+       if (result != 250) {
+           sm_end (NOTOK);
+           return RP_RPLY;
+       }
+    }
+
+send_options: ;
+    if (watch && EHLOset ("XVRB"))
+       smtalk (SM_HELO, "VERB on");
+    if (onex && EHLOset ("XONE"))
+       smtalk (SM_HELO, "ONEX");
+    if (queued && EHLOset ("XQUE"))
+       smtalk (SM_HELO, "QUED");
+
+    return RP_OK;
+}
+
+
+#ifdef MPOP
+# define MAXARGS  1000
+#endif /* MPOP */
+
+static int
+rclient (char *server, char *protocol, char *service)
+{
+    int sd;
+    char response[BUFSIZ];
+#ifdef MPOP
+    char *cp;
+#endif /* MPOP */
+
+    if ((sd = client (server, protocol, service, FALSE, response, sizeof(response))) != NOTOK)
+       return sd;
+
+#ifdef MPOP
+    if (!server && servers && (cp = strchr(servers, '/'))) {
+       char **ap;
+       char *arguments[MAXARGS];
+
+       smail_copyip (smail_brkstring (cp = getcpy (servers), " ", "\n"), arguments, MAXARGS);
+
+       for (ap = arguments; *ap; ap++)
+           if (**ap == '/') {
+               char *dp;
+
+               if ((dp = strrchr(*ap, '/')) && *++dp == NULL)
+                   *--dp = NULL;
+               snprintf (sm_tmpfil, sizeof(sm_tmpfil), "%s/smtpXXXXXX", *ap);
+               mktemp (sm_tmpfil);
+
+               if ((sd = creat (sm_tmpfil, 0600)) != NOTOK) {
+                   sm_ispool = 1;
+                   break;
+               }
+           }
+
+       free (cp);
+       if (sd != NOTOK)
+           return sd;
+    }
+#endif /* MPOP */
+
+    sm_ierror ("%s", response);
+    return NOTOK;
+}
+
+
+int
+sm_winit (int mode, char *from)
+{
+    char *smtpcom;
+
+#ifdef MPOP
+    if (sm_ispool && !sm_wfp) {
+       strlen (strcpy (sm_reply.text, "unable to create new spool file"));
+       sm_reply.code = NOTOK;
+       return RP_BHST;
+    }
+#endif /* MPOP */
+
+    switch (mode) {
+       case S_MAIL:
+           smtpcom = "MAIL";
+           break;
+
+       case S_SEND:
+           smtpcom = "SEND";
+           break;
+
+       case S_SOML:
+           smtpcom = "SOML";
+           break;
+
+       case S_SAML:
+           smtpcom = "SAML";
+           break;
+    }
+
+    switch (smtalk (SM_MAIL, "%s FROM:<%s>", smtpcom, from)) {
+       case 250: 
+           sm_addrs = 0;
+           return RP_OK;
+
+       case 500: 
+       case 501: 
+       case 552: 
+           return RP_PARM;
+
+       default: 
+           return RP_RPLY;
+    }
+}
+
+
+int
+sm_wadr (char *mbox, char *host, char *path)
+{
+    switch (smtalk (SM_RCPT, host && *host ? "RCPT TO:<%s%s@%s>"
+                                          : "RCPT TO:<%s%s>",
+                            path ? path : "", mbox, host)) {
+       case 250: 
+       case 251: 
+           sm_addrs++;
+           return RP_OK;
+
+       case 451: 
+#ifdef SENDMAILBUG
+           sm_addrs++;
+           return RP_OK;
+#endif /* SENDMAILBUG */
+       case 421: 
+       case 450: 
+       case 452: 
+           return RP_NO;
+
+       case 500: 
+       case 501: 
+           return RP_PARM;
+
+       case 550: 
+       case 551: 
+       case 552: 
+       case 553: 
+           return RP_USER;
+
+       default: 
+           return RP_RPLY;
+    }
+}
+
+
+int
+sm_waend (void)
+{
+    switch (smtalk (SM_DATA, "DATA")) {
+       case 354: 
+           sm_nl = TRUE;
+           return RP_OK;
+
+       case 451: 
+#ifdef SENDMAILBUG
+           sm_nl = TRUE;
+           return RP_OK;
+#endif /* SENDMAILBUG */
+       case 421: 
+           return RP_NO;
+
+       case 500: 
+       case 501: 
+       case 503: 
+       case 554: 
+           return RP_NDEL;
+
+       default: 
+           return RP_RPLY;
+    }
+}
+
+
+int
+sm_wtxt (char *buffer, int len)
+{
+    int result;
+
+    sm_alarmed = 0;
+    alarm (SM_TEXT);
+    result = sm_wstream (buffer, len);
+    alarm (0);
+
+    return (result == NOTOK ? RP_BHST : RP_OK);
+}
+
+
+int
+sm_wtend (void)
+{
+    if (sm_wstream ((char *) NULL, 0) == NOTOK)
+       return RP_BHST;
+
+    switch (smtalk (SM_DOT + 3 * sm_addrs, ".")) {
+       case 250: 
+       case 251: 
+           return RP_OK;
+
+       case 451: 
+#ifdef SENDMAILBUG
+           return RP_OK;
+#endif /* SENDMAILBUG */
+       case 452: 
+       default: 
+           return RP_NO;
+
+       case 552: 
+       case 554: 
+           return RP_NDEL;
+    }
+}
+
+
+int
+sm_end (int type)
+{
+    int status;
+    struct smtp sm_note;
+
+    if (sm_rfp == NULL && sm_wfp == NULL)
+       return RP_OK;
+
+    switch (type) {
+       case OK: 
+           smtalk (SM_QUIT, "QUIT");
+           break;
+
+       case NOTOK: 
+           sm_note.code = sm_reply.code;
+           strncpy (sm_note.text, sm_reply.text, sm_note.length = sm_reply.length);/* fall */
+       case DONE: 
+           if (smtalk (SM_RSET, "RSET") == 250 && type == DONE)
+               return RP_OK;
+           smtalk (SM_QUIT, "QUIT");
+           if (type == NOTOK) {
+               sm_reply.code = sm_note.code;
+               strncpy (sm_reply.text, sm_note.text, sm_reply.length = sm_note.length);
+           }
+           break;
+    }
+
+#ifdef MPOP
+    if (sm_ispool) {
+       sm_ispool = 0;
+
+       if (sm_wfp) {
+           unlink (sm_tmpfil);
+           fclose (sm_wfp);
+           sm_wfp = NULL;
+       }
+    }
+#endif /* MPOP */
+
+    if (sm_rfp != NULL) {
+       alarm (SM_CLOS);
+       fclose (sm_rfp);
+       alarm (0);
+    }
+    if (sm_wfp != NULL) {
+       alarm (SM_CLOS);
+       fclose (sm_wfp);
+       alarm (0);
+    }
+
+    status = 0;
+    sm_rfp = sm_wfp = NULL;
+    return (status ? RP_BHST : RP_OK);
+}
+
+
+#ifdef MPOP
+
+int
+sm_bulk (char *file)
+{
+    int        cc, i, j, k, result;
+    long pos;
+    char *dp, *bp, *cp, s;
+    char buffer[BUFSIZ], sender[BUFSIZ];
+    FILE *fp, *gp;
+
+    gp = NULL;
+    k = strlen (file) - sizeof(".bulk");
+    if ((fp = fopen (file, "r")) == NULL) {
+       int len;
+
+       snprintf (sm_reply.text, sizeof(sm_reply.text),
+               "unable to read %s: ", file);
+       bp = sm_reply.text;
+       len = strlen (bp);
+       bp += len;
+       if ((s = strerror (errno)))
+           strncpy (bp, s, sizeof(sm_reply.text) - len);
+       else
+           snprintf (bp, sizeof(sm_reply.text) - len, "Error %d", errno);
+       sm_reply.length = strlen (sm_reply.text);
+       sm_reply.code = NOTOK;
+       return RP_BHST;
+    }
+    if (sm_debug) {
+       printf ("reading file %s\n", file);
+       fflush (stdout);
+    }
+
+    i = j = 0;
+    while (fgets (buffer, sizeof(buffer), fp)) {
+       if (j++ == 0)
+           strncpy (sender, buffer + sizeof("MAIL FROM:") - 1, sizeof(sender));
+       if (strcmp (buffer, "DATA\r\n") == 0) {
+           i = 1;
+           break;
+       }
+    }
+    if (i == 0) {
+       if (sm_debug) {
+           printf ("no DATA...\n");
+           fflush (stdout);
+       }
+losing0:
+       snprintf (buffer, sizeof(buffer), "%s.bad", file);
+       rename (file, buffer);
+       if (gp) {
+           snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
+           unlink (buffer);
+           fclose (gp);
+       }
+       fclose (fp);
+       return RP_OK;
+    }
+    if (j < 3) {
+       if (sm_debug) {
+           printf ("no %srecipients...\n", j < 1 ? "sender or " : "");
+           fflush (stdout);
+       }
+       goto losing0;
+    }
+
+    if ((cp = malloc ((size_t) (cc = (pos = ftell (fp)) + 1))) == NULL) {
+       sm_reply.length = strlen (strcpy (sm_reply.text, "out of memory"));
+losing1: ;
+       sm_reply.code = NOTOK;
+       fclose (fp);
+       return RP_BHST;
+    }
+    fseek (fp, 0L, SEEK_SET);
+    for (dp = cp, i = 0; i++ < j; dp += strlen (dp))
+       if (fgets (dp, cc - (dp - cp), fp) == NULL) {
+           sm_reply.length = strlen (strcpy (sm_reply.text, "premature eof"));
+losing2:
+           free (cp);
+           goto losing1;
+       }
+    *dp = NULL;
+
+    for (dp = cp, i = cc - 1; i > 0; dp += cc, i -= cc)
+       if ((cc = write (fileno (sm_wfp), dp, i)) == NOTOK) {
+           int len;
+losing3:
+           strcpy (sm_reply.text, "error writing to server: ",
+               sizeof(sm_reply.text));
+           bp = sm_reply.text;
+           len = strlen (bp);
+           bp += len;
+           if ((s = strerror (errno)))
+               strncpy (bp, s, sizeof(sm_reply.text) - len);
+           else
+               snprintf (bp, sizeof(sm_reply.text) - len,
+                       "unknown error %d", errno);
+           sm_reply.length = strlen (sm_reply.text);
+           goto losing2;
+       }
+       else
+           if (sm_debug) {
+               printf ("wrote %d octets to server\n", cc);
+               fflush (stdout);
+           }
+
+    for (dp = cp, i = 0; i++ < j; dp = strchr(dp, '\n'), dp++) {
+       if (sm_debug) {
+           if (bp = strchr(dp, '\r'))
+               *bp = NULL;
+           printf ("=> %s\n", dp);
+           fflush (stdout);
+           if (bp)
+               *bp = '\r';
+       }
+
+       switch (smhear () + (i == 1 ? 1000 : i != j ? 2000 : 3000)) {
+           case 1000 + 250:
+               sm_addrs = 0;
+               result = RP_OK;
+               break;
+
+           case 1000 + 500: 
+           case 1000 + 501: 
+           case 1000 + 552: 
+           case 2000 + 500: 
+           case 2000 + 501:
+               result = RP_PARM;
+               smtalk (SM_RSET, "RSET");
+               free (cp);
+               goto losing0;
+
+           case 2000 + 250:
+           case 2000 + 251:
+               sm_addrs++;
+               result = RP_OK;
+               break;
+
+           case 2000 + 451: 
+#ifdef SENDMAILBUG
+               sm_addrs++;
+               result = RP_OK;
+               break;
+#endif
+           case 2000 + 421: 
+           case 2000 + 450: 
+           case 2000 + 452: 
+               result = RP_NO;
+               goto bad_addr;
+
+           case 2000 + 550: 
+           case 2000 + 551: 
+           case 2000 + 552: 
+           case 2000 + 553: 
+               result = RP_USER;
+bad_addr:
+               if (k <= 0 || strcmp (sender, "<>\r\n") == 0)
+                   break;
+               if (gp == NULL) {
+                   int     l;
+                   snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
+                   if ((gp = fopen (buffer, "w+")) == NULL)
+                       goto bad_data;
+                   fprintf (gp, "MAIL FROM:<>\r\nRCPT TO:%sDATA\r\n", sender);
+                   l = strlen (sender);
+                   fprintf (gp,
+                            "To: %*.*s\r\nSubject: Invalid addresses (%s)\r\n",
+                            l - 4, l - 4, sender + 1, file);
+                   fprintf (gp, "Date: %s\r\nFrom: Postmaster@%s\r\n\r\n",
+                            dtimenow (0), LocalName ());
+               }
+               if (bp = strchr(dp, '\r'))
+                   *bp = NULL;
+               fprintf (gp, "=>        %s\r\n", dp);
+               if (bp)
+                   *bp = '\r';
+               fprintf (gp, "<= %s\r\n", rp_string (result));
+               fflush (gp);
+               break;
+
+           case 3000 + 354: 
+#ifdef SENDMAILBUG
+ok_data:
+#endif
+               result = RP_OK;
+               break;
+
+           case 3000 + 451: 
+#ifdef SENDMAILBUG
+               goto ok_data;
+#endif
+           case 3000 + 421:
+               result = RP_NO;
+bad_data:
+               smtalk (SM_RSET, "RSET");
+               free (cp);
+               if (gp) {
+                   snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
+                   unlink (buffer);
+                   fclose (gp);
+               }
+               fclose (fp);
+               return result;
+
+           case 3000 + 500: 
+           case 3000 + 501: 
+           case 3000 + 503: 
+           case 3000 + 554: 
+               smtalk (SM_RSET, "RSET");
+               free (cp);
+               goto no_dice;
+
+           default:
+               result = RP_RPLY;
+               goto bad_data;
+       }
+    }
+    free (cp);
+
+    {
+#ifdef HAVE_ST_BLKSIZE
+       struct stat st;
+
+       if (fstat (fileno (sm_wfp), &st) == NOTOK || (cc = st.st_blksize) < BUFSIZ)
+           cc = BUFSIZ;
+#else
+       cc = BUFSIZ;
+#endif
+       if ((cp = malloc ((size_t) cc)) == NULL) {
+           smtalk (SM_RSET, "RSET");
+           sm_reply.length = strlen (strcpy (sm_reply.text, "out of memory"));
+           goto losing1;
+       }
+    }
+
+    fseek (fp, pos, SEEK_SET);
+    for (;;) {
+       int eof = 0;
+
+       for (dp = cp, i = cc; i > 0; dp += j, i -= j)
+           if ((j = fread (cp, sizeof(*cp), i, fp)) == OK) {
+               if (ferror (fp)) {
+                   int len;
+
+                   snprintf (sm_reply.text, sizeof(sm_reply.text),
+                       "error reading %s: ", file);
+                   bp = sm_reply.text;
+                   len = strlen (bp);
+                   bp += len;
+                   if ((s = strerror (errno)))
+                       strncpy (bp, s, sizeof(sm_reply.text) - len);
+                   else
+                       snprintf (bp, sizeof(sm_reply.text) - len,
+                               "unknown error %d", errno);
+                   sm_reply.length = strlen (sm_reply.text);
+                   goto losing2;
+               }
+               cc = dp - cp;
+               eof = 1;
+               break;
+           }
+
+       for (dp = cp, i = cc; i > 0; dp += j, i -= j)
+           if ((j = write (fileno (sm_wfp), dp, i)) == NOTOK)
+               goto losing3;
+           else
+               if (sm_debug) {
+                   printf ("wrote %d octets to server\n", j);
+                   fflush (stdout);
+               }
+
+       if (eof)
+           break;
+    }
+    free (cp);
+
+    switch (smhear ()) {
+       case 250: 
+       case 251: 
+#ifdef SENDMAILBUG
+ok_dot:
+#endif
+           result = RP_OK;
+           unlink (file);
+           break;
+
+       case 451: 
+#ifdef SENDMAILBUG
+           goto ok_dot;
+#endif
+       case 452: 
+       default: 
+           result = RP_NO;
+           if (gp) {
+               snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
+               unlink (buffer);
+               fclose (gp);
+               gp = NULL;
+           }
+           break;
+
+       case 552: 
+       case 554: 
+no_dice:
+           result = RP_NDEL;
+           if (k <= 0 || strcmp (sender, "<>\r\n") == 0) {
+               unlink (file);
+               break;
+           }
+           if (gp) {
+               fflush (gp);
+               ftruncate (fileno (gp), 0L);
+               fseek (gp, 0L, SEEK_SET);
+           }
+           else {
+               snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
+               if ((gp = fopen (buffer, "w")) == NULL)
+                   break;
+           }
+           fprintf (gp, "MAIL FROM:<>\r\nRCPT TO:%sDATA\r\n", sender);
+           i = strlen (sender);
+           fprintf (gp, "To: %*.*s\r\nSubject: Failed mail (%s)\r\n",
+                    i - 4, i - 4, sender + 1, file);
+            fprintf (gp, "Date: %s\r\nFrom: Postmaster@%s\r\n\r\n",
+                    dtimenow (0), LocalName ());
+           break;
+    }
+
+    if (gp) {
+       fputs ("\r\n------- Begin Returned message\r\n\r\n", gp);
+       fseek (fp, pos, SEEK_SET);
+       while (fgets (buffer, sizeof(buffer), fp)) {
+           if (buffer[0] == '-')
+               fputs ("- ", gp);
+           if (strcmp (buffer, ".\r\n"))
+               fputs (buffer, gp);
+       }
+       fputs ("\r\n------- End Returned Message\r\n\r\n.\r\n", gp);
+       fflush (gp);
+       if (!ferror (gp))
+           unlink (file);
+       fclose (gp);
+    }
+    fclose (fp);
+
+    return result;
+}
+#endif /* MPOP */
+
+
+static int
+sm_ierror (char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    vsnprintf (sm_reply.text, sizeof(sm_reply.text), fmt, ap);
+    va_end(ap);
+
+    sm_reply.length = strlen (sm_reply.text);
+    sm_reply.code = NOTOK;
+
+    return RP_BHST;
+}
+
+
+static int
+smtalk (int time, char *fmt, ...)
+{
+    va_list ap;
+    int result;
+    char buffer[BUFSIZ];
+
+    va_start(ap, fmt);
+    vsnprintf (buffer, sizeof(buffer), fmt, ap);
+    va_end(ap);
+
+    if (sm_debug) {
+       printf ("=> %s\n", buffer);
+       fflush (stdout);
+    }
+
+#ifdef MPOP
+    if (sm_ispool) {
+       char    file[BUFSIZ];
+
+       if (strcmp (buffer, ".") == 0)
+           time = SM_DOT;
+       fprintf (sm_wfp, "%s\r\n", buffer);
+       switch (time) {
+           case SM_DOT:
+               fflush (sm_wfp);
+               if (ferror (sm_wfp))
+                   return sm_werror ();
+               snprintf (file, sizeof(file), "%s%c.bulk", sm_tmpfil,
+                               (char) (sm_ispool + 'a' - 1));
+               if (rename (sm_tmpfil, file) == NOTOK) {
+                   int len;
+                   char *bp;
+
+                   snprintf (sm_reply.text, sizeof(sm_reply.text),
+                           "error renaming %s to %s: ", sm_tmpfil, file);
+                   bp = sm_reply.text;
+                   len = strlen (bp);
+                   bp += len;
+                   if ((s = strerror (errno)))
+                       strncpy (bp, s, sizeof(sm_reply.text) - len);
+                   else
+                       snprintf (bp, sizeof(sm_reply.text) - len,
+                               "unknown error %d", errno);
+                   sm_reply.length = strlen (sm_reply.text);
+                   sm_reply.code = NOTOK;
+                   return RP_BHST;
+               }
+               fclose (sm_wfp);
+               if (sm_wfp = fopen (sm_tmpfil, "w"))
+                   chmod (sm_tmpfil, 0600);
+               sm_ispool++;
+               /* and fall... */
+
+           case SM_MAIL:
+           case SM_RCPT:
+               result = 250;
+               break;
+
+           case SM_RSET:
+               fflush (sm_wfp);
+               ftruncate (fileno (sm_wfp), 0L);
+               fseek (sm_wfp, 0L, SEEK_SET);
+               result = 250;
+               break;
+
+           case SM_DATA:
+               result = 354;
+               break;
+
+           case SM_QUIT:
+               unlink (sm_tmpfil);
+               sm_ispool = 0;
+               result = 221;
+               break;
+
+           default:
+               result = 500;
+               break;
+       }
+       if (sm_debug) {
+           printf ("<= %d\n", result);
+           fflush (stdout);
+       }
+
+       sm_reply.text[sm_reply.length = 0] = NULL;
+       return (sm_reply.code = result);
+    }
+#endif /* MPOP */
+
+    sm_alarmed = 0;
+    alarm ((unsigned) time);
+    if ((result = sm_wrecord (buffer, strlen (buffer))) != NOTOK)
+       result = smhear ();
+    alarm (0);
+
+    return result;
+}
+
+
+/*
+ * write the buffer to the open SMTP channel
+ */
+
+static int
+sm_wrecord (char *buffer, int len)
+{
+    if (sm_wfp == NULL)
+       return sm_werror ();
+
+    fwrite (buffer, sizeof(*buffer), len, sm_wfp);
+    fputs ("\r\n", sm_wfp);
+    fflush (sm_wfp);
+
+    return (ferror (sm_wfp) ? sm_werror () : OK);
+}
+
+
+static int
+sm_wstream (char *buffer, int len)
+{
+    char  *bp;
+    static char lc = '\0';
+
+    if (sm_wfp == NULL)
+       return sm_werror ();
+
+    if (buffer == NULL && len == 0) {
+       if (lc != '\n')
+           fputs ("\r\n", sm_wfp);
+       lc = '\0';
+       return (ferror (sm_wfp) ? sm_werror () : OK);
+    }
+
+    for (bp = buffer; len > 0; bp++, len--) {
+       switch (*bp) {
+           case '\n': 
+               sm_nl = TRUE;
+               fputc ('\r', sm_wfp);
+               break;
+
+           case '.': 
+               if (sm_nl)
+                   fputc ('.', sm_wfp);/* FALL THROUGH */
+           default: 
+               sm_nl = FALSE;
+       }
+       fputc (*bp, sm_wfp);
+       if (ferror (sm_wfp))
+           return sm_werror ();
+    }
+
+    if (bp > buffer)
+       lc = *--bp;
+    return (ferror (sm_wfp) ? sm_werror () : OK);
+}
+
+
+#ifdef _AIX
+/*
+ * AIX by default will inline the strlen and strcpy commands by redefining
+ * them as __strlen and __strcpy respectively.  This causes compile problems
+ * with the #ifdef MPOP in the middle.  Should the #ifdef MPOP be removed,
+ * remove these #undefs.
+ */
+# undef strlen
+# undef strcpy
+#endif /* _AIX */
+
+static int
+sm_werror (void)
+{
+    sm_reply.length =
+       strlen (strcpy (sm_reply.text, sm_wfp == NULL ? "no socket opened"
+           : sm_alarmed ? "write to socket timed out"
+#ifdef MPOP
+           : sm_ispool ? "error writing to spool file"
+#endif
+           : "error writing to socket"));
+
+    return (sm_reply.code = NOTOK);
+}
+
+
+static int
+smhear (void)
+{
+    int i, code, cont, bc, rc, more;
+    char *bp, *rp;
+    char **ehlo, buffer[BUFSIZ];
+
+    if (doingEHLO) {
+       static int at_least_once = 0;
+
+       if (at_least_once) {
+           char *ep;
+
+           for (ehlo = EHLOkeys; *ehlo; ehlo++) {
+               ep = *ehlo;
+               free (ep);
+           }
+       } else {
+           at_least_once = 1;
+       }
+
+       ehlo = EHLOkeys;
+       *ehlo = NULL;
+    }
+
+again: ;
+
+    sm_reply.length = 0;
+    sm_reply.text[0] = 0;
+    rp = sm_reply.text;
+    rc = sizeof(sm_reply.text) - 1;
+
+    for (more = FALSE; sm_rrecord (bp = buffer, &bc) != NOTOK;) {
+       if (sm_debug) {
+           printf ("<= %s\n", buffer);
+           fflush (stdout);
+       }
+
+       if (doingEHLO
+               && strncmp (buffer, "250", sizeof("250") - 1) == 0
+               && (buffer[3] == '-' || doingEHLO == 2)
+               && buffer[4]) {
+           if (doingEHLO == 2) {
+               if ((*ehlo = malloc ((size_t) (strlen (buffer + 4) + 1)))) {
+                   strcpy (*ehlo++, buffer + 4);
+                   *ehlo = NULL;
+                   if (ehlo >= EHLOkeys + MAXEHLO)
+                       doingEHLO = 0;
+               }
+               else
+                   doingEHLO = 0;
+           }
+           else
+               doingEHLO = 2;
+       }
+
+       for (; bc > 0 && (!isascii (*bp) || !isdigit (*bp)); bp++, bc--)
+           continue;
+
+       cont = FALSE;
+       code = atoi (bp);
+       bp += 3, bc -= 3;
+       for (; bc > 0 && isspace (*bp); bp++, bc--)
+           continue;
+       if (bc > 0 && *bp == '-') {
+           cont = TRUE;
+           bp++, bc--;
+           for (; bc > 0 && isspace (*bp); bp++, bc--)
+               continue;
+       }
+
+       if (more) {
+           if (code != sm_reply.code || cont)
+               continue;
+           more = FALSE;
+       } else {
+           sm_reply.code = code;
+           more = cont;
+           if (bc <= 0) {
+               strncpy (buffer, sm_noreply, sizeof(buffer));
+               bp = buffer;
+               bc = strlen (sm_noreply);
+           }
+       }
+
+       if ((i = min (bc, rc)) > 0) {
+           strncpy (rp, bp, i);
+           rp += i, rc -= i;
+           if (more && rc > strlen (sm_moreply) + 1) {
+               strcpy (sm_reply.text + rc, sm_moreply);
+               rc += strlen (sm_moreply);
+           }
+       }
+       if (more)
+           continue;
+       if (sm_reply.code < 100) {
+           if (sm_verbose) {
+               printf ("%s\n", sm_reply.text);
+               fflush (stdout);
+           }
+           goto again;
+       }
+
+       sm_reply.length = rp - sm_reply.text;
+       return sm_reply.code;
+    }
+    return NOTOK;
+}
+
+
+static int
+sm_rrecord (char *buffer, int *len)
+{
+    if (sm_rfp == NULL)
+       return sm_rerror ();
+
+    buffer[*len = 0] = 0;
+
+    fgets (buffer, BUFSIZ, sm_rfp);
+    *len = strlen (buffer);
+    if (ferror (sm_rfp) || feof (sm_rfp))
+       return sm_rerror ();
+    if (buffer[*len - 1] != '\n')
+       while (getc (sm_rfp) != '\n' && !ferror (sm_rfp) && !feof (sm_rfp))
+           continue;
+    else
+       if (buffer[*len - 2] == '\r')
+           *len -= 1;
+    buffer[*len - 1] = 0;
+
+    return OK;
+}
+
+
+static int
+sm_rerror (void)
+{
+    sm_reply.length =
+       strlen (strcpy (sm_reply.text, sm_rfp == NULL ? "no socket opened"
+           : sm_alarmed ? "read from socket timed out"
+           : feof (sm_rfp) ? "premature end-of-file on socket"
+           : "error reading from socket"));
+
+    return (sm_reply.code = NOTOK);
+}
+
+
+static RETSIGTYPE
+alrmser (int i)
+{
+#ifndef        RELIABLE_SIGNALS
+    SIGNAL (SIGALRM, alrmser);
+#endif
+
+    sm_alarmed++;
+    if (sm_debug) {
+       printf ("timed out...\n");
+       fflush (stdout);
+    }
+}
+
+
+char *
+rp_string (int code)
+{
+    char *text;
+    static char buffer[BUFSIZ];
+
+    switch (sm_reply.code != NOTOK ? code : NOTOK) {
+       case RP_AOK:
+           text = "AOK";
+           break;
+
+       case RP_MOK:
+           text = "MOK";
+           break;
+
+       case RP_OK: 
+           text = "OK";
+           break;
+
+       case RP_RPLY: 
+           text = "RPLY";
+           break;
+
+       case RP_BHST: 
+       default: 
+           text = "BHST";
+           snprintf (buffer, sizeof(buffer), "[%s] %s", text, sm_reply.text);
+           return buffer;
+
+       case RP_PARM: 
+           text = "PARM";
+           break;
+
+       case RP_NO: 
+           text = "NO";
+           break;
+
+       case RP_USER: 
+           text = "USER";
+           break;
+
+       case RP_NDEL: 
+           text = "NDEL";
+           break;
+    }
+
+    snprintf (buffer, sizeof(buffer), "[%s] %3d %s",
+               text, sm_reply.code, sm_reply.text);
+    return buffer;
+}
+
+
+#ifdef MPOP
+
+static char *broken[MAXARGS + 1];
+
+static char **
+smail_brkstring (char *strg, char *brksep, char *brkterm)
+{
+    int bi;
+    char c, *sp;
+
+    sp = strg;
+
+    for (bi = 0; bi < MAXARGS; bi++) {
+       while (smail_brkany (c = *sp, brksep))
+           *sp++ = 0;
+       if (!c || smail_brkany (c, brkterm)) {
+           *sp = 0;
+           broken[bi] = 0;
+           return broken;
+       }
+
+       broken[bi] = sp;
+       while ((c = *++sp) && !smail_brkany (c, brksep) && !smail_brkany (c, brkterm))
+           continue;
+    }
+    broken[MAXARGS] = 0;
+
+    return broken;
+}
+
+
+/*
+ * returns 1 if chr in strg, 0 otherwise
+ */
+static int
+smail_brkany (char chr, char *strg)
+{
+    char *sp;
+    if (strg)
+       for (sp = strg; *sp; sp++)
+           if (chr == *sp)
+               return 1;
+    return 0;
+}
+
+/*
+ * copy a string array and return pointer to end
+ */
+char **
+smail_copyip (char **p, char **q, int len_q)
+{
+    while (*p && --len_q > 0)
+       *q++ = *p++;
+
+    *q = NULL;
+    return q;
+}
+
+#endif /* MPOP */
+
+
+static char *
+EHLOset (char *s)
+{
+    size_t len;
+    char *ep, **ehlo;
+
+    len = strlen (s);
+
+    for (ehlo = EHLOkeys; *ehlo; ehlo++) {
+       ep = *ehlo;
+       if (strncmp (ep, s, len) == 0) {
+           for (ep += len; *ep == ' '; ep++)
+               continue;
+           return ep;
+       }
+    }
+
+    return 0;
+}
diff --git a/mts/smtp/smtp.h b/mts/smtp/smtp.h
new file mode 100644 (file)
index 0000000..772035d
--- /dev/null
@@ -0,0 +1,295 @@
+
+/*
+ * smtp.h -- definitions for the nmh SMTP Interface
+ *
+ * $Id$
+ */
+
+/* various modes for SMTP */
+#define        S_MAIL  0
+#define        S_SEND  1
+#define        S_SOML  2
+#define        S_SAML  3
+
+struct smtp {
+    int code;
+    int length;
+    char text[BUFSIZ];
+};
+
+/*
+ * prototypes
+ */
+/* int client (); */
+int sm_init (char *, char *, int, int, int, int, int);
+int sm_winit (int, char *);
+int sm_wadr (char *, char *, char *);
+int sm_waend (void);
+int sm_wtxt (char *, int);
+int sm_wtend (void);
+int sm_end (int);
+char *rp_string (int);
+
+#ifdef MPOP
+int sm_bulk (char *);
+#endif
+
+
+/* The remainder of this file is derived from "mmdf.h" */
+
+/*
+ *     Copyright (C) 1979,1980,1981,1982,1983  University of Delaware
+ *     Used by permission, May, 1984.
+ */
+
+/*
+ *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
+ *     
+ *
+ *     Copyright (C) 1979,1980,1981,1982,1983  University of Delaware
+ *     
+ *     Department of Electrical Engineering
+ *     University of Delaware
+ *     Newark, Delaware  19711
+ *
+ *     Phone:  (302) 738-1163
+ *     
+ *     
+ *     This program module was developed as part of the University
+ *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
+ *     
+ *     Acquisition, use, and distribution of this module and its listings
+ *     are subject restricted to the terms of a license agreement.
+ *     Documents describing systems using this module must cite its source.
+ *
+ *     The above statements must be retained with all copies of this
+ *     program and may not be removed without the consent of the
+ *     University of Delaware.
+ *     
+ */
+
+/*                      Reply Codes for MMDF
+ *
+ *  Based on: "Revised FTP Reply Codes", by Jon Postel & Nancy Neigus Arpanet
+ *      RFC 640 / NIC 30843, in the "Arpanet Protocol Handbook", E.  Feinler
+ *      and J. Postel (eds.), NIC 7104, Network Information Center, SRI
+ *      International:  Menlo Park, CA.  (NTIS AD-A0038901)
+ *
+ *  Actual values are different, but scheme is same.  Codes must fit into
+ *  8-bits (to pass on exit() calls); fields are packed 2-3-3 and interpreted
+ *  as octal numbers.
+ *
+ *  Basic format:
+ *
+ *      0yz: positive completion; entire action done
+ *      1yz: positive intermediate; only part done
+ *      2yz: Transient negative completion; may work later
+ *      3yz: Permanent negative completion; you lose forever
+ *
+ *      x0z: syntax
+ *      x1z: general; doesn't fit any other category
+ *      x2z: connections; truly transfer-related
+ *      x3z: user/authentication/account
+ *      x4x: mail
+ *      x5z: file system
+ *
+ *      3-bit z field is unique to the reply.  In the following,
+ *      the RP_xVAL defines are available for masking to obtain a field.
+ */
+
+/*
+ * FIELD DEFINITIONS & BASIC VALUES
+ */
+
+/* FIELD 1:  Basic degree of success (2-bits) */
+
+#define RP_BTYP '\200'      /* good vs. bad; on => bad            */
+#define RP_BVAL '\300'      /* basic degree of success            */
+
+#define RP_BOK  '\000'      /* went fine; all done                */
+#define RP_BPOK '\100'      /* only the first part got done       */
+#define RP_BTNO '\200'      /* temporary failure; try later       */
+#define RP_BNO  '\300'      /* not now, nor never; you lose       */
+
+/* FIELD 2:  Basic domain of discourse (3-bits) */
+
+#define RP_CVAL '\070'      /* basic category (domain) of reply   */
+
+#define RP_CSYN '\000'      /* purely a matter of form            */
+#define RP_CGEN '\010'      /* couldn't find anywhere else for it */
+#define RP_CCON '\020'      /* data-transfer-related issue        */
+#define RP_CUSR '\030'      /* pertaining to the user             */
+#define RP_CMAI '\040'      /* specific to mail semantics         */
+#define RP_CFIL '\050'      /* file system                        */
+#define RP_CLIO '\060'      /* local i/o system                   */
+
+/* FIELD 3:  Specific value for this reply (3-bits) */
+
+#define RP_SVAL '\007'      /* specific value of reply            */
+
+
+/*
+ * SPECIFIC SUCCESS VALUES
+ */
+
+/*
+ * Complete Success
+ */
+
+/* done (e.g., w/transaction) */
+#define RP_DONE (RP_BOK | RP_CGEN | '\000')
+
+/* general-purpose OK */
+#define RP_OK   (RP_BOK | RP_CGEN | '\001')
+
+/* message is accepted (w/text) */
+#define RP_MOK  (RP_BOK | RP_CMAI | '\000')
+
+
+/*
+ * Partial Success
+ */
+
+/* you are the requestor */
+#define RP_MAST (RP_BPOK| RP_CGEN | '\000')
+
+/* you are the requestee */
+#define RP_SLAV (RP_BPOK| RP_CGEN | '\001')
+
+/* message address is accepted */
+#define RP_AOK  (RP_BPOK| RP_CMAI | '\000')
+
+
+/*
+ * SPECIFIC FALURE VALUES
+ */
+
+/*
+ * Partial Failure
+ */
+
+/* not now; maybe later */
+#define RP_AGN  (RP_BTNO | RP_CGEN | '\000')
+
+/* timeout */
+#define RP_TIME (RP_BTNO | RP_CGEN | '\001')
+
+/* no-op; nothing done, this time */
+#define RP_NOOP (RP_BTNO | RP_CGEN | '\002')
+
+/* encountered an end of file */
+#define RP_EOF  (RP_BTNO | RP_CGEN | '\003')
+
+/* channel went bad */
+#define RP_NET  (RP_BTNO | RP_CCON | '\000')
+
+/* foreign host screwed up */
+#define RP_BHST (RP_BTNO | RP_CCON | '\001')
+
+/* host went away */
+#define RP_DHST (RP_BTNO | RP_CCON | '\002')
+
+/* general net i/o problem */
+#define RP_NIO  (RP_BTNO | RP_CCON | '\004')
+
+/* error reading/writing file */
+#define RP_FIO  (RP_BTNO | RP_CFIL | '\000')
+
+/* unable to create file */
+#define RP_FCRT (RP_BTNO | RP_CFIL | '\001')
+
+/* unable to open file */
+#define RP_FOPN (RP_BTNO | RP_CFIL | '\002')
+
+/* general local i/o problem */
+#define RP_LIO  (RP_BTNO | RP_CLIO | '\000')
+
+/* resource currently locked */
+#define RP_LOCK (RP_BTNO | RP_CLIO | '\001')
+
+
+/*
+ * Complete Failure
+ */
+
+/* bad mechanism/path; try alternate? */
+#define RP_MECH (RP_BNO | RP_CGEN | '\000')
+
+/* general-purpose NO */
+#define RP_NO   (RP_BNO | RP_CGEN | '\001')
+
+/* general prototocol error */
+#define RP_PROT (RP_BNO | RP_CCON | '\000')
+
+/* bad reply code (PERMANENT ERROR) */
+#define RP_RPLY (RP_BNO | RP_CCON | '\001')
+
+/* couldn't deliver */
+#define RP_NDEL (RP_BNO | RP_CMAI | '\000')
+
+/* couldn't parse the request */
+#define RP_HUH  (RP_BNO | RP_CSYN | '\000')
+
+/* no such command defined */
+#define RP_NCMD (RP_BNO | RP_CSYN | '\001')
+
+/* bad parameter */
+#define RP_PARM (RP_BNO | RP_CSYN | '\002')
+
+/* command not implemented */
+#define RP_UCMD (RP_BNO | RP_CSYN | '\003')
+
+/* unknown user */
+#define RP_USER (RP_BNO | RP_CUSR | '\000')
+
+
+/*
+ * Macros to access reply info
+ */
+
+/* get the entire return value */
+#define rp_gval(val)    ((signed char) (val))
+
+
+/*
+ * The next three give the field's bits, within the whole value
+ */
+
+/* get the basic part of return value */
+#define rp_gbval(val)   (rp_gval (val) & RP_BVAL)
+
+/* get the domain part of value */
+#define rp_gcval(val)   (rp_gval (val) & RP_CVAL)
+
+/* get the specific part of value */
+#define rp_gsval(val)   (rp_gval (val) & RP_SVAL)
+
+
+/*
+ * The next three give the numeric value withing the field
+ */
+
+/* get the basic part right-shifted */
+#define rp_gbbit(val)   ((rp_gval (val) >> 6) & 03)
+
+/* get the domain part right-shifted */
+#define rp_gcbit(val)   ((rp_gval (val) >> 3) & 07)
+
+/* get the specific part right-shifted */
+#define rp_gsbit(val)   (rp_gval (val) & 07)
+
+
+/*
+ * MACHINE DEPENDENCY
+ *
+ * The following treat the value as strictly numeric.
+ * It relies on the negative values being numerically
+ * negative.
+ */
+
+/* is return value positive? */
+#define rp_isgood(val)  (rp_gval (val) >= 0)
+
+/* is return value negative? */
+#define rp_isbad(val)   (rp_gval (val) < 0)
+
diff --git a/sbr/Makefile.in b/sbr/Makefile.in
new file mode 100644 (file)
index 0000000..9aad677
--- /dev/null
@@ -0,0 +1,130 @@
+#
+# Makefile for sbr subdirectory
+#
+# $Id$
+#
+
+SHELL = /bin/sh
+
+top_srcdir = @top_srcdir@
+srcdir     = @srcdir@
+VPATH      = @srcdir@
+
+CC       = @CC@
+CFLAGS   = @CFLAGS@
+DEFS     = @DEFS@
+INCLUDES = -I.. -I. -I$(top_srcdir)
+
+AWK    = @AWK@
+LORDER = @LORDER@
+TSORT  = @TSORT@
+RANLIB = @RANLIB@
+
+LIBOBJS = @LIBOBJS@
+
+COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CFLAGS)
+
+.SUFFIXES:
+.SUFFIXES: .c .o
+
+.c.o:
+       $(COMPILE) $<
+
+# this header file is parsed to generate signal messages (sigmsg.h)
+SIGNAL_H = @SIGNAL_H@
+
+# source for library functions
+SRCS = add.c addrsbr.c ambigsw.c atooi.c brkstring.c check_charset.c \
+       closefds.c concat.c context_del.c context_find.c context_foil.c \
+       context_read.c context_replace.c context_save.c copy.c \
+       copyip.c cpydata.c cpydgst.c discard.c done.c error.c \
+       fdcompare.c folder_addmsg.c folder_delmsgs.c folder_free.c \
+       folder_pack.c folder_read.c folder_realloc.c gans.c \
+       getans.c getanswer.c getarguments.c getcpy.c getfolder.c fmt_addr.c \
+       fmt_compile.c fmt_new.c fmt_rfc2047.c fmt_scan.c lock_file.c \
+       m_atoi.c m_backup.c m_convert.c m_draft.c m_getfld.c m_gmprot.c \
+       m_maildir.c m_name.c m_scratch.c m_tmpfil.c makedir.c \
+       path.c peekc.c pidwait.c pidstatus.c print_help.c \
+       print_sw.c print_version.c push.c putenv.c pwd.c refile.c \
+       remdir.c r1bindex.c readconfig.c seq_add.c seq_bits.c seq_del.c \
+       seq_getnum.c seq_list.c seq_nameok.c seq_print.c seq_read.c \
+       seq_save.c seq_setcur.c seq_setprev.c seq_setunseen.c showfile.c \
+       signals.c smatch.c snprintb.c ssequal.c strcasecmp.c strindex.c \
+       trimcpy.c uprf.c vfgets.c fmt_def.c m_msgdef.c
+
+# source for compatibility functions
+COMPAT = snprintf.c strdup.c strerror.c ruserpass.c
+
+OBJS = add.o addrsbr.o ambigsw.o atooi.o brkstring.o check_charset.o \
+       closefds.o concat.o context_del.o context_find.o context_foil.o \
+       context_read.o context_replace.o context_save.o copy.o \
+       copyip.o cpydata.o cpydgst.o discard.o done.o error.o \
+       fdcompare.o folder_addmsg.o folder_delmsgs.o folder_free.o \
+       folder_pack.o folder_read.o folder_realloc.o gans.o \
+       getans.o getanswer.o getarguments.o getcpy.o getfolder.o fmt_addr.o \
+       fmt_compile.o fmt_new.o fmt_rfc2047.o fmt_scan.o lock_file.o \
+       m_atoi.o m_backup.o m_convert.o m_draft.o m_getfld.o m_gmprot.o \
+       m_maildir.o m_name.o m_scratch.o m_tmpfil.o makedir.o \
+       path.o peekc.o pidwait.o pidstatus.o print_help.o \
+       print_sw.o print_version.o push.o putenv.o pwd.o refile.o \
+       remdir.o r1bindex.o readconfig.o seq_add.o seq_bits.o seq_del.o \
+       seq_getnum.o seq_list.o seq_nameok.o seq_print.o seq_read.o \
+       seq_save.o seq_setcur.o seq_setprev.o seq_setunseen.o showfile.o \
+       signals.o smatch.o snprintb.o ssequal.o strcasecmp.o strindex.o \
+       trimcpy.o uprf.o vfgets.o fmt_def.o m_msgdef.o \
+       $(LIBOBJS)
+
+# auxiliary files
+AUX = Makefile.in sigmsg.awk
+
+# all files in this directory included in the distribution
+DIST = $(SRCS) $(COMPAT) $(AUX)
+
+# ========= DEPENDENCIES FOR BUILDING ==========
+
+# default target
+all: libmh.a
+
+sigmsg.h: sigmsg.awk
+       $(AWK) -f $(srcdir)/sigmsg.awk $(SIGNAL_H) > $@
+
+pidstatus.o: sigmsg.h
+
+libmh.a: $(OBJS)
+       rm -f libmh.a
+       ar cr libmh.a `$(LORDER) $(OBJS) | $(TSORT)`
+       $(RANLIB) libmh.a
+
+install:
+
+uninstall:
+
+# ========== DEPENDENCIES FOR CLEANUP ==========
+
+mostlyclean:
+       rm -f *.o *~
+
+clean: mostlyclean
+       rm -f libmh.a sigmsg.h
+
+distclean: clean
+       rm -f Makefile
+
+realclean: distclean
+
+superclean: realclean
+
+# ========== DEPENDENCIES FOR MAINTENANCE ==========
+
+subdir = sbr
+
+Makefile: Makefile.in ../config.status
+       cd .. && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+distdir = ../`cat ../distname`/$(subdir)
+nmhdist: $(DIST)
+       @echo "Copying distribution files in $(subdir)"
+       @for file in $(DIST); do \
+         cp -p $(srcdir)/$$file $(distdir); \
+       done
+
diff --git a/sbr/add.c b/sbr/add.c
new file mode 100644 (file)
index 0000000..f38110f
--- /dev/null
+++ b/sbr/add.c
@@ -0,0 +1,44 @@
+
+/*
+ * add.c -- If "s1" is NULL, this routine just creates a
+ *       -- copy of "s2" into newly malloc'ed memory.
+ *       --
+ *       -- If "s1" is not NULL, then copy the concatenation
+ *       -- of "s1" and "s2" (note the order) into newly
+ *       -- malloc'ed memory.  Then free "s1".
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+char *
+add (char *s2, char *s1)
+{
+    char *cp;
+    size_t len1 = 0, len2 = 0;
+
+    if (s1)
+       len1 = strlen (s1);
+    if (s2)
+       len2 = strlen (s2);
+
+
+    if (!(cp = malloc (len1 + len2 + 1)))
+       adios (NULL, "unable to allocate string storage");
+
+    /* Copy s1 and free it */
+    if (s1) {
+       memcpy (cp, s1, len1);
+       free (s1);
+    }
+
+    /* Copy s2 */
+    if (s2)
+       memcpy (cp + len1, s2, len2);
+
+    /* Now NULL terminate the string */
+    cp[len1 + len2] = '\0';
+
+    return cp;
+}
diff --git a/sbr/addrsbr.c b/sbr/addrsbr.c
new file mode 100644 (file)
index 0000000..0e2f0a4
--- /dev/null
@@ -0,0 +1,496 @@
+
+/*
+ * addrsbr.c -- parse addresses 822-style
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/addrsbr.h>
+#include <zotnet/mf/mf.h>
+
+/* High level parsing of addresses:
+
+   The routines in zotnet/mf/mf.c parse the syntactic representations of
+   addresses.  The routines in sbr/addrsbr.c associate semantics with those
+   addresses.  
+
+   If #ifdef DUMB is in effect, a full 822-style parser is called
+   for syntax recongition.  This breaks each address into its components.
+   Note however that no semantics are assumed about the parts or their
+   totality.  This means that implicit hostnames aren't made explicit,
+   and explicit hostnames aren't expanded to their "official" represenations.
+
+   If DUMB is not in effect, then this module does some
+   high-level thinking about what the addresses are.
+
+   1. for MMDF systems:
+
+       string%<uucp>@<local>   ->      string
+
+   2. for non-MMDF systems:
+
+       string@host.<uucp>      ->      host!string
+
+   3. for any system, an address interpreted relative to the local host:
+
+       string@<uucp>           ->      string
+
+   For cases (1) and (3) above, the leftmost host is extracted.  If it's not
+   present, the local host is used.  If the tests above fail, the address is
+   considered to be a real 822-style address.
+
+   If an explicit host is not present, then MH checks for a bang to indicate
+   an explicit UUCP-style address.  If so, this is noted.  If not, the host is
+   defaulted, typically to the local host.  The lack of an explict host is
+   also noted.
+
+   If an explicit 822-style host is present, then MH checks to see if it
+   can expand this to the official name for the host.  If the hostname is
+   unknown, the address is so typed.
+
+   To summarize, when we're all done, here's what MH knows about the address:
+
+   DUMB        -       type:   local, uucp, or network
+               host:   not locally defaulted, not explicitly expanded
+               everything else
+
+   other -     type:   local, uucp, network, unknown
+               everything else
+ */
+
+
+static int  ingrp = 0;
+static char *pers = NULL;
+static char *mbox = NULL;
+static char *host = NULL;
+static char *route = NULL;
+static char *grp = NULL;
+static char *note = NULL;
+static char err[BUFSIZ];
+static char adr[BUFSIZ];
+
+/*
+ * external prototypes
+ */
+char *getusername (void);
+
+
+char *
+getname (char *addrs)
+{
+    struct adrx *ap;
+
+    pers = mbox = host = route = grp = note = NULL;
+    err[0] = '\0';
+
+    if ((ap = getadrx (addrs ? addrs : "")) == NULL)
+       return NULL;
+
+    strncpy (adr, ap->text, sizeof(adr));
+    pers = ap->pers;
+    mbox = ap->mbox;
+    host = ap->host;
+    route = ap->path;
+    grp = ap->grp;
+    ingrp = ap->ingrp;
+    note = ap->note;
+    if (ap->err && *ap->err)
+       strncpy (err, ap->err, sizeof(err));
+
+    return adr;
+}
+
+
+struct mailname *
+getm (char *str, char *dfhost, int dftype, int wanthost, char *eresult)
+{
+    char *pp;
+    struct mailname *mp;
+#ifndef        DUMB
+    char *dp;
+#endif /* not DUMB */
+
+    if (err && err[0]) {
+       if (eresult)
+           strcpy (eresult, err);
+       else
+           if (wanthost == AD_HOST)
+               admonish (NULL, "bad address '%s' - %s", str, err);
+       return NULL;
+    }
+    if (pers == NULL
+           && mbox == NULL && host == NULL && route == NULL
+           && grp == NULL) {
+       if (eresult)
+           strcpy (eresult, "null address");
+       else
+           if (wanthost == AD_HOST)
+               admonish (NULL, "null address '%s'", str);
+       return NULL;
+    }
+    if (mbox == NULL && grp == NULL) {
+       if (eresult)
+           strcpy (eresult, "no mailbox in address");
+       else
+           if (wanthost == AD_HOST)
+               admonish (NULL, "no mailbox in address '%s'", str);
+       return NULL;
+    }
+
+    if (dfhost == NULL) {
+       dfhost = LocalName ();
+       dftype = LOCALHOST;
+    }
+
+    mp = (struct mailname *) calloc ((size_t) 1, sizeof(*mp));
+    if (mp == NULL) {
+       if (eresult)
+          strcpy (eresult, "insufficient memory to represent address");
+       else
+           if (wanthost == AD_HOST)
+               adios (NULL, "insufficient memory to represent address");
+       return NULL;
+    }
+
+    mp->m_next = NULL;
+    mp->m_text = getcpy (str);
+    if (pers)
+       mp->m_pers = getcpy (pers);
+
+    if (mbox == NULL) {
+       mp->m_type = BADHOST;
+       mp->m_nohost = 1;
+       mp->m_ingrp = ingrp;
+       mp->m_gname = getcpy (grp);
+       if (note)
+           mp->m_note = getcpy (note);
+       return mp;
+    }
+
+    if (host) {
+       mp->m_mbox = getcpy (mbox);
+       mp->m_host = getcpy (host);
+    }
+    else {
+       if ((pp = strchr(mbox, '!'))) {
+           *pp++ = '\0';
+           mp->m_mbox = getcpy (pp);
+           mp->m_host = getcpy (mbox);
+           mp->m_type = UUCPHOST;
+       }
+       else {
+           mp->m_nohost = 1;
+           mp->m_mbox = getcpy (mbox);
+#ifdef DUMB
+           if (route == NULL && dftype == LOCALHOST) {
+               mp->m_host = NULL;
+               mp->m_type = dftype;
+           }
+           else
+#endif /* DUMB */
+           {
+               mp->m_host = route ? NULL : getcpy (dfhost);
+               mp->m_type = route ? NETHOST : dftype;
+           }
+       }
+       goto got_host;
+    }
+
+    if (wanthost == AD_NHST)
+       mp->m_type = !strcasecmp (LocalName (), mp->m_host)
+           ? LOCALHOST : NETHOST;
+#ifdef DUMB
+    else
+       mp->m_type = strcasecmp (LocalName(), mp->m_host) ?  NETHOST : LOCALHOST;
+#else /* not DUMB */
+    else
+       if (pp = OfficialName (mp->m_host)) {
+    got_real_host: ;
+           free (mp->m_host);
+           mp->m_host = getcpy (pp);
+           mp->m_type = strcasecmp (LocalName(), mp->m_host) ? NETHOST : LOCALHOST;
+       }
+       else {
+           if (dp = strchr(mp->m_host, '.')) {
+               *dp = NULL;
+               if (pp = OfficialName (mp->m_host))
+                   goto got_real_host;
+               *dp = '.';
+           }
+           mp->m_type = BADHOST;
+       }
+#endif /* not DUMB */
+
+got_host: ;
+    if (route)
+       mp->m_path = getcpy (route);
+    mp->m_ingrp = ingrp;
+    if (grp)
+       mp->m_gname = getcpy (grp);
+    if (note)
+       mp->m_note = getcpy (note);
+
+    return mp;
+}
+
+
+void
+mnfree (struct mailname *mp)
+{
+    if (!mp)
+       return;
+
+    if (mp->m_text)
+       free (mp->m_text);
+    if (mp->m_pers)
+       free (mp->m_pers);
+    if (mp->m_mbox)
+       free (mp->m_mbox);
+    if (mp->m_host)
+       free (mp->m_host);
+    if (mp->m_path)
+       free (mp->m_path);
+    if (mp->m_gname)
+       free (mp->m_gname);
+    if (mp->m_note)
+       free (mp->m_note);
+
+    free ((char *) mp);
+}
+
+
+#define empty(s) ((s) ? (s) : "")
+
+char *
+auxformat (struct mailname *mp, int extras)
+{
+    static char addr[BUFSIZ];
+    static char buffer[BUFSIZ];
+
+#ifdef DUMB
+       if (mp->m_nohost)
+           strncpy (addr, mp->m_mbox ? mp->m_mbox : "", sizeof(addr));
+       else
+#endif /* DUMB */
+
+#ifndef        BANG
+       if (mp->m_type != UUCPHOST)
+           snprintf (addr, sizeof(addr), mp->m_host ? "%s%s@%s" : "%s%s",
+               empty(mp->m_path), empty(mp->m_mbox), mp->m_host);
+       else
+#endif /* not BANG */
+           snprintf (addr, sizeof(addr), "%s!%s", mp->m_host, mp->m_mbox);
+
+    if (!extras)
+       return addr;
+
+    if (mp->m_pers || mp->m_path)
+       if (mp->m_note)
+           snprintf (buffer, sizeof(buffer), "%s %s <%s>",
+                   legal_person (mp->m_pers ? mp->m_pers : mp->m_mbox),
+                   mp->m_note, addr);
+       else
+           snprintf (buffer, sizeof(buffer), "%s <%s>",
+                   legal_person (mp->m_pers ? mp->m_pers : mp->m_mbox),
+                   addr);
+    else
+       if (mp->m_note)
+           snprintf (buffer, sizeof(buffer), "%s %s", addr, mp->m_note);
+       else
+           strncpy (buffer, addr, sizeof(buffer));
+
+    return buffer;
+}
+
+
+/*
+ * address specific "sprintf"
+ */
+
+char *
+adrsprintf (char *local, char *domain)
+{
+    static char addr[BUFSIZ];
+
+    if (local == NULL)
+#ifdef REALLYDUMB
+       return getusername ();
+    else
+#endif /* REALLYDUMB */
+       local = getusername ();
+
+    if (domain == NULL)
+#ifdef REALLYDUMB
+       return local;
+    else
+#endif /* REALLYDUMB */
+       domain = LocalName ();
+
+#ifndef        BANG
+    snprintf (addr, sizeof(addr), "%s@%s", local, domain);
+#else /* BANG */
+    snprintf (addr, sizeof(addr), "%s!%s", domain, local);
+#endif /* BANG */
+
+    return addr;
+}
+
+
+#define        W_NIL   0x0000
+#define        W_MBEG  0x0001
+#define        W_MEND  0x0002
+#define        W_MBOX  (W_MBEG | W_MEND)
+#define        W_HBEG  0x0004
+#define        W_HEND  0x0008
+#define        W_HOST  (W_HBEG | W_HEND)
+#define        WBITS   "\020\01MBEG\02MEND\03HBEG\04HEND"
+
+/*
+ * Check if this is my address
+ */
+
+int
+ismymbox (struct mailname *np)
+{
+    int oops;
+    register int len, i;
+    register char *cp;
+    register char *pp;
+    char buffer[BUFSIZ];
+    struct mailname *mp;
+    static char *am = NULL;
+    static struct mailname mq={NULL};
+
+    /*
+     * If this is the first call, initialize
+     * list of alternate mailboxes.
+     */
+    if (am == NULL) {
+       mq.m_next = NULL;
+       mq.m_mbox = getusername ();
+       if ((am = context_find ("alternate-mailboxes")) == NULL)
+           am = getusername();
+       else {
+           mp = &mq;
+           oops = 0;
+           while ((cp = getname (am))) {
+               if ((mp->m_next = getm (cp, NULL, 0, AD_NAME, NULL)) == NULL) {
+                   admonish (NULL, "illegal address: %s", cp);
+                   oops++;
+               } else {
+                   mp = mp->m_next;
+                   mp->m_type = W_NIL;
+                   if (*mp->m_mbox == '*') {
+                       mp->m_type |= W_MBEG;
+                       mp->m_mbox++;
+                   }
+                   if (*(cp = mp->m_mbox + strlen (mp->m_mbox) - 1) == '*') {
+                       mp->m_type |= W_MEND;
+                       *cp = '\0';
+                   }
+                   if (mp->m_host) {
+                       if (*mp->m_host == '*') {
+                           mp->m_type |= W_HBEG;
+                           mp->m_host++;
+                       }
+                       if (*(cp = mp->m_host + strlen (mp->m_host) - 1) == '*') {
+                           mp->m_type |= W_HEND;
+                           *cp = '\0';
+                       }
+                   }
+                   if ((cp = getenv ("MHWDEBUG")) && *cp)
+                       fprintf (stderr, "mbox=\"%s\" host=\"%s\" %s\n",
+                           mp->m_mbox, mp->m_host,
+                           snprintb (buffer, sizeof(buffer), (unsigned) mp->m_type, WBITS));
+               }
+           }
+           if (oops)
+               advise (NULL, "please fix the %s: entry in your %s file",
+                       "alternate-mailboxes", mh_profile);
+       }
+    }
+
+    if (np == NULL) /* XXX */
+       return 0;
+    
+    switch (np->m_type) {
+       case NETHOST:
+           len = strlen (cp = LocalName ());
+           if (!uprf (np->m_host, cp) || np->m_host[len] != '.')
+               break;
+           goto local_test;
+
+       case UUCPHOST:
+           if (strcasecmp (np->m_host, SystemName()))
+               break;          /* fall */
+       case LOCALHOST:
+local_test: ;
+           if (!strcasecmp (np->m_mbox, mq.m_mbox))
+               return 1;
+           break;
+
+       default:
+           break;
+    }
+
+    /*
+     * Now scan through list of alternate
+     * mailboxes, and check for a match.
+     */
+    for (mp = &mq; mp->m_next;) {
+       mp = mp->m_next;
+       if (!np->m_mbox)
+           continue;
+       if ((len = strlen (cp = np->m_mbox))
+               < (i = strlen (pp = mp->m_mbox)))
+           continue;
+       switch (mp->m_type & W_MBOX) {
+           case W_NIL: 
+               if (strcasecmp (cp, pp))
+                   continue;
+               break;
+           case W_MBEG: 
+               if (strcasecmp (cp + len - i, pp))
+                   continue;
+               break;
+           case W_MEND: 
+               if (!uprf (cp, pp))
+                   continue;
+               break;
+           case W_MBEG | W_MEND: 
+               if (stringdex (pp, cp) < 0)
+                   continue;
+               break;
+       }
+
+       if (mp->m_nohost)
+           return 1;
+       if (np->m_host == NULL)
+           continue;
+       if ((len = strlen (cp = np->m_host))
+               < (i = strlen (pp = mp->m_host)))
+           continue;
+       switch (mp->m_type & W_HOST) {
+           case W_NIL: 
+               if (strcasecmp (cp, pp))
+                   continue;
+               break;
+           case W_HBEG: 
+               if (strcasecmp (cp + len - i, pp))
+                   continue;
+               break;
+           case W_HEND: 
+               if (!uprf (cp, pp))
+                   continue;
+               break;
+           case W_HBEG | W_HEND: 
+               if (stringdex (pp, cp) < 0)
+                   continue;
+               break;
+       }
+       return 1;
+    }
+
+    return 0;
+}
diff --git a/sbr/ambigsw.c b/sbr/ambigsw.c
new file mode 100644 (file)
index 0000000..48b7328
--- /dev/null
@@ -0,0 +1,16 @@
+
+/*
+ * ambigsw.c -- report an ambiguous switch
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+void
+ambigsw (char *arg, struct swit *swp)
+{
+    advise (NULL, "-%s ambiguous.  It matches", arg);
+    print_sw (arg, swp, "-");
+}
diff --git a/sbr/atooi.c b/sbr/atooi.c
new file mode 100644 (file)
index 0000000..ab58cb4
--- /dev/null
@@ -0,0 +1,24 @@
+
+/*
+ * atooi.c -- octal version of atoi()
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+int
+atooi(char *cp)
+{
+    register int i, base;
+
+    i = 0;
+    base = 8;
+    while (*cp >= '0' && *cp <= '7') {
+       i *= base;
+       i += *cp++ - '0';
+    }
+
+    return i;
+}
diff --git a/sbr/brkstring.c b/sbr/brkstring.c
new file mode 100644 (file)
index 0000000..715aaf5
--- /dev/null
@@ -0,0 +1,91 @@
+
+/*
+ * brkstring.c -- (destructively) split a string into
+ *             -- an array of substrings
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+/* allocate this number of pointers at a time */
+#define NUMBROKEN 256
+
+static char **broken = NULL;   /* array of substring start addresses */
+static int len = 0;            /* current size of "broken"           */
+
+/*
+ * static prototypes
+ */
+static int brkany (char, char *);
+
+
+char **
+brkstring (char *str, char *brksep, char *brkterm)
+{
+    int i;
+    char c, *s;
+
+    /* allocate initial space for pointers on first call */
+    if (!broken) {
+       len = NUMBROKEN;
+       if (!(broken = (char **) malloc ((size_t) (len * sizeof(*broken)))))
+           adios (NULL, "unable to malloc array in brkstring");
+    }
+
+    /*
+     * scan string, replacing separators with zeroes
+     * and enter start addresses in "broken".
+     */
+    s = str;
+
+    for (i = 0;; i++) {
+
+       /* enlarge pointer array, if necessary */
+       if (i >= len) {
+           len += NUMBROKEN;
+           if (!(broken = realloc (broken, (size_t) (len * sizeof(*broken)))))
+               adios (NULL, "unable to realloc array in brkstring");
+       }
+
+       while (brkany (c = *s, brksep))
+           *s++ = '\0';
+
+       /*
+        * we are either at the end of the string, or the
+        * terminator found has been found, so finish up.
+        */
+       if (!c || brkany (c, brkterm)) {
+           *s = '\0';
+           broken[i] = NULL;
+           return broken;
+       }
+
+       /* set next start addr */
+       broken[i] = s;
+
+       while ((c = *++s) && !brkany (c, brksep) && !brkany (c, brkterm))
+           ;   /* empty body */
+    }
+
+    return broken;     /* NOT REACHED */
+}
+
+
+/*
+ * If the character is in the string,
+ * return 1, else return 0.
+ */
+
+static int
+brkany (char c, char *str)
+{
+    char *s;
+
+    if (str) {
+       for (s = str; *s; s++)
+           if (c == *s)
+               return 1;
+    }
+    return 0;
+}
diff --git a/sbr/check_charset.c b/sbr/check_charset.c
new file mode 100644 (file)
index 0000000..e16f360
--- /dev/null
@@ -0,0 +1,65 @@
+
+/*
+ * check_charset.c -- routines for character sets
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+/*
+ * Check if we can display a given character set natively.
+ * We are passed the length of the initial part of the
+ * string to check, since we want to allow the name of the
+ * character set to be a substring of a larger string.
+ */
+
+int
+check_charset (char *str, int len) 
+{
+    static char *mm_charset = NULL;
+    static char *alt_charset = NULL;
+    static int mm_len;
+    static int alt_len;
+
+    /* Cache the name of our default character set */
+    if (!mm_charset) {
+       if (!(mm_charset = getenv ("MM_CHARSET")))
+           mm_charset = "US-ASCII";
+       mm_len = strlen (mm_charset);
+
+       /* US-ASCII is a subset of the ISO-8859-X character sets */
+       if (!strncasecmp("ISO-8859-", mm_charset, 9)) {
+           alt_charset = "US-ASCII";
+           alt_len = strlen (alt_charset);
+       }
+    }
+
+    /* Check if character set is OK */
+    if ((len == mm_len) && !strncasecmp(str, mm_charset, mm_len))
+       return 1;
+    if (alt_charset && (len == alt_len) && !strncasecmp(str, alt_charset, alt_len))
+       return 1;
+
+    return 0;
+}
+
+
+/*
+ * Return the name of the character set we are
+ * using for 8bit text.
+ */
+char *
+write_charset_8bit (void)
+{
+    static char *mm_charset = NULL;
+
+    /*
+     * Cache the name of the character set to
+     * use for 8bit text.
+     */
+    if (!mm_charset && !(mm_charset = getenv ("MM_CHARSET")))
+           mm_charset = "x-unknown";
+
+    return mm_charset;
+}
diff --git a/sbr/closefds.c b/sbr/closefds.c
new file mode 100644 (file)
index 0000000..86f91a7
--- /dev/null
@@ -0,0 +1,18 @@
+
+/*
+ * closefds.c -- close-up fd's
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+void
+closefds(int i)
+{
+    int nbits = OPEN_MAX;
+
+    for (; i < nbits; i++)
+       close (i);
+}
diff --git a/sbr/concat.c b/sbr/concat.c
new file mode 100644 (file)
index 0000000..939a484
--- /dev/null
@@ -0,0 +1,36 @@
+
+/*
+ * concat.c -- concatenate a variable number (minimum of 1)
+ *             of strings in managed memory
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+char *
+concat (char *s1, ...)
+{
+    char *cp, *dp, *sp;
+    size_t len;
+    va_list list;
+
+    len = strlen (s1) + 1;
+    va_start(list, s1); 
+    while ((cp = va_arg(list, char *)))
+       len += strlen (cp);
+    va_end(list);
+
+    if (!(dp = sp = malloc(len)))
+       adios (NULL, "unable to allocate string storage");
+
+    sp = copy(s1, sp);
+
+    va_start(list, s1); 
+    while ((cp = va_arg (list, char *)))
+       sp = copy(cp, sp);
+    va_end(list);
+
+    return dp;
+}
diff --git a/sbr/context_del.c b/sbr/context_del.c
new file mode 100644 (file)
index 0000000..702c15d
--- /dev/null
@@ -0,0 +1,42 @@
+
+/*
+ * context_del.c -- delete an entry from the context/profile list
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+/*
+ * Delete a key/value pair from the context/profile list.
+ * Return 0 if key is found, else return 1.
+ */
+
+int
+context_del (char *key)
+{
+    register struct node *np, *pp;
+
+    /* sanity check - check that context has been read */
+    if (defpath == NULL)
+       adios (NULL, "oops, context hasn't been read yet");
+
+    for (np = m_defs, pp = NULL; np; pp = np, np = np->n_next) {
+       if (!strcasecmp (np->n_name, key)) {
+           if (!np->n_context)
+               admonish (NULL, "bug: context_del(key=\"%s\")", np->n_name);
+           if (pp)
+               pp->n_next = np->n_next;
+           else
+               m_defs = np->n_next;
+           free (np->n_name);
+           if (np->n_field)
+               free (np->n_field);
+           free ((char *) np);
+           ctxflags |= CTXMOD;
+           return 0;
+       }
+    }
+
+    return 1;
+}
diff --git a/sbr/context_find.c b/sbr/context_find.c
new file mode 100644 (file)
index 0000000..1e32401
--- /dev/null
@@ -0,0 +1,25 @@
+
+/*
+ * context_find.c -- find an entry in the context/profile list
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+char *
+context_find (char *str)
+{
+    struct node *np;
+
+    /* sanity check - check that context has been read */
+    if (defpath == NULL)
+       adios (NULL, "oops, context hasn't been read yet");
+
+    for (np = m_defs; np; np = np->n_next)
+       if (!strcasecmp (np->n_name, str))
+           return (np->n_field);
+
+    return NULL;
+}
diff --git a/sbr/context_foil.c b/sbr/context_foil.c
new file mode 100644 (file)
index 0000000..208c45e
--- /dev/null
@@ -0,0 +1,52 @@
+
+/*
+ * context_foil.c -- foil search of profile and context
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+/*
+ * Foil search of users .mh_profile
+ * If error, return -1, else return 0
+ */
+
+int
+context_foil (char *path)
+{
+    register struct node *np;
+
+    defpath = context = "/dev/null";
+
+    /*
+     * If path is given, create a minimal profile/context list
+     */
+    if (path) {
+       if (!(m_defs = (struct node *) malloc (sizeof(*np)))) {
+           advise (NULL, "unable to allocate profile storage");
+           return -1;
+       }
+
+       np = m_defs;
+       if (!(np->n_name = strdup ("Path"))) {
+           advise (NULL, "strdup failed");
+           return -1;
+       }
+       if (!(np->n_field = strdup (path))) {
+           advise (NULL, "strdup failed");
+           return -1;
+       }
+       np->n_context = 0;
+       np->n_next = NULL;
+
+       if (mypath == NULL && (mypath = getenv ("HOME")) != NULL)
+           if (!(mypath = strdup (mypath))) {
+               advise (NULL, "strdup failed");
+               return -1;
+           }
+    }
+
+    return 0;
+}
+
diff --git a/sbr/context_read.c b/sbr/context_read.c
new file mode 100644 (file)
index 0000000..d2836ba
--- /dev/null
@@ -0,0 +1,111 @@
+
+/*
+ * context_read.c -- find and read profile and context files
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <errno.h>
+#include <pwd.h>
+
+extern int errno;
+
+void
+context_read (void)
+{
+    pid_t pid;
+    register char *cp, *pp;
+    char buf[BUFSIZ];
+    struct stat st;
+    register struct passwd *pw;
+    register FILE *ib;
+
+    if (defpath)
+       return;
+
+    /*
+     * Find user's home directory
+     */
+    if (!mypath) {
+       if ((mypath = getenv ("HOME")))
+           mypath = getcpy (mypath);
+       else
+           if ((pw = getpwuid (getuid ())) == NULL
+                   || pw->pw_dir == NULL
+                   || *pw->pw_dir == 0)
+               adios (NULL, "no HOME envariable");
+           else
+               mypath = getcpy (pw->pw_dir);
+       if ((cp = mypath + strlen (mypath) - 1) > mypath && *cp == '/')
+           *cp = 0;
+    }
+
+    /*
+     * open and read user's profile
+     */
+    if ((cp = getenv ("MH")) && *cp != '\0') {
+       defpath = path (cp, TFILE);
+       if ((ib = fopen (defpath, "r")) == NULL)
+           adios (defpath, "unable to read");
+       if (*cp != '/')
+           m_putenv ("MH", defpath);
+    } else {
+       defpath = concat (mypath, "/", mh_profile, NULL);
+
+       if ((ib = fopen (defpath, "r")) == NULL) {
+           switch (pid = vfork ()) {
+               case -1:
+                   adios ("fork", "unable to");
+
+               case 0:
+                   setgid (getgid ());
+                   setuid (getuid ());
+
+                   execlp (installproc, "install-mh", "-auto", NULL);
+                   fprintf (stderr, "unable to exec ");
+                   perror (installproc);
+                   _exit (-1);
+
+               default:
+                   if (pidwait (pid, 0)
+                           || (ib = fopen (defpath, "r")) == NULL)
+                       adios (NULL, "[install-mh aborted]");
+           }
+       }
+    }
+    readconfig (&m_defs, ib, mh_profile, 0);
+    fclose (ib);
+
+    /*
+     * Find user's nmh directory
+     */
+    if ((pp = context_find ("path")) && *pp != '\0') {
+       if (*pp != '/')
+           snprintf (buf, sizeof(buf), "%s/%s", mypath, pp);
+       else
+           strncpy (buf, pp, sizeof(buf));
+       if (stat(buf, &st) == -1) {
+           if (errno != ENOENT)
+               adios (buf, "error opening");
+           cp = concat ("Your MH-directory \"", buf,
+               "\" doesn't exist; Create it? ", NULL);
+           if (!getanswer(cp))
+               adios (NULL, "unable to access MH-directory \"%s\"", buf);
+           free (cp);
+           if (!makedir (buf))
+               adios (NULL, "unable to create", buf);
+       }
+    }
+
+    /*
+     * open and read user's context file
+     */
+    if (!(cp = getenv ("MHCONTEXT")) || *cp == '\0')
+       cp = context;
+    ctxpath = getcpy (m_maildir (cp));
+    if ((ib = fopen (ctxpath, "r"))) {
+       readconfig ((struct node **) 0, ib, cp, 1);
+       fclose (ib);
+    }
+}
diff --git a/sbr/context_replace.c b/sbr/context_replace.c
new file mode 100644 (file)
index 0000000..435127b
--- /dev/null
@@ -0,0 +1,69 @@
+
+/*
+ * context_replace.c -- add/replace an entry in the context/profile list
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+void
+context_replace (char *key, char *value)
+{
+    register struct node *np;
+
+    /* sanity check - check that context has been read */
+    if (defpath == NULL)
+       adios (NULL, "oops, context hasn't been read yet");
+
+    /*
+     * If list is emtpy, allocate head of profile/context list.
+     */
+    if (!m_defs) {
+       if (!(m_defs = (struct node *) malloc (sizeof(*np))))
+           adios (NULL, "unable to allocate profile storage");
+
+       np = m_defs;
+       np->n_name = getcpy (key);
+       np->n_field = getcpy (value);
+       np->n_context = 1;
+       np->n_next = NULL;
+       ctxflags |= CTXMOD;
+       return;
+    }
+
+    /*
+     * Search list of context/profile entries for
+     * this key, and replace its value if found.
+     */
+    for (np = m_defs;; np = np->n_next) {
+       if (!strcasecmp (np->n_name, key)) {
+           if (strcmp (value, np->n_field)) {
+               if (!np->n_context)
+                   admonish (NULL, "bug: context_replace(key=\"%s\",value=\"%s\")", key, value);
+               if (np->n_field)
+                   free (np->n_field);
+               np->n_field = getcpy (value);
+               ctxflags |= CTXMOD;
+           }
+           return;
+       }
+       if (!np->n_next)
+           break;
+    }
+
+    /*
+     * Else add this new entry at the end
+     */
+    np->n_next = (struct node *) malloc (sizeof(*np));
+    if (!np->n_next)
+       adios (NULL, "unable to allocate profile storage");
+
+    np = np->n_next;
+    np->n_name = getcpy (key);
+    np->n_field = getcpy (value);
+    np->n_context = 1;
+    np->n_next = NULL;
+    ctxflags |= CTXMOD;
+}
diff --git a/sbr/context_save.c b/sbr/context_save.c
new file mode 100644 (file)
index 0000000..b3f8168
--- /dev/null
@@ -0,0 +1,90 @@
+
+/*
+ * context_save.c -- write out the updated context file
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/signals.h>
+
+/*
+ * static prototypes
+ */
+static int m_chkids(void);
+
+
+void
+context_save (void)
+{
+    int action;
+    register struct node *np;
+    FILE *out;
+    sigset_t set, oset;
+
+    if (!(ctxflags & CTXMOD))
+       return;
+    ctxflags &= ~CTXMOD;
+
+    if ((action = m_chkids ()) > 0)
+       return;                 /* child did it for us */
+
+    /* block a few signals */
+    sigemptyset (&set);
+    sigaddset (&set, SIGHUP);
+    sigaddset (&set, SIGINT);
+    sigaddset (&set, SIGQUIT);
+    sigaddset (&set, SIGTERM);
+    SIGPROCMASK (SIG_BLOCK, &set, &oset);
+
+    if (!(out = fopen (ctxpath, "w")))
+       adios (ctxpath, "unable to write");
+    for (np = m_defs; np; np = np->n_next)
+       if (np->n_context)
+           fprintf (out, "%s: %s\n", np->n_name, np->n_field);
+    fclose (out);
+
+    SIGPROCMASK (SIG_SETMASK, &oset, &set); /* reset the signal mask */
+
+    if (action == 0)
+       _exit (0);              /* we are child, time to die */
+}
+
+/*
+ * This hack brought to you so we can handle set[ug]id MH programs.
+ * If we return -1, then no fork is made, we update .mh_profile
+ * normally, and return to the caller normally.  If we return 0,
+ * then the child is executing, .mh_profile is modified after
+ * we set our [ug]ids to the norm.  If we return > 0, then the
+ * parent is executed and .mh_profile has already be modified.
+ * We can just return to the caller immediately.
+ */
+
+static int
+m_chkids (void)
+{
+    int i;
+    pid_t pid;
+
+    if (getuid () == geteuid ())
+       return (-1);
+
+    for (i = 0; (pid = fork ()) == -1 && i < 5; i++)
+       sleep (5);
+
+    switch (pid) {
+       case -1:
+           break;
+
+       case 0:
+           setgid (getgid ());
+           setuid (getuid ());
+           break;
+
+       default:
+           pidwait (pid, -1);
+           break;
+    }
+
+    return pid;
+}
diff --git a/sbr/copy.c b/sbr/copy.c
new file mode 100644 (file)
index 0000000..577e811
--- /dev/null
@@ -0,0 +1,18 @@
+
+/*
+ * copy.c -- copy a string and return pointer to NULL terminator
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+char *
+copy(char *from, char *to)
+{
+    while ((*to = *from)) {
+       to++;
+       from++;
+    }
+    return (to);
+}
diff --git a/sbr/copyip.c b/sbr/copyip.c
new file mode 100644 (file)
index 0000000..8d7b6f5
--- /dev/null
@@ -0,0 +1,20 @@
+
+/*
+ * copyip.c -- copy a string array and return pointer to end
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+char **
+copyip (char **p, char **q, int len_q)
+{
+    while (*p && --len_q > 0)
+       *q++ = *p++;
+
+    *q = NULL;
+
+    return q;
+}
diff --git a/sbr/cpydata.c b/sbr/cpydata.c
new file mode 100644 (file)
index 0000000..a8c3375
--- /dev/null
@@ -0,0 +1,23 @@
+
+/*
+ * cpydata.c -- copy all data from one fd to another
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+void
+cpydata (int in, int out, char *ifile, char *ofile)
+{
+    int i;
+    char buffer[BUFSIZ];
+
+    while ((i = read(in, buffer, sizeof(buffer))) > 0) {
+       if (write(out, buffer, i) != i)
+           adios(ofile, "error writing");
+    }
+
+    if (i == -1)
+       adios(ifile, "error reading");
+}
diff --git a/sbr/cpydgst.c b/sbr/cpydgst.c
new file mode 100644 (file)
index 0000000..068fb6e
--- /dev/null
@@ -0,0 +1,65 @@
+
+/*
+ * cpydgst.c -- copy from one fd to another in encapsulating mode
+ *           -- (do dashstuffing of input data).
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+/*
+ * We want to perform the substitution
+ *
+ *     \n(-.*)\n      -->       \n- \1\n
+ *
+ * This is equivalent to the sed substitution
+ *
+ *     sed -e 's%^-%- -%' < ifile > ofile
+ *
+ *  but the routine below is faster than the pipe, fork, and exec.
+ */
+
+#define        S1 0
+#define        S2 1
+
+#define        output(c)   if (bp >= dp) {flush(); *bp++ = c;} else *bp++ = c
+#define        flush()     if ((j = bp - outbuf) && write (out, outbuf, j) != j) \
+                       adios (ofile, "error writing"); \
+                   else \
+                       bp = outbuf
+
+
+void
+cpydgst (int in, int out, char *ifile, char *ofile)
+{
+    register int i, j, state;
+    register char *cp, *ep;
+    register char *bp, *dp;
+    char buffer[BUFSIZ], outbuf[BUFSIZ];
+
+    dp = (bp = outbuf) + sizeof outbuf;
+    for (state = S1; (i = read (in, buffer, sizeof buffer)) > 0;)
+       for (ep = (cp = buffer) + i; cp < ep; cp++) {
+           if (*cp == '\0')
+               continue;
+           switch (state) {
+               case S1: 
+                   if (*cp == '-') {
+                       output ('-');
+                       output (' ');
+                   }
+                   state = S2; /* fall */
+
+               case S2: 
+                   output (*cp);
+                   if (*cp == '\n')
+                       state = S1;
+                   break;
+           }
+       }
+
+    if (i == -1)
+       adios (ifile, "error reading");
+    flush();
+}
diff --git a/sbr/discard.c b/sbr/discard.c
new file mode 100644 (file)
index 0000000..fffc0fa
--- /dev/null
@@ -0,0 +1,65 @@
+
+/*
+ * discard.c -- discard output on a file pointer
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+#ifdef HAVE_TERMIOS_H
+# include <termios.h>
+#else
+# ifdef HAVE_TERMIO_H
+#  include <termio.h>
+# else
+#  include <sgtty.h>
+# endif
+#endif
+
+#ifdef SCO_5_STDIO
+# define _ptr  __ptr
+# define _cnt  __cnt
+# define _base __base
+# define _filbuf(fp)  ((fp)->__cnt = 0, __filbuf(fp))
+#endif
+
+
+void
+discard (FILE *io)
+{
+#ifndef HAVE_TERMIOS_H
+# ifdef HAVE_TERMIO_H
+    struct termio tio;
+# else
+    struct sgttyb tio;
+# endif
+#endif
+
+    if (io == NULL)
+       return;
+
+#ifdef HAVE_TERMIOS_H
+    tcflush (fileno(io), TCOFLUSH);
+#else
+# ifdef HAVE_TERMIO_H
+    if (ioctl (fileno(io), TCGETA, &tio) != -1)
+       ioctl (fileno(io), TCSETA, &tio);
+# else
+    if (ioctl (fileno(io), TIOCGETP, (char *) &tio) != -1)
+       ioctl (fileno(io), TIOCSETP, (char *) &tio);
+# endif
+#endif
+
+#ifdef _FSTDIO
+    fpurge (io);
+#else
+# ifdef LINUX_STDIO
+    io->_IO_write_ptr = io->_IO_write_base;
+# else
+    if ((io->_ptr = io->_base))
+       io->_cnt = 0;
+# endif
+#endif
+}
+
diff --git a/sbr/done.c b/sbr/done.c
new file mode 100644 (file)
index 0000000..02465ea
--- /dev/null
@@ -0,0 +1,14 @@
+
+/*
+ * done.c -- terminate the program
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+void
+done (int status)
+{
+    exit (status);
+}
diff --git a/sbr/error.c b/sbr/error.c
new file mode 100644 (file)
index 0000000..0e02f2a
--- /dev/null
@@ -0,0 +1,140 @@
+
+/*
+ * error.c -- main error handling routines
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+#ifdef HAVE_WRITEV
+# include <sys/types.h>
+# include <sys/uio.h>
+#endif
+
+extern int errno;
+
+
+/*
+ * print out error message
+ */
+void
+advise (char *what, char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    advertise (what, NULL, fmt, ap);
+    va_end(ap);
+}
+
+
+/*
+ * print out error message and exit
+ */
+void
+adios (char *what, char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    advertise (what, NULL, fmt, ap);
+    va_end(ap);
+    done (1);
+}
+
+
+/*
+ * admonish the user
+ */
+void
+admonish (char *what, char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    advertise (what, "continuing...", fmt, ap);
+    va_end(ap);
+}
+
+
+/*
+ * main routine for printing error messages.
+ *
+ * Use writev() if available, for slightly better performance.
+ * Why?  Well, there are a couple of reasons.  Primarily, it
+ * gives a smoother output...  More importantly though, it's a
+ * sexy syscall()...
+ */
+void
+advertise (char *what, char *tail, char *fmt, va_list ap)
+{
+    int        eindex = errno;
+
+#ifdef HAVE_WRITEV
+    char buffer[BUFSIZ], err[BUFSIZ];
+    struct iovec iob[20], *iov;
+#endif
+
+    fflush (stdout);
+
+#ifdef HAVE_WRITEV
+    fflush (stderr);
+    iov = iob;
+
+    if (invo_name && *invo_name) {
+       iov->iov_len = strlen (iov->iov_base = invo_name);
+       iov++;
+       iov->iov_len = strlen (iov->iov_base = ": ");
+       iov++;
+    }
+    
+    vsnprintf (buffer, sizeof(buffer), fmt, ap);
+    iov->iov_len = strlen (iov->iov_base = buffer);
+    iov++;
+    if (what) {
+       if (*what) {
+           iov->iov_len = strlen (iov->iov_base = " ");
+           iov++;
+           iov->iov_len = strlen (iov->iov_base = what);
+           iov++;
+           iov->iov_len = strlen (iov->iov_base = ": ");
+           iov++;
+       }
+        if (!(iov->iov_base = strerror (eindex))) {
+           /* this shouldn't happen, but we'll test for it just in case */
+           snprintf (err, sizeof(err), "Error %d", eindex);
+           iov->iov_base = err;
+       }
+       iov->iov_len = strlen (iov->iov_base);
+       iov++;
+    }
+    if (tail && *tail) {
+       iov->iov_len = strlen (iov->iov_base = ", ");
+       iov++;
+       iov->iov_len = strlen (iov->iov_base = tail);
+       iov++;
+    }
+    iov->iov_len = strlen (iov->iov_base = "\n");
+    iov++;
+    writev (fileno (stderr), iob, iov - iob);
+#else
+    if (invo_name && *invo_name)
+       fprintf (stderr, "%s: ", invo_name);
+    vfprintf (stderr, fmt, ap);
+
+    if (what) {
+       char *s;
+
+       if (*what)
+           fprintf (stderr, " %s: ", what);
+       if ((s = strerror(eindex)))
+           fprintf (stderr, "%s", s);
+       else
+           fprintf (stderr, "Error %d", eindex);
+    }
+    if (tail)
+       fprintf (stderr, ", %s", tail);
+    fputc ('\n', stderr);
+#endif
+}
diff --git a/sbr/fdcompare.c b/sbr/fdcompare.c
new file mode 100644 (file)
index 0000000..3956a7b
--- /dev/null
@@ -0,0 +1,38 @@
+
+/*
+ * fdcompare.c -- are two files identical?
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+int
+fdcompare (int fd1, int fd2)
+{
+    register int i, n1, n2, resp;
+    register char *c1, *c2;
+    char b1[BUFSIZ], b2[BUFSIZ];
+
+    resp = 1;
+    while ((n1 = read (fd1, b1, sizeof(b1))) >= 0
+           && (n2 = read (fd2, b2, sizeof(b2))) >= 0
+           && n1 == n2) {
+       c1 = b1;
+       c2 = b2;
+       for (i = n1 < sizeof(b1) ? n1 : sizeof(b1); i--;)
+           if (*c1++ != *c2++) {
+               resp = 0;
+               goto leave;
+           }
+       if (n1 < sizeof(b1))
+           goto leave;
+    }
+    resp = 0;
+
+leave: ;
+    lseek (fd1, (off_t) 0, SEEK_SET);
+    lseek (fd2, (off_t) 0, SEEK_SET);
+    return resp;
+}
diff --git a/sbr/fmt_addr.c b/sbr/fmt_addr.c
new file mode 100644 (file)
index 0000000..782eb0a
--- /dev/null
@@ -0,0 +1,115 @@
+
+/*
+ * fmt_addr.c -- format an address field (from fmt_scan)
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/addrsbr.h>
+#include <h/fmt_scan.h>
+
+static char *buf;              /* our current working buffer  */
+static char *bufend;           /* end of working buffer       */
+static char *last_dst;         /* buf ptr at end of last call */
+static unsigned int bufsiz;    /* current size of buf         */
+
+#define BUFINCR 512            /* how much to expand buf when if fills */
+
+#define CPY(s) { cp = (s); while ((*dst++ = *cp++)) ; --dst; }
+
+/* check if there's enough room in buf for str.  add more mem if needed */
+#define CHECKMEM(str) \
+           if ((len = strlen (str)) >= bufend - dst) {\
+               int i = dst - buf;\
+               int n = last_dst - buf;\
+               bufsiz += ((dst + len - bufend) / BUFINCR + 1) * BUFINCR;\
+               buf = realloc (buf, bufsiz);\
+               dst = buf + i;\
+               last_dst = buf + n;\
+               if (! buf)\
+                   adios (NULL, "formataddr: couldn't get buffer space");\
+               bufend = buf + bufsiz;\
+           }
+
+
+/* fmt_scan will call this routine if the user includes the function
+ * "(formataddr {component})" in a format string.  "orig" is the
+ * original contents of the string register.  "str" is the address
+ * string to be formatted and concatenated onto orig.  This routine
+ * returns a pointer to the concatenated address string.
+ *
+ * We try to not do a lot of malloc/copy/free's (which is why we
+ * don't call "getcpy") but still place no upper limit on the
+ * length of the result string.
+ *
+ * This routine is placed in a separate library so it can be
+ * overridden by particular programs (e.g., "replsbr").
+ */
+
+char *
+formataddr (char *orig, char *str)
+{
+    register int len;
+    register int isgroup;
+    register char *dst;
+    register char *cp;
+    register char *sp;
+    register struct mailname *mp = NULL;
+
+    /* if we don't have a buffer yet, get one */
+    if (bufsiz == 0) {
+       buf = malloc (BUFINCR);
+       if (! buf)
+           adios (NULL, "formataddr: couldn't allocate buffer space");
+       last_dst = buf;         /* XXX */
+       bufsiz = BUFINCR - 6;  /* leave some slop */
+       bufend = buf + bufsiz;
+    }
+    /*
+     * If "orig" points to our buffer we can just pick up where we
+     * left off.  Otherwise we have to copy orig into our buffer.
+     */
+    if (orig == buf)
+       dst = last_dst;
+    else if (!orig || !*orig) {
+       dst = buf;
+       *dst = '\0';
+    } else {
+       dst = last_dst;         /* XXX */
+       CHECKMEM (orig);
+       CPY (orig);
+    }
+
+    /* concatenate all the new addresses onto 'buf' */
+    for (isgroup = 0; cp = getname (str); ) {
+       if ((mp = getm (cp, NULL, 0, fmt_norm, NULL)) == NULL)
+           continue;
+
+       if (isgroup && (mp->m_gname || !mp->m_ingrp)) {
+           *dst++ = ';';
+           isgroup = 0;
+       }
+       /* if we get here we're going to add an address */
+       if (dst != buf) {
+           *dst++ = ',';
+           *dst++ = ' ';
+       }
+       if (mp->m_gname) {
+           CHECKMEM (mp->m_gname);
+           CPY (mp->m_gname);
+           isgroup++;
+       }
+       sp = adrformat (mp);
+       CHECKMEM (sp);
+       CPY (sp);
+       mnfree (mp);
+    }
+
+    if (isgroup)
+       *dst++ = ';';
+
+    *dst = '\0';
+    last_dst = dst;
+    return (buf);
+}
diff --git a/sbr/fmt_compile.c b/sbr/fmt_compile.c
new file mode 100644 (file)
index 0000000..339ac0a
--- /dev/null
@@ -0,0 +1,643 @@
+
+/*
+ * fmt_compile.c -- "compile" format strings for fmt_scan
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/addrsbr.h>
+#include <zotnet/tws/tws.h>
+#include <h/fmt_scan.h>
+#include <h/fmt_compile.h>
+
+/*
+ * hash table for deciding if a component is "interesting"
+ */
+struct comp *wantcomp[128];
+
+static struct format *formatvec;       /* array to hold formats */
+static struct format *next_fp;         /* next free format slot */
+static struct format *fp;              /* current format slot   */
+static struct comp *cm;                        /* most recent comp ref  */
+static struct ftable *ftbl;            /* most recent func ref  */
+static int ncomp;
+static int infunction;                 /* function nesting cnt  */
+
+extern struct mailname fmt_mnull;
+
+/* ftable->type (argument type) */
+#define        TF_COMP    0        /* component expected                 */
+#define        TF_NUM     1        /* number expected                    */
+#define        TF_STR     2        /* string expected                    */
+#define        TF_EXPR    3        /* component or func. expected        */
+#define        TF_NONE    4        /* no argument                        */
+#define        TF_MYBOX   5        /* special - get current user's mbox  */
+#define        TF_NOW     6        /* special - get current unix time    */
+#define        TF_EXPR_SV 7        /* like expr but save current str reg */
+#define        TF_NOP     8        /* like expr but no result            */
+
+/* ftable->flags */
+#define        TFL_PUTS   1        /* implicit putstr if top level */
+#define        TFL_PUTN   2        /* implicit putnum if top level */
+
+struct ftable {
+    char *name;                /* function name                  */
+    char type;         /* argument type                  */
+    char f_type;       /* fmt type                       */
+    char extra;                /* arg. type dependent extra info */
+    char flags;
+};
+
+static struct ftable functable[] = {
+     { "nonzero",    TF_EXPR,  FT_V_NE,        FT_IF_V_NE,     0 },
+     { "zero",       TF_EXPR,  FT_V_EQ,        FT_IF_V_EQ,     0 },
+     { "eq",         TF_NUM,   FT_V_EQ,        FT_IF_V_EQ,     0 },
+     { "ne",         TF_NUM,   FT_V_NE,        FT_IF_V_NE,     0 },
+     { "gt",         TF_NUM,   FT_V_GT,        FT_IF_V_GT,     0 },
+     { "null",       TF_EXPR,  FT_S_NULL,      FT_IF_S_NULL,   0 },
+     { "nonnull",    TF_EXPR,  FT_S_NONNULL,   FT_IF_S,        0 },
+     { "match",      TF_STR,   FT_V_MATCH,     FT_IF_MATCH,    0 },
+     { "amatch",     TF_STR,   FT_V_AMATCH,    FT_IF_AMATCH,   0 },
+
+     { "putstr",     TF_EXPR,  FT_STR,         0,              0 },
+     { "putstrf",    TF_EXPR,  FT_STRF,        0,              0 },
+     { "putnum",     TF_EXPR,  FT_NUM,         0,              0 },
+     { "putnumf",    TF_EXPR,  FT_NUMF,        0,              0 },
+     { "putaddr",    TF_STR,   FT_PUTADDR,     0,              0 },
+     { "void",       TF_NOP,   0,              0,              0 },
+
+     { "comp",       TF_COMP,  FT_LS_COMP,     0,              TFL_PUTS },
+     { "lit",        TF_STR,   FT_LS_LIT,      0,              TFL_PUTS },
+     { "getenv",     TF_STR,   FT_LS_GETENV,   0,              TFL_PUTS },
+     { "profile",    TF_STR,   FT_LS_CFIND,    0,              TFL_PUTS },
+     { "decodecomp", TF_COMP,  FT_LS_DECODECOMP, 0,            TFL_PUTS },
+     { "decode",     TF_EXPR,  FT_LS_DECODE,   0,              TFL_PUTS },
+     { "trim",       TF_EXPR,  FT_LS_TRIM,     0,              0 },
+     { "compval",    TF_COMP,  FT_LV_COMP,     0,              TFL_PUTN },
+     { "compflag",   TF_COMP,  FT_LV_COMPFLAG, 0,              TFL_PUTN },
+     { "num",        TF_NUM,   FT_LV_LIT,      0,              TFL_PUTN },
+     { "msg",        TF_NONE,  FT_LV_DAT,      0,              TFL_PUTN },
+     { "cur",        TF_NONE,  FT_LV_DAT,      1,              TFL_PUTN },
+     { "size",       TF_NONE,  FT_LV_DAT,      2,              TFL_PUTN },
+     { "width",      TF_NONE,  FT_LV_DAT,      3,              TFL_PUTN },
+     { "unseen",     TF_NONE,  FT_LV_DAT,      4,              TFL_PUTN },
+     { "dat",        TF_NUM,   FT_LV_DAT,      0,              TFL_PUTN },
+     { "strlen",     TF_NONE,  FT_LV_STRLEN,   0,              TFL_PUTN },
+     { "me",         TF_MYBOX, FT_LS_LIT,      0,              TFL_PUTS },
+     { "plus",       TF_NUM,   FT_LV_PLUS_L,   0,              TFL_PUTN },
+     { "minus",      TF_NUM,   FT_LV_MINUS_L,  0,              TFL_PUTN },
+     { "divide",     TF_NUM,   FT_LV_DIVIDE_L, 0,              TFL_PUTN },
+     { "modulo",     TF_NUM,   FT_LV_MODULO_L, 0,              TFL_PUTN },
+     { "charleft",   TF_NONE,  FT_LV_CHAR_LEFT, 0,             TFL_PUTN },
+     { "timenow",    TF_NOW,   FT_LV_LIT,      0,              TFL_PUTN },
+
+     { "month",      TF_COMP,  FT_LS_MONTH,    FT_PARSEDATE,   TFL_PUTS },
+     { "lmonth",     TF_COMP,  FT_LS_LMONTH,   FT_PARSEDATE,   TFL_PUTS },
+     { "tzone",      TF_COMP,  FT_LS_ZONE,     FT_PARSEDATE,   TFL_PUTS },
+     { "day",        TF_COMP,  FT_LS_DAY,      FT_PARSEDATE,   TFL_PUTS },
+     { "weekday",    TF_COMP,  FT_LS_WEEKDAY,  FT_PARSEDATE,   TFL_PUTS },
+     { "tws",        TF_COMP,  FT_LS_822DATE,  FT_PARSEDATE,   TFL_PUTS },
+     { "sec",        TF_COMP,  FT_LV_SEC,      FT_PARSEDATE,   TFL_PUTN },
+     { "min",        TF_COMP,  FT_LV_MIN,      FT_PARSEDATE,   TFL_PUTN },
+     { "hour",       TF_COMP,  FT_LV_HOUR,     FT_PARSEDATE,   TFL_PUTN },
+     { "mday",       TF_COMP,  FT_LV_MDAY,     FT_PARSEDATE,   TFL_PUTN },
+     { "mon",        TF_COMP,  FT_LV_MON,      FT_PARSEDATE,   TFL_PUTN },
+     { "year",       TF_COMP,  FT_LV_YEAR,     FT_PARSEDATE,   TFL_PUTN },
+     { "yday",       TF_COMP,  FT_LV_YDAY,     FT_PARSEDATE,   TFL_PUTN },
+     { "wday",       TF_COMP,  FT_LV_WDAY,     FT_PARSEDATE,   TFL_PUTN },
+     { "zone",       TF_COMP,  FT_LV_ZONE,     FT_PARSEDATE,   TFL_PUTN },
+     { "clock",      TF_COMP,  FT_LV_CLOCK,    FT_PARSEDATE,   TFL_PUTN },
+     { "rclock",     TF_COMP,  FT_LV_RCLOCK,   FT_PARSEDATE,   TFL_PUTN },
+     { "sday",       TF_COMP,  FT_LV_DAYF,     FT_PARSEDATE,   TFL_PUTN },
+     { "szone",      TF_COMP,  FT_LV_ZONEF,    FT_PARSEDATE,   TFL_PUTN },
+     { "dst",        TF_COMP,  FT_LV_DST,      FT_PARSEDATE,   TFL_PUTN },
+     { "pretty",     TF_COMP,  FT_LS_PRETTY,   FT_PARSEDATE,   TFL_PUTS },
+     { "nodate",     TF_COMP,  FT_LV_COMPFLAG, FT_PARSEDATE,   TFL_PUTN },
+     { "date2local", TF_COMP,  FT_LOCALDATE,   FT_PARSEDATE,   0 },
+     { "date2gmt",   TF_COMP,  FT_GMTDATE,     FT_PARSEDATE,   0 },
+
+     { "pers",       TF_COMP,  FT_LS_PERS,     FT_PARSEADDR,   TFL_PUTS },
+     { "mbox",       TF_COMP,  FT_LS_MBOX,     FT_PARSEADDR,   TFL_PUTS },
+     { "host",       TF_COMP,  FT_LS_HOST,     FT_PARSEADDR,   TFL_PUTS },
+     { "path",       TF_COMP,  FT_LS_PATH,     FT_PARSEADDR,   TFL_PUTS },
+     { "gname",      TF_COMP,  FT_LS_GNAME,    FT_PARSEADDR,   TFL_PUTS },
+     { "note",       TF_COMP,  FT_LS_NOTE,     FT_PARSEADDR,   TFL_PUTS },
+     { "addr",       TF_COMP,  FT_LS_ADDR,     FT_PARSEADDR,   TFL_PUTS },
+     { "proper",     TF_COMP,  FT_LS_822ADDR,  FT_PARSEADDR,   TFL_PUTS },
+     { "type",       TF_COMP,  FT_LV_HOSTTYPE, FT_PARSEADDR,   TFL_PUTN },
+     { "ingrp",      TF_COMP,  FT_LV_INGRPF,   FT_PARSEADDR,   TFL_PUTN },
+     { "nohost",     TF_COMP,  FT_LV_NOHOSTF,  FT_PARSEADDR,   TFL_PUTN },
+     { "formataddr", TF_EXPR_SV,FT_FORMATADDR, FT_FORMATADDR,  0 },
+     { "friendly",   TF_COMP,  FT_LS_FRIENDLY, FT_PARSEADDR,   TFL_PUTS },
+
+     { "mymbox",     TF_COMP,  FT_LV_COMPFLAG, FT_MYMBOX,      TFL_PUTN },
+     { "addtoseq",   TF_STR,   FT_ADDTOSEQ,    0,              0 },
+
+     { NULL,         0,                0,              0,              0 }
+};
+
+/* Add new component to the hash table */
+#define NEWCOMP(cm,name)\
+       cm = ((struct comp *) calloc(1, sizeof (struct comp)));\
+       cm->c_name = name;\
+       ncomp++;\
+       i = CHASH(name);\
+       cm->c_next = wantcomp[i];\
+       wantcomp[i] = cm;
+
+#define NEWFMT (next_fp++)
+#define NEW(type,fill,wid)\
+       fp=NEWFMT; fp->f_type=(type); fp->f_fill=(fill); fp->f_width=(wid);
+
+/* Add (possibly new) component to the hash table */
+#define ADDC(name)\
+       FINDCOMP(cm, name);\
+       if (!cm) {\
+           NEWCOMP(cm,name);\
+       }\
+       fp->f_comp = cm;
+
+#define LV(type, value)                NEW(type,0,0); fp->f_value = (value);
+#define LS(type, str)          NEW(type,0,0); fp->f_text = (str);
+
+#define PUTCOMP(comp)          NEW(FT_COMP,0,0); ADDC(comp);
+#define PUTLIT(str)            NEW(FT_LIT,0,0); fp->f_text = (str);
+#define PUTC(c)                        NEW(FT_CHAR,0,0); fp->f_char = (c);
+
+static char *format_string;
+static char *usr_fstring;      /* for CERROR */
+
+#define CERROR(str) compile_error (str, cp)
+
+/*
+ * external prototypes
+ */
+extern char *getusername(void);
+
+/*
+ * static prototypes
+ */
+static struct ftable *lookup(char *);
+static void compile_error(char *, char *);
+static char *compile (char *);
+static char *do_spec(char *);
+static char *do_name(char *, int);
+static char *do_func(char *);
+static char *do_expr (char *, int);
+static char *do_loop(char *);
+static char *do_if(char *);
+
+
+static struct ftable *
+lookup(char *name)
+{
+    register struct ftable *t = functable;
+    register char *nm;
+    register char c = *name;
+
+    while ((nm = t->name)) {
+       if (*nm == c && strcmp (nm, name) == 0)
+           return (ftbl = t);
+
+       t++;
+    }
+    return (struct ftable *) 0;
+}
+
+
+static void
+compile_error(char *str, char *cp)
+{
+    int i, errpos, errctx;
+
+    errpos = cp - format_string;
+    errctx = errpos > 20 ? 20 : errpos;
+    usr_fstring[errpos] = '\0';
+
+    for (i = errpos-errctx; i < errpos; i++) {
+#ifdef LOCALE
+       if (iscntrl(usr_fstring[i]))
+#else
+       if (usr_fstring[i] < 32)
+#endif
+           usr_fstring[i] = '_';
+    }
+
+    advise(NULL, "\"%s\": format compile error - %s",
+          &usr_fstring[errpos-errctx], str);
+    adios (NULL, "%*s", errctx+1, "^");
+}
+
+/*
+ * Compile format string "fstring" into format list "fmt".
+ * Return the number of header components found in the format
+ * string.
+ */
+
+int
+fmt_compile(char *fstring, struct format **fmt)
+{
+    register char *cp;
+    int i;
+
+    if (format_string)
+       free (format_string);
+    format_string = getcpy (fstring);
+    usr_fstring = fstring;
+
+    /* init the component hash table. */
+    for (i = 0; i < sizeof(wantcomp)/sizeof(wantcomp[0]); i++)
+       wantcomp[i] = 0;
+
+    memset((char *) &fmt_mnull, 0, sizeof(fmt_mnull));
+
+    /* it takes at least 4 char to generate one format so we
+     * allocate a worst-case format array using 1/4 the length
+     * of the format string.  We actually need twice this much
+     * to handle both pre-processing (e.g., address parsing) and
+     * normal processing.
+     */
+    i = strlen(fstring)/2 + 1;
+    next_fp = formatvec = (struct format *)calloc ((size_t) i,
+                                                  sizeof(struct format));
+    if (next_fp == NULL)
+       adios (NULL, "unable to allocate format storage");
+
+    ncomp = 0;
+    infunction = 0;
+
+    cp = compile(format_string);
+    if (*cp) {
+       CERROR("extra '%>', '%|' or '%?'");
+    }
+    LV(FT_DONE, 0);            /* really done */
+    *fmt = formatvec;
+
+    return (ncomp);
+}
+
+static char *
+compile (char *sp)
+{
+    register char *cp = sp;
+    register int  c;
+
+    for (;;) {
+       sp = cp;
+       while ((c = *cp) && c != '%')
+           cp++;
+       *cp = 0;
+       switch (cp-sp) {
+       case 0:
+           break;
+       case 1:
+           PUTC(*sp);
+           break;
+       default:
+           PUTLIT(sp);
+           break;
+       }
+       if (c == 0)
+           return (cp);
+
+       switch (c = *++cp) {
+       case '%':
+           PUTC (*cp);
+           cp++;
+           break;
+
+       case '|':
+       case '>':
+       case '?':
+       case ']':
+           return (cp);
+
+       case '<':
+           cp = do_if(++cp);
+           break;
+
+       case '[':       /* ] */
+           cp = do_loop(++cp);
+           break;
+
+       case ';':       /* comment line */
+           cp++;
+           while ((c = *cp++) && c != '\n')
+               continue;
+           break;
+
+       default:
+           cp = do_spec(cp);
+           break;
+       }
+    }
+}
+
+
+static char *
+do_spec(char *sp)
+{
+    register char *cp = sp;
+    register int c;
+#ifndef        lint
+    register int ljust = 0;
+#endif /* not lint */
+    register int wid = 0;
+    register char fill = ' ';
+
+    c = *cp++;
+    if (c == '-') {
+       ljust++;
+       c = *cp++;
+    }
+    if (c == '0') {
+       fill = c;
+       c = *cp++;
+    }
+    while (isdigit(c)) {
+       wid = wid*10 + (c - '0');
+       c = *cp++;
+    }
+    if (c == '{') {
+       cp = do_name(cp, 0);
+       if (! infunction)
+           fp->f_type = wid? FT_COMPF : FT_COMP;
+    }
+    else if (c == '(') {
+       cp = do_func(cp);
+       if (! infunction) {
+           if (ftbl->flags & TFL_PUTS) {
+               LV( wid? FT_STRF : FT_STR, ftbl->extra);
+           }
+           else if (ftbl->flags & TFL_PUTN) {
+               LV( wid? FT_NUMF : FT_NUM, ftbl->extra);
+           }
+       }
+    }
+    else {
+       CERROR("component or function name expected");
+    }
+    if (ljust)
+       wid = -wid;
+    fp->f_width = wid;
+    fp->f_fill = fill;
+
+    return (cp);
+}
+
+static char *
+do_name(char *sp, int preprocess)
+{
+    register char *cp = sp;
+    register int c;
+    register int i;
+    static int primed = 0;
+
+    while (isalnum(c = *cp++) || c == '-' || c == '_')
+       ;
+    if (c != '}') {
+       CERROR("'}' expected");
+    }
+    cp[-1] = '\0';
+    PUTCOMP(sp);
+    switch (preprocess) {
+
+    case FT_PARSEDATE:
+       if (cm->c_type & CT_ADDR) {
+           CERROR("component used as both date and address");
+       }
+       if (! (cm->c_type & CT_DATE)) {
+           cm->c_tws = (struct tws *)
+                               calloc((size_t) 1, sizeof(*cm->c_tws));
+           fp->f_type = preprocess;
+           PUTCOMP(sp);
+           cm->c_type |= CT_DATE;
+       }
+       break;
+
+    case FT_MYMBOX:
+       if (!primed) {
+           ismymbox ((struct mailname *) 0);
+           primed++;
+       }
+       cm->c_type |= CT_MYMBOX;
+       /* fall through */
+    case FT_PARSEADDR:
+       if (cm->c_type & CT_DATE) {
+           CERROR("component used as both date and address");
+       }
+       if (! (cm->c_type & CT_ADDRPARSE)) {
+           cm->c_mn = &fmt_mnull;
+           fp->f_type = preprocess;
+           PUTCOMP(sp);
+           cm->c_type |= (CT_ADDR | CT_ADDRPARSE);
+       }
+       break;
+
+    case FT_FORMATADDR:
+       if (cm->c_type & CT_DATE) {
+           CERROR("component used as both date and address");
+       }
+       cm->c_type |= CT_ADDR;
+       break;
+    }
+    return (cp);
+}
+
+static char *
+do_func(char *sp)
+{
+    register char *cp = sp;
+    register int c;
+    register struct ftable *t;
+    register int n;
+    int mflag;         /* minus sign in NUM */
+
+    infunction++;
+
+    while (isalnum(c = *cp++)) 
+       ;
+    if (c != '(' && c != '{' && c != ' ' && c != ')') {
+       CERROR("'(', '{', ' ' or ')' expected");
+    }
+    cp[-1] = '\0';
+    if ((t = lookup (sp)) == 0) {
+       CERROR("unknown function");
+    }
+    if (isspace(c))
+       c = *cp++;
+
+    switch (t->type) {
+
+    case TF_COMP:
+       if (c != '{') {
+           CERROR("component name expected");
+       }
+       cp = do_name(cp, t->extra);
+       fp->f_type = t->f_type;
+       c = *cp++;
+       break;
+
+    case TF_NUM:
+       if ((mflag = (c == '-')))
+           c = *cp++;
+       n = 0;
+       while (isdigit(c)) {
+           n = n*10 + (c - '0');
+           c = *cp++;
+       }
+       if (mflag)
+           n = (-n);
+       LV(t->f_type,n);
+       break;
+
+    case TF_STR:
+       sp = cp - 1;
+       while (c && c != ')')
+           c = *cp++;
+       cp[-1] = '\0';
+       LS(t->f_type,sp);
+       break;
+
+    case TF_NONE:
+       LV(t->f_type,t->extra);
+       break;
+
+    case TF_MYBOX:
+       LS(t->f_type, getusername());
+       break;
+
+    case TF_NOW:
+       LV(t->f_type, time((time_t *) 0));
+       break;
+
+    case TF_EXPR_SV:
+       LV(FT_SAVESTR, 0);
+       /* fall through */
+    case TF_EXPR:
+       *--cp = c;
+       cp = do_expr(cp, t->extra);
+       LV(t->f_type, 0);
+       c = *cp++;
+       ftbl = t;
+       break;
+
+    case TF_NOP:
+       *--cp = c;
+       cp = do_expr(cp, t->extra);
+       c = *cp++;
+       ftbl = t;
+       break;
+    }
+    if (c != ')') {
+       CERROR("')' expected");
+    }
+    --infunction;
+    return (cp);
+}
+
+static char *
+do_expr (char *sp, int preprocess)
+{
+    register char *cp = sp;
+    register int  c;
+
+    if ((c = *cp++) == '{') {
+       cp = do_name (cp, preprocess);
+       fp->f_type = FT_LS_COMP;
+    } else if (c == '(') {
+       cp = do_func (cp);
+    } else if (c == ')') {
+       return (--cp);
+    } else if (c == '%' && *cp == '<') {
+       cp = do_if (cp+1);
+    } else {
+       CERROR ("'(', '{', '%<' or ')' expected");
+    }
+    return (cp);
+}
+
+static char *
+do_loop(char *sp)
+{
+    register char *cp = sp;
+    struct format *floop;
+
+    floop = next_fp;
+    cp = compile (cp);
+    if (*cp++ != ']')
+       CERROR ("']' expected");
+
+    LV(FT_DONE, 1);            /* not yet done */
+    LV(FT_GOTO, 0);
+    fp->f_skip = floop - fp;   /* skip backwards */
+
+    return cp;
+}
+
+static char *
+do_if(char *sp)
+{
+    register char *cp = sp;
+    register struct format *fexpr,
+                          *fif = (struct format *)NULL;
+    register int c = '<';
+
+    for (;;) {
+       if (c == '<') {                 /* doing an IF */
+           if ((c = *cp++) == '{') /*}*/{
+               cp = do_name(cp, 0);
+               fp->f_type = FT_LS_COMP;
+               LV (FT_IF_S, 0);
+           }
+           else if (c == '(') {
+               cp = do_func(cp);
+               /* see if we can merge the load and the "if" */
+               if (ftbl->f_type >= IF_FUNCS)
+                   fp->f_type = ftbl->extra;
+               else {
+                   LV (FT_IF_V_NE, 0);
+               }
+           }
+           else {
+               CERROR("'(' or '{' expected");  /*}*/
+           }
+       }
+
+       fexpr = fp;                     /* loc of [ELS]IF */
+       cp = compile (cp);              /* compile IF TRUE stmts */
+       if (fif)
+           fif->f_skip = next_fp - fif;
+
+       if ((c = *cp++) == '|') {       /* the last ELSE */
+           LV(FT_GOTO, 0);
+           fif = fp;                   /* loc of GOTO */
+           fexpr->f_skip = next_fp - fexpr;
+
+           fexpr = (struct format *)NULL;/* no extra ENDIF */
+
+           cp = compile (cp);          /* compile ELSE stmts */
+           fif->f_skip = next_fp - fif;
+           c = *cp++;
+       }
+       else if (c == '?') {            /* another ELSIF */
+           LV(FT_GOTO, 0);
+           fif = fp;                   /* loc of GOTO */
+           fexpr->f_skip = next_fp - fexpr;
+
+           c = '<';                    /* impersonate an IF */
+           continue;
+       }
+       break;
+    }
+
+    if (c != '>') {
+       CERROR("'>' expected.");
+    }
+
+    if (fexpr)                         /* IF ... [ELSIF ...] ENDIF */
+       fexpr->f_skip = next_fp - fexpr;
+
+    return (cp);
+}
diff --git a/sbr/fmt_def.c b/sbr/fmt_def.c
new file mode 100644 (file)
index 0000000..e3384e8
--- /dev/null
@@ -0,0 +1,10 @@
+
+/*
+ * fmt_def.c -- some defines for sbr/fmt_scan.c
+ *
+ * $Id$
+ */
+
+#include <h/addrsbr.h>
+
+int fmt_norm = AD_NAME;
diff --git a/sbr/fmt_new.c b/sbr/fmt_new.c
new file mode 100644 (file)
index 0000000..003ec2b
--- /dev/null
@@ -0,0 +1,105 @@
+
+/*
+ * fmt_new.c -- read format file/string and normalize
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+#define QUOTE '\\'
+
+static char *formats = 0;
+
+/*
+ * static prototypes
+ */
+static void normalize (char *);
+
+
+/*
+ * Get new format string
+ */
+
+char *
+new_fs (char *form, char *format, char *default_fs)
+{
+    struct stat st;
+    register FILE *fp;
+
+    if (formats)
+       free (formats);
+
+    if (form) {
+       if ((fp = fopen (etcpath (form), "r")) == NULL)
+           adios (form, "unable to open format file");
+
+       if (fstat (fileno (fp), &st) == -1)
+           adios (form, "unable to stat format file");
+
+       if (!(formats = malloc ((size_t) st.st_size + 1)))
+           adios (form, "unable to allocate space for format");
+
+       if (read (fileno(fp), formats, (int) st.st_size) != st.st_size)
+           adios (form, "error reading format file");
+
+       formats[st.st_size] = '\0';
+
+       fclose (fp);
+    } else {
+       formats = getcpy (format ? format : default_fs);
+    }
+
+    normalize (formats);       /* expand escapes */
+
+    return formats;
+}
+
+
+/*
+ * Expand escapes in format strings
+ */
+
+static void
+normalize (char *cp)
+{
+    char *dp;
+
+    for (dp = cp; *cp; cp++) {
+       if (*cp != QUOTE) {
+           *dp++ = *cp;
+       } else {
+           switch (*++cp) {
+               case 'b':
+                   *dp++ = '\b';
+                   break;
+
+               case 'f':
+                   *dp++ = '\f';
+                   break;
+
+               case 'n':
+                   *dp++ = '\n';
+                   break;
+
+               case 'r':
+                   *dp++ = '\r';
+                   break;
+
+               case 't':
+                   *dp++ = '\t';
+                   break;
+
+               case '\n':
+                   break;
+
+               case 0: 
+                   cp--;       /* fall */
+               default: 
+                   *dp++ = *cp;
+                   break;
+           }
+       }
+    }
+    *dp = '\0';
+}
diff --git a/sbr/fmt_rfc2047.c b/sbr/fmt_rfc2047.c
new file mode 100644 (file)
index 0000000..a9c3d0b
--- /dev/null
@@ -0,0 +1,235 @@
+
+/*
+ * fmt_rfc2047.c -- decode RFC-2047 header format 
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+static signed char hexindex[] = {
+    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+     0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1,
+    -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+    -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+};
+
+static signed char index_64[128] = {
+    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
+    52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
+    -1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
+    15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
+    -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
+    41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
+};
+
+#define char64(c) (((unsigned char) (c) > 127) ? -1 : index_64[(unsigned char) (c)])
+
+static int
+unqp (unsigned char byte1, unsigned char byte2)
+{
+    if (hexindex[byte1] == -1 || hexindex[byte2] == -1)
+       return -1;
+    return (hexindex[byte1] << 4 | hexindex[byte2]);
+}
+
+/* Check if character is linear whitespace */
+#define is_lws(c)  ((c) == ' ' || (c) == '\t' || (c) == '\n')
+
+
+/*
+ * Decode the string as a RFC-2047 header field
+ */
+
+int
+decode_rfc2047 (char *str, char *dst) 
+{
+    char *p, *q, *pp;
+    char *startofmime, *endofmime;
+    int c, quoted_printable;
+    int encoding_found = 0;    /* did we decode anything?                */
+    int between_encodings = 0; /* are we between two encodings?          */
+    int equals_pending = 0;    /* is there a '=' pending?                */
+    int whitespace = 0;                /* how much whitespace between encodings? */
+
+    if (!str)
+       return 0;
+
+    /*
+     * Do a quick and dirty check for the '=' character.
+     * This should quickly eliminate many cases.
+     */
+    if (!strchr (str, '='))
+       return 0;
+
+    for (p = str, q = dst; *p; p++) {
+       /*
+        * If we had an '=' character pending from
+        * last iteration, then add it first.
+        */
+       if (equals_pending) {
+           *q++ = '=';
+           equals_pending = 0;
+           between_encodings = 0;      /* we have added non-whitespace text */
+       }
+
+       if (*p != '=') {
+           /* count linear whitespace while between encodings */
+           if (between_encodings && is_lws(*p))
+               whitespace++;
+           else
+               between_encodings = 0;  /* we have added non-whitespace text */
+           *q++ = *p;
+           continue;
+       }
+
+       equals_pending = 1;     /* we have a '=' pending */
+
+       /* Check for initial =? */
+       if (*p == '=' && p[1] && p[1] == '?' && p[2]) {
+           startofmime = p + 2;
+
+           /* Scan ahead for the next '?' character */
+           for (pp = startofmime; *pp && *pp != '?'; pp++)
+               ;
+
+           if (!*pp)
+               continue;
+
+           /* Check if character set is OK */
+           if (!check_charset(startofmime, pp - startofmime))
+               continue;
+
+           startofmime = pp + 1;
+
+           /* Check for valid encoding type */
+           if (*startofmime != 'B' && *startofmime != 'b' &&
+               *startofmime != 'Q' && *startofmime != 'q')
+               continue;
+
+           /* Is encoding quoted printable or base64? */
+           quoted_printable = (*startofmime == 'Q' || *startofmime == 'q');
+           startofmime++;
+
+           /* Check for next '?' character */
+           if (*startofmime != '?')
+               continue;
+           startofmime++;
+
+           /*
+            * Scan ahead for the ending ?=
+            *
+            * While doing this, we will also check if encoded
+            * word has any embedded linear whitespace.
+            */
+           endofmime = NULL;
+           for (pp = startofmime; *pp && *(pp+1); pp++) {
+               if (is_lws(*pp)) {
+                   break;
+               } else if (*pp == '?' && pp[1] == '=') {
+                   endofmime = pp;
+                   break;
+               }
+           }
+           if (is_lws(*pp) || endofmime == NULL)
+               continue;
+
+           /*
+            * We've found an encoded word, so we can drop
+            * the '=' that was pending
+            */
+           equals_pending = 0;
+
+           /*
+            * If we are between two encoded words separated only by
+            * linear whitespace, then we ignore the whitespace.
+            * We will roll back the buffer the number of whitespace
+            * characters we've seen since last encoded word.
+            */
+           if (between_encodings)
+               q -= whitespace;
+
+           /* Now decode the text */
+           if (quoted_printable) {
+               for (pp = startofmime; pp < endofmime; pp++) {
+                   if (*pp == '=') {
+                       c = unqp (pp[1], pp[2]);
+                       if (c == -1)
+                           continue;
+                       if (c != 0)
+                           *q++ = c;
+                       pp += 2;
+                   } else if (*pp == '_') {
+                       *q++ = ' ';
+                   } else {
+                       *q++ = *pp;
+                   }
+               }
+           } else {
+               /* base64 */
+               int c1, c2, c3, c4;
+
+               pp = startofmime;
+               while (pp < endofmime) {
+                   /* 6 + 2 bits */
+                   while ((pp < endofmime) &&
+                          ((c1 = char64(*pp)) == -1)) {
+                       pp++;
+                   }
+                   if (pp < endofmime) {
+                       pp++;
+                   }
+                   while ((pp < endofmime) &&
+                          ((c2 = char64(*pp)) == -1)) {
+                       pp++;
+                   }
+                   if (pp < endofmime && c1 != -1 && c2 != -1) {
+                       *q++ = (c1 << 2) | (c2 >> 4);
+                       pp++;
+                   }
+                   /* 4 + 4 bits */
+                   while ((pp < endofmime) &&
+                          ((c3 = char64(*pp)) == -1)) {
+                       pp++;
+                   }
+                   if (pp < endofmime && c2 != -1 && c3 != -1) {
+                       *q++ = ((c2 & 0xF) << 4) | (c3 >> 2);
+                       pp++;
+                   }
+                   /* 2 + 6 bits */
+                   while ((pp < endofmime) &&
+                          ((c4 = char64(*pp)) == -1)) {
+                       pp++;
+                   }
+                   if (pp < endofmime && c3 != -1 && c4 != -1) {
+                       *q++ = ((c3 & 0x3) << 6) | (c4);
+                       pp++;
+                   }
+               }
+           }
+
+           /*
+            * Now that we are done decoding this particular
+            * encoded word, advance string to trailing '='.
+            */
+           p = endofmime + 1;
+
+           encoding_found = 1;         /* we found (at least 1) encoded word */
+           between_encodings = 1;      /* we have just decoded something     */
+           whitespace = 0;             /* re-initialize amount of whitespace */
+       }
+    }
+
+    /* If an equals was pending at end of string, add it now. */
+    if (equals_pending)
+       *q++ = '=';
+    *q = '\0';
+
+    return encoding_found;
+}
diff --git a/sbr/fmt_scan.c b/sbr/fmt_scan.c
new file mode 100644 (file)
index 0000000..2086a94
--- /dev/null
@@ -0,0 +1,826 @@
+
+/*
+ * fmt_scan.c -- format string interpretation
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/addrsbr.h>
+#include <h/fmt_scan.h>
+#include <zotnet/tws/tws.h>
+#include <h/fmt_compile.h>
+
+#define        NFMTS MAXARGS
+
+extern char *formataddr ();    /* hook for custom address formatting */
+
+#ifdef LBL
+struct msgs *fmt_current_folder; /* current folder (set by main program) */
+#endif
+
+extern int fmt_norm;           /* defined in sbr/fmt_def.c = AD_NAME */
+struct mailname fmt_mnull;
+
+/*
+ * static prototypes
+ */
+static int match (char *, char *);
+static char *get_x400_friendly (char *, char *, int);
+static int get_x400_comp (char *, char *, char *, int);
+
+
+/*
+ * test if string "sub" appears anywhere in
+ * string "str" (case insensitive).
+ */
+
+static int
+match (char *str, char *sub)
+{
+    int c1, c2;
+    char *s1, *s2;
+
+#ifdef LOCALE
+    while ((c1 = *sub)) {
+       c1 = (isalpha(c1) && isupper(c1)) ? tolower(c1) : c1;
+       while ((c2 = *str++) && c1 != ((isalpha(c2) && isupper(c2)) ? tolower(c2) : c2))
+           ;
+       if (! c2)
+           return 0;
+       s1 = sub + 1; s2 = str;
+       while ((c1 = *s1++) && ((isalpha(c1) && isupper(c1)) ? tolower(c1) : c1) == ((isalpha(c2 =*s2++) && isupper(c2)) ? tolower(c2) : c2))
+           ;
+       if (! c1)
+           return 1;
+    }
+#else
+    while ((c1 = *sub)) {
+       while ((c2 = *str++) && (c1 | 040) != (c2 | 040))
+           ;
+       if (! c2)
+           return 0;
+       s1 = sub + 1; s2 = str;
+       while ((c1 = *s1++) && (c1 | 040) == (*s2++ | 040))
+           ;
+       if (! c1)
+           return 1;
+    }
+#endif
+    return 1;
+}
+
+/*
+ * macros to format data
+ */
+
+#define PUTDF(cp, num, wid, fill)\
+       if (cp + wid < ep) {\
+           if ((i = (num)) < 0)\
+               i = -(num);\
+           if ((c = (wid)) < 0)\
+               c = -c;\
+           sp = cp + c;\
+           do {\
+               *--sp = (i % 10) + '0';\
+               i /= 10;\
+           } while (i > 0 && sp > cp);\
+           if (i > 0)\
+               *sp = '?';\
+           else if ((num) < 0 && sp > cp)\
+               *--sp = '-';\
+           while (sp > cp)\
+               *--sp = fill;\
+           cp += c;\
+           }
+
+#define PUTD(cp, num)\
+       if (cp < ep) {\
+           if ((i = (num)) == 0)\
+               *cp++ = '0';\
+           else {\
+               if ((i = (num)) < 0) {\
+                   *cp++ = '-';\
+                   i = -(num);\
+                   }\
+               c = 10;\
+               while (c <= i) \
+                   c *= 10;\
+               while (cp < ep && c > 1) {\
+                   c /= 10;\
+                   *cp++ = (i / c) + '0';\
+                   i %= c;\
+                   }\
+               }\
+           }
+
+#ifdef LOCALE
+#define PUTSF(cp, str, wid, fill) {\
+               ljust = 0;\
+               if ((i = (wid)) < 0) {\
+                       i = -i;\
+                       ljust++;\
+               }\
+               if ((sp = (str))) {\
+                       if (ljust) {\
+                               c = strlen(sp);\
+                               if (c > i)\
+                                       sp += c - i;\
+                               else {\
+                                       while( --i >= c && cp < ep)\
+                                               *cp++ = fill;\
+                                       i++;\
+                               }\
+                       } else {\
+                           while ((c = (unsigned char) *sp) && (iscntrl(c) || isspace(c)))\
+                               sp++;\
+                       }\
+                       while ((c = (unsigned char) *sp++) && --i >= 0 && cp < ep)\
+                               if (isgraph(c)) \
+                                   *cp++ = c;\
+                               else {\
+                                       while ((c = (unsigned char) *sp) && (iscntrl(c) || isspace(c)))\
+                                               sp++;\
+                                           *cp++ = ' ';\
+                               }\
+               }\
+               if (!ljust)\
+               while( --i >= 0 && cp < ep)\
+                   *cp++ = fill;\
+       }
+
+#define PUTS(cp, str) {\
+               if ((sp = (str))) {\
+                   while ((c = (unsigned char) *sp) && (iscntrl(c) || isspace(c)))\
+                       sp++;\
+                   while((c = (unsigned char) *sp++) && cp < ep)\
+                       if (isgraph(c)) \
+                           *cp++ = c;\
+                       else {\
+                           while ((c = (unsigned char) *sp) && (iscntrl(c) || isspace(c)))\
+                               sp++;\
+                           *cp++ = ' ';\
+                       }\
+               }\
+       }
+
+#else /* LOCALE */
+#define PUTSF(cp, str, wid, fill) {\
+               ljust = 0;\
+               if ((i = (wid)) < 0) {\
+                       i = -i;\
+                       ljust++;\
+               }\
+               if (sp = (str)) {\
+                       if (ljust) {\
+                               c = strlen(sp);\
+                               if (c > i)\
+                                       sp += c - i;\
+                               else {\
+                                       while( --i >= c && cp < ep)\
+                                               *cp++ = fill;\
+                                       i++;\
+                               }\
+                       } else {\
+                   while ((c = *sp) && c <= 32)\
+                       sp++;\
+                       }\
+                       while ((c = *sp++) && --i >= 0 && cp < ep)\
+                               if (c > 32) \
+                           *cp++ = c;\
+                       else {\
+                                       while ((c = *sp) && c <= 32)\
+                               sp++;\
+                           *cp++ = ' ';\
+                       }\
+               }\
+               if (!ljust)\
+               while( --i >= 0 && cp < ep)\
+                   *cp++ = fill;\
+               }
+
+#define PUTS(cp, str) {\
+               if (sp = (str)) {\
+                   while ((c = *sp) && c <= 32)\
+                       sp++;\
+                   while( (c = *sp++) && cp < ep)\
+                       if ( c > 32 ) \
+                           *cp++ = c;\
+                       else {\
+                           while ( (c = *sp) && c <= 32 )\
+                               sp++;\
+                           *cp++ = ' ';\
+                       }\
+               }\
+               }
+
+#endif /* LOCALE */
+
+
+static char *lmonth[] = { "January",  "February","March",   "April",
+                         "May",      "June",    "July",    "August",
+                         "September","October", "November","December" };
+
+static char *
+get_x400_friendly (char *mbox, char *buffer, int buffer_len)
+{
+    char given[BUFSIZ], surname[BUFSIZ];
+
+    if (mbox == NULL)
+       return NULL;
+    if (*mbox == '"')
+       mbox++;
+    if (*mbox != '/')
+       return NULL;
+
+    if (get_x400_comp (mbox, "/PN=", buffer, buffer_len)) {
+       for (mbox = buffer; mbox = strchr(mbox, '.'); )
+           *mbox++ = ' ';
+
+       return buffer;
+    }
+
+    if (!get_x400_comp (mbox, "/S=", surname, sizeof(surname)))
+       return NULL;
+
+    if (get_x400_comp (mbox, "/G=", given, sizeof(given)))
+       snprintf (buffer, buffer_len, "%s %s", given, surname);
+    else
+       snprintf (buffer, buffer_len, "%s", surname);
+
+    return buffer;
+}
+
+static int
+get_x400_comp (char *mbox, char *key, char *buffer, int buffer_len)
+{
+    int        idx;
+    char *cp;
+
+    if ((idx = stringdex (key, mbox)) < 0
+           || !(cp = strchr(mbox += idx + strlen (key), '/')))
+       return 0;
+
+    snprintf (buffer, buffer_len, "%*.*s", cp - mbox, cp - mbox, mbox);
+    return 1;
+}
+
+struct format *
+fmt_scan (struct format *format, char *scanl, int width, int *dat)
+{
+    char *cp, *ep, *sp;
+    char *savestr, *str = NULL;
+    char buffer[BUFSIZ], buffer2[BUFSIZ];
+    int i, c, ljust;
+    int value = 0;
+    time_t t;
+    struct format *fmt;
+    struct comp *comp;
+    struct tws *tws;
+    struct mailname *mn;
+
+    cp = scanl;
+    ep = scanl + width - 1;
+    fmt = format;
+
+    while (cp < ep) {
+       switch (fmt->f_type) {
+
+       case FT_COMP:
+           PUTS (cp, fmt->f_comp->c_text);
+           break;
+       case FT_COMPF:
+           PUTSF (cp, fmt->f_comp->c_text, fmt->f_width, fmt->f_fill);
+           break;
+
+       case FT_LIT:
+           sp = fmt->f_text;
+           while( (c = *sp++) && cp < ep)
+               *cp++ = c;
+           break;
+       case FT_LITF:
+           sp = fmt->f_text;
+           ljust = 0;
+           i = fmt->f_width;
+           if (i < 0) {
+               i = -i;
+               ljust++;                /* XXX should do something with this */
+           }
+           while( (c = *sp++) && --i >= 0 && cp < ep)
+               *cp++ = c;
+           while( --i >= 0 && cp < ep)
+               *cp++ = fmt->f_fill;
+           break;
+
+       case FT_STR:
+           PUTS (cp, str);
+           break;
+       case FT_STRF:
+           PUTSF (cp, str, fmt->f_width, fmt->f_fill);
+           break;
+       case FT_STRFW:
+           adios (NULL, "internal error (FT_STRFW)");
+
+       case FT_NUM:
+           PUTD (cp, value);
+           break;
+       case FT_NUMF:
+           PUTDF (cp, value, fmt->f_width, fmt->f_fill);
+           break;
+
+       case FT_CHAR:
+           *cp++ = fmt->f_char;
+           break;
+
+       case FT_DONE:
+           goto finished;
+
+       case FT_IF_S:
+           if (!(value = (str && *str))) {
+               fmt += fmt->f_skip;
+               continue;
+           }
+           break;
+
+       case FT_IF_S_NULL:
+           if (!(value = (str == NULL || *str == 0))) {
+               fmt += fmt->f_skip;
+               continue;
+           }
+           break;
+
+       case FT_IF_V_EQ:
+           if (value != fmt->f_value) {
+               fmt += fmt->f_skip;
+               continue;
+           }
+           break;
+
+       case FT_IF_V_NE:
+           if (value == fmt->f_value) {
+               fmt += fmt->f_skip;
+               continue;
+           }
+           break;
+
+       case FT_IF_V_GT:
+           if (value <= fmt->f_value) {
+               fmt += fmt->f_skip;
+               continue;
+           }
+           break;
+
+       case FT_IF_MATCH:
+           if (!(value = (str && match (str, fmt->f_text)))) {
+               fmt += fmt->f_skip;
+               continue;
+           }
+           break;
+
+       case FT_V_MATCH:
+           if (str)
+               value = match (str, fmt->f_text);
+           else
+               value = 0;
+           break;
+
+       case FT_IF_AMATCH:
+           if (!(value = (str && uprf (str, fmt->f_text)))) {
+               fmt += fmt->f_skip;
+               continue;
+           }
+           break;
+
+       case FT_V_AMATCH:
+           value = uprf (str, fmt->f_text);
+           break;
+
+       case FT_S_NONNULL:
+           value = (str != NULL && *str != 0);
+           break;
+
+       case FT_S_NULL:
+           value = (str == NULL || *str == 0);
+           break;
+
+       case FT_V_EQ:
+           value = (fmt->f_value == value);
+           break;
+
+       case FT_V_NE:
+           value = (fmt->f_value != value);
+           break;
+
+       case FT_V_GT:
+           value = (fmt->f_value > value);
+           break;
+
+       case FT_GOTO:
+           fmt += fmt->f_skip;
+           continue;
+
+       case FT_NOP:
+           break;
+
+       case FT_LS_COMP:
+           str = fmt->f_comp->c_text;
+           break;
+       case FT_LS_LIT:
+           str = fmt->f_text;
+           break;
+       case FT_LS_GETENV:
+           if (!(str = getenv (fmt->f_text)))
+               str = "";
+           break;
+       case FT_LS_CFIND:
+           if (!(str = context_find (fmt->f_text)))
+               str = "";
+           break;
+
+       case FT_LS_DECODECOMP:
+           if (decode_rfc2047(fmt->f_comp->c_text, buffer2))
+               str = buffer2;
+           else
+               str = fmt->f_comp->c_text;
+           break;
+
+       case FT_LS_DECODE:
+           if (str && decode_rfc2047(str, buffer2))
+               str = buffer2;
+           break;
+
+       case FT_LS_TRIM:
+           if (str) {
+                   char *xp;
+
+                   strncpy(buffer, str, sizeof(buffer));
+                   str = buffer;
+                   while (isspace(*str))
+                           str++;
+                   ljust = 0;
+                   if ((i = fmt->f_width) < 0) {
+                           i = -i;
+                           ljust++;
+                   }
+
+                   if (!ljust && i > 0 && strlen(str) > i)
+                           str[i] = '\0';
+                   xp = str;
+                   xp += strlen(str) - 1;
+                   while (xp > str && isspace(*xp))
+                           *xp-- = '\0';
+                   if (ljust && i > 0 && strlen(str) > i)
+                       str += strlen(str) - i;
+           }
+           break;
+
+       case FT_LV_COMPFLAG:
+           value = fmt->f_comp->c_flags;
+           break;
+       case FT_LV_COMP:
+           value = (comp = fmt->f_comp)->c_text ? atoi(comp->c_text) : 0;
+           break;
+       case FT_LV_LIT:
+           value = fmt->f_value;
+           break;
+       case FT_LV_DAT:
+           value = dat[fmt->f_value];
+           break;
+       case FT_LV_STRLEN:
+           value = strlen(str);
+           break;
+       case FT_LV_CHAR_LEFT:
+           value = width - (cp - scanl);
+           break;
+       case FT_LV_PLUS_L:
+           value += fmt->f_value;
+           break;
+       case FT_LV_MINUS_L:
+           value = fmt->f_value - value;
+           break;
+       case FT_LV_DIVIDE_L:
+           if (fmt->f_value)
+               value = value / fmt->f_value;
+           else
+               value = 0;
+           break;
+       case FT_LV_MODULO_L:
+           if (fmt->f_value)
+               value = value % fmt->f_value;
+           else
+               value = 0;
+           break;
+       case FT_SAVESTR:
+           savestr = str;
+           break;
+
+       case FT_LV_SEC:
+           value = fmt->f_comp->c_tws->tw_sec;
+           break;
+       case FT_LV_MIN:
+           value = fmt->f_comp->c_tws->tw_min;
+           break;
+       case FT_LV_HOUR:
+           value = fmt->f_comp->c_tws->tw_hour;
+           break;
+       case FT_LV_MDAY:
+           value = fmt->f_comp->c_tws->tw_mday;
+           break;
+       case FT_LV_MON:
+           value = fmt->f_comp->c_tws->tw_mon + 1;
+           break;
+       case FT_LS_MONTH:
+           str = tw_moty[fmt->f_comp->c_tws->tw_mon];
+           break;
+       case FT_LS_LMONTH:
+           str = lmonth[fmt->f_comp->c_tws->tw_mon];
+           break;
+       case FT_LS_ZONE:
+           str = dtwszone (fmt->f_comp->c_tws);
+           break;
+       case FT_LV_YEAR:
+           value = fmt->f_comp->c_tws->tw_year;
+           break;
+       case FT_LV_WDAY:
+           if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
+               set_dotw (tws);
+           value = tws->tw_wday;
+           break;
+       case FT_LS_DAY:
+           if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
+               set_dotw (tws);
+           str = tw_dotw[tws->tw_wday];
+           break;
+       case FT_LS_WEEKDAY:
+           if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
+               set_dotw (tws);
+           str = tw_ldotw[tws->tw_wday];
+           break;
+       case FT_LV_YDAY:
+           value = fmt->f_comp->c_tws->tw_yday;
+           break;
+       case FT_LV_ZONE:
+           value = fmt->f_comp->c_tws->tw_zone;
+           break;
+       case FT_LV_CLOCK:
+           if ((value = fmt->f_comp->c_tws->tw_clock) == 0)
+               value = dmktime(fmt->f_comp->c_tws);
+           break;
+       case FT_LV_RCLOCK:
+           if ((value = fmt->f_comp->c_tws->tw_clock) == 0)
+               value = dmktime(fmt->f_comp->c_tws);
+           value = time((time_t *) 0) - value;
+           break;
+       case FT_LV_DAYF:
+           if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
+               set_dotw (tws);
+           switch (fmt->f_comp->c_tws->tw_flags & TW_SDAY) {
+               case TW_SEXP:
+                   value = 1; break;
+               case TW_SIMP:
+                   value = 0; break;
+               default:
+                   value = -1; break;
+           }
+       case FT_LV_ZONEF:
+           if ((fmt->f_comp->c_tws->tw_flags & TW_SZONE) == TW_SZEXP)
+                   value = 1;
+           else
+                   value = -1;
+           break;
+       case FT_LV_DST:
+           value = fmt->f_comp->c_tws->tw_flags & TW_DST;
+           break;
+       case FT_LS_822DATE:
+           str = dasctime (fmt->f_comp->c_tws , TW_ZONE);
+           break;
+       case FT_LS_PRETTY:
+           str = dasctime (fmt->f_comp->c_tws, TW_NULL);
+           break;
+
+       case FT_LS_PERS:
+           str = fmt->f_comp->c_mn->m_pers;
+           break;
+       case FT_LS_MBOX:
+           str = fmt->f_comp->c_mn->m_mbox;
+           break;
+       case FT_LS_HOST:
+           str = fmt->f_comp->c_mn->m_host;
+           break;
+       case FT_LS_PATH:
+           str = fmt->f_comp->c_mn->m_path;
+           break;
+       case FT_LS_GNAME:
+           str = fmt->f_comp->c_mn->m_gname;
+           break;
+       case FT_LS_NOTE:
+           str = fmt->f_comp->c_mn->m_note;
+           break;
+       case FT_LS_822ADDR:
+           str = adrformat( fmt->f_comp->c_mn );
+           break;
+       case FT_LV_HOSTTYPE:
+           value = fmt->f_comp->c_mn->m_type;
+           break;
+       case FT_LV_INGRPF:
+           value = fmt->f_comp->c_mn->m_ingrp;
+           break;
+       case FT_LV_NOHOSTF:
+           value = fmt->f_comp->c_mn->m_nohost;
+           break;
+       case FT_LS_ADDR:
+       case FT_LS_FRIENDLY:
+           if ((mn = fmt->f_comp->c_mn) == &fmt_mnull) {
+               str = fmt->f_comp->c_text;
+               break;
+           }
+           if (fmt->f_type == FT_LS_ADDR)
+               goto unfriendly;
+           if ((str = mn->m_pers) == NULL)
+               if ((str = mn->m_note)) {
+                   strncpy (buffer, str, sizeof(buffer));
+                   str = buffer;
+                   if (*str == '(')
+                       str++;
+                   sp = str + strlen(str) - 1;
+                   if (*sp == ')') {
+                       *sp-- = '\0';
+                       while (sp >= str)
+                           if (*sp == ' ')
+                               *sp-- = '\0';
+                           else
+                               break;
+                   }
+               } else if (!(str = get_x400_friendly (mn->m_mbox,
+                               buffer, sizeof(buffer)))) {
+       unfriendly: ;
+                 switch (mn->m_type) {
+                   case LOCALHOST:
+                       str = mn->m_mbox;
+                       break;
+                   case UUCPHOST:
+                       snprintf (buffer, sizeof(buffer), "%s!%s",
+                               mn->m_host, mn->m_mbox);
+                       str = buffer;
+                       break;
+                   default:
+                       if (mn->m_mbox) {
+                           snprintf (buffer, sizeof(buffer), "%s@%s",
+                               mn->m_mbox, mn->m_host);
+                           str= buffer;
+                       }
+                       else
+                           str = mn->m_text;
+                       break;
+                 }
+               }
+           break;
+
+       case FT_LOCALDATE:
+           comp = fmt->f_comp;
+           if ((t = comp->c_tws->tw_clock) == 0)
+               t = dmktime(comp->c_tws);
+           tws = dlocaltime(&t);
+           *comp->c_tws = *tws;
+           break;
+
+       case FT_GMTDATE:
+           comp = fmt->f_comp;
+           if ((t = comp->c_tws->tw_clock) == 0)
+               t = dmktime(comp->c_tws);
+           tws = dgmtime(&t);
+           *comp->c_tws = *tws;
+           break;
+
+       case FT_PARSEDATE:
+           comp = fmt->f_comp;
+           if ((sp = comp->c_text) && (tws = dparsetime(sp))) {
+               *comp->c_tws = *tws;
+               comp->c_flags = 0;
+           } else if (comp->c_flags >= 0) {
+               memset ((char *) comp->c_tws, 0, sizeof *comp->c_tws);
+               comp->c_flags = 1;
+           }
+           break;
+
+       case FT_FORMATADDR:
+           /* hook for custom address list formatting (see replsbr.c) */
+           str = formataddr (savestr, str);
+           break;
+
+       case FT_PUTADDR:
+           /* output the str register as an address component,
+            * splitting it into multiple lines if necessary.  The
+            * value reg. contains the max line length.  The lit.
+            * field may contain a string to prepend to the result
+            * (e.g., "To: ")
+            */
+           {
+           char *lp, *lastb;
+           int indent, wid, len;
+
+           lp = str;
+           wid = value;
+           len = strlen (str);
+           sp = fmt->f_text;
+           indent = strlen (sp);
+           wid -= indent;
+           while( (c = *sp++) && cp < ep)
+               *cp++ = c;
+           while (len > wid) {
+               /* try to break at a comma; failing that, break at a
+                * space, failing that, just split the line.
+                */
+               lastb = 0; sp = lp + wid;
+               while (sp > lp && (c = *--sp) != ',') {
+                   if (! lastb && isspace(c))
+                       lastb = sp - 1;
+               }
+               if (sp == lp)
+                   if (! (sp = lastb))
+                       sp = lp + wid - 1;
+               len -= sp - lp + 1;
+               while (cp < ep && lp <= sp)
+                   *cp++ = *lp++;
+               *cp++ = '\n';
+               for (i=indent; cp < ep && i > 0; i--)
+                   *cp++ = ' ';
+               while (isspace(*lp))
+                   lp++, len--;
+           }
+           PUTS (cp, lp);
+           }
+           break;
+
+       case FT_PARSEADDR:
+           comp = fmt->f_comp;
+           if (comp->c_mn != &fmt_mnull)
+               mnfree (comp->c_mn);
+           if ((sp = comp->c_text) && (sp = getname(sp)) &&
+               (mn = getm (sp, NULL, 0, fmt_norm, NULL))) {
+               comp->c_mn = mn;
+               while (getname(""))
+                   ;
+           } else {
+               while (getname(""))             /* XXX */
+                   ;
+               comp->c_mn = &fmt_mnull;
+           }
+           break;
+           
+       case FT_MYMBOX:
+           /*
+            * if there's no component, we say true.  Otherwise we
+            * say "true" only if we can parse the address and it
+            * matches one of our addresses.
+            */
+           comp = fmt->f_comp;
+           if (comp->c_mn != &fmt_mnull)
+               mnfree (comp->c_mn);
+           if ((sp = comp->c_text) && (sp = getname(sp)) &&
+               (mn = getm (sp, NULL, 0, AD_NAME, NULL))) {
+               comp->c_mn = mn;
+               comp->c_flags = ismymbox(mn);
+               while ((sp = getname(sp)))
+                   if (comp->c_flags == 0 &&
+                       (mn = getm (sp, NULL, 0, AD_NAME, NULL)))
+                       comp->c_flags |= ismymbox(mn);
+           } else {
+               while (getname(""))             /* XXX */
+                   ;
+               comp->c_flags = (comp->c_text == 0);
+               comp->c_mn = &fmt_mnull;
+           }
+           break;
+
+       case FT_ADDTOSEQ:
+#ifdef LBL
+           /* If we're working on a folder (as opposed to a file), add the
+            * current msg to sequence given in literal field.  Don't
+            * disturb string or value registers.
+            */
+           if (fmt_current_folder)
+                   seq_addmsg(fmt_current_folder, fmt->f_text, dat[0], -1);
+#endif
+           break;
+       }
+       fmt++;
+    }
+#ifndef JLR
+    finished:;
+    if (cp[-1] != '\n')
+       *cp++ = '\n';
+    *cp   = 0;
+    return ((struct format *)0);
+#else /* JLR */
+    if (cp[-1] != '\n')
+       *cp++ = '\n';
+    while (fmt->f_type != FT_DONE)
+       fmt++;
+
+    finished:;    
+    *cp = '\0';
+    return (fmt->f_value ? ++fmt : (struct format *) 0);
+
+#endif /* JLR */
+}
diff --git a/sbr/folder_addmsg.c b/sbr/folder_addmsg.c
new file mode 100644 (file)
index 0000000..6858f4d
--- /dev/null
@@ -0,0 +1,194 @@
+
+/*
+ * folder_addmsg.c -- Link message into folder
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+#include <errno.h>
+
+/*
+ * Link message into a folder.  Return the new number
+ * of the message.  If an error occurs, return -1.
+ */
+
+int
+folder_addmsg (struct msgs **mpp, char *msgfile, int selected,
+               int unseen, int preserve)
+{
+    int infd, outfd, linkerr, first_time, msgnum;
+    char *nmsg, newmsg[BUFSIZ];
+    struct msgs *mp;
+    struct stat st1, st2;
+
+    first_time = 1;    /* this is first attempt */
+    mp = *mpp;
+
+    /*
+     * We might need to make several attempts
+     * in order to add the message to the folder.
+     */
+    for (;;) {
+       /*
+        * Get the message number we will attempt to add.
+        */
+       if (first_time) {
+           /* should we preserve the numbering of the message? */
+           if (preserve && (msgnum = m_atoi (msgfile)) > 0) {
+               ;
+           } else if (mp->nummsg == 0) {
+               /* check if we are adding to empty folder */
+               msgnum = 1;
+           } else {
+               /* else use highest message number + 1 */
+               msgnum = mp->hghmsg + 1;
+           }
+           first_time = 0;
+       } else {
+           /* another attempt, so try next higher message number */
+           msgnum++;
+       }
+
+       /*
+        * See if we need more space.  If we need space at the
+        * end, then we allocate space for an addition 100 messages.
+        * If we need space at the beginning of the range, then just
+        * extend message status range to cover this message number.
+         */
+       if (msgnum > mp->hghoff) {
+           if ((mp = folder_realloc (mp, mp->lowoff, msgnum + 100)))
+               *mpp = mp;
+           else {
+               advise (NULL, "unable to allocate folder storage");
+               return -1;
+           }
+       } else if (msgnum < mp->lowoff) {
+           if ((mp = folder_realloc (mp, msgnum, mp->hghoff)))
+               *mpp = mp;
+           else {
+               advise (NULL, "unable to allocate folder storage");
+               return -1;
+           }
+       }
+
+       /*
+        * If a message is already in that slot,
+        * then loop to next available slot.
+        */
+       if (does_exist (mp, msgnum))
+           continue;
+
+       /* setup the bit flags for this message */
+       clear_msg_flags (mp, msgnum);
+       set_exists (mp, msgnum);
+
+       /* should we set the SELECT_UNSEEN bit? */
+       if (unseen) {
+           set_unseen (mp, msgnum);
+       }
+
+       /* should we set the SELECTED bit? */
+       if (selected) {
+           set_selected (mp, msgnum);
+
+           /* check if highest or lowest selected */
+           if (mp->numsel == 0) {
+               mp->lowsel = msgnum;
+               mp->hghsel = msgnum;
+           } else {
+               if (msgnum < mp->lowsel)
+                   mp->lowsel = msgnum;
+               if (msgnum > mp->hghsel)
+                   mp->hghsel = msgnum;
+           }
+
+           /* increment number selected */
+           mp->numsel++;
+       }
+
+       /*
+        * check if this is highest or lowest message
+         */
+       if (mp->nummsg == 0) {
+           mp->lowmsg = msgnum;
+           mp->hghmsg = msgnum;
+       } else {
+           if (msgnum < mp->lowmsg)
+               mp->lowmsg = msgnum;
+           if (msgnum > mp->hghmsg)
+               mp->hghmsg = msgnum;
+       }
+
+       /* increment message count */
+       mp->nummsg++;
+
+       nmsg = m_name (msgnum);
+       snprintf (newmsg, sizeof(newmsg), "%s/%s", mp->foldpath, nmsg);
+
+       /*
+        * Now try to link message into folder
+        */
+       if (link (msgfile, newmsg) != -1) {
+           return msgnum;
+       } else {
+           linkerr = errno;
+
+#ifdef EISREMOTE
+           if (linkerr == EISREMOTE)
+               linkerr = EXDEV;
+#endif /* EISREMOTE */
+
+           /*
+            * Check if the file in our desired location is the same
+            * as the source file.  If so, then just leave it alone
+            * and return.  Otherwise, we will continue the main loop
+            * and try again at another slot (hghmsg+1).
+            */
+           if (linkerr == EEXIST) {
+               if (stat (msgfile, &st2) == 0 && stat (newmsg, &st1) == 0
+                   && st2.st_ino == st1.st_ino) {
+                   return msgnum;
+               } else {
+                   continue;
+               }
+           }
+
+           /*
+            * If link failed because we are trying to link
+            * across devices, then check if there is a message
+            * already in the desired location.  If so, then return
+            * error, else just copy the message.
+            */
+           if (linkerr == EXDEV) {
+               if (stat (newmsg, &st1) == 0) {
+                   advise (NULL, "message %s:%s already exists", newmsg);
+                   return -1;
+               } else {
+                   if ((infd = open (msgfile, O_RDONLY)) == -1) {
+                       advise (msgfile, "unable to open message %s");
+                       return -1;
+                   }
+                   fstat (infd, &st1);
+                   if ((outfd = creat (newmsg, (int) st1.st_mode & 0777)) == -1) {
+                       advise (newmsg, "unable to create");
+                       close (infd);
+                       return -1;
+                   }
+                   cpydata (infd, outfd, msgfile, newmsg);
+                   close (infd);
+                   close (outfd);
+                   return msgnum;
+               }
+           }
+
+           /*
+            * Else, some other type of link error,
+            * so just return error.
+            */
+           advise (newmsg, "error linking %s to", msgfile);
+           return -1;
+       }
+    }
+}
diff --git a/sbr/folder_delmsgs.c b/sbr/folder_delmsgs.c
new file mode 100644 (file)
index 0000000..e5374b5
--- /dev/null
@@ -0,0 +1,115 @@
+
+/*
+ * folder_delmsgs.c -- "remove" SELECTED messages from a folder
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+/*
+ * 1) If we are using an external rmmproc, then exec it.
+ * 2) Else if unlink_msgs is non-zero, then unlink the
+ *    SELECTED messages.
+ * 3) Else rename SELECTED messages by prefixing name
+ *    with a standard prefix.
+ *
+ * If there is an error, return -1, else return 0.
+ */
+
+int
+folder_delmsgs (struct msgs *mp, int unlink_msgs)
+{
+    pid_t pid;
+    int msgnum, vecp, retval = 0;
+    char buf[100], *dp, **vec;
+
+    /*
+     * If "rmmproc" is defined, exec it to remove messages.
+     */
+    if (rmmproc) {
+       /* Unset the EXISTS flag for each message to be removed */
+       for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
+           if (is_selected (mp, msgnum))
+               unset_exists (mp, msgnum);
+       }
+
+       /* Mark that the sequence information has changed */
+       mp->msgflags |= SEQMOD;
+
+       if (mp->numsel > MAXARGS - 2)
+           adios (NULL, "more than %d messages for %s exec", MAXARGS - 2,
+                  rmmproc);
+       vec = (char **) calloc ((size_t) (mp->numsel + 2), sizeof(*vec));
+       if (vec == NULL)
+           adios (NULL, "unable to allocate exec vector");
+       vecp = 1;
+       for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
+           if (is_selected (mp, msgnum) &&
+               !(vec[vecp++] = strdup (m_name (msgnum))))
+               adios (NULL, "strdup failed");
+       }
+       vec[vecp] = NULL;
+
+       fflush (stdout);
+       vec[0] = r1bindex (rmmproc, '/');
+
+       switch (pid = vfork()) {
+       case -1:
+           advise ("fork", "unable to");
+           return -1;
+
+       case 0:
+           execvp (rmmproc, vec);
+           fprintf (stderr, "unable to exec ");
+           perror (rmmproc);
+           _exit (-1);
+
+       default:
+           return (pidwait (pid, -1));
+       }
+    }
+
+    /*
+     * Either unlink or rename the SELECTED messages
+     */
+    for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
+       if (is_selected (mp, msgnum)) {
+           /* unselect message */
+           unset_selected (mp, msgnum);
+           mp->numsel--;
+
+           dp = m_name (msgnum);
+
+           if (unlink_msgs) {
+               /* just unlink the messages */
+               if (unlink (dp) == -1) {
+                   admonish (dp, "unable to unlink");
+                   retval = -1;
+                   continue;
+               }
+           } else {
+               /* or rename messages with standard prefix */
+               strncpy (buf, m_backup (dp), sizeof(buf));
+               if (rename (dp, buf) == -1) {
+                   admonish (buf, "unable to rename %s to", dp);
+                   retval = -1;
+                   continue;
+               }
+           }
+
+           /* If removal was successful, decrement message count */
+           unset_exists (mp, msgnum);
+           mp->nummsg--;
+       }
+    }
+
+    /* Sanity check */
+    if (mp->numsel != 0)
+       adios (NULL, "oops, mp->numsel should be 0");
+
+    /* Mark that the sequence information has changed */
+    mp->msgflags |= SEQMOD;
+
+    return retval;
+}
diff --git a/sbr/folder_free.c b/sbr/folder_free.c
new file mode 100644 (file)
index 0000000..1c1051d
--- /dev/null
@@ -0,0 +1,28 @@
+
+/*
+ * folder_free.c -- free a folder/message structure
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+void
+folder_free (struct msgs *mp)
+{
+    int i;
+
+    if (!mp)
+       return;
+
+    if (mp->foldpath)
+       free (mp->foldpath);
+
+    /* free the sequence names */
+    for (i = 0; mp->msgattrs[i]; i++)
+       free (mp->msgattrs[i]);
+
+    free (mp->msgstats);       /* free message status area   */
+    free (mp);                 /* free main folder structure */
+}
diff --git a/sbr/folder_pack.c b/sbr/folder_pack.c
new file mode 100644 (file)
index 0000000..05365a8
--- /dev/null
@@ -0,0 +1,86 @@
+
+/*
+ * folder_pack.c -- pack (renumber) the messages in a folder
+ *               -- into a contiguous range from 1 to n.
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+/*
+ * Pack the message in a folder.
+ * Return -1 if error, else return 0.
+ */
+
+int
+folder_pack (struct msgs **mpp, int verbose)
+{
+    int hole, msgnum, newcurrent = 0;
+    char newmsg[BUFSIZ], oldmsg[BUFSIZ];
+    struct msgs *mp;
+
+    mp = *mpp;
+
+    /*
+     * Just return if folder is empty.
+     */
+    if (mp->nummsg == 0)
+       return 0;
+
+    /*
+     * Make sure we have message status space allocated
+     * for all numbers from 1 to current high message.
+     */
+    if (mp->lowoff > 1) {
+       if ((mp = folder_realloc (mp, 1, mp->hghmsg)))
+           *mpp = mp;
+       else {
+           advise (NULL, "unable to allocate folder storage");
+           return -1;
+       }
+    }
+
+    for (msgnum = mp->lowmsg, hole = 1; msgnum <= mp->hghmsg; msgnum++) {
+       if (does_exist (mp, msgnum)) {
+           if (msgnum != hole) {
+               strncpy (newmsg, m_name (hole), sizeof(newmsg));
+               strncpy (oldmsg, m_name (msgnum), sizeof(oldmsg));
+               if (verbose)
+                   printf ("message %s becomes %s\n", oldmsg, newmsg);
+
+               /* move the message file */
+               if (rename (oldmsg, newmsg) == -1) {
+                   advise (newmsg, "unable to rename %s to", oldmsg);
+                   return -1;
+               }
+
+               /* check if this is the current message */
+               if (msgnum == mp->curmsg)
+                   newcurrent = hole;
+
+               /* copy the attribute flags for this message */
+               copy_msg_flags (mp, hole, msgnum);
+
+               if (msgnum == mp->lowsel)
+                   mp->lowsel = hole;
+               if (msgnum == mp->hghsel)
+                   mp->hghsel = hole;
+
+               /* mark that sequence information has been modified */
+               mp->msgflags |= SEQMOD;
+           }
+           hole++;
+       }
+    }
+
+    /* record the new number for the high/low message */
+    mp->lowmsg = 1;
+    mp->hghmsg = hole - 1;
+
+    /* update the "cur" sequence */
+    if (newcurrent != 0)
+       seq_setcur (mp, newcurrent);
+
+    return 0;
+}
diff --git a/sbr/folder_read.c b/sbr/folder_read.c
new file mode 100644 (file)
index 0000000..0d01293
--- /dev/null
@@ -0,0 +1,163 @@
+
+/*
+ * folder_read.c -- initialize folder structure and read folder
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+/* We allocate the `mi' array 1024 elements at a time */
+#define        NUMMSGS  1024
+
+/*
+ * 1) Create the folder/message structure
+ * 2) Read the directory (folder) and temporarily
+ *    record the numbers of the messages we have seen.
+ * 3) Then allocate the array for message attributes and
+ *    set the initial flags for all messages we've seen.
+ * 4) Read and initialize the sequence information.
+ */
+
+struct msgs *
+folder_read (char *name)
+{
+    int msgnum, prefix_len, len, *mi;
+    struct msgs *mp;
+    struct stat st;
+    struct dirent *dp;
+    DIR *dd;
+
+    name = m_mailpath (name);
+    if (!(dd = opendir (name))) {
+       free (name);
+       return NULL;
+    }
+
+    if (stat (name, &st) == -1) {
+       free (name);
+       return NULL;
+    }
+
+    /* Allocate the main structure for folder information */
+    if (!(mp = (struct msgs *) malloc ((size_t) sizeof(*mp))))
+       adios (NULL, "unable to allocate folder storage");
+
+    clear_folder_flags (mp);
+    mp->foldpath = name;
+    mp->lowmsg = 0;
+    mp->hghmsg = 0;
+    mp->curmsg = 0;
+    mp->lowsel = 0;
+    mp->hghsel = 0;
+    mp->numsel = 0;
+    mp->nummsg = 0;
+
+    if (access (name, W_OK) == -1 || st.st_uid != getuid())
+       set_readonly (mp);
+    prefix_len = strlen(BACKUP_PREFIX);
+
+    /*
+     * Allocate a temporary place to record the
+     * name of the messages in this folder.
+     */
+    len = NUMMSGS;
+    if (!(mi = (int *) malloc ((size_t) (len * sizeof(*mi)))))
+       adios (NULL, "unable to allocate storage");
+
+    while ((dp = readdir (dd))) {
+       if ((msgnum = m_atoi (dp->d_name))) {
+           /*
+            * Check if we need to allocate more
+            * temporary elements for message names.
+            */
+           if (mp->nummsg >= len) {
+               len += NUMMSGS;
+               if (!(mi = (int *) realloc (mi,
+                       (size_t) (len * sizeof(*mi))))) {
+                   adios (NULL, "unable to allocate storage");
+               }
+           }
+
+           /* Check if this is the first message we've seen */
+           if (mp->nummsg == 0) {
+               mp->lowmsg = msgnum;
+               mp->hghmsg = msgnum;
+           } else {
+               /* Check if this is it the highest or lowest we've seen? */
+               if (msgnum < mp->lowmsg)
+                  mp->lowmsg = msgnum;
+               if (msgnum > mp->hghmsg)
+                  mp->hghmsg = msgnum;
+           }
+
+           /*
+            * Now increment count, and record message
+            * number in a temporary place for now.
+            */
+           mi[mp->nummsg++] = msgnum;
+
+       } else {
+           switch (dp->d_name[0]) {
+               case '.': 
+               case ',': 
+#ifdef MHE
+               case '+': 
+#endif /* MHE */
+                   continue;
+
+               default: 
+                   /* skip any files beginning with backup prefix */
+                   if (!strncmp (dp->d_name, BACKUP_PREFIX, prefix_len))
+                       continue;
+
+                   /* skip the LINK file */
+                   if (!strcmp (dp->d_name, LINK))
+                       continue;
+
+                   /* indicate that there are other files in folder */
+                   set_other_files (mp);
+                   continue;
+           }
+       }
+    }
+
+    closedir (dd);
+    mp->lowoff = max (mp->lowmsg, 1);
+
+    /* Go ahead and allocate space for 100 additional messages. */
+    mp->hghoff = mp->hghmsg + 100;
+
+    /* for testing, allocate minimal necessary space */
+    /* mp->hghoff = max (mp->hghmsg, 1); */
+
+    /*
+     * Allocate space for status of each message.
+     */
+    if (!(mp->msgstats = malloc (MSGSTATSIZE(mp, mp->lowoff, mp->hghoff))))
+       adios (NULL, "unable to allocate storage for msgstats");
+
+    /*
+     * Clear all the flag bits for all the message
+     * status entries we just allocated.
+     */
+    for (msgnum = mp->lowoff; msgnum <= mp->hghoff; msgnum++)
+       clear_msg_flags (mp, msgnum);
+
+    /*
+     * Scan through the array of messages we've seen and
+     * setup the initial flags for those messages in the
+     * newly allocated mp->msgstats area.
+     */
+    for (msgnum = 0; msgnum < mp->nummsg; msgnum++)
+       set_exists (mp, mi[msgnum]);
+
+    free (mi);         /* We don't need this anymore    */
+
+    /*
+     * Read and initialize the sequence information.
+     */
+    seq_read (mp);
+
+    return mp;
+}
diff --git a/sbr/folder_realloc.c b/sbr/folder_realloc.c
new file mode 100644 (file)
index 0000000..f3130f1
--- /dev/null
@@ -0,0 +1,90 @@
+
+/*
+ * folder_realloc.c -- realloc a folder/msgs structure
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+/*
+ * Reallocate some of the space in the folder
+ * structure (currently just message status array).
+ *
+ * Return pointer to new folder structure.
+ * If error, return NULL.
+ */
+
+struct msgs *
+folder_realloc (struct msgs *mp, int lo, int hi)
+{
+    int msgnum;
+
+    /* sanity checks */
+    if (lo < 1)
+       adios (NULL, "BUG: called folder_realloc with lo (%d) < 1", lo);
+    if (hi < 1)
+       adios (NULL, "BUG: called folder_realloc with hi (%d) < 1", hi);
+    if (mp->nummsg > 0 && lo > mp->lowmsg)
+       adios (NULL, "BUG: called folder_realloc with lo (%d) > mp->lowmsg (%d)",
+              lo, mp->lowmsg);
+    if (mp->nummsg > 0 && hi < mp->hghmsg)
+       adios (NULL, "BUG: called folder_realloc with hi (%d) < mp->hghmsg (%d)",
+              hi, mp->hghmsg);
+
+    /* Check if we really need to reallocate anything */
+    if (lo == mp->lowoff && hi == mp->hghoff)
+       return mp;
+
+    if (lo == mp->lowoff) {
+       /*
+        * We are just extending (or shrinking) the end of message
+        * status array.  So we don't have to move anything and can
+        * just realloc the message status array.
+        */
+       if (!(mp->msgstats = realloc (mp->msgstats, MSGSTATSIZE(mp, lo, hi)))) {
+           advise (NULL, "unable to reallocate message storage");
+           return NULL;
+       }
+    } else {
+       /*
+        * We are changing the offset of the message status
+        * array.  So we will need to shift everything.
+        */
+       seqset_t *tmpstats;
+
+       /* first allocate the new message status space */
+       if (!(tmpstats = malloc (MSGSTATSIZE(mp, lo, hi)))) {
+           advise (NULL, "unable to reallocate message storage");
+           return NULL;
+       }
+
+       /* then copy messages status array with shift */
+       if (mp->nummsg > 0) {
+           for (msgnum = mp->lowmsg; msgnum <= mp->hghmsg; msgnum++)
+               tmpstats[msgnum - lo] = mp->msgstats[msgnum - mp->lowoff];
+       }
+       free(mp->msgstats);
+       mp->msgstats = tmpstats;
+    }
+
+    mp->lowoff = lo;
+    mp->hghoff = hi;
+
+    /*
+     * Clear all the flags for entries outside
+     * the current message range for this folder.
+     */
+    if (mp->nummsg > 0) {
+       for (msgnum = mp->lowoff; msgnum < mp->lowmsg; msgnum++)
+           clear_msg_flags (mp, msgnum);
+       for (msgnum = mp->hghmsg + 1; msgnum <= mp->hghoff; msgnum++)
+           clear_msg_flags (mp, msgnum);
+    } else {
+       /* no messages, so clear entire range */
+       for (msgnum = mp->lowoff; msgnum <= mp->hghoff; msgnum++)
+           clear_msg_flags (mp, msgnum);
+    }
+
+    return mp;
+}
diff --git a/sbr/gans.c b/sbr/gans.c
new file mode 100644 (file)
index 0000000..dcfb316
--- /dev/null
@@ -0,0 +1,49 @@
+
+/*
+ * gans.c -- get an answer from the user
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+int
+gans (char *prompt, struct swit *ansp)
+{
+    register int i;
+    register char *cp;
+    register struct swit *ap;
+    char ansbuf[BUFSIZ];
+
+    for (;;) {
+       printf ("%s", prompt);
+       fflush (stdout);
+       cp = ansbuf;
+       while ((i = getchar ()) != '\n') {
+           if (i == EOF)
+               return 0;
+           if (cp < &ansbuf[sizeof ansbuf - 1]) {
+#ifdef LOCALE
+               i = (isalpha(i) && isupper(i)) ? tolower(i) : i;
+#else
+               if (i >= 'A' && i <= 'Z')
+                   i += 'a' - 'A';
+#endif
+               *cp++ = i;
+           }
+       }
+       *cp = '\0';
+       if (ansbuf[0] == '?' || cp == ansbuf) {
+           printf ("Options are:\n");
+           for (ap = ansp; ap->sw; ap++)
+               printf ("  %s\n", ap->sw);
+           continue;
+       }
+       if ((i = smatch (ansbuf, ansp)) < 0) {
+           printf ("%s: %s.\n", ansbuf, i == -1 ? "unknown" : "ambiguous");
+           continue;
+       }
+       return i;
+    }
+}
diff --git a/sbr/getans.c b/sbr/getans.c
new file mode 100644 (file)
index 0000000..987ed3c
--- /dev/null
@@ -0,0 +1,75 @@
+
+/*
+ * getans.c -- get an answer from the user and return a string array
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/signals.h>
+#include <setjmp.h>
+#include <signal.h>
+
+static char ansbuf[BUFSIZ];
+static jmp_buf sigenv;
+
+/*
+ * static prototypes
+ */
+static RETSIGTYPE intrser (int);
+
+
+char **
+getans (char *prompt, struct swit *ansp)
+{
+    int i;
+    SIGNAL_HANDLER istat;
+    char *cp, **cpp;
+
+    if (!(setjmp (sigenv))) {
+       istat = SIGNAL (SIGINT, intrser);
+    } else {
+       SIGNAL (SIGINT, istat);
+       return NULL;
+    }
+
+    for (;;) {
+       printf ("%s", prompt);
+       fflush (stdout);
+       cp = ansbuf;
+       while ((i = getchar ()) != '\n') {
+           if (i == EOF)
+               longjmp (sigenv, 1);
+           if (cp < &ansbuf[sizeof ansbuf - 1])
+               *cp++ = i;
+       }
+       *cp = '\0';
+       if (ansbuf[0] == '?' || cp == ansbuf) {
+           printf ("Options are:\n");
+           print_sw (ALL, ansp, "");
+           continue;
+       }
+       cpp = brkstring (ansbuf, " ", NULL);
+       switch (smatch (*cpp, ansp)) {
+           case AMBIGSW: 
+               ambigsw (*cpp, ansp);
+               continue;
+           case UNKWNSW: 
+               printf (" -%s unknown. Hit <CR> for help.\n", *cpp);
+               continue;
+           default: 
+               SIGNAL (SIGINT, istat);
+               return cpp;
+       }
+    }
+}
+
+
+static RETSIGTYPE
+intrser (int i)
+{
+    /*
+     * should this be siglongjmp?
+     */
+    longjmp (sigenv, 1);
+}
diff --git a/sbr/getanswer.c b/sbr/getanswer.c
new file mode 100644 (file)
index 0000000..f38a834
--- /dev/null
@@ -0,0 +1,19 @@
+
+/*
+ * getanswer.c -- get a yes/no answer from the user
+ */
+
+#include <h/mh.h>
+#include <stdio.h>
+
+
+int
+getanswer (char *prompt)
+{
+    static int interactive = -1;
+
+    if (interactive < 0)
+       interactive = isatty (fileno (stdin)) ? 1 : 0;
+
+    return (interactive ? gans (prompt, anoyes) : 1);
+}
diff --git a/sbr/getarguments.c b/sbr/getarguments.c
new file mode 100644 (file)
index 0000000..1c61d9b
--- /dev/null
@@ -0,0 +1,48 @@
+
+/*
+ * getarguments.c -- Get the argument vector ready to go.
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+char **
+getarguments (char *invo_name, int argc, char **argv, int check_context)
+{
+    char *cp, **ap, **bp, **arguments;
+    int n = 0;
+
+    /*
+     * Check if profile/context specifies any arguments
+     */
+    if (check_context && (cp = context_find (invo_name))) {
+       cp = getcpy (cp);               /* make copy    */
+       ap = brkstring (cp, " ", "\n"); /* split string */
+
+       /* Count number of arguments split */
+       bp = ap;
+       while (*bp++)
+           n++;
+    }
+
+    if (!(arguments = (char **) malloc ((argc + n) * sizeof(*arguments))))
+       adios (NULL, "unable to malloc argument storage");
+    bp = arguments;
+
+    /* Copy any arguments from profile/context */
+    if (n > 0) {
+       while (*ap)
+           *bp++ = *ap++;
+     }
+
+    /* Copy arguments from command line */
+    argv++;
+    while (*argv)
+       *bp++ = *argv++;
+
+    /* Now NULL terminate the array */
+    *bp = NULL;
+
+    return arguments;
+}
diff --git a/sbr/getcpy.c b/sbr/getcpy.c
new file mode 100644 (file)
index 0000000..07bc365
--- /dev/null
@@ -0,0 +1,32 @@
+
+/*
+ * getcpy.c -- copy a string in managed memory
+ *
+ * THIS IS OBSOLETE.  NEED TO REPLACE ALL OCCURENCES
+ * OF GETCPY WITH STRDUP.  BUT THIS WILL REQUIRE
+ * CHANGING PARTS OF THE CODE TO DEAL WITH NULL VALUES.
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+char *
+getcpy (char *str)
+{
+    char *cp;
+    size_t len;
+
+    if (str) {
+       len = strlen(str) + 1;
+       if (!(cp = malloc (len)))
+           adios (NULL, "unable to allocate string storage");
+       memcpy (cp, str, len);
+    } else {
+       if (!(cp = malloc ((size_t) 1)))
+           adios (NULL, "unable to allocate string storage");
+       *cp = '\0';
+    }
+    return cp;
+}
diff --git a/sbr/getfolder.c b/sbr/getfolder.c
new file mode 100644 (file)
index 0000000..21db9a9
--- /dev/null
@@ -0,0 +1,32 @@
+
+/*
+ * getfolder.c -- get the current or default folder
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+char *
+getfolder(int wantcurrent)
+{
+    register char *folder;
+
+    /*
+     * If wantcurrent == 1, then try the current folder first
+     */
+    if (wantcurrent && (folder = context_find (pfolder)) && *folder != '\0')
+       return folder;
+
+    /*
+     * Else try the Inbox profile entry
+     */
+    if ((folder = context_find (inbox)) && *folder != '\0')
+       return folder;
+
+    /*
+     * Else return compile time default.
+     */
+    return defaultfolder;
+}
diff --git a/sbr/lock_file.c b/sbr/lock_file.c
new file mode 100644 (file)
index 0000000..fac895b
--- /dev/null
@@ -0,0 +1,555 @@
+
+/*
+ * lock.c -- routines to lock/unlock files
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/signals.h>
+
+#ifdef HAVE_ERRNO_H
+# include <errno.h>
+#endif
+
+#ifdef MMDFONLY
+# include <mmdfonly.h>
+# include <lockonly.h>
+#endif /* MMDFONLY */
+
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#else
+# include <sys/file.h>
+#endif
+
+#if defined(LOCKF_LOCKING) || defined(FLOCK_LOCKING)
+# include <sys/file.h>
+#endif
+
+#include <signal.h>
+
+extern int errno;
+
+#ifdef LOCKDIR
+char *lockdir = LOCKDIR;
+#endif
+
+/* Are we using any kernel locking? */
+#if defined (FLOCK_LOCKING) || defined(LOCKF_LOCKING) || defined(FCNTL_LOCKING)
+# define KERNEL_LOCKING
+#endif
+
+#ifdef DOT_LOCKING
+
+/* struct for getting name of lock file to create */
+struct lockinfo {
+    char curlock[BUFSIZ];
+    char tmplock[BUFSIZ];
+};
+
+/*
+ * Amount of time to wait before
+ * updating ctime of lock file.
+ */
+#define        NSECS 20
+
+/*
+ * How old does a lock file need to be
+ * before we remove it.
+ */
+#define RSECS 180
+
+/* struct for recording and updating locks */
+struct lock {
+    int        l_fd;
+    char *l_lock;
+    struct lock *l_next;
+};
+
+/* top of list containing all open locks */
+static struct lock *l_top = NULL;
+#endif /* DOT_LOCKING */
+
+/*
+ * static prototypes
+ */
+#ifdef KERNEL_LOCKING
+static int lkopen_kernel (char *, int, mode_t);
+#endif
+
+#ifdef DOT_LOCKING
+static int lkopen_dot (char *, int, mode_t);
+static int lockit (struct lockinfo *);
+static void lockname (char *, struct lockinfo *, int);
+static void timerON (char *, int);
+static void timerOFF (int);
+static RETSIGTYPE alrmser (int);
+#endif
+
+
+/*
+ * Base routine to open and lock a file,
+ * and return a file descriptor.
+ */
+
+int
+lkopen (char *file, int access, mode_t mode)
+{
+#ifdef KERNEL_LOCKING
+    return lkopen_kernel(file, access, mode);
+#endif
+
+#ifdef DOT_LOCKING
+    return lkopen_dot(file, access, mode);
+#endif
+}
+
+
+/*
+ * Base routine to close and unlock a file,
+ * given a file descriptor.
+ */
+
+int
+lkclose (int fd, char *file)
+{
+#ifdef FCNTL_LOCKING
+    struct flock buf;
+#endif
+
+#ifdef DOT_LOCKING
+    struct lockinfo lkinfo;
+#endif
+
+    if (fd == -1)
+       return 0;
+
+#ifdef FCNTL_LOCKING
+    buf.l_type   = F_UNLCK;
+    buf.l_whence = SEEK_SET;
+    buf.l_start  = 0;
+    buf.l_len    = 0;
+    fcntl(fd, F_SETLK, &buf);
+#endif
+
+#ifdef FLOCK_LOCKING
+    flock (fd, LOCK_UN);
+#endif
+
+#ifdef LOCKF_LOCKING
+    /* make sure we unlock the whole thing */
+    lseek (fd, (off_t) 0, SEEK_SET);
+    lockf (fd, F_ULOCK, 0L);
+#endif 
+
+#ifdef DOT_LOCKING
+    lockname (file, &lkinfo, 0);       /* get name of lock file */
+    unlink (lkinfo.curlock);           /* remove lock file      */
+    timerOFF (fd);                     /* turn off lock timer   */
+#endif
+
+    return (close (fd));
+}
+
+
+/*
+ * Base routine to open and lock a file,
+ * and return a FILE pointer
+ */
+
+FILE *
+lkfopen (char *file, char *mode)
+{
+    int fd, access;
+    FILE *fp;
+
+    if (strcmp (mode, "r") == 0)
+       access = O_RDONLY;
+    else
+       access = O_RDWR;
+
+    if ((fd = lkopen (file, access, 0)) == -1)
+       return NULL;
+
+    if ((fp = fdopen (fd, mode)) == NULL) {
+       close (fd);
+       return NULL;
+    }
+
+    return fp;
+}
+
+
+/*
+ * Base routine to close and unlock a file,
+ * given a FILE pointer
+ */
+
+int
+lkfclose (FILE *fp, char *file)
+{
+#ifdef FCNTL_LOCKING
+    struct flock buf;
+#endif
+
+#ifdef DOT_LOCKING
+    struct lockinfo lkinfo;
+#endif
+
+    if (fp == NULL)
+       return 0;
+
+#ifdef FCNTL_LOCKING
+    buf.l_type   = F_UNLCK;
+    buf.l_whence = SEEK_SET;
+    buf.l_start  = 0;
+    buf.l_len    = 0;
+    fcntl(fileno(fp), F_SETLK, &buf);
+#endif
+
+#ifdef FLOCK_LOCKING
+    flock (fileno(fp), LOCK_UN);
+#endif
+
+#ifdef LOCKF_LOCKING
+    /* make sure we unlock the whole thing */
+    fseek (fp, 0L, SEEK_SET);
+    lockf (fileno(fp), F_ULOCK, 0L);
+#endif
+
+#ifdef DOT_LOCKING
+    lockname (file, &lkinfo, 0);       /* get name of lock file */
+    unlink (lkinfo.curlock);           /* remove lock file      */
+    timerOFF (fileno(fp));             /* turn off lock timer   */
+#endif
+
+    return (fclose (fp));
+}
+
+
+#ifdef KERNEL_LOCKING
+
+/*
+ * open and lock a file, using kernel locking
+ */
+
+static int
+lkopen_kernel (char *file, int access, mode_t mode)
+{
+    int fd, i, j;
+
+# ifdef FCNTL_LOCKING
+    struct flock buf;
+# endif /* FCNTL_LOCKING */
+
+    for (i = 0; i < 5; i++) {
+
+# if defined(LOCKF_LOCKING) || defined(FCNTL_LOCKING)
+       /* remember the original mode */
+       j = access;
+
+       /* make sure we open at the beginning */
+       access &= ~O_APPEND;
+
+       /*
+        * We MUST have write permission or
+        * lockf/fcntl() won't work
+        */
+       if ((access & 03) == O_RDONLY) {
+           access &= ~O_RDONLY;
+           access |= O_RDWR;
+       }
+# endif /* LOCKF_LOCKING || FCNTL_LOCKING */
+
+       if ((fd = open (file, access | O_NDELAY, mode)) == -1)
+           return -1;
+
+# ifdef FCNTL_LOCKING
+       buf.l_type   = F_WRLCK;
+       buf.l_whence = SEEK_SET;
+       buf.l_start  = 0;
+       buf.l_len    = 0;
+       if (fcntl (fd, F_SETLK, &buf) != -1)
+           return fd;
+# endif
+
+# ifdef FLOCK_LOCKING
+       if (flock (fd, LOCK_EX | LOCK_NB) != -1)
+           return fd;
+# endif
+
+# ifdef LOCKF_LOCKING
+       if (lockf (fd, F_TLOCK, 0L) != -1) {
+           /* see if we should be at the end */
+           if (j & O_APPEND)
+               lseek (fd, (off_t) 0, SEEK_END);
+           return fd;
+       }
+# endif
+
+       j = errno;
+       close (fd);
+       sleep (5);
+    }
+
+    close (fd);
+    errno = j;
+    return -1;
+}
+
+#endif /* KERNEL_LOCKING */
+
+
+#ifdef DOT_LOCKING
+
+/*
+ * open and lock a file, using dot locking
+ */
+
+static int
+lkopen_dot (char *file, int access, mode_t mode)
+{
+    int i, fd;
+    time_t curtime;
+    struct lockinfo lkinfo;
+    struct stat st;
+
+    /* open the file */
+    if ((fd = open (file, access, mode)) == -1)
+       return -1;
+
+    /*
+     * Get the name of the eventual lock file, as well
+     * as a name for a temporary lock file.
+     */
+    lockname (file, &lkinfo, 1);
+
+    for (i = 0;;) {
+       /* attempt to create lock file */
+       if (lockit (&lkinfo) == 0) {
+           /* if successful, turn on timer and return */
+           timerON (lkinfo.curlock, fd);
+           return fd;
+       } else {
+           /*
+            * Abort locking, if we fail to lock after 5 attempts
+            * and are never able to stat the lock file.
+            */
+           if (stat (lkinfo.curlock, &st) == -1) {
+               if (i++ > 5)
+                   return -1;
+               sleep (5);
+           } else {
+               i = 0;
+               time (&curtime);
+
+               /* check for stale lockfile, else sleep */
+               if (curtime > st.st_ctime + RSECS)
+                   unlink (lkinfo.curlock);
+               else
+                   sleep (5);
+           }
+       }
+    }
+}
+
+/*
+ * Routine that actually tries to create
+ * the lock file.
+ */
+
+static int
+lockit (struct lockinfo *li)
+{
+    int fd;
+    char *curlock, *tmplock;
+
+#if 0
+    char buffer[128];
+#endif
+
+    curlock = li->curlock;
+    tmplock = li->tmplock;
+
+    /* create the temporary lock file */
+    if ((fd = creat(tmplock, 0600)) == -1)
+       return -1;
+
+#if 0
+    /* write our process id into lock file */
+    snprintf (buffer, sizeof(buffer), "nmh lock: pid %d\n", (int) getpid());
+    write(fd, buffer, strlen(buffer) + 1);
+#endif
+
+    close (fd);
+
+    /*
+     * Now try to create the real lock file
+     * by linking to the temporary file.
+     */
+    fd = link(tmplock, curlock);
+    unlink(tmplock);
+
+    return (fd == -1 ? -1 : 0);
+}
+
+/*
+ * Get name of lock file, and temporary lock file
+ */
+
+static void
+lockname (char *file, struct lockinfo *li, int isnewlock)
+{
+    int bplen, tmplen;
+    char *bp, *cp;
+
+#if 0
+    struct stat st;
+#endif
+
+    if ((cp = strrchr (file, '/')) == NULL || *++cp == 0)
+       cp = file;
+
+    bp = li->curlock;
+    bplen = 0;
+#ifdef LOCKDIR
+    snprintf (bp, sizeof(li->curlock), "%s/", lockdir);
+    tmplen = strlen (bp);
+    bp    += tmplen;
+    bplen += tmplen;
+#else
+    if (cp != file) {
+       snprintf (bp, sizeof(li->curlock), "%.*s", cp - file, file);
+       tmplen = strlen (bp);
+       bp    += tmplen;
+       bplen += tmplen;
+    }
+#endif
+
+#if 0
+    /*
+     * mmdf style dot locking.  Currently not supported.
+     * If we start supporting mmdf style dot locking,
+     * we will need to change the return value of lockname
+     */
+    if (stat (file, &st) == -1)
+       return -1;
+
+    snprintf (bp, sizeof(li->curlock) - bplen, "LCK%05d.%05d",
+       st.st_dev, st.st_ino);
+#endif
+
+    snprintf (bp, sizeof(li->curlock) - bplen, "%s.lock", cp);
+
+    /*
+     * If this is for a new lock, create a name for
+     * the temporary lock file for lockit()
+     */
+    if (isnewlock) {
+       if ((cp = strrchr (li->curlock, '/')) == NULL || *++cp == 0)
+           strncpy (li->tmplock, ",LCK.XXXXXX", sizeof(li->tmplock));
+       else
+           snprintf (li->tmplock, sizeof(li->tmplock), "%.*s,LCK.XXXXXX",
+                    cp - li->curlock, li->curlock);
+       mktemp (li->tmplock);
+       unlink (li->tmplock);   /* remove any stray */
+    }
+}
+
+
+/*
+ * Add new lockfile to the list of open lockfiles
+ * and start the lock file timer.
+ */
+
+static void
+timerON (char *curlock, int fd)
+{
+    struct lock *lp;
+    size_t len;
+
+    if (!(lp = (struct lock *) malloc (sizeof(*lp))))
+       return;
+
+    len = strlen(curlock) + 1;
+    lp->l_fd = fd;
+    if (!(lp->l_lock = malloc (len))) {
+       free ((char *) lp);
+       return;
+    }
+    memcpy (lp->l_lock, curlock, len);
+    lp->l_next = l_top;
+
+    if (!l_top) {
+       /* perhaps SIGT{STP,TIN,TOU} ? */
+       SIGNAL (SIGALRM, alrmser);
+       alarm (NSECS);
+    }
+
+    l_top = lp;
+}
+
+
+/*
+ * Search through the list of lockfiles for the
+ * current lockfile, and remove it from the list.
+ */
+
+static void
+timerOFF (int fd)
+{
+    struct lock *pp, *lp;
+
+    alarm(0);
+
+    if (l_top) {
+       for (pp = lp = l_top; lp; pp = lp, lp = lp->l_next) {
+           if (lp->l_fd == fd)
+               break;
+       }
+       if (lp) {
+           if (lp == l_top)
+               l_top = lp->l_next;
+           else
+               pp->l_next = lp->l_next;
+
+           free (lp->l_lock);
+           free (lp);
+       }
+    }
+
+    /* if there are locks left, restart timer */
+    if (l_top)
+       alarm (NSECS);
+}
+
+
+/*
+ * If timer goes off, we update the ctime of all open
+ * lockfiles, so another command doesn't remove them.
+ */
+
+static RETSIGTYPE
+alrmser (int sig)
+{
+    int j;
+    char *lockfile;
+    struct lock *lp;
+
+#ifndef        RELIABLE_SIGNALS
+    SIGNAL (SIGALRM, alrmser);
+#endif
+
+    /* update the ctime of all the lock files */
+    for (lp = l_top; lp; lp = lp->l_next) {
+       lockfile = lp->l_lock;
+       if (*lockfile && (j = creat (lockfile, 0600)) != -1)
+           close (j);
+    }
+
+    /* restart the alarm */
+    alarm (NSECS);
+}
+
+#endif /* DOT_LOCKING */
diff --git a/sbr/m_atoi.c b/sbr/m_atoi.c
new file mode 100644 (file)
index 0000000..b1b5133
--- /dev/null
@@ -0,0 +1,32 @@
+
+/*
+ * m_atoi.c -- Parse a string representation of a message number, and
+ *          -- return the numeric value of the message.  If the string
+ *          -- contains any non-digit characters, then return 0.
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+int
+m_atoi (char *str)
+{
+    int i;
+    char *cp;
+
+    for (i = 0, cp = str; *cp; cp++) {
+#ifdef LOCALE
+       if (!isdigit(*cp))
+#else
+       if (*cp < '0' || *cp > '9')
+#endif
+           return 0;
+
+       i *= 10;
+       i += (*cp - '0');
+    }
+
+    return i;
+}
diff --git a/sbr/m_backup.c b/sbr/m_backup.c
new file mode 100644 (file)
index 0000000..aa9edc8
--- /dev/null
@@ -0,0 +1,26 @@
+
+/*
+ * m_backup.c -- construct a backup file
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+char *
+m_backup (char *file)
+{
+    char *cp;
+    static char buffer[BUFSIZ];
+
+    if ((cp = r1bindex(file, '/')) == file)
+       snprintf(buffer, sizeof(buffer), "%s%s",
+               BACKUP_PREFIX, cp);
+    else
+       snprintf(buffer, sizeof(buffer), "%.*s%s%s", cp - file, file,
+               BACKUP_PREFIX, cp);
+
+    unlink(buffer);
+    return buffer;
+}
diff --git a/sbr/m_convert.c b/sbr/m_convert.c
new file mode 100644 (file)
index 0000000..e9a5d18
--- /dev/null
@@ -0,0 +1,443 @@
+
+/*
+ * m_convert.c -- parse a message range or sequence and set SELECTED
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+/*
+ * error codes for sequence
+ * and message range processing
+ */
+#define        BADMSG  (-2)
+#define        BADRNG  (-3)
+#define        BADNEW  (-4)
+#define        BADNUM  (-5)
+#define        BADLST  (-6)
+
+#define        FIRST   1
+#define        LAST    2
+
+#define        getnew(mp) (mp->hghmsg + 1)
+
+static int convdir;    /* convert direction */
+static char *delimp;
+
+/*
+ * static prototypes
+ */
+static int m_conv (struct msgs *, char *, int);
+static int attr (struct msgs *, char *);
+
+
+int
+m_convert (struct msgs *mp, char *name)
+{
+    int first, last, found, range, err;
+    char *bp, *cp;
+
+    /* check if user defined sequence */
+    err = attr (mp, cp = name);
+
+    if (err == -1)
+       return 0;
+    else if (err < 0)
+       goto badmsg;
+    else if (err > 0)
+       return 1;
+    /*
+     * else err == 0, so continue
+     */
+
+    found = 0;
+
+    /*
+     * Check for special "new" sequence, which
+     * is valid only if ALLOW_NEW is set.
+     */
+    if ((mp->msgflags & ALLOW_NEW) && !strcmp (cp, "new")) {
+       if ((err = first = getnew (mp)) <= 0)
+           goto badmsg;
+       else
+           goto single;
+    }
+
+    if (!strcmp (cp, "all"))
+       cp = "first-last";
+
+    if ((err = first = m_conv (mp, cp, FIRST)) <= 0)
+       goto badmsg;
+
+    cp = delimp;
+    if (*cp != '\0' && *cp != '-' && *cp != ':') {
+badelim:
+       advise (NULL, "illegal argument delimiter: `%c'(0%o)", *delimp, *delimp);
+       return 0;
+    }
+
+    if (*cp == '-') {
+       cp++;
+       if ((err = last = m_conv (mp, cp, LAST)) <= 0) {
+badmsg:
+           switch (err) {
+           case BADMSG: 
+               advise (NULL, "no %s message", cp);
+               break;
+
+           case BADNUM: 
+               advise (NULL, "message %s doesn't exist", cp);
+               break;
+
+           case BADRNG: 
+               advise (NULL, "message %s out of range 1-%d", cp, mp->hghmsg);
+               break;
+
+           case BADLST: 
+badlist:
+               advise (NULL, "bad message list %s", name);
+               break;
+
+           case BADNEW:
+               advise (NULL, "folder full, no %s message", name);
+               break;
+
+           default: 
+               advise (NULL, "no messages match specification");
+           }
+           return 0;
+       }
+
+       if (last < first)
+           goto badlist;
+       if (*delimp)
+           goto badelim;
+       if (first > mp->hghmsg || last < mp->lowmsg) {
+rangerr:
+           advise (NULL, "no messages in range %s", name);
+           return 0;
+       }
+
+       /* tighten the range to search */
+       if (last > mp->hghmsg)
+           last = mp->hghmsg;
+       if (first < mp->lowmsg)
+           first = mp->lowmsg;
+
+    } else if (*cp == ':') {
+       cp++;
+       if (*cp == '-') {
+           convdir = -1;
+           cp++;
+       } else {
+           if (*cp == '+') {
+               convdir = 1;
+               cp++;
+           }
+       }
+       if ((range = atoi (bp = cp)) == 0)
+           goto badlist;
+       while (isdigit (*bp))
+           bp++;
+       if (*bp)
+           goto badelim;
+       if ((convdir > 0 && first > mp->hghmsg)
+           || (convdir < 0 && first < mp->lowmsg))
+           goto rangerr;
+
+       /* tighten the range to search */
+       if (first < mp->lowmsg)
+           first = mp->lowmsg;
+       if (first > mp->hghmsg)
+           first = mp->hghmsg;
+
+       for (last = first;
+            last >= mp->lowmsg && last <= mp->hghmsg;
+            last += convdir)
+           if (does_exist (mp, last))
+               if (--range <= 0)
+                   break;
+       if (last < mp->lowmsg)
+           last = mp->lowmsg;
+       if (last > mp->hghmsg)
+           last = mp->hghmsg;
+       if (last < first) {
+           range = last;
+           last = first;
+           first = range;
+       }
+    } else {
+
+single:
+       /*
+        * Single Message
+        *
+        * If ALLOW_NEW is set, then allow selecting of an
+        * empty slot.  If ALLOW_NEW is not set, then we
+        * check if message is in-range and exists.
+        */
+       if (mp->msgflags & ALLOW_NEW) {
+           set_select_empty (mp, first);
+       } else {
+           if (first > mp->hghmsg
+               || first < mp->lowmsg
+               || !(does_exist (mp, first))) {
+               if (!strcmp (name, "cur") || !strcmp (name, "."))
+                   advise (NULL, "no %s message", name);
+               else
+                   advise (NULL, "message %d doesn't exist", first);
+               return 0;
+           }
+       }
+       last = first;   /* range of 1 */
+    }
+
+    /*
+     * Cycle through the range and select the messages
+     * that exist.  If ALLOW_NEW is set, then we also check
+     * if we are selecting an empty slot.
+     */
+    for (; first <= last; first++) {
+       if (does_exist (mp, first) ||
+           ((mp->msgflags & ALLOW_NEW) && is_select_empty (mp, first))) {
+           if (!is_selected (mp, first)) {
+               set_selected (mp, first);
+               mp->numsel++;
+               if (mp->lowsel == 0 || first < mp->lowsel)
+                   mp->lowsel = first;
+               if (first > mp->hghsel)
+                   mp->hghsel = first;
+           }
+           found++;
+       }
+    }
+
+    if (!found)
+       goto rangerr;
+
+    return 1;
+}
+
+/*
+ * Convert the various message names to
+ * there numeric value.
+ *
+ * n     (integer)
+ * prev
+ * next
+ * first
+ * last
+ * cur
+ * .     (same as cur)
+ */
+
+static int
+m_conv (struct msgs *mp, char *str, int call)
+{
+    register int i;
+    register char *cp, *bp;
+    char buf[16];
+
+    convdir = 1;
+    cp = bp = str;
+    if (isdigit (*cp)) {
+       while (isdigit (*bp))
+           bp++;
+       delimp = bp;
+       i = atoi (cp);
+
+       if (i <= mp->hghmsg)
+           return i;
+       else if (*delimp || call == LAST)
+           return mp->hghmsg + 1;
+       else if (mp->msgflags & ALLOW_NEW)
+           return BADRNG;
+       else
+           return BADNUM;
+    }
+
+#ifdef LOCALE
+    /* doesn't enforce lower case */
+    for (bp = buf; (isalpha(*cp) || *cp == '.')
+               && (bp - buf < sizeof(buf) - 1); )
+#else
+    for (bp = buf; ((*cp >= 'a' && *cp <= 'z') || *cp == '.')
+               && (bp - buf < sizeof(buf) - 1); )
+#endif /* LOCALE */
+    {
+       *bp++ = *cp++;
+    }
+    *bp++ = '\0';
+    delimp = cp;
+
+    if (!strcmp (buf, "first"))
+       return (mp->hghmsg || !(mp->msgflags & ALLOW_NEW)
+               ? mp->lowmsg : BADMSG);
+
+    if (!strcmp (buf, "last")) {
+       convdir = -1;
+       return (mp->hghmsg || !(mp->msgflags & ALLOW_NEW) ? mp->hghmsg : BADMSG);
+    }
+
+    if (!strcmp (buf, "cur") || !strcmp (buf, "."))
+       return (mp->curmsg > 0 ? mp->curmsg : BADMSG);
+
+    if (!strcmp (buf, "prev")) {
+       convdir = -1;
+       for (i = (mp->curmsg <= mp->hghmsg) ? mp->curmsg - 1 : mp->hghmsg;
+               i >= mp->lowmsg; i--) {
+           if (does_exist (mp, i))
+               return i;
+       }
+       return BADMSG;
+    }
+
+    if (!strcmp (buf, "next")) {
+       for (i = (mp->curmsg >= mp->lowmsg) ? mp->curmsg + 1 : mp->lowmsg;
+               i <= mp->hghmsg; i++) {
+           if (does_exist (mp, i))
+               return i;
+       }
+       return BADMSG;
+    }
+
+    return BADLST;
+}
+
+/*
+ * Handle user defined sequences.
+ * They can take the following forms:
+ *
+ * seq
+ * seq:prev
+ * seq:next
+ * seq:first
+ * seq:last
+ * seq:+n
+ * seq:-n
+ * seq:n
+ */
+
+static int
+attr (struct msgs *mp, char *cp)
+{
+    register char *dp;
+    char *bp = NULL;
+    register int i, j;
+    int found,
+       inverted = 0,
+       range = 0,              /* no range */
+       first = 0;
+
+    /* hack for "cur-name", "cur-n", etc. */
+    if (!strcmp (cp, "cur"))
+       return 0;
+    if (ssequal ("cur:", cp))  /* this code need to be rewritten... */
+       return 0;
+
+    /* Check for sequence negation */
+    if ((dp = context_find (nsequence)) && *dp != '\0' && ssequal (dp, cp)) {
+       inverted = 1;
+       cp += strlen (dp);
+    }
+
+    convdir = 1;       /* convert direction */
+
+    for (dp = cp; *dp && isalnum(*dp); dp++)
+       continue;
+
+    if (*dp == ':') {
+       bp = dp++;
+       range = 1;
+
+       /*
+        * seq:prev  (or)
+        * seq:next  (or)
+        * seq:first (or)
+        * seq:last
+        */
+       if (isalpha (*dp)) {
+           if (!strcmp (dp, "prev")) {
+               convdir = -1;
+               first = (mp->curmsg > 0) && (mp->curmsg <= mp->hghmsg)
+                       ? mp->curmsg - 1
+                       : mp->hghmsg;
+           }
+           else if (!strcmp (dp, "next")) {
+               convdir = 1;
+               first = (mp->curmsg >= mp->lowmsg)
+                           ? mp->curmsg + 1
+                           : mp->lowmsg;
+           }
+           else if (!strcmp (dp, "first")) {
+               convdir = 1;
+           }
+           else if (!strcmp (dp, "last")) {
+               convdir = -1;
+           }
+           else
+               return BADLST;
+       } else {
+           /*
+            * seq:n  (or)
+            * seq:+n (or)
+            * seq:-n
+             */
+           if (*dp == '+')
+               dp++;
+           else if (*dp == '-') {
+               dp++;
+               convdir = -1;
+           }
+           if ((range = atoi(dp)) == 0)
+               return BADLST;
+           while (isdigit (*dp))
+               dp++;
+           if (*dp)
+               return BADLST;
+       }
+
+       *bp = '\0';     /* temporarily terminate sequence name */
+    }
+
+    i = seq_getnum (mp, cp);   /* get index of sequence */
+
+    if (bp)
+       *bp = ':';              /* restore sequence name */
+    if (i == -1)
+       return 0;
+
+    found = 0; /* count the number we select for this argument */
+
+    for (j = first ? first : (convdir > 0) ? mp->lowmsg : mp->hghmsg;
+               j >= mp->lowmsg && j <= mp->hghmsg; j += convdir) {
+       if (does_exist (mp, j)
+               && inverted ? !in_sequence (mp, i, j) : in_sequence (mp, i, j)) {
+           if (!is_selected (mp, j)) {
+               set_selected (mp, j);
+               mp->numsel++;
+               if (mp->lowsel == 0 || j < mp->lowsel)
+                   mp->lowsel = j;
+               if (j > mp->hghsel)
+                   mp->hghsel = j;
+           }
+           found++;
+
+           /*
+            * If we have a range, then break out
+            * once we've found enough.
+             */
+           if (range && found >= range)
+               break;
+       }
+    }
+
+    if (found > 0)
+       return found;
+
+    if (first)
+       return BADMSG;
+    advise (NULL, "sequence %s %s", cp, inverted ? "full" : "empty");
+    return -1;
+}
diff --git a/sbr/m_draft.c b/sbr/m_draft.c
new file mode 100644 (file)
index 0000000..712e173
--- /dev/null
@@ -0,0 +1,88 @@
+
+/*
+ * m_draft.c -- construct the name of a draft message
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <errno.h>
+
+extern int errno;
+
+
+char *
+m_draft (char *folder, char *msg, int use, int *isdf)
+{
+    register char *cp;
+    register struct msgs *mp;
+    struct stat st;
+    static char buffer[BUFSIZ];
+
+    if (*isdf == -1 || folder == NULL || *folder == '\0') {
+       if (*isdf == -1 || (cp = context_find ("Draft-Folder")) == NULL) {
+           *isdf = 0;
+           return m_maildir (msg && *msg ? msg : draft);
+       } else {
+           folder = path (*cp == '+' || *cp == '@' ? cp + 1 : cp,
+                   *cp != '@' ? TFOLDER : TSUBCWF);
+       }
+    }
+    *isdf = 1;
+    
+    chdir (m_maildir (""));
+    strncpy (buffer, m_maildir (folder), sizeof(buffer));
+    if (stat (buffer, &st) == -1) {
+       if (errno != ENOENT)
+           adios (buffer, "error on folder");
+       cp = concat ("Create folder \"", buffer, "\"? ", NULL);
+       if (!getanswer (cp))
+           done (0);
+       free (cp);
+       if (!makedir (buffer))
+           adios (NULL, "unable to create folder %s", buffer);
+    }
+
+    if (chdir (buffer) == -1)
+       adios (buffer, "unable to change directory to");
+
+    if (!(mp = folder_read (folder)))
+       adios (NULL, "unable to read folder %s", folder);
+
+    /*
+     * Make sure we have enough message status space for all
+     * the message numbers from 1 to "new", since we might
+     * select an empty slot.  If we add more space at the
+     * end, go ahead and add 10 additional slots.
+     */
+    if (mp->hghmsg >= mp->hghoff) {
+       if (!(mp = folder_realloc (mp, 1, mp->hghmsg + 10)))
+           adios (NULL, "unable to allocate folder storage");
+    } else if (mp->lowoff > 1) {
+       if (!(mp = folder_realloc (mp, 1, mp->hghoff)))
+           adios (NULL, "unable to allocate folder storage");
+    }
+
+    mp->msgflags |= ALLOW_NEW; /* allow the "new" sequence */
+
+    /*
+     * If we have been give a valid message name, then use that.
+     * Else, if we are given the "use" option, then use the
+     * current message.  Else, use special sequence "new".
+     */
+    if (!m_convert (mp, msg && *msg ? msg : use ? "cur" : "new"))
+       done (1);
+    seq_setprev (mp);
+
+    if (mp->numsel > 1)
+       adios (NULL, "only one message draft at a time!");
+
+    snprintf (buffer, sizeof(buffer), "%s/%s", mp->foldpath, m_name (mp->lowsel));
+    cp = buffer;
+
+    seq_setcur (mp, mp->lowsel);/* set current message for folder */
+    seq_save (mp);             /* synchronize message sequences  */
+    folder_free (mp);          /* free folder/message structure  */
+
+    return cp;
+}
diff --git a/sbr/m_getfld.c b/sbr/m_getfld.c
new file mode 100644 (file)
index 0000000..8697f20
--- /dev/null
@@ -0,0 +1,748 @@
+
+/*
+ * m_getfld.c -- read/parse a message
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <zotnet/mts/mts.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
+   an inter-message delimiter.  It really is four CTRL-A:s followed by a
+   newline.  Unfortunately, MMDF will convert this delimiter *inside* a
+   message to a CTRL-B followed by three CTRL-A:s and a newline.  This
+   caused the old version of m_getfld() to declare eom prematurely.  The
+   fix was a lot slower than
+
+               c == '\001' && peekc (iob) == '\001'
+
+   but it worked, and to increase generality, MBOX style maildrops could
+   be parsed as well.  Unfortunately the speed issue finally caught up with
+   us since this routine is at the very heart of MH.
+
+   To speed things up considerably, the routine Eom() was made an auxilary
+   function called by the macro eom().  Unless we are bursting a maildrop,
+   the eom() macro returns FALSE saying we aren't at the end of the
+   message.
+
+   The next thing to do is to read the mts.conf file and initialize
+   delimiter[] and delimlen accordingly...
+
+   After mhl was made a built-in in msh, m_getfld() worked just fine
+   (using m_unknown() at startup).  Until one day: a message which was
+   the result of a bursting was shown. Then, since the burst boundaries
+   aren't CTRL-A:s, m_getfld() would blinding plunge on past the boundary.
+   Very sad.  The solution: introduce m_eomsbr().  This hook gets called
+   after the end of each line (since testing for eom involves an fseek()).
+   This worked fine, until one day: a message with no body portion arrived.
+   Then the
+
+                  while (eom (c = Getc (iob), iob))
+                       continue;
+
+   loop caused m_getfld() to return FMTERR.  So, that logic was changed to
+   check for (*eom_action) and act accordingly.
+
+   This worked fine, until one day: someone didn't use four CTRL:A's as
+   their delimiters.  So, the bullet got bit and we read mts.h and
+   continue to struggle on.  It's not that bad though, since the only time
+   the code gets executed is when inc (or msh) calls it, and both of these
+   have already called mts_init().
+
+   ------------------------
+   (Written by Van Jacobson for the mh6 m_getfld, January, 1986):
+
+   This routine was accounting for 60% of the cpu time used by most mh
+   programs.  I spent a bit of time tuning and it now accounts for <10%
+   of the time used.  Like any heavily tuned routine, it's a bit
+   complex and you want to be sure you understand everything that it's
+   doing before you start hacking on it.  Let me try to emphasize
+   that:  every line in this atrocity depends on every other line,
+   sometimes in subtle ways.  You should understand it all, in detail,
+   before trying to change any part.  If you do change it, test the
+   result thoroughly (I use a hand-constructed test file that exercises
+   all the ways a header name, header body, header continuation,
+   header-body separator, body line and body eom can align themselves
+   with respect to a buffer boundary).  "Minor" bugs in this routine
+   result in garbaged or lost mail.
+
+   If you hack on this and slow it down, I, my children and my
+   children's children will curse you.
+
+   This routine gets used on three different types of files: normal,
+   single msg files, "packed" unix or mmdf mailboxs (when used by inc)
+   and packed, directoried bulletin board files (when used by msh).
+   The biggest impact of different file types is in "eom" testing.  The
+   code has been carefully organized to test for eom at appropriate
+   times and at no other times (since the check is quite expensive).
+   I have tried to arrange things so that the eom check need only be
+   done on entry to this routine.  Since an eom can only occur after a
+   newline, this is easy to manage for header fields.  For the msg
+   body, we try to efficiently search the input buffer to see if
+   contains the eom delimiter.  If it does, we take up to the
+   delimiter, otherwise we take everything in the buffer.  (The change
+   to the body eom/copy processing produced the most noticeable
+   performance difference, particularly for "inc" and "show".)
+
+   There are three qualitatively different things this routine busts
+   out of a message: field names, field text and msg bodies.  Field
+   names are typically short (~8 char) and the loop that extracts them
+   might terminate on a colon, newline or max width.  I considered
+   using a Vax "scanc" to locate the end of the field followed by a
+   "bcopy" but the routine call overhead on a Vax is too large for this
+   to work on short names.  If Berkeley ever makes "inline" part of the
+   C optimiser (so things like "scanc" turn into inline instructions) a
+   change here would be worthwhile.
+
+   Field text is typically 60 - 100 characters so there's (barely)
+   a win in doing a routine call to something that does a "locc"
+   followed by a "bmove".  About 30% of the fields have continuations
+   (usually the 822 "received:" lines) and each continuation generates
+   another routine call.  "Inline" would be a big win here, as well.
+
+   Messages, as of this writing, seem to come in two flavors: small
+   (~1K) and long (>2K).  Most messages have 400 - 600 bytes of headers
+   so message bodies average at least a few hundred characters.
+   Assuming your system uses reasonably sized stdio buffers (1K or
+   more), this routine should be able to remove the body in large
+   (>500 byte) chunks.  The makes the cost of a call to "bcopy"
+   small but there is a premium on checking for the eom in packed
+   maildrops.  The eom pattern is always a simple string so we can
+   construct an efficient pattern matcher for it (e.g., a Vax "matchc"
+   instruction).  Some thought went into recognizing the start of
+   an eom that has been split across two buffers.
+
+   This routine wants to deal with large chunks of data so, rather
+   than "getc" into a local buffer, it uses stdio's buffer.  If
+   you try to use it on a non-buffered file, you'll get what you
+   deserve.  This routine "knows" that struct FILEs have a _ptr
+   and a _cnt to describe the current state of the buffer and
+   it knows that _filbuf ignores the _ptr & _cnt and simply fills
+   the buffer.  If stdio on your system doesn't work this way, you
+   may have to make small changes in this routine.
+   
+   This routine also "knows" that an EOF indication on a stream is
+   "sticky" (i.e., you will keep getting EOF until you reposition the
+   stream).  If your system doesn't work this way it is broken and you
+   should complain to the vendor.  As a consequence of the sticky
+   EOF, this routine will never return any kind of EOF status when
+   there is data in "name" or "buf").
+  */
+
+
+/*
+ * static prototypes
+ */
+static int m_Eom (int, FILE *);
+static unsigned char *matchc(int, char *, int, char *);
+static unsigned char *locc(int, unsigned char *, unsigned char);
+
+#define Getc(iob)      getc(iob)
+#define eom(c,iob)     (msg_style != MS_DEFAULT && \
+                        (((c) == *msg_delim && m_Eom(c,iob)) ||\
+                         (eom_action && (*eom_action)(c))))
+
+static unsigned char **pat_map;
+
+/*
+ * defined in sbr/m_msgdef.c = 0
+ * This is a disgusting hack for "inc" so it can know how many
+ * characters were stuffed in the buffer on the last call
+ * (see comments in uip/scansbr.c).
+ */
+extern int msg_count;
+
+/*
+ * defined in sbr/m_msgdef.c = MS_DEFAULT
+ */
+extern int msg_style;
+
+/*
+ * The "full" delimiter string for a packed maildrop consists
+ * of a newline followed by the actual delimiter.  E.g., the
+ * full string for a Unix maildrop would be: "\n\nFrom ".
+ * "Fdelim" points to the start of the full string and is used
+ * in the BODY case of the main routine to search the buffer for
+ * a possible eom.  Msg_delim points to the first character of
+ * the actual delim. string (i.e., fdelim+1).  Edelim
+ * points to the 2nd character of actual delimiter string.  It
+ * is used in m_Eom because the first character of the string
+ * has been read and matched before m_Eom is called.
+ */
+extern char *msg_delim;         /* defined in sbr/m_msgdef.c = "" */
+static unsigned char *fdelim;
+static unsigned char *delimend;
+static int fdelimlen;
+static unsigned char *edelim;
+static int edelimlen;
+
+static int (*eom_action)() = NULL;
+
+#ifdef _FSTDIO
+# define _ptr    _p            /* Gag   */
+# define _cnt    _r            /* Retch */
+# define _filbuf __srget       /* Puke  */
+#endif
+
+#ifdef SCO_5_STDIO
+# define _ptr  __ptr
+# define _cnt  __cnt
+# define _base __base
+# define _filbuf(fp)  ((fp)->__cnt = 0, __filbuf(fp))
+#endif
+
+
+int
+m_getfld (int state, unsigned char *name, unsigned char *buf,
+          int bufsz, FILE *iob)
+{
+    register unsigned char  *bp, *cp, *ep, *sp;
+    register int cnt, c, i, j;
+
+    if ((c = Getc(iob)) < 0) {
+       msg_count = 0;
+       *buf = 0;
+       return FILEEOF;
+    }
+    if (eom (c, iob)) {
+       if (! eom_action) {
+           /* flush null messages */
+           while ((c = Getc(iob)) >= 0 && eom (c, iob))
+               ;
+           if (c >= 0)
+               ungetc(c, iob);
+       }
+       msg_count = 0;
+       *buf = 0;
+       return FILEEOF;
+    }
+
+    switch (state) {
+       case FLDEOF: 
+       case BODYEOF: 
+       case FLD: 
+           if (c == '\n' || c == '-') {
+               /* we hit the header/body separator */
+               while (c != '\n' && (c = Getc(iob)) >= 0)
+                   ;
+
+               if (c < 0 || (c = Getc(iob)) < 0 || eom (c, iob)) {
+                   if (! eom_action) {
+                       /* flush null messages */
+                       while ((c = Getc(iob)) >= 0 && eom (c, iob))
+                           ;
+                       if (c >= 0)
+                           ungetc(c, iob);
+                   }
+                   msg_count = 0;
+                   *buf = 0;
+                   return FILEEOF;
+               }
+               state = BODY;
+               goto body;
+           }
+           /*
+            * get the name of this component.  take characters up
+            * to a ':', a newline or NAMESZ-1 characters, whichever
+            * comes first.  
+            */
+           cp = name;
+           i = NAMESZ - 1;
+           for (;;) {
+#ifdef LINUX_STDIO
+               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;
+#else
+               bp = sp = (unsigned char *) iob->_ptr - 1;
+               j = (cnt = iob->_cnt+1) < i ? cnt : i;
+#endif
+               while ((c = *bp++) != ':' && c != '\n' && --j >= 0)
+                   *cp++ = c;
+
+               j = bp - sp;
+               if ((cnt -= j) <= 0) {
+#ifdef LINUX_STDIO
+                   iob->_IO_read_ptr = iob->_IO_read_end;
+                   if (__underflow(iob) == EOF) {
+#else
+                   if (_filbuf(iob) == EOF) {
+#endif
+                       *cp = *buf = 0;
+                       advise (NULL, "eof encountered in field \"%s\"", name);
+                       return FMTERR;
+                   }
+#ifdef LINUX_STDIO
+               iob->_IO_read_ptr++; /* NOT automatic in __underflow()! */
+#endif
+               } else {
+#ifdef LINUX_STDIO
+                   iob->_IO_read_ptr = bp + 1;
+#else
+                   iob->_ptr = bp + 1;
+                   iob->_cnt = cnt - 1;
+#endif
+               }
+               if (c == ':')
+                   break;
+
+               /*
+                * something went wrong.  possibilities are:
+                *  . hit a newline (error)
+                *  . got more than namesz chars. (error)
+                *  . 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;
+               }
+               if ((i -= j) <= 0) {
+                   *cp = *buf = 0;
+                   advise (NULL, "field name \"%s\" exceeds %d bytes", name, NAMESZ - 1);
+                   state = LENERR;
+                   goto finish;
+               }
+           }
+
+           while (isspace (*--cp) && cp >= name)
+               ;
+           *++cp = 0;
+           /* fall through */
+
+       case FLDPLUS: 
+           /*
+            * get (more of) the text of a field.  take
+            * characters up to the end of this field (newline
+            * followed by non-blank) or bufsz-1 characters.
+            */
+           cp = buf; i = bufsz-1;
+           for (;;) {
+#ifdef LINUX_STDIO
+               cnt = (long) iob->_IO_read_end - (long) iob->_IO_read_ptr;
+               bp = (unsigned char *) --iob->_IO_read_ptr;
+#else
+               cnt = iob->_cnt++;
+               bp = (unsigned char *) --iob->_ptr;
+#endif
+               c = cnt < i ? cnt : i;
+               while ((ep = locc( c, bp, '\n' ))) {
+                   /*
+                    * if we hit the end of this field, return.
+                    */
+                   if ((j = *++ep) != ' ' && j != '\t') {
+#ifdef LINUX_STDIO
+                       j = ep - (unsigned char *) iob->_IO_read_ptr;
+                       memcpy (cp, iob->_IO_read_ptr, j);
+                       iob->_IO_read_ptr = ep;
+#else
+                       j = ep - (unsigned char *) iob->_ptr;
+                       memcpy (cp, iob->_ptr, j);
+                       iob->_ptr = ep;
+                       iob->_cnt -= j;
+#endif
+                       cp += j;
+                       state = FLD;
+                       goto finish;
+                   }
+                   c -= ep - bp;
+                   bp = ep;
+               }
+               /*
+                * end of input or dest buffer - copy what we've found.
+                */
+#ifdef LINUX_STDIO
+               c += bp - (unsigned char *) iob->_IO_read_ptr;
+               memcpy( cp, iob->_IO_read_ptr, c);
+#else
+               c += bp - (unsigned char *) iob->_ptr;
+               memcpy( cp, iob->_ptr, c);
+#endif
+               i -= c;
+               cp += c;
+               if (i <= 0) {
+                   /* the dest buffer is full */
+#ifdef LINUX_STDIO
+                   iob->_IO_read_ptr += c;
+#else
+                   iob->_cnt -= c;
+                   iob->_ptr += c;
+#endif
+                   state = FLDPLUS;
+                   break;
+               }
+               /* 
+                * There's one character left in the input buffer.
+                * Copy it & fill the buffer.  If the last char
+                * was a newline and the next char is not whitespace,
+                * this is the end of the field.  Otherwise loop.
+                */
+               --i;
+#ifdef LINUX_STDIO
+               *cp++ = j = *(iob->_IO_read_ptr + c);
+               iob->_IO_read_ptr = iob->_IO_read_end;
+               c = __underflow(iob);
+               iob->_IO_read_ptr++;    /* NOT automatic! */
+#else
+               *cp++ = j = *(iob->_ptr + c);
+               c = _filbuf(iob);
+#endif
+               if ((j == '\0' || j == '\n') && c != ' ' && c != '\t') {
+                   if (c != EOF) {
+#ifdef LINUX_STDIO
+                       --iob->_IO_read_ptr;
+#else
+                       --iob->_ptr;
+                       ++iob->_cnt;
+#endif
+                   }
+                   state = FLD;
+                   break;
+               }
+           }
+           break;
+
+       case BODY: 
+       body:
+           /*
+            * get the message body up to bufsz characters or the
+            * end of the message.  Sleazy hack: if bufsz is negative
+            * we assume that we were called to copy directly into
+            * the output buffer and we don't add an eos.
+            */
+           i = (bufsz < 0) ? -bufsz : bufsz-1;
+#ifdef LINUX_STDIO
+           bp = (unsigned char *) --iob->_IO_read_ptr;
+           cnt = (long) iob->_IO_read_end - (long) iob->_IO_read_ptr;
+#else
+           bp = (unsigned char *) --iob->_ptr;
+           cnt = ++iob->_cnt;
+#endif
+           c = (cnt < i ? cnt : i);
+           if (msg_style != MS_DEFAULT && c > 1) {
+               /*
+                * packed maildrop - only take up to the (possible)
+                * start of the next message.  This "matchc" should
+                * probably be a Boyer-Moore matcher for non-vaxen,
+                * particularly since we have the alignment table
+                * all built for the end-of-buffer test (next).
+                * But our vax timings indicate that the "matchc"
+                * instruction is 50% faster than a carefully coded
+                * B.M. matcher for most strings.  (So much for elegant
+                * algorithms vs. brute force.)  Since I (currently)
+                * run MH on a vax, we use the matchc instruction. --vj
+                */
+               if ((ep = matchc( fdelimlen, fdelim, c, bp )))
+                   c = ep - bp + 1;
+               else {
+                   /*
+                    * There's no delim in the buffer but there may be
+                    * a partial one at the end.  If so, we want to leave
+                    * it so the "eom" check on the next call picks it up.
+                    * Use a modified Boyer-Moore matcher to make this
+                    * check relatively cheap.  The first "if" figures
+                    * out what position in the pattern matches the last
+                    * character in the buffer.  The inner "while" matches
+                    * the pattern against the buffer, backwards starting
+                    * at that position.  Note that unless the buffer
+                    * ends with one of the characters in the pattern
+                    * (excluding the first and last), we do only one test.
+                    */
+                   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.
+                                    */
+                                   c = (ep - bp) + 2;
+                           break;
+                       }
+                           /* try matching one less char of delim string */
+                           ep = bp + c - 1;
+                       } while (--sp > fdelim);
+                   }
+               }
+           }
+           memcpy( buf, bp, c );
+#ifdef LINUX_STDIO
+           iob->_IO_read_ptr += c;
+#else
+           iob->_cnt -= c;
+           iob->_ptr += c;
+#endif
+           if (bufsz < 0) {
+               msg_count = c;
+               return (state);
+           }
+           cp = buf + c;
+           break;
+
+       default: 
+           adios (NULL, "m_getfld() called with bogus state of %d", state);
+    }
+finish:
+    *cp = 0;
+    msg_count = cp - buf;
+    return (state);
+}
+
+
+#ifdef RPATHS
+static char unixbuf[BUFSIZ] = "";
+#endif /* RPATHS */
+
+void
+m_unknown(FILE *iob)
+{
+    register int c;
+    register long pos;
+    char text[10];
+    register char *cp;
+    register char *delimstr;
+
+/*
+ * Figure out what the message delimitter string is for this
+ * maildrop.  (This used to be part of m_Eom but I didn't like
+ * the idea of an "if" statement that could only succeed on the
+ * first call to m_Eom getting executed on each call, i.e., at
+ * every newline in the message).
+ *
+ * If the first line of the maildrop is a Unix "From " line, we
+ * say the style is MBOX and eat the rest of the line.  Otherwise
+ * we say the style is MMDF and look for the delimiter string
+ * specified when nmh was built (or from the mts.conf file).
+ */
+
+    msg_style = MS_UNKNOWN;
+
+    pos = ftell (iob);
+    if (fread (text, sizeof(*text), 5, iob) == 5
+           && 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);
+       if (mmdlm2 == NULL || *mmdlm2 == 0)
+           mmdlm2 = "\001\001\001\001\n";
+       delimstr = mmdlm2;
+       msg_style = MS_MMDF;
+    }
+    c = strlen (delimstr);
+    fdelim = (unsigned char *) malloc((size_t) (c + 3));
+    *fdelim++ = '\0';
+    *fdelim = '\n';
+    msg_delim = (char *)fdelim+1;
+    edelim = (unsigned char *)msg_delim+1;
+    fdelimlen = c + 1;
+    edelimlen = c - 1;
+    strcpy (msg_delim, delimstr);
+    delimend = (unsigned char *)msg_delim + edelimlen;
+    if (edelimlen <= 1)
+       adios (NULL, "maildrop delimiter must be at least 2 bytes");
+    /*
+     * build a Boyer-Moore end-position map for the matcher in m_getfld.
+     * N.B. - we don't match just the first char (since it's the newline
+     * separator) or the last char (since the matchc would have found it
+     * if it was a real delim).
+     */
+    pat_map = (unsigned char **) calloc (256, sizeof(unsigned char *));
+
+    for (cp = (char *) fdelim + 1; cp < (char *) delimend; cp++ )
+       pat_map[*cp] = (unsigned char *) cp;
+
+    if (msg_style == MS_MMDF) {
+       /* flush extra msg hdrs */
+       while ((c = Getc(iob)) >= 0 && eom (c, iob))
+           ;
+       if (c >= 0)
+           ungetc(c, iob);
+    }
+}
+
+
+void
+m_eomsbr (int (*action)())
+{
+    if ((eom_action = action)) {
+       msg_style = MS_MSH;
+       *msg_delim = 0;
+       fdelimlen = 1;
+       delimend = fdelim;
+    } else {
+       msg_style = MS_MMDF;
+       msg_delim = (char *)fdelim + 1;
+       fdelimlen = strlen((char *)fdelim);
+       delimend = (unsigned char *)(msg_delim + edelimlen);
+    }
+}
+
+
+/*
+ * test for msg delimiter string
+ */
+
+static int
+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
+           || strncmp (text, (char *)edelim, edelimlen)) {
+       if (i == 0 && msg_style == MS_MBOX)
+           /* the final newline in the (brain damaged) unix-format
+            * maildrop is part of the delimitter - delete it.
+            */
+           return 1;
+
+#if 0
+       fseek (iob, pos, SEEK_SET);
+#endif
+
+       fseek (iob, (long)(pos-1), SEEK_SET);
+       getc (iob);             /* should be OK */
+       return 0;
+    }
+
+    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 *
+matchc(int patln, char *pat, int strln, char *str)
+{
+       register char *es = str + strln - patln;
+       register char *sp;
+       register char *pp;
+       register char *ep = pat + patln;
+       register char pc = *pat++;
+
+       for(;;) {
+               while (pc != *str++)
+                       if (str > es)
+                               return 0;
+
+               sp = str; pp = pat;
+               while (pp < ep && *sp++ == *pp)
+                       pp++;
+               if (pp >= ep) 
+                       return ((unsigned char *)--str);
+       }
+}
+
+
+/*
+ * Locate character "term" in the next "cnt" characters of "src".
+ * If found, return its address, otherwise return 0.
+ */
+
+static unsigned char *
+locc(int cnt, unsigned char *src, unsigned char term)
+{
+    while (*src++ != term && --cnt > 0);
+
+    return (cnt > 0 ? --src : (unsigned char *)0);
+}
+
diff --git a/sbr/m_gmprot.c b/sbr/m_gmprot.c
new file mode 100644 (file)
index 0000000..3f54c45
--- /dev/null
@@ -0,0 +1,17 @@
+
+/*
+ * m_gmprot.c -- return the msg-protect value
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+int
+m_gmprot (void)
+{
+    register char *cp;
+
+    return atooi ((cp = context_find ("msg-protect")) && *cp ? cp : msgprot);
+}
diff --git a/sbr/m_maildir.c b/sbr/m_maildir.c
new file mode 100644 (file)
index 0000000..11c6ee3
--- /dev/null
@@ -0,0 +1,94 @@
+
+/*
+ * m_maildir.c -- get the path for the mail directory
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+#define        CWD     "./"
+#define        NCWD    (sizeof(CWD) - 1)
+#define        DOT     "."
+#define        DOTDOT  ".."
+#define        PWD     "../"
+#define        NPWD    (sizeof(PWD) - 1)
+
+static char mailfold[BUFSIZ];
+
+/*
+ * static prototypes
+ */
+static char *exmaildir (char *);
+
+
+char *
+m_maildir (char *folder)
+{
+    register char *cp, *ep;
+
+    if ((cp = exmaildir (folder))
+           && (ep = cp + strlen (cp) - 1) > cp
+           && *ep == '/')
+       *ep = '\0';
+
+    return cp;
+}
+
+
+char *
+m_mailpath (char *folder)
+{
+    register char *cp;
+    char maildir[BUFSIZ];
+
+    if (*folder != '/'
+           && strncmp (folder, CWD, NCWD)
+           && strcmp (folder, DOT)
+           && strcmp (folder, DOTDOT)
+           && strncmp (folder, PWD, NPWD)) {
+       strncpy (maildir, mailfold, sizeof(maildir));   /* preserve... */
+       cp = getcpy (m_maildir (folder));
+       strncpy (mailfold, maildir, sizeof(mailfold));
+    } else {
+       cp = path (folder, TFOLDER);
+    }
+
+    return cp;
+}
+
+
+static char *
+exmaildir (char *folder)
+{
+    register char *cp, *pp;
+
+    /* use current folder if none is specified */
+    if (folder == NULL)
+       folder = getfolder(1);
+
+    if (!(*folder != '/'
+           && strncmp (folder, CWD, NCWD)
+           && strcmp (folder, DOT)
+           && strcmp (folder, DOTDOT)
+           && strncmp (folder, PWD, NPWD))) {
+       strncpy (mailfold, folder, sizeof(mailfold));
+       return mailfold;
+    }
+
+    cp = mailfold;
+    if ((pp = context_find ("path")) && *pp) {
+       if (*pp != '/') {
+           sprintf (cp, "%s/", mypath);
+           cp += strlen (cp);
+       }
+       cp = copy (pp, cp);
+    } else {
+       cp = copy (path ("./", TFOLDER), cp);
+    }
+    if (cp[-1] != '/')
+       *cp++ = '/';
+    strcpy (cp, folder);
+
+    return mailfold;
+}
diff --git a/sbr/m_msgdef.c b/sbr/m_msgdef.c
new file mode 100644 (file)
index 0000000..89e0f29
--- /dev/null
@@ -0,0 +1,31 @@
+
+/*
+ * m_msgdef.c -- some defines for sbr/m_getfld.c
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+/*
+ * disgusting hack for "inc" so it can know how many characters
+ * were stuffed in the buffer on the last call (see comments
+ * in uip/scansbr.c)
+ */
+int msg_count = 0;
+
+int msg_style = MS_DEFAULT;
+
+/*
+ * The "full" delimiter string for a packed maildrop consists
+ * of a newline followed by the actual delimiter.  E.g., the
+ * full string for a Unix maildrop would be: "\n\nFrom ".
+ * "Fdelim" points to the start of the full string and is used
+ * in the BODY case of the main routine to search the buffer for
+ * a possible eom.  Msg_delim points to the first character of
+ * the actual delim. string (i.e., fdelim+1).  Edelim
+ * points to the 2nd character of actual delimiter string.  It
+ * is used in m_Eom because the first character of the string
+ * has been read and matched before m_Eom is called.
+ */
+char *msg_delim = "";
diff --git a/sbr/m_name.c b/sbr/m_name.c
new file mode 100644 (file)
index 0000000..dd8303e
--- /dev/null
@@ -0,0 +1,21 @@
+
+/*
+ * m_name.c -- return a message number as a string
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+static char name[BUFSIZ];
+
+
+char *
+m_name (int num)
+{
+    if (num <= 0)
+       return "?";
+
+    snprintf (name, sizeof(name), "%d", num);
+    return name;
+}
diff --git a/sbr/m_scratch.c b/sbr/m_scratch.c
new file mode 100644 (file)
index 0000000..123a498
--- /dev/null
@@ -0,0 +1,26 @@
+
+/*
+ * m_scratch.c -- construct a scratch file
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+char *
+m_scratch (char *file, char *template)
+{
+    char *cp;
+    static char buffer[BUFSIZ], tmpfil[BUFSIZ];
+
+    snprintf (tmpfil, sizeof(tmpfil), "%sXXXXXX", template);
+    mktemp (tmpfil);
+    if ((cp = r1bindex (file, '/')) == file)
+       strncpy (buffer, tmpfil, sizeof(buffer));
+    else
+       snprintf (buffer, sizeof(buffer), "%.*s%s", cp - file, file, tmpfil);
+    unlink (buffer);
+
+    return buffer;
+}
diff --git a/sbr/m_tmpfil.c b/sbr/m_tmpfil.c
new file mode 100644 (file)
index 0000000..1b4f354
--- /dev/null
@@ -0,0 +1,20 @@
+
+/*
+ * m_tmpfil.c -- construct a temporary file
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+char *
+m_tmpfil (char *template)
+{
+    static char tmpfil[BUFSIZ];
+
+    snprintf (tmpfil, sizeof(tmpfil), "/tmp/%sXXXXXX", template);
+    unlink(mktemp(tmpfil));
+
+    return tmpfil;
+}
diff --git a/sbr/makedir.c b/sbr/makedir.c
new file mode 100644 (file)
index 0000000..6cf9e03
--- /dev/null
@@ -0,0 +1,79 @@
+
+/*
+ * makedir.c -- make a directory
+ *
+ * $Id$
+ */
+
+/*
+ * Modified to try recursive create.
+ */
+
+#include <h/mh.h>
+#include <errno.h>
+#include <sys/param.h>
+#include <sys/file.h>
+
+extern int errno;
+       
+int
+makedir (char *dir)
+{
+    pid_t pid;
+    register char *cp;
+    register char *c;
+    char path[PATH_MAX];
+
+    context_save();    /* save the context file */
+    fflush(stdout);
+
+    if (getuid () == geteuid ()) {
+           c = strncpy(path, dir, sizeof(path));     
+
+           while ((c = strchr((c + 1), '/')) != NULL) {        
+                   *c = (char)0;
+                   if (access(path, X_OK)) {
+                           if (errno != ENOENT){
+                                   advise (dir, "unable to create directory");
+                                   return 0;
+                           }                       
+                           if (mkdir(path, 0775)) {
+                                   advise (dir, "unable to create directory");
+                                   return 0;
+                           }
+                   }
+                   *c = '/';
+           }
+           if (mkdir (dir, 0755) == -1) {
+                   advise (dir, "unable to create directory");
+                   return 0;
+           }
+    } else {
+       switch (pid = vfork()) {
+       case -1: 
+           advise ("fork", "unable to");
+           return 0;
+
+       case 0: 
+           setgid (getgid ());
+           setuid (getuid ());
+
+           execl ("/bin/mkdir", "mkdir", dir, NULL);
+           execl ("/usr/bin/mkdir", "mkdir", dir, NULL);
+           fprintf (stderr, "unable to exec ");
+           perror ("mkdir");
+           _exit (-1);
+
+       default: 
+           if (pidXwait(pid, "mkdir"))
+               return 0;
+           break;
+       }
+    }
+
+    if (!(cp = context_find ("folder-protect")))
+       cp = foldprot;
+    chmod (dir, atooi (cp));
+    return 1;
+}
diff --git a/sbr/path.c b/sbr/path.c
new file mode 100644 (file)
index 0000000..df5d963
--- /dev/null
@@ -0,0 +1,161 @@
+
+/*
+ * path.c -- return a pathname
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+#define        CWD     "./"
+#define        NCWD    (sizeof(CWD) - 1)
+#define        DOT     "."
+#define        DOTDOT  ".."
+#define        PWD     "../"
+#define        NPWD    (sizeof(PWD) - 1)
+
+static char *pwds;
+
+/*
+ * static prototypes
+ */
+static char *expath(char *,int);
+static void compath(char *);
+
+
+char *
+path(char *name, int flag)
+{
+    register char *cp, *ep;
+
+    if ((cp = expath (name, flag))
+           && (ep = cp + strlen (cp) - 1) > cp
+           && *ep == '/')
+       *ep = '\0';
+
+    return cp;
+}
+
+
+static char *
+expath (char *name, int flag)
+{
+    register char *cp, *ep;
+    char buffer[BUFSIZ];
+
+    if (flag == TSUBCWF) {
+       snprintf (buffer, sizeof(buffer), "%s/%s", getfolder (1), name);
+       name = m_mailpath (buffer);
+       compath (name);
+       snprintf (buffer, sizeof(buffer), "%s/", m_maildir (""));
+       if (ssequal (buffer, name)) {
+           cp = name;
+           name = getcpy (name + strlen (buffer));
+           free (cp);
+       }
+       flag = TFOLDER;
+    }
+
+    if (*name == '/'
+           || (flag == TFOLDER
+               && (strncmp (name, CWD, NCWD)
+                   && strcmp (name, DOT)
+                   && strcmp (name, DOTDOT)
+                   && strncmp (name, PWD, NPWD))))
+       return getcpy (name);
+
+    if (pwds == NULL)
+       pwds = pwd ();
+
+    if (strcmp (name, DOT) == 0 || strcmp (name, CWD) == 0)
+       return getcpy (pwds);
+
+    ep = pwds + strlen (pwds);
+    if ((cp = strrchr(pwds, '/')) == NULL)
+       cp = ep;
+    else
+       if (cp == pwds)
+           cp++;
+
+    if (strncmp (name, CWD, NCWD) == 0)
+       name += NCWD;
+
+    if (strcmp (name, DOTDOT) == 0 || strcmp (name, PWD) == 0) {
+       snprintf (buffer, sizeof(buffer), "%.*s", cp - pwds, pwds);
+       return getcpy (buffer);
+    }
+
+    if (strncmp (name, PWD, NPWD) == 0)
+       name += NPWD;
+    else
+       cp = ep;
+
+    snprintf (buffer, sizeof(buffer), "%.*s/%s", cp - pwds, pwds, name);
+    return getcpy (buffer);
+}
+
+
+static void
+compath (char *f)
+{
+    register char *cp, *dp;
+
+    if (*f != '/')
+       return;
+
+    for (cp = f; *cp;)
+       if (*cp == '/') {
+           switch (*++cp) {
+               case 0: 
+                   if (--cp > f)
+                       *cp = '\0';
+                   break;
+
+               case '/': 
+                   for (dp = cp; *dp == '/'; dp++)
+                       continue;
+                   strcpy (cp--, dp);
+                   continue;
+
+               case '.': 
+                   if (strcmp (cp, DOT) == 0) {
+                       if (cp > f + 1)
+                           cp--;
+                       *cp = '\0';
+                       break;
+                   }
+                   if (strcmp (cp, DOTDOT) == 0) {
+                       for (cp -= 2; cp > f; cp--)
+                           if (*cp == '/')
+                               break;
+                       if (cp <= f)
+                           cp = f + 1;
+                       *cp = '\0';
+                       break;
+                   }
+                   if (strncmp (cp, PWD, NPWD) == 0) {
+                       for (dp = cp - 2; dp > f; dp--)
+                           if (*dp == '/')
+                               break;
+                       if (dp <= f)
+                           dp = f;
+                       strcpy (dp, cp + NPWD - 1);
+                       cp = dp;
+                       continue;
+                   }
+                   if (strncmp (cp, CWD, NCWD) == 0) {
+                       strcpy (cp - 1, cp + NCWD - 1);
+                       cp--;
+                       continue;
+                   }
+                   continue;
+
+               default: 
+                   cp++;
+                   continue;
+           }
+           break;
+       }
+       else
+           cp++;
+}
diff --git a/sbr/peekc.c b/sbr/peekc.c
new file mode 100644 (file)
index 0000000..d77039b
--- /dev/null
@@ -0,0 +1,19 @@
+
+/*
+ * peekc.c -- peek at the next character in a stream
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+int
+peekc(FILE *fp)
+{
+    register int c;
+
+    c = getc(fp);
+    ungetc(c, fp);
+    return c;
+}
diff --git a/sbr/pidstatus.c b/sbr/pidstatus.c
new file mode 100644 (file)
index 0000000..aa66464
--- /dev/null
@@ -0,0 +1,63 @@
+
+/*
+ * pidstatus.c -- report child's status
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+/*
+ * auto-generated header
+ */
+#include <sigmsg.h>
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+#ifndef WTERMSIG
+# define WTERMSIG(s) ((int)((s) & 0x7F))
+#endif
+
+#ifndef WCOREDUMP
+# define WCOREDUMP(s) ((s) & 0x80)
+#endif
+
+int
+pidstatus (int status, FILE *fp, char *cp)
+{
+    int signum;
+
+/*
+ * I have no idea what this is for (rc)
+ * so I'm commenting it out for right now.
+ *
+ *  if ((status & 0xff00) == 0xff00)
+ *      return status;
+ */
+
+    /* If child process returned normally */
+    if (WIFEXITED(status)) {
+       if ((signum = WEXITSTATUS(status))) {
+           if (cp)
+               fprintf (fp, "%s: ", cp);
+           fprintf (fp, "exit %d\n", signum);
+       }
+    } else if (WIFSIGNALED(status)) {
+       /* If child process terminated due to receipt of a signal */
+       signum = WTERMSIG(status);
+       if (signum != SIGINT) {
+           if (cp)
+               fprintf (fp, "%s: ", cp);
+           fprintf (fp, "signal %d", signum);
+           if (signum >= 0 && signum < sizeof(sigmsg) && sigmsg[signum] != NULL)
+               fprintf (fp, " (%s%s)\n", sigmsg[signum],
+                        WCOREDUMP(status) ? ", core dumped" : "");
+           else
+               fprintf (fp, "%s\n", WCOREDUMP(status) ? " (core dumped)" : "");
+       }
+    }
+
+    return status;
+}
diff --git a/sbr/pidwait.c b/sbr/pidwait.c
new file mode 100644 (file)
index 0000000..4bd02e3
--- /dev/null
@@ -0,0 +1,53 @@
+
+/*
+ * pidwait.c -- wait for child to exit
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/signals.h>
+#include <signal.h>
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+int
+pidwait (pid_t id, int sigsok)
+{
+    pid_t pid;
+    sigset_t set, oset;
+
+#ifdef WAITINT
+    int status;
+#else
+    union wait status;
+#endif
+
+    if (sigsok == -1) {
+       /* block a couple of signals */
+       sigemptyset (&set);
+       sigaddset (&set, SIGINT);
+       sigaddset (&set, SIGQUIT);
+       SIGPROCMASK (SIG_BLOCK, &set, &oset);
+    }
+
+#ifdef HAVE_WAITPID
+    pid = waitpid(id, &status, 0);
+#else
+    while ((pid = wait(&status)) != -1 && pid != id)
+       continue;
+#endif
+
+    if (sigsok == -1) {
+       /* reset the signal mask */
+       SIGPROCMASK (SIG_SETMASK, &oset, &set);
+    }
+
+#ifdef WAITINT
+    return (pid == -1 ? -1 : status);
+#else
+    return (pid == -1 ? -1 : status.w_status);
+#endif
+}
diff --git a/sbr/print_help.c b/sbr/print_help.c
new file mode 100644 (file)
index 0000000..1ceeb85
--- /dev/null
@@ -0,0 +1,29 @@
+
+/*
+ * print_help.c -- print a help message, and possibly the
+ *              -- profile/context entries for this command
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+void
+print_help (char *str, struct swit *swp, int print_context)
+{
+    char *s;
+
+    /* print Usage string */
+    printf ("Usage: %s\n", str);
+
+    /* print all the switches */
+    printf ("  switches are:\n");
+    print_sw (ALL, swp, "-");
+
+    /*
+     * check if we should print any profile entries
+     */
+    if (print_context && (s = context_find (invo_name)))
+       printf ("\nProfile: %s\n", s);
+}
diff --git a/sbr/print_sw.c b/sbr/print_sw.c
new file mode 100644 (file)
index 0000000..c9c1159
--- /dev/null
@@ -0,0 +1,53 @@
+
+/*
+ * print_sw.c -- print switches
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+void
+print_sw (char *substr, struct swit *swp, char *prefix)
+{
+    int len, optno;
+    register int i;
+    register char *cp, *cp1, *sp;
+    char buf[128];
+
+    len = strlen(substr);
+    for (; swp->sw; swp++) {
+       /* null matches all strings */
+       if (!*substr || (ssequal (substr, swp->sw) && len >= swp->minchars)) {
+           optno = 0;
+           /* next switch */
+           if ((sp = (&swp[1])->sw)) {
+               if (!*substr && sp[0] == 'n' && sp[1] == 'o' &&
+                       strcmp (&sp[2], swp->sw) == 0 && (
+                       ((&swp[1])->minchars == 0 && swp->minchars == 0) ||
+                       ((&swp[1])->minchars == (swp->minchars) + 2)))
+                   optno++;
+           }
+
+           if (swp->minchars > 0) {
+               cp = buf;
+               *cp++ = '(';
+               if (optno) {
+                   strcpy (cp, "[no]");
+                   cp += strlen (cp);
+               }
+               for (cp1 = swp->sw, i = 0; i < swp->minchars; i++)
+                   *cp++ = *cp1++;
+               *cp++ = ')';
+               while ((*cp++ = *cp1++));
+               printf ("  %s%s\n", prefix, buf);
+           } else {
+               if (!swp->minchars)
+                   printf(optno ? "  %s[no]%s\n" : "  %s%s\n", prefix, swp->sw);
+           }
+           if (optno)
+               swp++;  /* skip -noswitch */
+       }
+    }
+}
diff --git a/sbr/print_version.c b/sbr/print_version.c
new file mode 100644 (file)
index 0000000..1b37b1e
--- /dev/null
@@ -0,0 +1,15 @@
+
+/*
+ * print_version.c -- print a version string
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+void
+print_version (char *invo_name)
+{
+    printf("%s -- %s\n", invo_name, version_str);
+}
diff --git a/sbr/push.c b/sbr/push.c
new file mode 100644 (file)
index 0000000..05c3942
--- /dev/null
@@ -0,0 +1,48 @@
+
+/*
+ * push.c -- push a fork into the background
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/signals.h>
+#include <signal.h>
+
+
+void
+push(void)
+{
+    pid_t pid;
+    int i;
+
+    for (i = 0; (pid = fork()) == -1 && i < 5; i++)
+       sleep (5);
+
+    switch (pid) {
+       case -1:
+           /* fork error */
+           advise (NULL, "unable to fork, so can't push...");
+           break;
+
+       case 0:
+           /* child, block a few signals and continue */
+           SIGNAL (SIGHUP, SIG_IGN);
+           SIGNAL (SIGINT, SIG_IGN);
+           SIGNAL (SIGQUIT, SIG_IGN);
+           SIGNAL (SIGTERM, SIG_IGN);
+#ifdef SIGTSTP
+           SIGNAL (SIGTSTP, SIG_IGN);
+           SIGNAL (SIGTTIN, SIG_IGN);
+           SIGNAL (SIGTTOU, SIG_IGN);
+#endif
+           freopen ("/dev/null", "r", stdin);
+           freopen ("/dev/null", "w", stdout);
+           break;
+
+       default:
+           /* parent, just exit */
+           done (0);
+    }
+}
+
diff --git a/sbr/putenv.c b/sbr/putenv.c
new file mode 100644 (file)
index 0000000..2c6af0d
--- /dev/null
@@ -0,0 +1,76 @@
+
+/*
+ * putenv.c -- (un)set an envariable
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+extern char **environ;
+
+/*
+ * prototypes
+ */
+int m_putenv (char *, char *);
+int unputenv (char *);
+static int nvmatch (char *, char *);
+
+
+int
+m_putenv (char *name, char *value)
+{
+    register int i;
+    register char **ep, **nep, *cp;
+
+    if (!(cp = malloc ((size_t) (strlen (name) + strlen (value) + 2))))
+       return 1;
+
+    sprintf (cp, "%s=%s", name, value);
+
+    for (ep = environ, i = 0; *ep; ep++, i++)
+       if (nvmatch (name, *ep)) {
+           *ep = cp;
+           return 0;
+       }
+
+    if (!(nep = (char **) malloc ((size_t) ((i + 2) * sizeof(*nep)))))
+       return 1;
+
+    for (ep = environ, i = 0; *ep; nep[i++] = *ep++)
+       continue;
+    nep[i++] = cp;
+    nep[i] = NULL;
+    environ = nep;
+    return 0;
+}
+
+
+int
+unputenv (char *name)
+{
+    char **ep, **nep;
+
+    for (ep = environ; *ep; ep++)
+       if (nvmatch (name, *ep))
+           break;
+    if (*ep == NULL)
+       return 1;
+
+    for (nep = ep + 1; *nep; nep++)
+       continue;
+    *ep = *--nep;
+    *nep = NULL;
+    return 0;
+}
+
+
+static int
+nvmatch (char *s1, char *s2)
+{
+    while (*s1 == *s2++)
+       if (*s1++ == '=')
+           return 1;
+
+    return (*s1 == '\0' && *--s2 == '=');
+}
diff --git a/sbr/pwd.c b/sbr/pwd.c
new file mode 100644 (file)
index 0000000..0036d20
--- /dev/null
+++ b/sbr/pwd.c
@@ -0,0 +1,115 @@
+
+/*
+ * pwd.c -- return the current working directory
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+static char curwd[PATH_MAX];
+
+
+char *
+pwd(void)
+{
+    register char *cp;
+
+    if (!getcwd (curwd, PATH_MAX)) {
+       admonish (NULL, "unable to determine working directory");
+       if (!mypath || !*mypath
+               || (strcpy (curwd, mypath), chdir (curwd)) == -1) {
+           strcpy (curwd, "/");
+           chdir (curwd);
+       }
+       return curwd;
+    }
+
+    if ((cp = curwd + strlen (curwd) - 1) > curwd && *cp == '/')
+       *cp = '\0';
+
+    return curwd;
+}
+
+
+#if 0
+
+/*
+ * Currently commented out.  Everyone seems
+ * to have a native version these days.
+ */
+
+/*
+ * getwd() - get the current working directory
+ */
+
+int
+getwd(char *cwd)
+{
+    int found;
+    char tmp1[BUFSIZ], tmp2[BUFSIZ];
+    struct stat st1, st2, root;
+    register struct direct *dp;
+    register DIR *dd;
+
+    strcpy (cwd, "/");
+    stat ("/", &root);
+
+    for (;;) {
+       if ((dd = opendir ("..")) == NULL)
+           return -1;
+       if (stat (".", &st2) == -1 || stat ("..", &st1) == -1)
+           goto out;
+       if (st2.st_ino == root.st_ino && st2.st_dev == root.st_dev) {
+           closedir (dd);
+           return chdir (cwd);
+       }
+
+       if (st2.st_ino == st1.st_ino && st2.st_dev == st1.st_dev) {
+           closedir (dd);
+           chdir ("/");
+           if ((dd = opendir (".")) == NULL)
+               return -1;
+           if (stat (".", &st1) < 0)
+               goto out;
+           if (st2.st_dev != st1.st_dev)
+               while (dp = readdir (dd)) {
+                   if (stat (dp->d_name, &st1) == -1)
+                       goto out;
+                   if (st2.st_dev == st1.st_dev) {
+                       snprintf (tmp1, sizeof(tmp1), "%s%s", dp->d_name, cwd);
+                       strcpy (cwd + 1, tmp1);
+                       closedir (dd);
+                       return (chdir (cwd));
+                   }
+               }
+           else {
+               closedir (dd);
+               return (chdir (cwd));
+           }
+       }
+
+       found = 0;
+       while (dp = readdir (dd)) {
+           snprintf (tmp2, sizeof(tmp2), "../%s", dp->d_name);
+           if (stat (tmp2, &st1) != -1
+                   && st1.st_ino == st2.st_ino
+                   && st1.st_dev == st2.st_dev) {
+               closedir (dd);
+               found++;
+               chdir ("..");
+               snprintf (tmp1, sizeof(tmp1), "%s%s", dp->d_name, cwd);
+               strcpy (cwd + 1, tmp1);
+               break;
+           }
+       }
+       if (!found)
+           goto out;
+    }
+
+out: ;
+    closedir (dd);
+    return -1;
+}
+
+#endif
diff --git a/sbr/r1bindex.c b/sbr/r1bindex.c
new file mode 100644 (file)
index 0000000..087ed78
--- /dev/null
@@ -0,0 +1,32 @@
+
+/*
+ * r1bindex.c -- Given a string and a character, return a pointer
+ *            -- to the right of the rightmost occurrence of the
+ *            -- character.  If the character doesn't occur, the
+ *            -- pointer will be at the beginning of the string.
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+char *
+r1bindex(char *str, int chr)
+{
+    char *cp;
+
+    /* find null at the end of the string */
+    for (cp = str; *cp; cp++)
+       continue;
+
+    /* backup to the rightmost character */
+    --cp;
+
+    /* now search for the rightmost occurrence of the character */
+    while (cp >= str && *cp != chr)
+       --cp;
+
+    /* now move one to the right */
+    return (++cp);
+}
diff --git a/sbr/readconfig.c b/sbr/readconfig.c
new file mode 100644 (file)
index 0000000..9cd128e
--- /dev/null
@@ -0,0 +1,109 @@
+
+/*
+ * readconfig.c -- base routine to read nmh configuration files
+ *              -- such as nmh profile, context file, or mhn.defaults.
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+struct procstr {
+    char *procname;
+    char **procnaddr;
+};
+
+static struct procstr procs[] = {
+    { "context",       &context },
+    { "mh-sequences",  &mh_seq },
+    { "buildmimeproc", &buildmimeproc },
+    { "faceproc",      &faceproc },
+    { "fileproc",      &fileproc },
+    { "incproc",       &incproc },
+    { "installproc",   &installproc },
+    { "lproc",         &lproc },
+    { "mailproc",      &mailproc },
+    { "mhlproc",       &mhlproc },
+    { "moreproc",      &moreproc },
+    { "mshproc",       &mshproc },
+    { "packproc",      &packproc },
+    { "postproc",      &postproc },
+    { "rmfproc",       &rmfproc },
+    { "rmmproc",       &rmmproc },
+    { "sendproc",      &sendproc },
+    { "showmimeproc",  &showmimeproc },
+    { "showproc",      &showproc },
+    { "vmhproc",       &vmhproc },
+    { "whatnowproc",   &whatnowproc },
+    { "whomproc",      &whomproc },
+    { NULL,            NULL }
+};
+
+static struct node **opp = NULL;
+
+
+void
+readconfig (struct node **npp, FILE *ib, char *file, int ctx)
+{
+    register int state;
+    register char *cp;
+    char name[NAMESZ], field[BUFSIZ];
+    register struct node *np;
+    register struct procstr *ps;
+
+    if (npp == NULL && (npp = opp) == NULL) {
+       admonish (NULL, "bug: readconfig called but pump not primed");
+       return;
+    }
+
+    for (state = FLD;;) {
+       switch (state = m_getfld (state, name, field, sizeof(field), ib)) {
+           case FLD:
+           case FLDPLUS:
+           case FLDEOF:
+               if (!(np = (struct node *) malloc (sizeof(*np))))
+                   adios (NULL, "unable to allocate profile storage");
+               *npp = np;
+               *(npp = &np->n_next) = NULL;
+               np->n_name = getcpy (name);
+               if (state == FLDPLUS) {
+                   cp = getcpy (field);
+                   while (state == FLDPLUS) {
+                       state = m_getfld (state, name, field, sizeof(field), ib);
+                       cp = add (field, cp);
+                   }
+                   np->n_field = trimcpy (cp);
+                   free (cp);
+               } else {
+                   np->n_field = trimcpy (field);
+               }
+               np->n_context = ctx;
+
+               /*
+                * Now scan the list of `procs' and link in the
+                * field value to the global variable.
+                */
+               for (ps = procs; ps->procname; ps++)
+                   if (strcmp (np->n_name, ps->procname) == 0) {
+                       *ps->procnaddr = np->n_field;
+                       break;
+                   }
+               if (state == FLDEOF)
+                   break;
+               continue;
+
+           case BODY:
+           case BODYEOF:
+               adios (NULL, "no blank lines are permitted in %s", file);
+
+           case FILEEOF:
+               break;
+
+           default:
+               adios (NULL, "%s is poorly formatted", file);
+       }
+       break;
+    }
+
+    opp = npp;
+}
diff --git a/sbr/refile.c b/sbr/refile.c
new file mode 100644 (file)
index 0000000..17c6715
--- /dev/null
@@ -0,0 +1,49 @@
+
+/*
+ * refile.c -- call the "fileproc" to refile the
+ *          -- msg or draft into another folder
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+int
+refile (char **arg, char *file)
+{
+    pid_t pid;
+    register int vecp;
+    char *vec[MAXARGS];
+
+    vecp = 0;
+    vec[vecp++] = r1bindex (fileproc, '/');
+    vec[vecp++] = "-nolink";   /* override bad .mh_profile defaults */
+    vec[vecp++] = "-nopreserve";
+    vec[vecp++] = "-file";
+    vec[vecp++] = file;
+
+    if (arg) {
+       while (*arg)
+           vec[vecp++] = *arg++;
+    }
+    vec[vecp] = NULL;
+
+    context_save();    /* save the context file */
+    fflush(stdout);
+
+    switch (pid = vfork()) {
+       case -1: 
+           advise ("fork", "unable to");
+           return -1;
+
+       case 0: 
+           execvp (fileproc, vec);
+           fprintf (stderr, "unable to exec ");
+           perror (fileproc);
+           _exit (-1);
+
+       default: 
+           return (pidwait (pid, -1));
+    }
+}
diff --git a/sbr/remdir.c b/sbr/remdir.c
new file mode 100644 (file)
index 0000000..4ef1649
--- /dev/null
@@ -0,0 +1,22 @@
+
+/*
+ * remdir.c -- remove a directory
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+int
+remdir (char *dir)
+{
+    context_save();    /* save the context file */
+    fflush(stdout);
+
+    if (rmdir(dir) == -1) {
+       admonish (dir, "unable to remove directory");
+       return 0;
+    }
+    return 1;
+}
diff --git a/sbr/ruserpass.c b/sbr/ruserpass.c
new file mode 100644 (file)
index 0000000..68809df
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 1985 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <pwd.h>
+#include <errno.h>
+
+static FILE *cfile;
+
+#ifndef MAXHOSTNAMELEN
+# define MAXHOSTNAMELEN 64
+#endif
+
+#define        DEFAULT 1
+#define        LOGIN   2
+#define        PASSWD  3
+#define        ACCOUNT 4
+#define MACDEF  5
+#define        ID      10
+#define        MACH    11
+
+static char tokval[100];
+
+struct toktab {
+    char *tokstr;
+    int tval;
+};
+
+static struct toktab toktabs[] = {
+    { "default",  DEFAULT },
+    { "login",    LOGIN },
+    { "password", PASSWD },
+    { "passwd",   PASSWD },
+    { "account",  ACCOUNT },
+    { "machine",  MACH },
+    { "macdef",   MACDEF },
+    { 0,          0 }
+};
+
+/*
+ * prototypes
+ */
+static int token(void);
+
+
+int
+ruserpass(char *host, char **aname, char **apass)
+{
+    char *hdir, buf[BUFSIZ];
+    int t, usedefault = 0;
+    struct stat stb;
+    extern int errno;
+
+    hdir = getenv("HOME");
+    if (hdir == NULL)
+       hdir = ".";
+    snprintf(buf, sizeof(buf), "%s/.netrc", hdir);
+    cfile = fopen(buf, "r");
+    if (cfile == NULL) {
+       if (errno != ENOENT)
+           perror(buf);
+       goto done;
+    }
+
+    while ((t = token())) {
+       switch(t) {
+       case DEFAULT:
+           usedefault = 1;
+           /* FALL THROUGH */
+
+       case MACH:
+           if (!usedefault) {
+               if (token() != ID)
+                   continue;
+               /*
+                * Allow match either for user's host name.
+                */
+               if (strcasecmp(host, tokval) == 0)
+                   goto match;
+               continue;
+           }
+match:
+           while ((t = token()) && t != MACH && t != DEFAULT) {
+               switch(t) {
+               case LOGIN:
+                   if (token() && *aname == 0) {
+                       *aname = malloc((size_t) strlen(tokval) + 1);
+                       strcpy(*aname, tokval);
+                   }
+                   break;
+               case PASSWD:
+                   if (fstat(fileno(cfile), &stb) >= 0 &&
+                       (stb.st_mode & 077) != 0) {
+                       fprintf(stderr, "Error - .netrc file not correct mode.\n");
+                       fprintf(stderr, "Remove password or correct mode.\n");
+                       goto bad;
+                   }
+                   if (token() && *apass == 0) {
+                       *apass = malloc((size_t) strlen(tokval) + 1);
+                       strcpy(*apass, tokval);
+                   }
+                   break;
+               case ACCOUNT:
+                   break;
+
+               case MACDEF:
+                   goto done_close;
+                   break;
+               default:
+                   fprintf(stderr, "Unknown .netrc keyword %s\n", tokval);
+                   break;
+               }
+           }
+           goto done;
+       }
+    }
+
+done_close:
+    fclose(cfile);
+
+done:
+    if (!*aname) {
+       char tmp[80];
+       char *myname;
+
+       if ((myname = getlogin()) == NULL) {
+           struct passwd *pp;
+
+           if ((pp = getpwuid (getuid())) != NULL)
+               myname = pp->pw_name;
+       }
+       printf("Name (%s:%s): ", host, myname);
+
+       fgets(tmp, sizeof(tmp) - 1, stdin);
+       tmp[strlen(tmp) - 1] = '\0';
+       if (*tmp != '\0') {
+           myname = tmp;
+       }
+
+       *aname = malloc((size_t) strlen(myname) + 1);
+       strcpy (*aname, myname);
+    }
+
+    if (!*apass) {
+       char prompt[256];
+       char *mypass;
+
+       snprintf(prompt, sizeof(prompt), "Password (%s:%s): ", host, *aname);
+       mypass = getpass (prompt);
+       
+       if (*mypass == '\0') {
+           mypass = *aname;
+       }
+
+       *apass = malloc((size_t) strlen(mypass) + 1);
+       strcpy (*apass, mypass);
+    }
+
+    return(0);
+bad:
+    fclose(cfile);
+    return(-1);
+}
+
+static int
+token(void)
+{
+    char *cp;
+    int c;
+    struct toktab *t;
+
+    if (feof(cfile))
+       return (0);
+    while ((c = getc(cfile)) != EOF &&
+          (c == '\n' || c == '\t' || c == ' ' || c == ','))
+       continue;
+    if (c == EOF)
+       return (0);
+    cp = tokval;
+    if (c == '"') {
+       while ((c = getc(cfile)) != EOF && c != '"') {
+           if (c == '\\')
+               c = getc(cfile);
+           *cp++ = c;
+       }
+    } else {
+       *cp++ = c;
+       while ((c = getc(cfile)) != EOF
+              && c != '\n' && c != '\t' && c != ' ' && c != ',') {
+           if (c == '\\')
+               c = getc(cfile);
+           *cp++ = c;
+       }
+    }
+    *cp = 0;
+    if (tokval[0] == 0)
+       return (0);
+    for (t = toktabs; t->tokstr; t++)
+       if (!strcmp(t->tokstr, tokval))
+           return (t->tval);
+    return (ID);
+}
diff --git a/sbr/seq_add.c b/sbr/seq_add.c
new file mode 100644 (file)
index 0000000..b586fc3
--- /dev/null
@@ -0,0 +1,189 @@
+
+/*
+ * seq_add.c -- add message(s) to a sequence
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+/*
+ * Add all the SELECTED messages to a (possibly new) sequence.
+ *
+ * If public ==  1, make sequence public.
+ * If public ==  0, make sequence private.
+ * If public == -1, leave the public/private bit alone for existing
+ *                  sequences.  For new sequences, set this bit based
+ *                  on its readonly status.
+ *
+ * If error, return 0, else return 1.
+ */
+
+int
+seq_addsel (struct msgs *mp, char *cp, int public, int zero)
+{
+    int i, msgnum, new_seq = 1;
+
+    if (!seq_nameok (cp))
+       return 0;
+
+    /*
+     * We keep mp->curmsg and "cur" sequence in sync.
+     * See seq_list() and seq_init().
+     */
+    if (!strcmp (current,cp))
+       mp->curmsg = mp->hghsel;
+
+    /*
+     * Get the number for this sequence
+     */
+    for (i = 0; mp->msgattrs[i]; i++) {
+       if (!strcmp (mp->msgattrs[i], cp)) {
+           new_seq = 0;
+           break;
+       }
+    }
+
+    /*
+     * If this is a new sequence, add a slot for it
+     */
+    if (new_seq) {
+       if (i >= NUMATTRS) {
+           advise (NULL, "only %d sequences allowed (no room for %s)!", NUMATTRS, cp);
+           return 0;
+       }
+       if (!(mp->msgattrs[i] = strdup (cp))) {
+           advise (NULL, "strdup failed");
+           return 0;
+       }
+       mp->msgattrs[i + 1] = NULL;
+    }
+
+    /*
+     * If sequence is new, or zero flag is set, then first
+     * clear the bit for this sequence from all messages.
+     */
+    if (new_seq || zero) {
+       for (msgnum = mp->lowmsg; msgnum <= mp->hghmsg; msgnum++)
+           clear_sequence (mp, i, msgnum);
+    }
+
+    /*
+     * Now flip on the bit for this sequence
+     * for all selected messages.
+     */
+    for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
+       if (is_selected (mp, msgnum))
+           add_sequence (mp, i, msgnum);
+
+    /*
+     * Set the public/private bit for this sequence.
+     */
+    if (public == 1)
+       make_seq_public (mp, i);
+    else if (public == 0)
+       make_seq_private (mp, i);
+    else if (new_seq) {
+       /*
+        * If public == -1, then only set the
+        * public/private bit for new sequences.
+         */
+       if (is_readonly (mp))
+           make_seq_private (mp, i);
+       else
+           make_seq_public (mp, i);
+    }
+
+    mp->msgflags |= SEQMOD;
+    return 1;
+}
+
+
+/*
+ * Add a message to a (possibly new) sequence.
+ *
+ * If public ==  1, make sequence public.
+ * If public ==  0, make sequence private.
+ * If public == -1, leave the public/private bit alone for existing
+ *                  sequences.  For new sequences, set this bit based
+ *                  on its readonly status.
+ *
+ * If error, return 0, else return 1.
+ */
+
+int
+seq_addmsg (struct msgs *mp, char *cp, int msgnum, int public, int zero)
+{
+    int i, j, new_seq = 1;
+
+    if (!seq_nameok (cp))
+       return 0;
+
+    /*
+     * keep mp->curmsg and msgattrs["cur"] in sync - see seq_list()
+     */
+    if (!strcmp (current,cp))
+       mp->curmsg = msgnum;    
+
+    /*
+     * Get the number for this sequence
+     */
+    for (i = 0; mp->msgattrs[i]; i++) {
+       if (!strcmp (mp->msgattrs[i], cp)) {
+           new_seq = 0;
+           break;
+       }
+    }
+
+    /*
+     * If this is a new sequence, add a slot for it
+     */
+    if (new_seq) {
+       if (i >= NUMATTRS) {
+           advise (NULL, "only %d sequences allowed (no room for %s)!", NUMATTRS, cp);
+           return 0;
+       }
+       if (!(mp->msgattrs[i] = strdup (cp))) {
+           advise (NULL, "strdup failed");
+           return 0;
+       }
+       mp->msgattrs[i + 1] = NULL;
+    }
+
+    /*
+     * If sequence is new, or zero flag is set, then first
+     * clear the bit for this sequence from all messages.
+     */
+    if (new_seq || zero) {
+       for (j = mp->lowmsg; j <= mp->hghmsg; j++)
+           clear_sequence (mp, i, j);
+    }
+
+    /*
+     * Now flip on the bit for this sequence
+     * for this particular message.
+     */
+    add_sequence (mp, i, msgnum);
+
+    /*
+     * Set the public/private bit for this sequence.
+     */
+    if (public == 1)
+       make_seq_public (mp, i);
+    else if (public == 0)
+       make_seq_private (mp, i);
+    else if (new_seq) {
+       /*
+        * If public == -1, then only set the
+         * public/private bit for new sequences.
+         */
+       if (is_readonly (mp))
+           make_seq_private (mp, i);
+       else
+           make_seq_public (mp, i);
+    }
+
+    mp->msgflags |= SEQMOD;
+    return 1;
+}
diff --git a/sbr/seq_bits.c b/sbr/seq_bits.c
new file mode 100644 (file)
index 0000000..b0f6e6c
--- /dev/null
@@ -0,0 +1,27 @@
+
+/*
+ * seq_bits.c -- return the snprintb() string for a sequence
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+char *
+seq_bits (struct msgs *mp)
+{
+    int i;
+    size_t len;
+    static char buffer[BUFSIZ];
+
+    strncpy (buffer, MBITS, sizeof(buffer));
+
+    for (i = 0; mp->msgattrs[i]; i++) {
+       len = strlen (buffer);
+       snprintf (buffer + len, sizeof(buffer) - len,
+               "%c%s", FFATTRSLOT + 1 + i, mp->msgattrs[i]);
+    }
+
+    return buffer;
+}
diff --git a/sbr/seq_del.c b/sbr/seq_del.c
new file mode 100644 (file)
index 0000000..ed70c5d
--- /dev/null
@@ -0,0 +1,130 @@
+
+/*
+ * seq_del.c -- delete message(s) from a sequence
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+/*
+ * Delete all SELECTED messages from sequence
+ *
+ * If public ==  1, make sequence public.
+ * If public ==  0, make sequence private.
+ * If public == -1, leave the public/private bit alone for existing
+ *                  sequences.  For new sequences, set this bit based
+ *                  on its readonly status.
+ *
+ * If error, return 0, else return 1.
+ */
+
+int
+seq_delsel (struct msgs *mp, char *cp, int public, int zero)
+{
+    int i, msgnum, new_seq = 1;
+
+    if (!seq_nameok (cp))
+       return 0;
+
+    /*
+     * Get the number for this sequence
+     */
+    for (i = 0; mp->msgattrs[i]; i++) {
+       if (!strcmp (mp->msgattrs[i], cp)) {
+           new_seq = 0;
+           break;
+       }
+    }
+
+    /*
+     * If the zero flag is set, first add all existing
+     * messages in this folder to the sequence.
+     */
+    if (zero) {
+       /*
+        * create the sequence, if necessary
+        */
+       if (new_seq) {
+           if (i >= NUMATTRS) {
+               advise (NULL, "only %d sequences allowed (no room for %s)!", NUMATTRS, cp);
+               return 0;
+           }
+           if (!(mp->msgattrs[i] = strdup (cp))) {
+               advise (NULL, "strdup failed");
+               return 0;
+           }
+           mp->msgattrs[i + 1] = NULL;
+       }
+       /*
+        * now add sequence bit to all existing messages
+        */
+       for (msgnum = mp->lowmsg; msgnum <= mp->hghmsg; msgnum++) {
+           if (does_exist (mp, msgnum))
+               add_sequence (mp, i, msgnum);
+           else
+               clear_sequence (mp, i, msgnum);
+       }
+    } else {
+       if (new_seq) {
+           advise (NULL, "no such sequence as %s", cp);
+           return 0;
+       }
+    }
+
+    /*
+     * Now clear the bit on all selected messages
+     */
+    for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
+       if (is_selected (mp, msgnum))
+           clear_sequence (mp, i, msgnum);
+
+    /*
+     * Set the public/private bit for this sequence.
+     */
+    if (public == 1)
+       make_seq_public (mp, i);
+    else if (public == 0)
+       make_seq_private (mp, i);
+    else if (new_seq) {
+       /*
+        * If public == -1, then only set the
+        * public/private bit for new sequences.
+        */
+       if (is_readonly (mp))
+           make_seq_private (mp, i);
+       else
+           make_seq_public (mp, i);
+    }
+
+    mp->msgflags |= SEQMOD;
+    return 1;
+}
+
+
+/*
+ * Delete message from sequence.
+ *
+ * If error, return 0, else return 1.
+ */
+
+int
+seq_delmsg (struct msgs *mp, char *cp, int msgnum)
+{
+    int i;
+
+    if (!seq_nameok (cp))
+       return 0;
+
+    for (i = 0; mp->msgattrs[i]; i++) {
+       if (!strcmp (mp->msgattrs[i], cp)) {
+           clear_sequence (mp, i, msgnum);
+           mp->msgflags |= SEQMOD;
+           return 1;
+       }
+    }
+
+    advise (NULL, "no such sequence as %s", cp);
+    return 0;
+}
diff --git a/sbr/seq_getnum.c b/sbr/seq_getnum.c
new file mode 100644 (file)
index 0000000..c44f177
--- /dev/null
@@ -0,0 +1,22 @@
+
+/*
+ * seq_getnum.c -- find the index for a sequence
+ *              -- return -1 if sequence doesn't exist
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+int
+seq_getnum (struct msgs *mp, char *seqname)
+{
+    int i;
+
+    for (i = 0; mp->msgattrs[i]; i++)
+       if (!strcmp (mp->msgattrs[i], seqname))
+           return i;
+
+    return -1;
+}
diff --git a/sbr/seq_list.c b/sbr/seq_list.c
new file mode 100644 (file)
index 0000000..afa8671
--- /dev/null
@@ -0,0 +1,104 @@
+
+/*
+ * seq_list.c -- Get all messages in a sequence and return them
+ *            -- as a space separated list of message ranges.
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+/* allocate this much buffer space at a time */
+#define MAXBUFFER 1024
+
+/* static buffer to collect the sequence line */
+static char *buffer = NULL;
+static int len = 0;
+
+
+char *
+seq_list(struct msgs *mp, char *seqname)
+{
+    int i, j, seqnum;
+    char *bp;
+
+    /* On first invocation, allocate initial buffer space */
+    if (!buffer) {
+       len = MAXBUFFER;
+       if (!(buffer = malloc ((size_t) len)))
+           adios (NULL, "unable to malloc storage in seq_list");
+    }
+
+    /*
+     * Special processing for "cur" sequence.  We assume that the
+     * "cur" sequence and mp->curmsg are in sync (see seq_add.c).
+     * This is returned, even if message doesn't exist or the
+     * folder is empty.
+     */
+    if (!strcmp (current, seqname)) {
+       if (mp->curmsg) {       
+           sprintf(buffer, "%s", m_name(mp->curmsg));
+           return (buffer);
+       } else
+           return (NULL);
+    }
+
+    /* If the folder is empty, just return NULL */
+    if (mp->nummsg == 0)
+       return NULL;
+
+    /* Get the index of the sequence */
+    if ((seqnum = seq_getnum (mp, seqname)) == -1)
+       return NULL;
+
+    bp = buffer;
+
+    for (i = mp->lowmsg; i <= mp->hghmsg; ++i) {
+       /*
+        * If message doesn't exist, or isn't in
+        * the sequence, then continue.
+        */
+       if (!does_exist(mp, i) || !in_sequence(mp, seqnum, i))
+           continue;
+
+       /*
+        * See if we need to enlarge buffer.  Since we don't know
+        * exactly how many character this particular message range
+        * will need, we enlarge the buffer if we are within
+        * 50 characters of the end.
+        */
+       if (bp - buffer > len - 50) {
+           char *newbuf;
+
+           len += MAXBUFFER;
+           if (!(newbuf = realloc (buffer, (size_t) len)))
+               adios (NULL, "unable to realloc storage in seq_list");
+           bp = newbuf + (bp - buffer);
+           buffer = newbuf;
+       }
+
+       /*
+        * If this is not the first message range in
+        * the list, first add a space.
+        */
+       if (bp > buffer)
+           *bp++ = ' ';
+
+       sprintf(bp, "%s", m_name(i));
+       bp += strlen(bp);
+       j = i;                  /* Remember beginning of message range */
+
+       /*
+        * Scan to the end of this message range
+        */
+       for (++i; i <= mp->hghmsg && does_exist(mp, i) && in_sequence(mp, seqnum, i);
+            ++i)
+           ;
+
+       if (i - j > 1) {
+           sprintf(bp, "-%s", m_name(i - 1));
+           bp += strlen(bp);
+       }
+    }
+    return (bp > buffer? buffer : NULL);
+}
diff --git a/sbr/seq_nameok.c b/sbr/seq_nameok.c
new file mode 100644 (file)
index 0000000..66319b5
--- /dev/null
@@ -0,0 +1,54 @@
+
+/*
+ * seq_nameok.c -- check if a sequence name is ok
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+int
+seq_nameok (char *s)
+{
+    char *pp;
+
+    if (s == NULL || *s == '\0') {
+       advise (NULL, "empty sequence name");
+       return 0;
+    }
+
+    /*
+     * Make sure sequence name doesn't clash with one
+     * of the `reserved' sequence names.
+     */
+    if (!(strcmp (s, "new") &&
+         strcmp (s, "all") &&
+         strcmp (s, "first") &&
+         strcmp (s, "last") &&
+         strcmp (s, "prev") &&
+         strcmp (s, "next"))) {
+       advise (NULL, "illegal sequence name: %s", s);
+       return 0;
+    }
+
+    /*
+     * First character in a sequence name must be
+     * an alphabetic character ...
+     */
+    if (!isalpha (*s)) {
+       advise (NULL, "illegal sequence name: %s", s);
+       return 0;
+    }
+
+    /*
+     * and can be followed by zero or more alphanumeric characters
+     */
+    for (pp = s + 1; *pp; pp++)
+       if (!isalnum (*pp)) {
+           advise (NULL, "illegal sequence name: %s", s);
+           return 0;
+       }
+
+    return 1;
+}
diff --git a/sbr/seq_print.c b/sbr/seq_print.c
new file mode 100644 (file)
index 0000000..ee34fd9
--- /dev/null
@@ -0,0 +1,46 @@
+
+/*
+ * seq_print.c -- Routines to print sequence information.
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+#define empty(s) ((s) ? (s) : "")
+
+/*
+ * Print all the sequences in a folder
+ */
+void
+seq_printall (struct msgs *mp)
+{
+    int i;
+    char *list;
+
+    for (i = 0; mp->msgattrs[i]; i++) {
+       list = seq_list (mp, mp->msgattrs[i]);
+       printf ("%s%s: %s\n", mp->msgattrs[i],
+           is_seq_private (mp, i) ? " (private)" : "", empty(list));
+    }
+}
+
+
+/*
+ * Print a particular sequence in a folder
+ */
+void
+seq_print (struct msgs *mp, char *seqname)
+{
+    int i;
+    char *list;
+
+    /* get the index of sequence */
+    i = seq_getnum (mp, seqname);
+
+    /* get sequence information */
+    list = seq_list (mp, seqname);
+
+    printf ("%s%s: %s\n", seqname,
+       (i == -1) ? "" : is_seq_private(mp, i) ? " (private)" : "", empty(list));
+}
diff --git a/sbr/seq_read.c b/sbr/seq_read.c
new file mode 100644 (file)
index 0000000..649a360
--- /dev/null
@@ -0,0 +1,235 @@
+
+/*
+ * seq_read.c -- read the .mh_sequence file and
+ *            -- initialize sequence information
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+/*
+ * static prototypes
+ */
+static int seq_init (struct msgs *, char *, char *);
+static void seq_public (struct msgs *);
+static void seq_private (struct msgs *);
+
+
+/*
+ * Get the sequence information for this folder from
+ * .mh_sequence (or equivalent specified in .mh_profile)
+ * or context file (for private sequences).
+ */
+
+void
+seq_read (struct msgs *mp)
+{
+    /* sanity check - check that context has been read */
+    if (defpath == NULL)
+       adios (NULL, "oops, context hasn't been read yet");
+
+    /*
+     * Initialize the list of sequence names.  Go ahead and
+     * add the "cur" sequence to the list of sequences.
+     */
+    mp->msgattrs[0] = getcpy (current);
+    mp->msgattrs[1] = NULL;
+    make_all_public (mp);      /* initially, make all public */
+
+    /* If folder is empty, don't scan for sequence information */
+    if (mp->nummsg == 0)
+       return;
+
+    /* Initialize the public sequences */
+    seq_public (mp);
+
+    /* Initialize the private sequences */
+    seq_private (mp);
+}
+
+
+/*
+ * read folder's sequences file for public sequences
+ */
+
+static void
+seq_public (struct msgs *mp)
+{
+    int state;
+    char *cp, seqfile[PATH_MAX];
+    char name[NAMESZ], field[BUFSIZ];
+    FILE *fp;
+
+    /*
+     * If mh_seq == NULL (such as if nmh been compiled with
+     * NOPUBLICSEQ), or if *mh_seq == '\0' (the user has defined
+     * the "mh-sequences" profile entry, but left it empty),
+     * then just return, and do not initialize any public sequences.
+     */
+    if (mh_seq == NULL || *mh_seq == '\0')
+       return;
+
+    /* get filename of sequence file */
+    snprintf (seqfile, sizeof(seqfile), "%s/%s", mp->foldpath, mh_seq);
+
+    if ((fp = fopen (seqfile, "r")) == NULL)
+       return;
+
+    /* Use m_getfld to scan sequence file */
+    for (state = FLD;;) {
+       switch (state = m_getfld (state, name, field, sizeof(field), fp)) {
+           case FLD: 
+           case FLDPLUS:
+           case FLDEOF: 
+               if (state == FLDPLUS) {
+                   cp = getcpy (field);
+                   while (state == FLDPLUS) {
+                       state = m_getfld (state, name, field, sizeof(field), fp);
+                       cp = add (field, cp);
+                   }
+                   seq_init (mp, getcpy (name), trimcpy (cp));
+                   free (cp);
+               } else {
+                   seq_init (mp, getcpy (name), trimcpy (field));
+               }
+               if (state == FLDEOF)
+                   break;
+               continue;
+
+           case BODY: 
+           case BODYEOF: 
+               adios (NULL, "no blank lines are permitted in %s", seqfile);
+               /* fall */
+
+           case FILEEOF:
+               break;
+
+           default: 
+               adios (NULL, "%s is poorly formatted", seqfile);
+       }
+       break;  /* break from for loop */
+    }
+
+    fclose (fp);
+}
+
+
+/*
+ * Scan profile/context list for private sequences.
+ *
+ * We search the context list for all keys that look like
+ * "atr-seqname-folderpath", and add them as private sequences.
+ */
+
+static void
+seq_private (struct msgs *mp)
+{
+    int i, j, alen, plen;
+    char *cp;
+    struct node *np;
+
+    alen = strlen ("atr-");
+    plen = strlen (mp->foldpath) + 1;
+
+    for (np = m_defs; np; np = np->n_next) {
+       if (ssequal ("atr-", np->n_name)
+               && (j = strlen (np->n_name) - plen) > alen
+               && *(np->n_name + j) == '-'
+               && strcmp (mp->foldpath, np->n_name + j + 1) == 0) {
+           cp = getcpy (np->n_name + alen);
+           *(cp + j - alen) = '\0';
+           if ((i = seq_init (mp, cp, getcpy (np->n_field))) != -1)
+               make_seq_private (mp, i);
+       }
+    }
+}
+
+
+/*
+ * Add the name of sequence to the list of folder sequences.
+ * Then parse the list of message ranges for this
+ * sequence, and setup the various bit flags for each
+ * message in the sequence.
+ *
+ * Return internal index for the sequence if successful.
+ * Return -1 on error.
+ */
+
+static int
+seq_init (struct msgs *mp, char *name, char *field)
+{
+    int i, j, k, is_current;
+    char *cp, **ap;
+
+    /*
+     * Check if this is "cur" sequence,
+     * so we can do some special things.
+     */
+    is_current = !strcmp (current, name);
+
+    /*
+     * Search for this sequence name to see if we've seen
+     * it already.  If we've seen this sequence before,
+     * then clear the bit for this sequence from all the
+     * mesages in this folder.
+     */
+    for (i = 0; mp->msgattrs[i]; i++) {
+       if (!strcmp (mp->msgattrs[i], name)) {
+           for (j = mp->lowmsg; j <= mp->hghmsg; j++)
+               clear_sequence (mp, i, j);
+           break;
+       }
+    }
+
+    /* Return error, if too many sequences */
+    if (i >= NUMATTRS) {
+       free (name);
+       free (field);
+       return -1;
+    }
+
+    /*
+     * If we've already seen this sequence name, just free the
+     * name string.  Else add it to the list of sequence names.
+     */
+    if (mp->msgattrs[i]) {
+       free (name);
+    } else {
+       mp->msgattrs[i] = name;
+       mp->msgattrs[i + 1] = NULL;
+    }
+
+    /*
+     * Split up the different message ranges at whitespace
+     */
+    for (ap = brkstring (field, " ", "\n"); *ap; ap++) {
+       if ((cp = strchr(*ap, '-')))
+           *cp++ = '\0';
+       if ((j = m_atoi (*ap)) > 0) {
+           k = cp ? m_atoi (cp) : j;
+
+           /*
+            * Keep mp->curmsg and "cur" sequence in synch.  Unlike
+            * other sequences, this message doesn't need to exist.
+            * Think about the series of command (rmm; next) to
+            * understand why this can be the case.  But if it does
+            * exist, we will still set the bit flag for it like
+            * other sequences.
+            */
+           if (is_current)
+               mp->curmsg = j;
+           /*
+            * We iterate through messages in this range
+            * and flip on bit for this sequence.
+            */
+           for (; j <= k; j++) {
+               if (j >= mp->lowmsg && j <= mp->hghmsg && does_exist(mp, j))
+                   add_sequence (mp, i, j);
+           }
+       }
+    }
+
+    free (field);      /* free string containing message ranges */
+    return i;
+}
diff --git a/sbr/seq_save.c b/sbr/seq_save.c
new file mode 100644 (file)
index 0000000..e0f42ae
--- /dev/null
@@ -0,0 +1,115 @@
+
+/*
+ * seq_save.c -- 1) synchronize sequences
+ *            -- 2) save public sequences
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/signals.h>
+
+
+/*
+ * 1.  If sequence is public and folder is readonly,
+ *     then change it to be private
+ * 2a. If sequence is public, then add it to the sequences file
+ *     in folder (name specified by mh-sequences profile entry).
+ * 2b. If sequence is private, then add it to the
+ *     context file.
+ */
+
+void
+seq_save (struct msgs *mp)
+{
+    int i;
+    char flags, *cp, attr[BUFSIZ], seqfile[PATH_MAX];
+    FILE *fp;
+    sigset_t set, oset;
+
+    /* sanity check - check that context has been read */
+    if (defpath == NULL)
+       adios (NULL, "oops, context hasn't been read yet");
+
+    /* check if sequence information has changed */
+    if (!(mp->msgflags & SEQMOD))
+       return;
+    mp->msgflags &= ~SEQMOD;
+
+    fp = NULL;
+    flags = mp->msgflags;      /* record folder flags */
+
+    /*
+     * If no mh-sequences file is defined, or if a mh-sequences file
+     * is defined but empty (*mh_seq == '\0'), then pretend folder
+     * is readonly.  This will force all sequences to be private.
+     */
+    if (mh_seq == NULL || *mh_seq == '\0')
+       set_readonly (mp);
+    else
+       snprintf (seqfile, sizeof(seqfile), "%s/%s", mp->foldpath, mh_seq);
+
+    for (i = 0; mp->msgattrs[i]; i++) {
+       snprintf (attr, sizeof(attr), "atr-%s-%s", mp->msgattrs[i], mp->foldpath);
+
+       /* get space separated list of sequence ranges */
+       if (!(cp = seq_list(mp, mp->msgattrs[i]))) {
+           context_del (attr);                 /* delete sequence from context */
+           continue;
+       }
+
+       if (is_readonly(mp) || is_seq_private(mp, i)) {
+priv:
+           /*
+            * sequence is private
+            */
+           context_replace (attr, cp);         /* update sequence in context   */
+       } else {
+           /*
+            * sequence is public
+            */
+           context_del (attr);                 /* delete sequence from context */
+
+           if (!fp) {
+               /*
+                * Attempt to open file for public sequences.
+                * If that fails (probably because folder is
+                * readonly), then make sequence private.
+                */
+               if ((fp = fopen (seqfile, "w")) == NULL
+                       && (unlink (seqfile) == -1 ||
+                           (fp = fopen (seqfile, "w")) == NULL)) {
+                   admonish (attr, "unable to write");
+                   goto priv;
+               }
+
+               /* block a few signals */
+               sigemptyset (&set);
+               sigaddset(&set, SIGHUP);
+               sigaddset(&set, SIGINT);
+               sigaddset(&set, SIGQUIT);
+               sigaddset(&set, SIGTERM);
+               SIGPROCMASK (SIG_BLOCK, &set, &oset);
+           }
+           fprintf (fp, "%s: %s\n", mp->msgattrs[i], cp);
+       }
+    }
+
+    if (fp) {
+       fclose (fp);
+       SIGPROCMASK (SIG_SETMASK, &oset, &set);  /* reset signal mask */
+    } else {
+       /*
+        * If folder is not readonly, and we didn't save any
+        * public sequences, then remove that file.
+        */
+       if (!is_readonly(mp))
+           unlink (seqfile);
+    }
+
+    /*
+     * Reset folder flag, since we may be
+     * pretending that folder is readonly.
+     */
+    mp->msgflags = flags;
+}
diff --git a/sbr/seq_setcur.c b/sbr/seq_setcur.c
new file mode 100644 (file)
index 0000000..7e7acba
--- /dev/null
@@ -0,0 +1,19 @@
+
+/*
+ * seq_setcur.c -- set the current message ("cur" sequence) for a folder
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+void
+seq_setcur (struct msgs *mp, int msgnum)
+{
+    /*
+     * Just call seq_addmsg() to update the
+     * "cur" sequence.
+     */
+    seq_addmsg (mp, current, msgnum, -1, 1);
+}
diff --git a/sbr/seq_setprev.c b/sbr/seq_setprev.c
new file mode 100644 (file)
index 0000000..e5161c5
--- /dev/null
@@ -0,0 +1,42 @@
+
+/*
+ * seq_setprev.c -- set the Previous-Sequence
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+/*
+ * Add all the messages currently SELECTED to
+ * the Previous-Sequence.  This way, when the next
+ * command is given, there is a convenient way to
+ * selected all the messages used in the previous
+ * command.
+ */
+
+void
+seq_setprev (struct msgs *mp)
+{
+    char **ap, *cp, *dp;
+
+    /*
+     * Get the list of sequences for Previous-Sequence
+     * and split them.
+     */
+    if ((cp = context_find (psequence))) {
+       dp = getcpy (cp);
+       if (!(ap = brkstring (dp, " ", "\n")) || !*ap) {
+           free (dp);
+           return;
+       }
+    } else {
+       return;
+    }
+
+    /* Now add all SELECTED messages to each sequence */
+    for (; *ap; ap++)
+       seq_addsel (mp, *ap, -1, 1);
+
+    free (dp);
+}
diff --git a/sbr/seq_setunseen.c b/sbr/seq_setunseen.c
new file mode 100644 (file)
index 0000000..906b0ac
--- /dev/null
@@ -0,0 +1,58 @@
+
+/*
+ * seq_setunseen.c -- add/delete all messages which have the SELECT_UNSEEN
+ *                 -- bit set to/from the Unseen-Sequence
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+/*
+ * We scan through the folder and act upon all messages
+ * that are marked with the SELECT_UNSEEN bit.
+ *
+ * If seen == 1, delete messages from unseen sequence.
+ * If seen == 0, add messages to unseen sequence.
+ */
+
+void
+seq_setunseen (struct msgs *mp, int seen)
+{
+    int msgnum;
+    char **ap, *cp, *dp;
+
+    /*
+     * Get the list of sequences for Unseen-Sequence
+     * and split them.
+     */
+    if ((cp = context_find (usequence))) {
+       dp = getcpy (cp);
+       if (!(ap = brkstring (dp, " ", "\n")) || !*ap) {
+           free (dp);
+           return;
+       }
+    } else {
+       return;
+    }
+
+    /*
+     * Now add/delete each message which has the SELECT_UNSEEN
+     * bit set to/from each of these sequences.
+     */
+    for (; *ap; ap++) {
+       if (seen) {
+           /* make sure sequence exists first */
+           if (seq_getnum(mp, *ap) != -1)
+               for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
+                   if (is_unseen (mp, msgnum))
+                       seq_delmsg (mp, *ap, msgnum);
+       } else {
+           for (msgnum = mp->lowmsg; msgnum <= mp->hghmsg; msgnum++)
+               if (is_unseen (mp, msgnum))
+                   seq_addmsg (mp, *ap, msgnum, -1, 0);
+       }
+    }
+
+    free (dp);
+}
diff --git a/sbr/showfile.c b/sbr/showfile.c
new file mode 100644 (file)
index 0000000..7ed13ca
--- /dev/null
@@ -0,0 +1,65 @@
+
+/*
+ * showfile.c -- invoke the `lproc' command
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+int
+showfile (char **arg, char *file)
+{
+    pid_t pid;
+    int isdraft, vecp;
+    char *vec[MAXARGS];
+
+    context_save();    /* save the context file */
+    fflush(stdout);
+
+    /*
+     * If you have your lproc listed as "mhl",
+     * then really invoked the mhlproc instead
+     * (which is usually mhl anyway).
+     */
+    if (!strcmp (r1bindex (lproc, '/'), "mhl"))
+       lproc = mhlproc;
+
+    switch (pid = vfork()) {
+    case -1:
+       /* fork error */
+       advise ("fork", "unable to");
+       return 1;
+
+    case 0:
+       /* child */
+       vecp = 0;
+       vec[vecp++] = r1bindex (lproc, '/');
+       isdraft = 1;
+       if (arg) {
+           while (*arg) {
+               if (**arg != '-')
+                   isdraft = 0;
+               vec[vecp++] = *arg++;
+           }
+       }
+       if (isdraft) {
+           if (!strcmp (vec[0], "show"))
+               vec[vecp++] = "-file";
+           vec[vecp++] = file;
+       }
+       vec[vecp] = NULL;
+
+       execvp (lproc, vec);
+       fprintf (stderr, "unable to exec ");
+       perror (lproc);
+       _exit (-1);
+
+    default:
+       /* parent */
+       return (pidwait (pid, -1) & 0377 ? 1 : 0);
+    }
+
+    return 1;  /* NOT REACHED */
+}
diff --git a/sbr/sigmsg.awk b/sbr/sigmsg.awk
new file mode 100755 (executable)
index 0000000..d4f6e52
--- /dev/null
@@ -0,0 +1,87 @@
+# 
+# sigmsg.awk -- awk/nawk/gawk script to generate sigmsg.h
+#
+# provided by Geoff Wing <mason@werple.apana.org.au>
+#
+# $Id$
+#
+# On SunOS 4.1.3 - user-functions don't work properly, also \" problems
+# Without 0 + hacks some nawks compare numbers as strings
+#
+/^[\t ]*#[\t ]*define[\t _]*SIG[A-Z][A-Z0-9]*[\t ]*[1-9][0-9]*/ { 
+    sigindex = index($0, "SIG")
+    sigtail = substr($0, sigindex, 80)
+    split(sigtail, tmp)
+    signam = substr(tmp[1], 4, 20)
+    signum = tmp[2]
+    if (sig[signum] == "") {
+       sig[signum] = signam
+       if (0 + max < 0 + signum && signum < 60)
+           max = signum
+       if (signam == "ABRT")   { msg[signum] = "abort" }
+       if (signam == "ALRM")   { msg[signum] = "alarm" }
+       if (signam == "BUS")    { msg[signum] = "bus error" }
+       if (signam == "CHLD")   { msg[signum] = "death of child" }
+       if (signam == "CLD")    { msg[signum] = "death of child" }
+       if (signam == "CONT")   { msg[signum] = "continued" }
+       if (signam == "EMT")    { msg[signum] = "EMT instruction" }
+       if (signam == "FPE")    { msg[signum] = "floating point exception" }
+       if (signam == "HUP")    { msg[signum] = "hangup" }
+       if (signam == "ILL")    { msg[signum] = "illegal hardware instruction" }
+       if (signam == "INFO")   { msg[signum] = "status request from keyboard" }
+       if (signam == "INT")    { msg[signum] = "interrupt" }
+       if (signam == "IO")     { msg[signum] = "i/o ready" }
+       if (signam == "IOT")    { msg[signum] = "IOT instruction" }
+       if (signam == "KILL")   { msg[signum] = "killed" }
+       if (signam == "LOST")   { msg[signum] = "resource lost" }
+       if (signam == "PIPE")   { msg[signum] = "broken pipe" }
+       if (signam == "POLL")   { msg[signum] = "pollable event occurred" }
+       if (signam == "PROF")   { msg[signum] = "profile signal" }
+       if (signam == "PWR")    { msg[signum] = "power fail" }
+       if (signam == "QUIT")   { msg[signum] = "quit" }
+       if (signam == "SEGV")   { msg[signum] = "segmentation fault" }
+       if (signam == "SYS")    { msg[signum] = "invalid system call" }
+       if (signam == "TERM")   { msg[signum] = "terminated" }
+       if (signam == "TRAP")   { msg[signum] = "trace trap" }
+       if (signam == "URG")    { msg[signum] = "urgent condition" }
+       if (signam == "USR1")   { msg[signum] = "user-defined signal 1" }
+       if (signam == "USR2")   { msg[signum] = "user-defined signal 2" }
+       if (signam == "VTALRM") { msg[signum] = "virtual time alarm" }
+       if (signam == "WINCH")  { msg[signum] = "window size changed" }
+       if (signam == "XCPU")   { msg[signum] = "cpu limit exceeded" }
+       if (signam == "XFSZ")   { msg[signum] = "file size limit exceeded" }
+    }
+}
+
+END {
+    ps = "%s"
+    ifdstr = sprintf("\t%cstopped%s%c,\n", 34, ps, 34)
+
+    print "\n/*"
+    print " * sigmsg.h -- architecture-customized signal messages for nmh"
+    print " *"
+    print " * automatically generated by sigmsg.awk"
+    print " */\n"
+    printf("%s  %d\n\n", "#define SIGCOUNT", max)
+    print "char *sigmsg[SIGCOUNT+2] = {"
+    print "\tNULL,"
+
+    for (i = 1; i <= 0 + max; i++)
+       if (msg[i] == "") {
+           if (sig[i] == "")
+               printf("\tNULL,\n")
+           else if (sig[i] == "STOP")
+               printf ifdstr, " (signal)", " (signal)"
+           else if (sig[i] == "TSTP")
+               printf ifdstr, "", ""
+           else if (sig[i] == "TTIN")
+               printf ifdstr, " (tty input)", " (tty input)"
+           else if (sig[i] == "TTOU")
+               printf ifdstr, " (tty output)", " (tty output)"
+           else
+               printf("\t%cSIG%s%c,\n", 34, sig[i], 34)
+       } else
+           printf("\t%c%s%c,\n", 34, msg[i], 34)
+    print "\tNULL"
+    print "};"
+}
diff --git a/sbr/signals.c b/sbr/signals.c
new file mode 100644 (file)
index 0000000..a8c12d6
--- /dev/null
@@ -0,0 +1,116 @@
+
+/*
+ * signals.c -- general signals interface for nmh
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/signals.h>
+
+
+int
+SIGPROCMASK (int how, const sigset_t *set, sigset_t *oset)
+{
+#ifdef POSIX_SIGNALS
+    return sigprocmask(how, set, oset);
+#else
+# ifdef BSD_SIGNALS
+    switch(how) {
+    case SIG_BLOCK:
+       *oset = sigblock(*set);
+       break;
+    case SIG_UNBLOCK:
+       sigfillset(*oset);
+       *oset = sigsetmask(*oset);
+       sigsetmask(*oset & ~(*set));
+       break;
+    case SIG_SETMASK:
+       *oset = sigsetmask(*set);
+       break;
+    default:
+       adios(NULL, "unknown flag in SIGPROCMASK");
+       break;
+    }
+    return 0;
+# endif
+#endif
+}
+
+
+/*
+ * A version of the function `signal' that uses reliable
+ * signals, if the machine supports them.  Also, (assuming
+ * OS support), it restarts interrupted system calls for all
+ * signals except SIGALRM.
+ */
+
+SIGNAL_HANDLER
+SIGNAL (int sig, SIGNAL_HANDLER func)
+{
+#ifdef POSIX_SIGNALS
+    struct sigaction act, oact;
+
+    act.sa_handler = func;
+    sigemptyset(&act.sa_mask);
+    act.sa_flags = 0;
+
+    if (sig == SIGALRM) {
+# ifdef SA_INTERRUPT
+       act.sa_flags |= SA_INTERRUPT;     /* SunOS */
+# endif
+    } else {
+# ifdef SA_RESTART
+       act.sa_flags |= SA_RESTART;       /* SVR4, BSD4.4 */
+# endif
+    }
+    if (sigaction(sig, &act, &oact) < 0)
+       return (SIG_ERR);
+    return (oact.sa_handler);
+#else
+    return signal (sig, func);
+#endif
+}
+
+
+/*
+ * A version of the function `signal' that will set
+ * the handler of `sig' to `func' if the signal is
+ * not currently set to SIG_IGN.  Also uses reliable
+ * signals if available.
+ */
+SIGNAL_HANDLER
+SIGNAL2 (int sig, SIGNAL_HANDLER func)
+{
+#ifdef POSIX_SIGNALS
+    struct sigaction act, oact;
+
+    if (sigaction(sig, NULL, &oact) < 0)
+       return (SIG_ERR);
+    if (oact.sa_handler != SIG_IGN) {
+       act.sa_handler = func;
+       sigemptyset(&act.sa_mask);
+       act.sa_flags = 0;
+
+       if (sig == SIGALRM) {
+# ifdef SA_INTERRUPT
+           act.sa_flags |= SA_INTERRUPT;     /* SunOS */
+# endif
+       } else {
+# ifdef SA_RESTART
+           act.sa_flags |= SA_RESTART;       /* SVR4, BSD4.4 */
+# endif
+       }
+       if (sigaction(sig, &act, &oact) < 0)
+           return (SIG_ERR);
+    }
+    return (oact.sa_handler);
+#else
+    SIGNAL_HANDLER ofunc;
+
+    if ((ofunc = signal (sig, SIG_IGN)) != SIG_IGN)
+       signal (sig, func);
+    return ofunc;
+#endif
+}
+
diff --git a/sbr/smatch.c b/sbr/smatch.c
new file mode 100644 (file)
index 0000000..54ff550
--- /dev/null
@@ -0,0 +1,45 @@
+
+/*
+ * smatch.c -- match a switch (option)
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+int
+smatch(char *string, struct swit *swp)
+{
+    char *sp, *tcp;
+    int firstone, len;
+    struct swit *tp;
+
+    firstone = UNKWNSW;
+
+    if (!string)
+       return firstone;
+    len = strlen(string);
+
+    for (tp = swp; tp->sw; tp++) {
+       tcp = tp->sw;
+       if (len < abs(tp->minchars))
+           continue;                   /* no match */
+       for (sp = string; *sp == *tcp++;) {
+           if (*sp++ == '\0')
+               return (tp - swp);      /* exact match */
+       }
+       if (*sp) {
+           if (*sp != ' ')
+               continue;               /* no match */
+           if (*--tcp == '\0')
+               return (tp - swp);      /* exact match */
+       }
+       if (firstone == UNKWNSW)
+           firstone = tp - swp;
+       else
+           firstone = AMBIGSW;
+    }
+
+    return (firstone);
+}
diff --git a/sbr/snprintb.c b/sbr/snprintb.c
new file mode 100644 (file)
index 0000000..06140c8
--- /dev/null
@@ -0,0 +1,38 @@
+
+/*
+ * snprintb.c -- snprintf a %b string
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+char *
+snprintb (char *buffer, size_t n, unsigned v, char *bits)
+{
+    register int i, j;
+    register char c, *bp;
+
+    snprintf (buffer, n, bits && *bits == 010 ? "0%o" : "0x%x", v);
+    bp = buffer + strlen(buffer);
+
+    if (bits && *++bits) {
+       j = 0;
+       *bp++ = '<';
+       while ((i = *bits++))
+           if (v & (1 << (i - 1))) {
+               if (j++)
+                   *bp++ = ',';
+               for (; (c = *bits) > 32; bits++)
+                   *bp++ = c;
+           }
+           else
+               for (; *bits > 32; bits++)
+                   continue;
+       *bp++ = '>';
+       *bp = 0;
+    }
+
+    return buffer;
+}
diff --git a/sbr/snprintf.c b/sbr/snprintf.c
new file mode 100644 (file)
index 0000000..efb6df0
--- /dev/null
@@ -0,0 +1,1045 @@
+/*
+ * snprintf.c -- formatted output to a string
+ *
+ * This is an implementation of snprintf() and vsnprintf()
+ * taken from the Apache web server.  This is only used on
+ * systems which do not have a native version.
+ */
+
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ * This code is based on, and used with the permission of, the
+ * SIO stdio-replacement strx_* functions by Panos Tsirigotis
+ * <panos@alumni.cs.colorado.edu> for xinetd.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <netinet/in.h>
+
+typedef enum {
+    NO = 0, YES = 1
+} boolean_e;
+
+#ifndef FALSE
+#define FALSE                  0
+#endif
+#ifndef TRUE
+#define TRUE                   1
+#endif
+#define NUL                    '\0'
+#define INT_NULL               ((int *)0)
+#define WIDE_INT               long
+
+typedef struct {
+    char *curpos;
+    char *endpos;
+} ap_vformatter_buff;
+
+typedef WIDE_INT wide_int;
+typedef unsigned WIDE_INT u_wide_int;
+typedef int bool_int;
+
+#define S_NULL                 "(null)"
+#define S_NULL_LEN             6
+
+#define FLOAT_DIGITS           6
+#define EXPONENT_LENGTH                10
+
+/* These macros allow correct support of 8-bit characters on systems which
+ * support 8-bit characters.  Pretty dumb how the cast is required, but
+ * that's legacy libc for ya.  These new macros do not support EOF like
+ * the standard macros do.  Tough.
+ */
+#define ap_isalpha(c) (isalpha(((unsigned char)(c))))
+#define ap_isdigit(c) (isdigit(((unsigned char)(c))))
+#define ap_islower(c) (islower(((unsigned char)(c))))
+
+/*
+ * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions
+ *
+ * XXX: this is a magic number; do not decrease it
+ */
+#define NUM_BUF_SIZE           512
+
+/*
+ * cvt.c - IEEE floating point formatting routines for FreeBSD
+ * from GNU libc-4.6.27.  Modified to be thread safe.
+ */
+
+/*
+ *    ap_ecvt converts to decimal
+ *      the number of digits is specified by ndigit
+ *      decpt is set to the position of the decimal point
+ *      sign is set to 0 for positive, 1 for negative
+ */
+
+#define        NDIG    80
+
+/* buf must have at least NDIG bytes */
+static char *ap_cvt(double arg, int ndigits, int *decpt, int *sign, int eflag, char *buf)
+{
+    register int r2;
+    double fi, fj;
+    register char *p, *p1;
+
+    if (ndigits >= NDIG - 1)
+       ndigits = NDIG - 2;
+    r2 = 0;
+    *sign = 0;
+    p = &buf[0];
+    if (arg < 0) {
+       *sign = 1;
+       arg = -arg;
+    }
+    arg = modf(arg, &fi);
+    p1 = &buf[NDIG];
+    /*
+     * Do integer part
+     */
+    if (fi != 0) {
+       p1 = &buf[NDIG];
+       while (fi != 0) {
+           fj = modf(fi / 10, &fi);
+           *--p1 = (int) ((fj + .03) * 10) + '0';
+           r2++;
+       }
+       while (p1 < &buf[NDIG])
+           *p++ = *p1++;
+    }
+    else if (arg > 0) {
+       while ((fj = arg * 10) < 1) {
+           arg = fj;
+           r2--;
+       }
+    }
+    p1 = &buf[ndigits];
+    if (eflag == 0)
+       p1 += r2;
+    *decpt = r2;
+    if (p1 < &buf[0]) {
+       buf[0] = '\0';
+       return (buf);
+    }
+    while (p <= p1 && p < &buf[NDIG]) {
+       arg *= 10;
+       arg = modf(arg, &fj);
+       *p++ = (int) fj + '0';
+    }
+    if (p1 >= &buf[NDIG]) {
+       buf[NDIG - 1] = '\0';
+       return (buf);
+    }
+    p = p1;
+    *p1 += 5;
+    while (*p1 > '9') {
+       *p1 = '0';
+       if (p1 > buf)
+           ++ * --p1;
+       else {
+           *p1 = '1';
+           (*decpt)++;
+           if (eflag == 0) {
+               if (p > buf)
+                   *p = '0';
+               p++;
+           }
+       }
+    }
+    *p = '\0';
+    return (buf);
+}
+
+static char *ap_ecvt(double arg, int ndigits, int *decpt, int *sign, char *buf)
+{
+    return (ap_cvt(arg, ndigits, decpt, sign, 1, buf));
+}
+
+static char *ap_fcvt(double arg, int ndigits, int *decpt, int *sign, char *buf)
+{
+    return (ap_cvt(arg, ndigits, decpt, sign, 0, buf));
+}
+
+/*
+ * ap_gcvt  - Floating output conversion to
+ * minimal length string
+ */
+
+static char *ap_gcvt(double number, int ndigit, char *buf, boolean_e altform)
+{
+    int sign, decpt;
+    register char *p1, *p2;
+    register int i;
+    char buf1[NDIG];
+
+    p1 = ap_ecvt(number, ndigit, &decpt, &sign, buf1);
+    p2 = buf;
+    if (sign)
+       *p2++ = '-';
+    for (i = ndigit - 1; i > 0 && p1[i] == '0'; i--)
+       ndigit--;
+    if ((decpt >= 0 && decpt - ndigit > 4)
+       || (decpt < 0 && decpt < -3)) {         /* use E-style */
+       decpt--;
+       *p2++ = *p1++;
+       *p2++ = '.';
+       for (i = 1; i < ndigit; i++)
+           *p2++ = *p1++;
+       *p2++ = 'e';
+       if (decpt < 0) {
+           decpt = -decpt;
+           *p2++ = '-';
+       }
+       else
+           *p2++ = '+';
+       if (decpt / 100 > 0)
+           *p2++ = decpt / 100 + '0';
+       if (decpt / 10 > 0)
+           *p2++ = (decpt % 100) / 10 + '0';
+       *p2++ = decpt % 10 + '0';
+    }
+    else {
+       if (decpt <= 0) {
+           if (*p1 != '0')
+               *p2++ = '.';
+           while (decpt < 0) {
+               decpt++;
+               *p2++ = '0';
+           }
+       }
+       for (i = 1; i <= ndigit; i++) {
+           *p2++ = *p1++;
+           if (i == decpt)
+               *p2++ = '.';
+       }
+       if (ndigit < decpt) {
+           while (ndigit++ < decpt)
+               *p2++ = '0';
+           *p2++ = '.';
+       }
+    }
+    if (p2[-1] == '.' && !altform)
+       p2--;
+    *p2 = '\0';
+    return (buf);
+}
+
+/*
+ * The INS_CHAR macro inserts a character in the buffer and writes
+ * the buffer back to disk if necessary
+ * It uses the char pointers sp and bep:
+ *      sp points to the next available character in the buffer
+ *      bep points to the end-of-buffer+1
+ * While using this macro, note that the nextb pointer is NOT updated.
+ *
+ * NOTE: Evaluation of the c argument should not have any side-effects
+ */
+#define INS_CHAR(c, sp, bep, cc)                               \
+           {                                                   \
+               if (sp >= bep) {                                \
+                   vbuff->curpos = sp;                         \
+                   if (flush_func(vbuff))                      \
+                       return -1;                              \
+                   sp = vbuff->curpos;                         \
+                   bep = vbuff->endpos;                        \
+               }                                               \
+               *sp++ = (c);                                    \
+               cc++;                                           \
+           }
+
+#define NUM( c )                       ( c - '0' )
+
+#define STR_TO_DEC( str, num )         \
+    num = NUM( *str++ ) ;              \
+    while ( ap_isdigit( *str ) )               \
+    {                                  \
+       num *= 10 ;                     \
+       num += NUM( *str++ ) ;          \
+    }
+
+/*
+ * This macro does zero padding so that the precision
+ * requirement is satisfied. The padding is done by
+ * adding '0's to the left of the string that is going
+ * to be printed.
+ */
+#define FIX_PRECISION( adjust, precision, s, s_len )   \
+    if ( adjust )                                      \
+       while ( s_len < precision )                     \
+       {                                               \
+           *--s = '0' ;                                \
+           s_len++ ;                                   \
+       }
+
+/*
+ * Macro that does padding. The padding is done by printing
+ * the character ch.
+ */
+#define PAD( width, len, ch )  do              \
+       {                                       \
+           INS_CHAR( ch, sp, bep, cc ) ;       \
+           width-- ;                           \
+       }                                       \
+       while ( width > len )
+
+/*
+ * Prefix the character ch to the string str
+ * Increase length
+ * Set the has_prefix flag
+ */
+#define PREFIX( str, length, ch )       *--str = ch ; length++ ; has_prefix = YES
+
+
+/*
+ * Convert num to its decimal format.
+ * Return value:
+ *   - a pointer to a string containing the number (no sign)
+ *   - len contains the length of the string
+ *   - is_negative is set to TRUE or FALSE depending on the sign
+ *     of the number (always set to FALSE if is_unsigned is TRUE)
+ *
+ * The caller provides a buffer for the string: that is the buf_end argument
+ * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
+ * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
+ */
+static char *conv_10(register wide_int num, register bool_int is_unsigned,
+                    register bool_int *is_negative, char *buf_end,
+                    register int *len)
+{
+    register char *p = buf_end;
+    register u_wide_int magnitude;
+
+    if (is_unsigned) {
+       magnitude = (u_wide_int) num;
+       *is_negative = FALSE;
+    }
+    else {
+       *is_negative = (num < 0);
+
+       /*
+        * On a 2's complement machine, negating the most negative integer 
+        * results in a number that cannot be represented as a signed integer.
+        * Here is what we do to obtain the number's magnitude:
+        *      a. add 1 to the number
+        *      b. negate it (becomes positive)
+        *      c. convert it to unsigned
+        *      d. add 1
+        */
+       if (*is_negative) {
+           wide_int t = num + 1;
+
+           magnitude = ((u_wide_int) -t) + 1;
+       }
+       else
+           magnitude = (u_wide_int) num;
+    }
+
+    /*
+     * We use a do-while loop so that we write at least 1 digit 
+     */
+    do {
+       register u_wide_int new_magnitude = magnitude / 10;
+
+       *--p = (char) (magnitude - new_magnitude * 10 + '0');
+       magnitude = new_magnitude;
+    }
+    while (magnitude);
+
+    *len = buf_end - p;
+    return (p);
+}
+
+
+
+static char *conv_in_addr(struct in_addr *ia, char *buf_end, int *len)
+{
+    unsigned addr = ntohl(ia->s_addr);
+    char *p = buf_end;
+    bool_int is_negative;
+    int sub_len;
+
+    p = conv_10((addr & 0x000000FF)      , TRUE, &is_negative, p, &sub_len);
+    *--p = '.';
+    p = conv_10((addr & 0x0000FF00) >>  8, TRUE, &is_negative, p, &sub_len);
+    *--p = '.';
+    p = conv_10((addr & 0x00FF0000) >> 16, TRUE, &is_negative, p, &sub_len);
+    *--p = '.';
+    p = conv_10((addr & 0xFF000000) >> 24, TRUE, &is_negative, p, &sub_len);
+
+    *len = buf_end - p;
+    return (p);
+}
+
+
+
+static char *conv_sockaddr_in(struct sockaddr_in *si, char *buf_end, int *len)
+{
+    char *p = buf_end;
+    bool_int is_negative;
+    int sub_len;
+
+    p = conv_10(ntohs(si->sin_port), TRUE, &is_negative, p, &sub_len);
+    *--p = ':';
+    p = conv_in_addr(&si->sin_addr, p, &sub_len);
+
+    *len = buf_end - p;
+    return (p);
+}
+
+
+
+/*
+ * Convert a floating point number to a string formats 'f', 'e' or 'E'.
+ * The result is placed in buf, and len denotes the length of the string
+ * The sign is returned in the is_negative argument (and is not placed
+ * in buf).
+ */
+static char *conv_fp(register char format, register double num,
+    boolean_e add_dp, int precision, bool_int *is_negative,
+    char *buf, int *len)
+{
+    register char *s = buf;
+    register char *p;
+    int decimal_point;
+    char buf1[NDIG];
+
+    if (format == 'f')
+       p = ap_fcvt(num, precision, &decimal_point, is_negative, buf1);
+    else                       /* either e or E format */
+       p = ap_ecvt(num, precision + 1, &decimal_point, is_negative, buf1);
+
+    /*
+     * Check for Infinity and NaN
+     */
+    if (ap_isalpha(*p)) {
+       *len = strlen(strcpy(buf, p));
+       *is_negative = FALSE;
+       return (buf);
+    }
+
+    if (format == 'f') {
+       if (decimal_point <= 0) {
+           *s++ = '0';
+           if (precision > 0) {
+               *s++ = '.';
+               while (decimal_point++ < 0)
+                   *s++ = '0';
+           }
+           else if (add_dp)
+               *s++ = '.';
+       }
+       else {
+           while (decimal_point-- > 0)
+               *s++ = *p++;
+           if (precision > 0 || add_dp)
+               *s++ = '.';
+       }
+    }
+    else {
+       *s++ = *p++;
+       if (precision > 0 || add_dp)
+           *s++ = '.';
+    }
+
+    /*
+     * copy the rest of p, the NUL is NOT copied
+     */
+    while (*p)
+       *s++ = *p++;
+
+    if (format != 'f') {
+       char temp[EXPONENT_LENGTH];     /* for exponent conversion */
+       int t_len;
+       bool_int exponent_is_negative;
+
+       *s++ = format;          /* either e or E */
+       decimal_point--;
+       if (decimal_point != 0) {
+           p = conv_10((wide_int) decimal_point, FALSE, &exponent_is_negative,
+                       &temp[EXPONENT_LENGTH], &t_len);
+           *s++ = exponent_is_negative ? '-' : '+';
+
+           /*
+            * Make sure the exponent has at least 2 digits
+            */
+           if (t_len == 1)
+               *s++ = '0';
+           while (t_len--)
+               *s++ = *p++;
+       }
+       else {
+           *s++ = '+';
+           *s++ = '0';
+           *s++ = '0';
+       }
+    }
+
+    *len = s - buf;
+    return (buf);
+}
+
+
+/*
+ * Convert num to a base X number where X is a power of 2. nbits determines X.
+ * For example, if nbits is 3, we do base 8 conversion
+ * Return value:
+ *      a pointer to a string containing the number
+ *
+ * The caller provides a buffer for the string: that is the buf_end argument
+ * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
+ * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
+ */
+static char *conv_p2(register u_wide_int num, register int nbits,
+                    char format, char *buf_end, register int *len)
+{
+    register int mask = (1 << nbits) - 1;
+    register char *p = buf_end;
+    static const char low_digits[] = "0123456789abcdef";
+    static const char upper_digits[] = "0123456789ABCDEF";
+    register const char *digits = (format == 'X') ? upper_digits : low_digits;
+
+    do {
+       *--p = digits[num & mask];
+       num >>= nbits;
+    }
+    while (num);
+
+    *len = buf_end - p;
+    return (p);
+}
+
+
+/*
+ * Do format conversion placing the output in buffer
+ */
+int ap_vformatter(int (*flush_func)(ap_vformatter_buff *),
+    ap_vformatter_buff *vbuff, const char *fmt, va_list ap)
+{
+    register char *sp;
+    register char *bep;
+    register int cc = 0;
+    register int i;
+
+    register char *s = NULL;
+    char *q;
+    int s_len;
+
+    register int min_width = 0;
+    int precision = 0;
+    enum {
+       LEFT, RIGHT
+    } adjust;
+    char pad_char;
+    char prefix_char;
+
+    double fp_num;
+    wide_int i_num = (wide_int) 0;
+    u_wide_int ui_num;
+
+    char num_buf[NUM_BUF_SIZE];
+    char char_buf[2];          /* for printing %% and %<unknown> */
+
+    /*
+     * Flag variables
+     */
+    boolean_e is_long;
+    boolean_e alternate_form;
+    boolean_e print_sign;
+    boolean_e print_blank;
+    boolean_e adjust_precision;
+    boolean_e adjust_width;
+    bool_int is_negative;
+
+    sp = vbuff->curpos;
+    bep = vbuff->endpos;
+
+    while (*fmt) {
+       if (*fmt != '%') {
+           INS_CHAR(*fmt, sp, bep, cc);
+       }
+       else {
+           /*
+            * Default variable settings
+            */
+           adjust = RIGHT;
+           alternate_form = print_sign = print_blank = NO;
+           pad_char = ' ';
+           prefix_char = NUL;
+
+           fmt++;
+
+           /*
+            * Try to avoid checking for flags, width or precision
+            */
+           if (!ap_islower(*fmt)) {
+               /*
+                * Recognize flags: -, #, BLANK, +
+                */
+               for (;; fmt++) {
+                   if (*fmt == '-')
+                       adjust = LEFT;
+                   else if (*fmt == '+')
+                       print_sign = YES;
+                   else if (*fmt == '#')
+                       alternate_form = YES;
+                   else if (*fmt == ' ')
+                       print_blank = YES;
+                   else if (*fmt == '0')
+                       pad_char = '0';
+                   else
+                       break;
+               }
+
+               /*
+                * Check if a width was specified
+                */
+               if (ap_isdigit(*fmt)) {
+                   STR_TO_DEC(fmt, min_width);
+                   adjust_width = YES;
+               }
+               else if (*fmt == '*') {
+                   min_width = va_arg(ap, int);
+                   fmt++;
+                   adjust_width = YES;
+                   if (min_width < 0) {
+                       adjust = LEFT;
+                       min_width = -min_width;
+                   }
+               }
+               else
+                   adjust_width = NO;
+
+               /*
+                * Check if a precision was specified
+                *
+                * XXX: an unreasonable amount of precision may be specified
+                * resulting in overflow of num_buf. Currently we
+                * ignore this possibility.
+                */
+               if (*fmt == '.') {
+                   adjust_precision = YES;
+                   fmt++;
+                   if (ap_isdigit(*fmt)) {
+                       STR_TO_DEC(fmt, precision);
+                   }
+                   else if (*fmt == '*') {
+                       precision = va_arg(ap, int);
+                       fmt++;
+                       if (precision < 0)
+                           precision = 0;
+                   }
+                   else
+                       precision = 0;
+               }
+               else
+                   adjust_precision = NO;
+           }
+           else
+               adjust_precision = adjust_width = NO;
+
+           /*
+            * Modifier check
+            */
+           if (*fmt == 'l') {
+               is_long = YES;
+               fmt++;
+           }
+           else {
+               if (*fmt == 'h')  /* "short" backward compatibility */
+                   ++fmt;
+               is_long = NO;
+           }
+
+           /*
+            * Argument extraction and printing.
+            * First we determine the argument type.
+            * Then, we convert the argument to a string.
+            * On exit from the switch, s points to the string that
+            * must be printed, s_len has the length of the string
+            * The precision requirements, if any, are reflected in s_len.
+            *
+            * NOTE: pad_char may be set to '0' because of the 0 flag.
+            *   It is reset to ' ' by non-numeric formats
+            */
+           switch (*fmt) {
+           case 'u':
+               if (is_long)
+                   i_num = va_arg(ap, u_wide_int);
+               else
+                   i_num = (wide_int) va_arg(ap, unsigned int);
+               s = conv_10(i_num, 1, &is_negative,
+                           &num_buf[NUM_BUF_SIZE], &s_len);
+               FIX_PRECISION(adjust_precision, precision, s, s_len);
+               break;
+
+           case 'd':
+           case 'i':
+               if (is_long)
+                   i_num = va_arg(ap, wide_int);
+               else
+                   i_num = (wide_int) va_arg(ap, int);
+               s = conv_10(i_num, 0, &is_negative,
+                           &num_buf[NUM_BUF_SIZE], &s_len);
+               FIX_PRECISION(adjust_precision, precision, s, s_len);
+
+               if (is_negative)
+                   prefix_char = '-';
+               else if (print_sign)
+                   prefix_char = '+';
+               else if (print_blank)
+                   prefix_char = ' ';
+               break;
+
+
+           case 'o':
+               if (is_long)
+                   ui_num = va_arg(ap, u_wide_int);
+               else
+                   ui_num = (u_wide_int) va_arg(ap, unsigned int);
+               s = conv_p2(ui_num, 3, *fmt,
+                           &num_buf[NUM_BUF_SIZE], &s_len);
+               FIX_PRECISION(adjust_precision, precision, s, s_len);
+               if (alternate_form && *s != '0') {
+                   *--s = '0';
+                   s_len++;
+               }
+               break;
+
+
+           case 'x':
+           case 'X':
+               if (is_long)
+                   ui_num = (u_wide_int) va_arg(ap, u_wide_int);
+               else
+                   ui_num = (u_wide_int) va_arg(ap, unsigned int);
+               s = conv_p2(ui_num, 4, *fmt,
+                           &num_buf[NUM_BUF_SIZE], &s_len);
+               FIX_PRECISION(adjust_precision, precision, s, s_len);
+               if (alternate_form && i_num != 0) {
+                   *--s = *fmt;        /* 'x' or 'X' */
+                   *--s = '0';
+                   s_len += 2;
+               }
+               break;
+
+
+           case 's':
+               s = va_arg(ap, char *);
+               if (s != NULL) {
+                   s_len = strlen(s);
+                   if (adjust_precision && precision < s_len)
+                       s_len = precision;
+               }
+               else {
+                   s = S_NULL;
+                   s_len = S_NULL_LEN;
+               }
+               pad_char = ' ';
+               break;
+
+
+           case 'f':
+           case 'e':
+           case 'E':
+               fp_num = va_arg(ap, double);
+               /*
+                * * We use &num_buf[ 1 ], so that we have room for the sign
+                */
+               s = conv_fp(*fmt, fp_num, alternate_form,
+                       (adjust_precision == NO) ? FLOAT_DIGITS : precision,
+                           &is_negative, &num_buf[1], &s_len);
+               if (is_negative)
+                   prefix_char = '-';
+               else if (print_sign)
+                   prefix_char = '+';
+               else if (print_blank)
+                   prefix_char = ' ';
+               break;
+
+
+           case 'g':
+           case 'G':
+               if (adjust_precision == NO)
+                   precision = FLOAT_DIGITS;
+               else if (precision == 0)
+                   precision = 1;
+               /*
+                * * We use &num_buf[ 1 ], so that we have room for the sign
+                */
+               s = ap_gcvt(va_arg(ap, double), precision, &num_buf[1],
+                           alternate_form);
+               if (*s == '-')
+                   prefix_char = *s++;
+               else if (print_sign)
+                   prefix_char = '+';
+               else if (print_blank)
+                   prefix_char = ' ';
+
+               s_len = strlen(s);
+
+               if (alternate_form && (q = strchr(s, '.')) == NULL) {
+                   s[s_len++] = '.';
+                   s[s_len] = '\0'; /* delimit for following strchr() */
+               }
+               if (*fmt == 'G' && (q = strchr(s, 'e')) != NULL)
+                   *q = 'E';
+               break;
+
+
+           case 'c':
+               char_buf[0] = (char) (va_arg(ap, int));
+               s = &char_buf[0];
+               s_len = 1;
+               pad_char = ' ';
+               break;
+
+
+           case '%':
+               char_buf[0] = '%';
+               s = &char_buf[0];
+               s_len = 1;
+               pad_char = ' ';
+               break;
+
+
+           case 'n':
+               *(va_arg(ap, int *)) = cc;
+               break;
+
+               /*
+                * This is where we extend the printf format, with a second
+                * type specifier
+                */
+           case 'p':
+               switch(*++fmt) {
+                   /*
+                    * If the pointer size is equal to the size of an unsigned
+                    * integer we convert the pointer to a hex number, otherwise 
+                    * we print "%p" to indicate that we don't handle "%p".
+                    */
+               case 'p':
+                   ui_num = (u_wide_int) va_arg(ap, void *);
+
+                   if (sizeof(char *) <= sizeof(u_wide_int))
+                               s = conv_p2(ui_num, 4, 'x',
+                                           &num_buf[NUM_BUF_SIZE], &s_len);
+                   else {
+                       s = "%p";
+                       s_len = 2;
+                       prefix_char = NUL;
+                   }
+                   pad_char = ' ';
+                   break;
+
+                   /* print a struct sockaddr_in as a.b.c.d:port */
+               case 'I':
+                   {
+                       struct sockaddr_in *si;
+
+                       si = va_arg(ap, struct sockaddr_in *);
+                       if (si != NULL) {
+                           s = conv_sockaddr_in(si, &num_buf[NUM_BUF_SIZE], &s_len);
+                           if (adjust_precision && precision < s_len)
+                               s_len = precision;
+                       }
+                       else {
+                           s = S_NULL;
+                           s_len = S_NULL_LEN;
+                       }
+                       pad_char = ' ';
+                   }
+                   break;
+
+                   /* print a struct in_addr as a.b.c.d */
+               case 'A':
+                   {
+                       struct in_addr *ia;
+
+                       ia = va_arg(ap, struct in_addr *);
+                       if (ia != NULL) {
+                           s = conv_in_addr(ia, &num_buf[NUM_BUF_SIZE], &s_len);
+                           if (adjust_precision && precision < s_len)
+                               s_len = precision;
+                       }
+                       else {
+                           s = S_NULL;
+                           s_len = S_NULL_LEN;
+                       }
+                       pad_char = ' ';
+                   }
+                   break;
+
+               case NUL:
+                   /* if %p ends the string, oh well ignore it */
+                   continue;
+
+               default:
+                   s = "bogus %p";
+                   s_len = 8;
+                   prefix_char = NUL;
+                   break;
+               }
+               break;
+
+           case NUL:
+               /*
+                * The last character of the format string was %.
+                * We ignore it.
+                */
+               continue;
+
+
+               /*
+                * The default case is for unrecognized %'s.
+                * We print %<char> to help the user identify what
+                * option is not understood.
+                * This is also useful in case the user wants to pass
+                * the output of format_converter to another function
+                * that understands some other %<char> (like syslog).
+                * Note that we can't point s inside fmt because the
+                * unknown <char> could be preceded by width etc.
+                */
+           default:
+               char_buf[0] = '%';
+               char_buf[1] = *fmt;
+               s = char_buf;
+               s_len = 2;
+               pad_char = ' ';
+               break;
+           }
+
+           if (prefix_char != NUL && s != S_NULL && s != char_buf) {
+               *--s = prefix_char;
+               s_len++;
+           }
+
+           if (adjust_width && adjust == RIGHT && min_width > s_len) {
+               if (pad_char == '0' && prefix_char != NUL) {
+                   INS_CHAR(*s, sp, bep, cc);
+                   s++;
+                   s_len--;
+                   min_width--;
+               }
+               PAD(min_width, s_len, pad_char);
+           }
+
+           /*
+            * Print the string s. 
+            */
+           for (i = s_len; i != 0; i--) {
+               INS_CHAR(*s, sp, bep, cc);
+               s++;
+           }
+
+           if (adjust_width && adjust == LEFT && min_width > s_len)
+               PAD(min_width, s_len, pad_char);
+       }
+       fmt++;
+    }
+    vbuff->curpos = sp;
+    return cc;
+}
+
+
+static int snprintf_flush(ap_vformatter_buff *vbuff)
+{
+    /* if the buffer fills we have to abort immediately, there is no way
+     * to "flush" a snprintf... there's nowhere to flush it to.
+     */
+    return -1;
+}
+
+
+int snprintf(char *buf, size_t len, const char *format,...)
+{
+    int cc;
+    va_list ap;
+    ap_vformatter_buff vbuff;
+
+    if (len == 0)
+       return 0;
+
+    /* save one byte for nul terminator */
+    vbuff.curpos = buf;
+    vbuff.endpos = buf + len - 1;
+    va_start(ap, format);
+    cc = ap_vformatter(snprintf_flush, &vbuff, format, ap);
+    va_end(ap);
+    *vbuff.curpos = '\0';
+    return (cc == -1) ? len : cc;
+}
+
+
+int vsnprintf(char *buf, size_t len, const char *format, va_list ap)
+{
+    int cc;
+    ap_vformatter_buff vbuff;
+
+    if (len == 0)
+       return 0;
+
+    /* save one byte for nul terminator */
+    vbuff.curpos = buf;
+    vbuff.endpos = buf + len - 1;
+    cc = ap_vformatter(snprintf_flush, &vbuff, format, ap);
+    *vbuff.curpos = '\0';
+    return (cc == -1) ? len : cc;
+}
diff --git a/sbr/ssequal.c b/sbr/ssequal.c
new file mode 100644 (file)
index 0000000..dd5c097
--- /dev/null
@@ -0,0 +1,27 @@
+
+/*
+ * ssequal.c -- check if a string is a substring of another
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+/*
+ * Check if s1 is a substring of s2.
+ * If yes, then return 1, else return 0.
+ */
+
+int
+ssequal (char *s1, char *s2)
+{
+    if (!s1)
+       s1 = "";
+    if (!s2)
+       s2 = "";
+
+    while (*s1)
+       if (*s1++ != *s2++)
+           return 0;
+    return 1;
+}
diff --git a/sbr/strcasecmp.c b/sbr/strcasecmp.c
new file mode 100644 (file)
index 0000000..bbb26cb
--- /dev/null
@@ -0,0 +1,53 @@
+
+/*
+ * strcasecmp.c -- compare strings, ignoring case
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+/*
+ * Our version of strcasecmp has to deal with NULL strings.
+ * Once that is fixed in the rest of the code, we can use the
+ * native version, instead of this one.
+ */
+
+int
+strcasecmp (const char *s1, const char *s2) 
+{
+    const unsigned char *us1, *us2;
+
+    us1 = (const unsigned char *) s1,
+    us2 = (const unsigned char *) s2;
+
+    if (!us1)
+       us1 = "";
+    if (!us2)
+       us2 = "";
+    while (tolower(*us1) == tolower(*us2++)) 
+       if (*us1++ == '\0')
+           return (0);
+    return (tolower(*us1) - tolower(*--us2));
+}
+
+int
+strncasecmp (const char *s1, const char *s2, size_t n)
+{
+    const unsigned char *us1, *us2;
+
+    if (n != 0) { 
+       us1 = (const unsigned char *) s1,
+       us2 = (const unsigned char *) s2;
+
+       do {  
+           if (tolower(*us1) != tolower(*us2++))
+               return (tolower(*us1) - tolower(*--us2));
+           if (*us1++ == '\0')
+               break;  
+       } while (--n != 0);
+    } 
+    return (0);
+}
diff --git a/sbr/strdup.c b/sbr/strdup.c
new file mode 100644 (file)
index 0000000..05a017e
--- /dev/null
@@ -0,0 +1,25 @@
+
+/*
+ * strdup.c -- duplicate a string
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+char *
+strdup (char *str)
+{
+    char *cp;
+    size_t len;
+
+    if (!str)
+       return NULL;
+
+    len = strlen(str) + 1;
+    if (!(cp = malloc (len)))
+       return NULL;
+    memcpy (cp, str, len);
+    return cp;
+}
diff --git a/sbr/strerror.c b/sbr/strerror.c
new file mode 100644 (file)
index 0000000..d780955
--- /dev/null
@@ -0,0 +1,21 @@
+
+/*
+ * strerror.c -- get error message string
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+
+char *
+strerror (int errnum)
+{
+   if (errnum > 0 && errnum < sys_nerr)
+       return sys_errlist[errnum];
+   else
+       return NULL;
+}
diff --git a/sbr/strindex.c b/sbr/strindex.c
new file mode 100644 (file)
index 0000000..03910fd
--- /dev/null
@@ -0,0 +1,23 @@
+
+/*
+ * strindex.c -- "unsigned" lexical index
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+int
+stringdex (char *p1, char *p2)
+{
+    char *p;
+
+    if (p1 == NULL || p2 == NULL)
+       return -1;
+
+    for (p = p2; *p; p++)
+       if (uprf (p, p1))
+           return (p - p2);
+
+    return -1;
+}
diff --git a/sbr/trimcpy.c b/sbr/trimcpy.c
new file mode 100644 (file)
index 0000000..d135548
--- /dev/null
@@ -0,0 +1,38 @@
+
+/*
+ * trimcpy.c -- strip leading and trailing whitespace,
+ *           -- replace internal whitespace with spaces,
+ *           -- then return a copy.
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+char *
+trimcpy (char *cp)
+{
+    char *sp;
+
+    /* skip over leading whitespace */
+    while (isspace(*cp))
+       cp++;
+
+    /* start at the end and zap trailing whitespace */
+    for (sp = cp + strlen(cp) - 1; sp >= cp; sp--) {
+       if (isspace(*sp))
+           *sp = '\0';
+       else
+           break;
+    }
+
+    /* replace remaining whitespace with spaces */
+    for (sp = cp; *sp; sp++) {
+       if (isspace(*sp))
+           *sp = ' ';
+    }
+
+    /* now return a copy */
+    return getcpy(cp);
+}
diff --git a/sbr/uprf.c b/sbr/uprf.c
new file mode 100644 (file)
index 0000000..7adc321
--- /dev/null
@@ -0,0 +1,39 @@
+
+/*
+ * uprf.c -- "unsigned" lexical prefix
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+#define TO_LOWER 040
+#define NO_MASK  000
+
+
+int
+uprf (char *c1, char *c2)
+{
+    int c, mask;
+
+    if (!(c1 && c2))
+       return 0;
+
+    while ((c = *c2++))
+    {
+#ifdef LOCALE
+       c &= 0xff;
+       mask = *c1 & 0xff;
+       c = (isalpha(c) && isupper(c)) ? tolower(c) : c;
+       mask = (isalpha(mask) && isupper(mask)) ? tolower(mask) : mask;
+       if (c != mask)
+#else
+       mask = (isalpha(c) && isalpha(*c1)) ?  TO_LOWER : NO_MASK;
+       if ((c | mask) != (*c1 | mask))
+#endif
+           return 0;
+       else
+           c1++;
+    }
+    return 1;
+}
diff --git a/sbr/vfgets.c b/sbr/vfgets.c
new file mode 100644 (file)
index 0000000..52a8e74
--- /dev/null
@@ -0,0 +1,73 @@
+
+/*
+ * vfgets.c -- virtual fgets
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+#define        QUOTE   '\\'
+
+
+int
+vfgets (FILE *in, char **bp)
+{
+    int toggle;
+    char *cp, *dp, *ep, *fp;
+    static int len = 0;
+    static char *pp = NULL;
+
+    if (pp == NULL)
+       if (!(pp = malloc ((size_t) (len = BUFSIZ))))
+           adios (NULL, "unable to allocate string storage");
+
+    for (ep = (cp = pp) + len - 1;;) {
+       if (fgets (cp, ep - cp + 1, in) == NULL) {
+           if (cp != pp) {
+               *bp = pp;
+               return 0;
+           }
+           return (ferror (in) && !feof (in) ? -1 : 1);
+       }
+
+       if ((dp = cp + strlen (cp) - 2) < cp || *dp != QUOTE) {
+wrong_guess:
+           if (cp > ++dp)
+               adios (NULL, "vfgets() botch -- you lose big");
+           if (*dp == '\n') {
+               *bp = pp;
+               return 0;
+           } else {
+               cp = ++dp;
+           }
+       } else {
+           for (fp = dp - 1, toggle = 0; fp >= cp; fp--) {
+               if (*fp != QUOTE)
+                   break;
+               else
+                   toggle = !toggle;
+           }
+           if (toggle)
+               goto wrong_guess;
+
+           if (*++dp == '\n') {
+               *--dp = 0;
+               cp = dp;
+           } else {
+               cp = ++dp;
+           }
+       }
+
+       if (cp >= ep) {
+           int curlen = cp - pp;
+
+           if (!(dp = realloc (pp, (size_t) (len += BUFSIZ)))) {
+               adios (NULL, "unable to allocate string storage");
+           } else {
+               cp = dp + curlen;
+               ep = (pp = dp) + len - 1;
+           }
+       }
+    }
+}
diff --git a/stamp-h.in b/stamp-h.in
new file mode 100644 (file)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
diff --git a/uip/Makefile.in b/uip/Makefile.in
new file mode 100644 (file)
index 0000000..c292bd2
--- /dev/null
@@ -0,0 +1,307 @@
+#
+# Makefile for uip subdirectory
+#
+# $Id$
+#
+
+SHELL = /bin/sh
+
+top_srcdir = @top_srcdir@
+srcdir     = @srcdir@
+VPATH      = @srcdir@
+
+prefix      = @prefix@
+exec_prefix = @exec_prefix@
+bindir      = @bindir@
+libdir      = @libdir@
+etcdir      = @sysconfdir@
+
+CC       = @CC@
+CFLAGS   = @CFLAGS@
+DEFS     = @DEFS@
+HESIOD_INCLUDES = @HESIOD_INCLUDES@
+INCLUDES = -I.. -I$(srcdir) -I$(top_srcdir) $(HESIOD_INCLUDES)
+LDFLAGS  = @LDFLAGS@
+
+LIBS     = @LIBS@
+MTSLIB   = @MTSLIB@
+KRB4_LIBS = @KRB4_LIBS@
+HESIOD_LIBS = @HESIOD_LIBS@
+LOCALLIBS = ../config/version.o ../config/config.o ../sbr/libmh.a ../$(MTSLIB) ../zotnet/libzot.a
+LINKLIBS = $(LOCALLIBS) $(KRB4_LIBS) $(HESIOD_LIBS) $(LIBS)
+
+TERMLIB = @TERMLIB@
+LEXLIB  = @LEXLIB@
+POPLIB  = @POPLIB@
+
+COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CFLAGS)
+LINK    = $(CC) $(LDFLAGS) -o $@
+LN = ln
+
+INSTALL         = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+
+.SUFFIXES:
+.SUFFIXES: .c .o
+
+.c.o:
+       $(COMPILE) $<
+
+# commands to build
+CMDS = ali anno burst comp dist flist folder forw inc mark mhbuild \
+       mhlist mhmail mhn mhparam mhpath mhshow mhstore mhtest msgchk \
+       msh packf pick prompter refile repl rmf rmm scan send show \
+       sortm viamail whatnow whom
+
+## removed this from CMDS until I can fix it
+## OTHERCMDS = vmh
+
+# commands that are links to other commands
+LCMDS = flists folders next prev
+
+# misc support binaries
+MISC = ap conflict dp fmtdump install-mh mhl post rcvdist rcvpack \
+       rcvstore rcvtty slocal spost
+
+# source files
+SRCS = ali.c aliasbr.c anno.c annosbr.c ap.c burst.c comp.c \
+       conflict.c dist.c distsbr.c dp.c dropsbr.c flist.c fmtdump.c \
+       folder.c forw.c ftpsbr.c inc.c install-mh.c mark.c md5.c mhbuild.c \
+       mhbuildsbr.c mhcachesbr.c mhfree.c mhl.c mhlist.c mhlistsbr.c mhlsbr.c \
+       mhmail.c mhmisc.c mhn.c mhoutsbr.c mhparam.c mhparse.c mhpath.c mhshow.c \
+       mhshowsbr.c mhstore.c mhstoresbr.c mhtest.c msgchk.c msh.c mshcmds.c packf.c \
+       pick.c picksbr.c popi.c popsbr.c post.c prompter.c rcvdist.c rcvpack.c \
+       rcvstore.c rcvtty.c refile.c repl.c replsbr.c rmf.c rmm.c scan.c \
+       scansbr.c send.c sendsbr.c show.c slocal.c sortm.c spost.c \
+       termsbr.c viamail.c vmh.c vmhsbr.c vmhtest.c whatnow.c whatnowproc.c \
+       whatnowsbr.c whom.c wmh.c
+
+# auxiliary files
+AUX = Makefile.in
+
+# all files in this directory included in the distribution
+DIST = $(SRCS) $(AUX)
+
+# ========== DEFAULT TARGET ==========
+
+all: $(CMDS) $(MISC)
+
+# ========= DEPENDENCIES FOR BUILDING ==========
+
+ali: ali.o aliasbr.o $(LOCALLIBS)
+       $(LINK) ali.o aliasbr.o $(LINKLIBS)
+
+ap: ap.o termsbr.o $(LOCALLIBS)
+       $(LINK) ap.o termsbr.o $(LINKLIBS) $(TERMLIB)
+
+anno: anno.o annosbr.o $(LOCALLIBS)
+       $(LINK) anno.o annosbr.o $(LINKLIBS)
+
+burst: burst.o $(LOCALLIBS)
+       $(LINK) burst.o $(LINKLIBS)
+
+comp: comp.o whatnowproc.o whatnowsbr.o sendsbr.o annosbr.o distsbr.o $(LOCALLIBS)
+       $(LINK) comp.o whatnowproc.o whatnowsbr.o sendsbr.o annosbr.o distsbr.o $(LINKLIBS)
+
+conflict: conflict.o aliasbr.o $(LOCALLIBS)
+       $(LINK) conflict.o aliasbr.o $(LINKLIBS)
+
+dist: dist.o whatnowproc.o whatnowsbr.o sendsbr.o annosbr.o distsbr.o $(LOCALLIBS)
+       $(LINK) dist.o whatnowproc.o whatnowsbr.o sendsbr.o annosbr.o distsbr.o $(LINKLIBS)
+
+dp: dp.o termsbr.o $(LOCALLIBS)
+       $(LINK) dp.o termsbr.o $(LINKLIBS) $(TERMLIB)
+
+flist: flist.o $(LOCALLIBS)
+       $(LINK) flist.o $(LINKLIBS)
+
+fmtdump: fmtdump.o $(LOCALLIBS)
+       $(LINK) fmtdump.o $(LINKLIBS)
+
+folder: folder.o $(LOCALLIBS)
+       $(LINK) folder.o $(LINKLIBS)
+
+forw: forw.o whatnowproc.o whatnowsbr.o sendsbr.o annosbr.o distsbr.o $(LOCALLIBS)
+       $(LINK) forw.o whatnowproc.o whatnowsbr.o sendsbr.o annosbr.o distsbr.o $(LINKLIBS)
+
+inc: inc.o scansbr.o dropsbr.o termsbr.o $(POPLIB) $(LOCALLIBS)
+       $(LINK) inc.o scansbr.o dropsbr.o termsbr.o $(POPLIB) $(LINKLIBS) $(TERMLIB)
+
+install-mh: install-mh.o $(LOCALLIBS)
+       $(LINK) install-mh.o $(LINKLIBS)
+
+mark: mark.o $(LOCALLIBS)
+       $(LINK) mark.o $(LINKLIBS)
+
+mhbuild: mhbuild.o mhbuildsbr.o mhcachesbr.o mhlistsbr.o mhoutsbr.o mhmisc.o mhfree.o ftpsbr.o termsbr.o md5.o $(LOCALLIBS)
+       $(LINK) mhbuild.o mhbuildsbr.o mhcachesbr.o mhlistsbr.o mhoutsbr.o mhmisc.o mhfree.o ftpsbr.o md5.o $(LINKLIBS) $(TERMLIB)
+
+mhl: mhl.o mhlsbr.o termsbr.o $(LOCALLIBS)
+       $(LINK) mhl.o mhlsbr.o termsbr.o $(LINKLIBS) $(TERMLIB)
+
+mhlist: mhlist.o mhparse.o mhcachesbr.o mhlistsbr.o mhmisc.o mhfree.o ftpsbr.o termsbr.o md5.o $(LOCALLIBS)
+       $(LINK) mhlist.o mhparse.o mhcachesbr.o mhlistsbr.o mhmisc.o mhfree.o ftpsbr.o termsbr.o md5.o $(LINKLIBS) $(TERMLIB)
+
+mhmail: mhmail.o $(LOCALLIBS)
+       $(LINK) mhmail.o $(LINKLIBS)
+
+mhn: mhn.o mhparse.o mhcachesbr.o mhshowsbr.o mhlistsbr.o mhstoresbr.o mhmisc.o mhfree.o ftpsbr.o termsbr.o md5.o $(LOCALLIBS)
+       $(LINK) mhn.o mhparse.o mhcachesbr.o mhshowsbr.o mhlistsbr.o mhstoresbr.o mhmisc.o mhfree.o ftpsbr.o termsbr.o md5.o $(LINKLIBS) $(TERMLIB)
+
+mhparam: mhparam.o $(LOCALLIBS)
+       $(LINK) mhparam.o $(LINKLIBS)
+
+mhpath: mhpath.o $(LOCALLIBS)
+       $(LINK) mhpath.o $(LINKLIBS)
+
+mhshow: mhshow.o mhparse.o mhcachesbr.o mhshowsbr.o mhlistsbr.o mhmisc.o mhfree.o ftpsbr.o termsbr.o md5.o $(LOCALLIBS)
+       $(LINK) mhshow.o mhparse.o mhcachesbr.o mhshowsbr.o mhlistsbr.o mhmisc.o mhfree.o ftpsbr.o termsbr.o md5.o $(LINKLIBS) $(TERMLIB)
+
+mhstore: mhstore.o mhparse.o mhcachesbr.o mhshowsbr.o mhlistsbr.o mhstoresbr.o mhmisc.o mhfree.o ftpsbr.o termsbr.o md5.o $(LOCALLIBS)
+       $(LINK) mhstore.o mhparse.o mhcachesbr.o mhshowsbr.o mhlistsbr.o mhstoresbr.o mhmisc.o mhfree.o ftpsbr.o termsbr.o md5.o $(LINKLIBS) $(TERMLIB)
+
+mhtest: mhtest.o mhparse.o mhcachesbr.o mhoutsbr.o mhmisc.o mhfree.o ftpsbr.o termsbr.o md5.o $(LOCALLIBS)
+       $(LINK) mhtest.o mhparse.o mhcachesbr.o mhoutsbr.o mhmisc.o mhfree.o ftpsbr.o termsbr.o md5.o $(LINKLIBS) $(TERMLIB)
+
+msgchk: msgchk.o $(POPLIB) $(LOCALLIBS)
+       $(LINK) msgchk.o $(POPLIB) $(LINKLIBS)
+
+msh: msh.o mshcmds.o vmhsbr.o picksbr.o scansbr.o dropsbr.o mhlsbr.o termsbr.o $(LOCALLIBS)
+       $(LINK) msh.o mshcmds.o vmhsbr.o picksbr.o scansbr.o dropsbr.o mhlsbr.o termsbr.o $(LINKLIBS) $(TERMLIB)
+
+packf: packf.o dropsbr.o $(LOCALLIBS)
+       $(LINK) packf.o dropsbr.o $(LINKLIBS)
+
+pick: pick.o picksbr.o $(LOCALLIBS)
+       $(LINK) pick.o picksbr.o $(LINKLIBS)
+
+post: post.o aliasbr.o $(LOCALLIBS)
+       $(LINK) post.o aliasbr.o $(LINKLIBS)
+
+prompter: prompter.o $(LOCALLIBS)
+       $(LINK) prompter.o $(LINKLIBS)
+
+rcvdist: rcvdist.o distsbr.o $(LOCALLIBS)
+       $(LINK) rcvdist.o distsbr.o $(LINKLIBS)
+
+rcvpack: rcvpack.o dropsbr.o $(LOCALLIBS)
+       $(LINK) rcvpack.o dropsbr.o $(LINKLIBS)
+
+rcvstore: rcvstore.o $(LOCALLIBS)
+       $(LINK) rcvstore.o $(LINKLIBS)
+
+rcvtty: rcvtty.o scansbr.o termsbr.o $(LOCALLIBS)
+       $(LINK) rcvtty.o scansbr.o termsbr.o $(LINKLIBS) $(TERMLIB)
+
+refile: refile.o $(LOCALLIBS)
+       $(LINK) refile.o $(LINKLIBS)
+
+repl: repl.o replsbr.o whatnowproc.o whatnowsbr.o sendsbr.o annosbr.o distsbr.o $(LOCALLIBS)
+       $(LINK) repl.o replsbr.o whatnowproc.o whatnowsbr.o sendsbr.o annosbr.o distsbr.o $(LINKLIBS)
+
+rmf: rmf.o $(LOCALLIBS)
+       $(LINK) rmf.o $(LINKLIBS)
+
+rmm: rmm.o $(LOCALLIBS)
+       $(LINK) rmm.o $(LINKLIBS)
+
+scan: scan.o scansbr.o termsbr.o $(LOCALLIBS)
+       $(LINK) scan.o scansbr.o termsbr.o $(LINKLIBS) $(TERMLIB)
+
+send: send.o sendsbr.o annosbr.o distsbr.o $(LOCALLIBS)
+       $(LINK) send.o sendsbr.o annosbr.o distsbr.o $(LINKLIBS)
+
+show: show.o mhlsbr.o termsbr.o $(LOCALLIBS)
+       $(LINK) show.o mhlsbr.o termsbr.o $(LINKLIBS) $(TERMLIB)
+
+slocal: slocal.o aliasbr.o dropsbr.o $(LOCALLIBS)
+       $(LINK) slocal.o aliasbr.o dropsbr.o $(LINKLIBS)
+
+sortm: sortm.o $(LOCALLIBS)
+       $(LINK) sortm.o $(LINKLIBS)
+
+spost: spost.o aliasbr.o $(LOCALLIBS)
+       $(LINK) spost.o aliasbr.o $(LINKLIBS)
+
+viamail: viamail.o mhmisc.o mhoutsbr.o sendsbr.o annosbr.o distsbr.o $(LOCALLIBS)
+       $(LINK) viamail.o mhmisc.o mhoutsbr.o sendsbr.o annosbr.o distsbr.o $(LINKLIBS) $(TERMLIB)
+
+vmh: vmh.o vmhsbr.o $(LOCALLIBS)
+       $(LINK) vmh.o vmhsbr.o $(LINKLIBS) $(TERMLIB)
+
+whatnow: whatnow.o whatnowsbr.o sendsbr.o annosbr.o distsbr.o $(LOCALLIBS)
+       $(LINK) whatnow.o whatnowsbr.o sendsbr.o annosbr.o distsbr.o $(LINKLIBS)
+
+whom: whom.o distsbr.o $(LOCALLIBS)
+       $(LINK) whom.o distsbr.o $(LINKLIBS)
+
+# ========== DEPENDENCIES FOR INSTALLING ==========
+
+# install everything
+install: install-cmds install-lcmds install-misc
+
+# install commands
+install-cmds:
+       $(top_srcdir)/mkinstalldirs $(bindir)
+       for cmd in $(CMDS); do \
+         $(INSTALL_PROGRAM) $$cmd $(bindir)/$$cmd; \
+       done
+
+# install links
+install-lcmds:
+       rm -f $(bindir)/flists
+       rm -f $(bindir)/folders
+       rm -f $(bindir)/prev
+       rm -f $(bindir)/next
+       $(LN) $(bindir)/flist  $(bindir)/flists
+       $(LN) $(bindir)/folder $(bindir)/folders
+       $(LN) $(bindir)/show   $(bindir)/prev
+       $(LN) $(bindir)/show   $(bindir)/next
+
+# install misc support binaries
+install-misc:
+       $(top_srcdir)/mkinstalldirs $(libdir)
+       for misc in $(MISC); do \
+         $(INSTALL_PROGRAM) $$misc $(libdir)/$$misc; \
+       done
+
+uninstall:
+       for cmd in $(CMDS); do \
+         rm -f $(bindir)/$$cmd; \
+       done
+       for lcmd in $(LCMDS); do \
+         rm -f $(bindir)/$$lcmd; \
+       done
+       for misc in $(MISC); do \
+         rm -f $(libdir)/$$misc; \
+       done
+
+# ========== DEPENDENCIES FOR CLEANUP ==========
+
+mostlyclean:
+       rm -f *.o *~
+
+clean: mostlyclean
+       rm -f $(CMDS) $(MISC)
+
+distclean: clean
+       rm -f Makefile
+
+realclean: distclean
+
+superclean: realclean
+
+# ========== DEPENDENCIES FOR MAINTENANCE ==========
+
+subdir = uip
+
+Makefile: Makefile.in ../config.status
+       cd .. && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+
+distdir = ../`cat ../distname`/$(subdir)
+nmhdist: $(DIST)
+       @echo "Copying distribution files in $(subdir)"
+       @for file in $(DIST); do \
+         cp -p $(srcdir)/$$file $(distdir); \
+       done
+
diff --git a/uip/ali.c b/uip/ali.c
new file mode 100644 (file)
index 0000000..bbbf0c1
--- /dev/null
+++ b/uip/ali.c
@@ -0,0 +1,253 @@
+
+/*
+ * ali.c -- list nmh mail aliases
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/addrsbr.h>
+#include <h/aliasbr.h>
+
+/*
+ * maximum number of names
+ */
+#define        NVEC 50
+
+static struct swit switches[] = {
+#define        ALIASW                0
+    { "alias aliasfile", 0 },
+#define        NALIASW               1
+    { "noalias", -7 },
+#define        LISTSW                2
+    { "list", 0 },
+#define        NLISTSW               3
+    { "nolist", 0 },
+#define        NORMSW                4
+    { "normalize", 0 },
+#define        NNORMSW               5
+    { "nonormalize", 0 },
+#define        USERSW                6
+    { "user", 0 },
+#define        NUSERSW               7
+    { "nouser", 0 },
+#define VERSIONSW             8
+    { "version", 0 },
+#define        HELPSW                9
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+static int pos = 1;
+
+extern struct aka *akahead;
+
+/*
+ * prototypes
+ */
+void print_aka (char *, int, int);
+void print_usr (char *, int, int);
+
+
+int
+main (int argc, char **argv)
+{
+    int i, vecp = 0, inverted = 0, list = 0;
+    int noalias = 0, normalize = AD_NHST;
+    char *cp, **ap, **argp, buf[BUFSIZ];
+    char *vec[NVEC], **arguments;
+    struct aka *ak;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    mts_init (invo_name);
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown", cp);
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf), "%s [switches] aliases ...",
+                       invo_name);
+                   print_help (buf, switches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version (invo_name);
+                   done (1);
+
+               case ALIASW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   if ((i = alias (cp)) != AK_OK)
+                       adios (NULL, "aliasing error in %s - %s", cp, akerror (i));
+                   continue;
+               case NALIASW: 
+                   noalias++;
+                   continue;
+
+               case LISTSW: 
+                   list++;
+                   continue;
+               case NLISTSW: 
+                   list = 0;
+                   continue;
+
+               case NORMSW: 
+                   normalize = AD_HOST;
+                   continue;
+               case NNORMSW: 
+                   normalize = AD_NHST;
+                   continue;
+
+               case USERSW: 
+                   inverted++;
+                   continue;
+               case NUSERSW: 
+                   inverted = 0;
+                   continue;
+           }
+       }
+       vec[vecp++] = cp;
+    }
+
+    if (!noalias) {
+       /* allow Aliasfile: profile entry */
+       if ((cp = context_find ("Aliasfile"))) {
+           char *dp = NULL;
+
+           for (ap = brkstring(dp = getcpy(cp), " ", "\n"); ap && *ap; ap++)
+               if ((i = alias (*ap)) != AK_OK)
+                   adios (NULL, "aliasing error in %s - %s", *ap, akerror (i));
+           if (dp)
+               free(dp);
+       }
+       alias (AliasFile);
+    }
+
+    /*
+     * If -user is specified
+     */
+    if (inverted) {
+       if (vecp == 0)
+           adios (NULL, "usage: %s -user addresses ...  (you forgot the addresses)",
+                  invo_name);
+
+       for (i = 0; i < vecp; i++)
+           print_usr (vec[i], list, normalize);
+
+       done (0);
+    }
+
+    if (vecp) {
+       /* print specified aliases */
+       for (i = 0; i < vecp; i++)
+           print_aka (akvalue (vec[i]), list, 0);
+    } else {
+       /* print them all */
+       for (ak = akahead; ak; ak = ak->ak_next) {
+           printf ("%s: ", ak->ak_name);
+           pos += strlen (ak->ak_name) + 1;
+           print_aka (akresult (ak), list, pos);
+       }
+    }
+
+    done (0);
+}
+
+void
+print_aka (char *p, int list, int margin)
+{
+    char c;
+
+    if (p == NULL) {
+       printf ("<empty>\n");
+       return;
+    }
+
+    while ((c = *p++)) {
+       switch (c) {
+           case ',': 
+               if (*p) {
+                   if (list)
+                       printf ("\n%*s", margin, "");
+                   else {
+                       if (pos >= 68) {
+                           printf (",\n ");
+                           pos = 2;
+                       } else {
+                           printf (", ");
+                           pos += 2;
+                       }
+                   }
+               }
+
+           case 0: 
+               break;
+
+           default: 
+               pos++;
+               putchar (c);
+       }
+    }
+
+    putchar ('\n');
+    pos = 1;
+}
+
+void
+print_usr (char *s, int list, int norm)
+{
+    register char *cp, *pp, *vp;
+    register struct aka *ak;
+    register struct mailname *mp, *np;
+
+    if ((pp = getname (s)) == NULL)
+       adios (NULL, "no address in \"%s\"", s);
+    if ((mp = getm (pp, NULL, 0, norm, NULL)) == NULL)
+       adios (NULL, "bad address \"%s\"", s);
+    while (getname (""))
+       continue;
+
+    vp = NULL;
+    for (ak = akahead; ak; ak = ak->ak_next) {
+       pp = akresult (ak);
+       while ((cp = getname (pp))) {
+           if ((np = getm (cp, NULL, 0, norm, NULL)) == NULL)
+               continue;
+           if (!strcasecmp (mp->m_host, np->m_host)
+                   && !strcasecmp (mp->m_mbox, np->m_mbox)) {
+               vp = vp ? add (ak->ak_name, add (",", vp))
+                   : getcpy (ak->ak_name);
+               mnfree (np);
+               while (getname (""))
+                   continue;
+               break;
+           }
+           mnfree (np);
+       }
+    }
+    mnfree (mp);
+
+#if 0
+    printf ("%s: ", s);
+    print_aka (vp ? vp : s, list, pos += strlen (s) + 1);
+#else
+    print_aka (vp ? vp : s, list, 0);
+#endif
+
+    if (vp)
+       free (vp);
+}
diff --git a/uip/aliasbr.c b/uip/aliasbr.c
new file mode 100644 (file)
index 0000000..8ea8c13
--- /dev/null
@@ -0,0 +1,598 @@
+
+/*
+ * aliasbr.c -- new aliasing mechanism
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/aliasbr.h>
+#include <grp.h>
+#include <pwd.h>
+
+static int akvis;
+static char *akerrst;
+
+struct aka *akahead = NULL;
+struct aka *akatail = NULL;
+
+struct home *homehead = NULL;
+struct home *hometail = NULL;
+
+/*
+ * prototypes
+ */
+int alias (char *); 
+int akvisible (void);
+void init_pw (void);
+char *akresult (struct aka *);
+char *akvalue (char *);
+char *akerror (int);
+
+static  char *akval (struct aka *, char *);
+static int aleq (char *, char *);
+static char *scanp (char *);
+static char *getp (char *);
+static char *seekp (char *, char *, char **);
+static int addfile (struct aka *, char *);
+static int addgroup (struct aka *, char *);
+static int addmember (struct aka *, char *);
+static int addall (struct aka *);
+static char *getalias (char *);
+static void add_aka (struct aka *, char *);
+static struct aka *akalloc (char *);
+static struct home *hmalloc (struct passwd *);
+#ifndef MMDFMTS
+struct home *seek_home (char *);
+#endif
+
+
+char *
+akvalue (char *s)
+{
+    register char *v;
+
+    if (akahead == NULL)
+       alias (AliasFile);
+
+    akvis = -1;
+    v = akval (akahead, s);
+    if (akvis == -1)
+       akvis = 0;
+    return v;
+}
+
+
+int
+akvisible (void)
+{
+    return akvis;
+}
+
+
+char *
+akresult (struct aka *ak)
+{
+    register char *cp = NULL, *dp, *pp;
+    register struct adr *ad;
+
+    for (ad = ak->ak_addr; ad; ad = ad->ad_next) {
+       pp = ad->ad_local ? akval (ak->ak_next, ad->ad_text)
+           : getcpy (ad->ad_text);
+
+       if (cp) {
+           dp = cp;
+           cp = concat (cp, ",", pp, NULL);
+           free (dp);
+           free (pp);
+       }
+       else
+           cp = pp;
+    }
+
+    if (akvis == -1)
+       akvis = ak->ak_visible;
+    return cp;
+}
+
+
+static char *
+akval (struct aka *ak, char *s)
+{
+    if (!s)
+       return s;                       /* XXX */
+
+    for (; ak; ak = ak->ak_next)
+       if (aleq (s, ak->ak_name))
+           return akresult (ak);
+
+    return getcpy (s);
+}
+
+
+static int
+aleq (char *string, char *aliasent)
+{
+    register char c;
+
+    while ((c = *string++))
+       if (*aliasent == '*')
+           return 1;
+       else
+           if ((c | 040) != (*aliasent | 040))
+               return 0;
+           else
+               aliasent++;
+
+    return (*aliasent == 0 || *aliasent == '*');
+}
+
+
+int
+alias (char *file)
+{
+    int i;
+    register char *bp, *cp, *pp;
+    char lc, *ap;
+    register struct aka *ak = NULL;
+    register FILE *fp;
+
+    if (*file != '/'
+           && (strncmp (file, "./", 2) && strncmp (file, "../", 3)))
+       file = etcpath (file);
+    if ((fp = fopen (file, "r")) == NULL) {
+       akerrst = file;
+       return AK_NOFILE;
+    }
+
+    while (vfgets (fp, &ap) == OK) {
+       bp = ap;
+       switch (*(pp = scanp (bp))) {
+           case '<':           /* recurse a level */
+               if (!*(cp = getp (pp + 1))) {
+                   akerrst = "'<' without alias-file";
+                   fclose (fp);
+                   return AK_ERROR;
+               }
+               if ((i = alias (cp)) != AK_OK) {
+                   fclose (fp);
+                   return i;
+               }
+
+           case ':':           /* comment */
+           case ';': 
+           case '#':
+           case 0: 
+               continue;
+       }
+
+       akerrst = bp;
+       if (!*(cp = seekp (pp, &lc, &ap))) {
+           fclose (fp);
+           return AK_ERROR;
+       }
+       if (!(ak = akalloc (cp))) {
+           fclose (fp);
+           return AK_LIMIT;
+       }
+       switch (lc) {
+           case ':': 
+               ak->ak_visible = 0;
+               break;
+
+           case ';': 
+               ak->ak_visible = 1;
+               break;
+
+           default: 
+               fclose (fp);
+               return AK_ERROR;
+       }
+
+       switch (*(pp = scanp (ap))) {
+           case 0:             /* EOL */
+               fclose (fp);
+               return AK_ERROR;
+
+           case '<':           /* read values from file */
+               if (!*(cp = getp (pp + 1))) {
+                   fclose (fp);
+                   return AK_ERROR;
+               }
+               if (!addfile (ak, cp)) {
+                   fclose (fp);
+                   return AK_NOFILE;
+               }
+               break;
+
+           case '=':           /* UNIX group */
+               if (!*(cp = getp (pp + 1))) {
+                   fclose (fp);
+                   return AK_ERROR;
+               }
+               if (!addgroup (ak, cp)) {
+                   fclose (fp);
+                   return AK_NOGROUP;
+               }
+               break;
+
+           case '+':           /* UNIX group members */
+               if (!*(cp = getp (pp + 1))) {
+                   fclose (fp);
+                   return AK_ERROR;
+               }
+               if (!addmember (ak, cp)) {
+                   fclose (fp);
+                   return AK_NOGROUP;
+               }
+               break;
+
+           case '*':           /* Everyone */
+               addall (ak);
+               break;
+
+           default:            /* list */
+               while ((cp = getalias (pp)))
+                   add_aka (ak, cp);
+               break;
+       }
+    }
+
+    fclose (fp);
+    return AK_OK;
+}
+
+
+char *
+akerror (int i)
+{
+    static char buffer[BUFSIZ];
+
+    switch (i) {
+       case AK_NOFILE: 
+           snprintf (buffer, sizeof(buffer), "unable to read '%s'", akerrst);
+           break;
+
+       case AK_ERROR: 
+           snprintf (buffer, sizeof(buffer), "error in line '%s'", akerrst);
+           break;
+
+       case AK_LIMIT: 
+           snprintf (buffer, sizeof(buffer), "out of memory while on '%s'", akerrst);
+           break;
+
+       case AK_NOGROUP: 
+           snprintf (buffer, sizeof(buffer), "no such group as '%s'", akerrst);
+           break;
+
+       default: 
+           snprintf (buffer, sizeof(buffer), "unknown error (%d)", i);
+           break;
+    }
+
+    return buffer;
+}
+
+
+static char *
+scanp (char *p)
+{
+    while (isspace (*p))
+       p++;
+    return p;
+}
+
+
+static char *
+getp (char *p)
+{
+    register char  *cp = scanp (p);
+
+    p = cp;
+    while (!isspace (*cp) && *cp)
+       cp++;
+    *cp = 0;
+
+    return p;
+}
+
+
+static char *
+seekp (char *p, char *c, char **a)
+{
+    register char *cp;
+
+    p = cp = scanp (p);
+    while (!isspace (*cp) && *cp && *cp != ':' && *cp != ';')
+       cp++;
+    *c = *cp;
+    *cp++ = 0;
+    *a = cp;
+
+    return p;
+}
+
+
+static int
+addfile (struct aka *ak, char *file)
+{
+    register char *cp;
+    char buffer[BUFSIZ];
+    register FILE *fp;
+
+    if (!(fp = fopen (etcpath (file), "r"))) {
+       akerrst = file;
+       return 0;
+    }
+
+    while (fgets (buffer, sizeof buffer, fp))
+       while ((cp = getalias (buffer)))
+           add_aka (ak, cp);
+
+    fclose (fp);
+    return 1;
+}
+
+
+static int
+addgroup (struct aka *ak, char *grp)
+{
+    register char *gp;
+    register struct group *gr = getgrnam (grp);
+    register struct home *hm = NULL;
+
+    if (!gr)
+       gr = getgrgid (atoi (grp));
+    if (!gr) {
+       akerrst = grp;
+       return 0;
+    }
+
+#ifndef DBMPWD
+    if (homehead == NULL)
+       init_pw ();
+#endif /* DBMPWD */
+
+    while ((gp = *gr->gr_mem++))
+#ifdef DBMPWD
+    {
+       struct passwd *pw;
+#endif /* DBMPWD */
+       for (hm = homehead; hm; hm = hm->h_next)
+           if (!strcmp (hm->h_name, gp)) {
+               add_aka (ak, hm->h_name);
+               break;
+           }
+#ifdef DBMPWD
+        if ((pw = getpwnam(gp)))
+       {
+               hmalloc(pw);
+               add_aka (ak, gp);
+       }
+    }
+#endif /* DBMPWD */
+
+    return 1;
+}
+
+
+static int
+addmember (struct aka *ak, char *grp)
+{
+    gid_t gid;
+    register struct group *gr = getgrnam (grp);
+    register struct home *hm = NULL;
+
+    if (gr)
+       gid = gr->gr_gid;
+    else {
+       gid = atoi (grp);
+       gr = getgrgid (gid);
+    }
+    if (!gr) {
+       akerrst = grp;
+       return 0;
+    }
+
+#ifndef DBMPWD
+    if (homehead == NULL)
+#endif /* DBMPWD */
+       init_pw ();
+
+    for (hm = homehead; hm; hm = hm->h_next)
+       if (hm->h_gid == gid)
+           add_aka (ak, hm->h_name);
+
+    return 1;
+}
+
+
+static int
+addall (struct aka *ak)
+{
+    int noshell = NoShell == NULL || *NoShell == 0;
+    register struct home *hm;
+
+#ifndef DBMPWD
+    if (homehead == NULL)
+#endif /* DBMPWD */
+       init_pw ();
+    if (Everyone < 0)
+       Everyone = EVERYONE;
+
+    for (hm = homehead; hm; hm = hm->h_next)
+       if (hm->h_uid > Everyone
+               && (noshell || strcmp (hm->h_shell, NoShell)))
+           add_aka (ak, hm->h_name);
+
+    return homehead != NULL;
+}
+
+
+static char *
+getalias (char *addrs)
+{
+    register char *pp, *qp;
+    static char *cp = NULL;
+
+    if (cp == NULL)
+       cp = addrs;
+    else
+       if (*cp == 0)
+           return (cp = NULL);
+
+    for (pp = cp; isspace (*pp); pp++)
+       continue;
+    if (*pp == 0)
+       return (cp = NULL);
+    for (qp = pp; *qp != 0 && *qp != ','; qp++)
+       continue;
+    if (*qp == ',')
+       *qp++ = 0;
+    for (cp = qp, qp--; qp > pp; qp--)
+       if (*qp != 0)
+           if (isspace (*qp))
+               *qp = 0;
+           else
+               break;
+
+    return pp;
+}
+
+
+static void
+add_aka (struct aka *ak, char *pp)
+{
+    register struct adr *ad, *ld;
+
+    for (ad = ak->ak_addr, ld = NULL; ad; ld = ad, ad = ad->ad_next)
+       if (!strcmp (pp, ad->ad_text))
+           return;
+
+    ad = (struct adr *) malloc (sizeof(*ad));
+    if (ad == NULL)
+       return;
+    ad->ad_text = getcpy (pp);
+    ad->ad_local = strchr(pp, '@') == NULL && strchr(pp, '!') == NULL;
+    ad->ad_next = NULL;
+    if (ak->ak_addr)
+       ld->ad_next = ad;
+    else
+       ak->ak_addr = ad;
+}
+
+
+void
+init_pw (void)
+{
+    register struct passwd  *pw;
+#ifdef DBMPWD
+    static int init;
+  
+    if (!init)
+    {
+          /* if the list has yet to be initialized */
+           /* zap the list, and rebuild from scratch */
+           homehead=NULL;
+           hometail=NULL;
+           init++;
+#endif /* DBMPWD */
+
+    setpwent ();
+
+    while ((pw = getpwent ()))
+       if (!hmalloc (pw))
+           break;
+
+    endpwent ();
+#ifdef DBMPWD
+    }
+#endif /* DBMPWD */
+}
+
+
+static struct aka *
+akalloc (char *id)
+{
+    register struct aka *p;
+
+    if (!(p = (struct aka *) malloc (sizeof(*p))))
+       return NULL;
+
+    p->ak_name = getcpy (id);
+    p->ak_visible = 0;
+    p->ak_addr = NULL;
+    p->ak_next = NULL;
+    if (akatail != NULL)
+       akatail->ak_next = p;
+    if (akahead == NULL)
+       akahead = p;
+    akatail = p;
+
+    return p;
+}
+
+
+static struct home *
+hmalloc (struct passwd *pw)
+{
+    register struct home *p;
+
+    if (!(p = (struct home *) malloc (sizeof(*p))))
+       return NULL;
+
+    p->h_name = getcpy (pw->pw_name);
+    p->h_uid = pw->pw_uid;
+    p->h_gid = pw->pw_gid;
+    p->h_home = getcpy (pw->pw_dir);
+    p->h_shell = getcpy (pw->pw_shell);
+    p->h_ngrps = 0;
+    p->h_next = NULL;
+    if (hometail != NULL)
+       hometail->h_next = p;
+    if (homehead == NULL)
+       homehead = p;
+    hometail = p;
+
+    return p;
+}
+
+
+#ifndef        MMDFMTS
+struct home *
+seek_home (char *name)
+{
+    register struct home *hp;
+#ifdef DBMPWD
+    struct passwd *pw;
+    char lname[32];
+    char *c,*c1;
+#else  /* DBMPWD */
+
+    if (homehead == NULL)
+       init_pw ();
+#endif /* DBMPWD */
+
+    for (hp = homehead; hp; hp = hp->h_next)
+       if (!strcasecmp (name, hp->h_name))
+           return hp;
+
+#ifdef DBMPWD
+    /*
+     * The only place where there might be problems.
+     * This assumes that ALL usernames are kept in lowercase.
+     */
+    for (c = name, c1 = lname; *c && (c1 - lname < sizeof(lname) - 1); c++, c1++) {
+        if (isalpha(*c) && isupper(*c))
+           *c1 = tolower (*c);
+       else
+           *c1 = *c;
+    }
+    *c1 = '\0';
+    if ((pw = getpwnam(lname)))
+       return(hmalloc(pw));
+#endif /* DBMPWD */
+       
+    return NULL;
+}
+#endif /* MMDFMTS */
diff --git a/uip/anno.c b/uip/anno.c
new file mode 100644 (file)
index 0000000..c318a22
--- /dev/null
@@ -0,0 +1,213 @@
+
+/*
+ * anno.c -- annotate messages
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+/*
+ * We allocate space for messages (msgs array)
+ * this number of elements at a time.
+ */
+#define MAXMSGS  256
+
+
+static struct swit switches[] = {
+#define        COMPSW  0
+    { "component field", 0 },
+#define        INPLSW  1
+    { "inplace", 0 },
+#define        NINPLSW 2
+    { "noinplace", 0 },
+#define        DATESW  3
+    { "date", 0 },
+#define        NDATESW 4
+    { "nodate", 0 },
+#define        TEXTSW  5
+    { "text body", 0 },
+#define VERSIONSW 6
+    { "version", 0 },
+#define        HELPSW  7
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+/*
+ * static prototypes
+ */
+static void make_comp (char **);
+
+
+int
+main (int argc, char **argv)
+{
+    int inplace = 1, datesw = 1;
+    int nummsgs, maxmsgs, msgnum;
+    char *cp, *maildir, *comp = NULL;
+    char *text = NULL, *folder = NULL, buf[BUFSIZ];
+    char **argp, **arguments, **msgs;
+    struct msgs *mp;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    /*
+     * Allocate the initial space to record message
+     * names and ranges.
+     */
+    nummsgs = 0;
+    maxmsgs = MAXMSGS;
+    if (!(msgs = (char **) malloc ((size_t) (maxmsgs * sizeof(*msgs)))))
+       adios (NULL, "unable to allocate storage");
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown", cp);
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf), "%s [+folder] [msgs] [switches]",
+                       invo_name);
+                   print_help (buf, switches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case COMPSW: 
+                   if (comp)
+                       adios (NULL, "only one component at a time!");
+                   if (!(comp = *argp++) || *comp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+
+               case DATESW: 
+                   datesw++;
+                   continue;
+               case NDATESW: 
+                   datesw = 0;
+                   continue;
+
+               case INPLSW: 
+                   inplace++;
+                   continue;
+               case NINPLSW: 
+                   inplace = 0;
+                   continue;
+
+               case TEXTSW: 
+                   if (text)
+                       adios (NULL, "only one body at a time!");
+                   if (!(text = *argp++) || *text == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+           }
+       }
+       if (*cp == '+' || *cp == '@') {
+           if (folder)
+               adios (NULL, "only one folder at a time!");
+           else
+               folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       } else {
+           /*
+            * Check if we need to allocate more space
+            * for message name/ranges.
+            */
+           if (nummsgs >= maxmsgs) {
+               maxmsgs += MAXMSGS;
+               if (!(msgs = (char **) realloc (msgs,
+                       (size_t) (maxmsgs * sizeof(*msgs)))))
+                   adios (NULL, "unable to reallocate msgs storage");
+           }
+           msgs[nummsgs++] = cp;
+       }
+    }
+
+#ifdef UCI
+    if (strcmp(invo_name, "fanno") == 0)       /* ugh! */
+       datesw = 0;
+#endif /* UCI */
+
+    if (!context_find ("path"))
+       free (path ("./", TFOLDER));
+    if (!nummsgs)
+       msgs[nummsgs++] = "cur";
+    if (!folder)
+       folder = getfolder (1);
+    maildir = m_maildir (folder);
+
+    if (chdir (maildir) == NOTOK)
+       adios (maildir, "unable to change directory to");
+
+    /* read folder and create message structure */
+    if (!(mp = folder_read (folder)))
+       adios (NULL, "unable to read folder %s", folder);
+
+    /* check for empty folder */
+    if (mp->nummsg == 0)
+       adios (NULL, "no messages in %s", folder);
+
+    /* parse all the message ranges/sequences and set SELECTED */
+    for (msgnum = 0; msgnum < nummsgs; msgnum++)
+       if (!m_convert (mp, msgs[msgnum]))
+           done (1);
+
+    make_comp (&comp);
+
+    /* annotate all the SELECTED messages */
+    for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
+       if (is_selected(mp, msgnum))
+           annotate (m_name (msgnum), comp, text, inplace, datesw);
+
+    context_replace (pfolder, folder); /* update current folder  */
+    seq_setcur (mp, mp->lowsel);       /* update current message */
+    seq_save (mp);     /* synchronize message sequences */
+    folder_free (mp);  /* free folder/message structure */
+    context_save ();   /* save the context file         */
+    done (0);
+}
+
+
+static void
+make_comp (char **ap)
+{
+    register char *cp;
+    char buffer[BUFSIZ];
+
+    if (*ap == NULL) {
+       printf ("Enter component name: ");
+       fflush (stdout);
+
+       if (fgets (buffer, sizeof buffer, stdin) == NULL)
+           done (1);
+       *ap = trimcpy (buffer);
+    }
+
+    if ((cp = *ap + strlen (*ap) - 1) > *ap && *cp == ':')
+       *cp = 0;
+    if (strlen (*ap) == 0)
+       adios (NULL, "null component name");
+    if (**ap == '-')
+       adios (NULL, "invalid component name %s", *ap);
+    if (strlen (*ap) >= NAMESZ)
+       adios (NULL, "too large component name %s", *ap);
+
+    for (cp = *ap; *cp; cp++)
+       if (!isalnum (*cp) && *cp != '-')
+           adios (NULL, "invalid component name %s", *ap);
+}
+
diff --git a/uip/annosbr.c b/uip/annosbr.c
new file mode 100644 (file)
index 0000000..c237022
--- /dev/null
@@ -0,0 +1,111 @@
+
+/*
+ * annosbr.c -- prepend annotation to messages
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <zotnet/tws/tws.h>
+#include <fcntl.h>
+#include <errno.h>
+
+extern int  errno;
+
+/*
+ * static prototypes
+ */
+static int annosbr (int, char *, char *, char *, int, int);
+
+
+int
+annotate (char *file, char *comp, char *text, int inplace, int datesw)
+{
+    int i, fd;
+
+    /* open and lock the file to be annotated */
+    if ((fd = lkopen (file, O_RDWR, 0)) == NOTOK) {
+       switch (errno) {
+           case ENOENT: 
+               break;
+
+           default: 
+               admonish (file, "unable to lock and open");
+               break;
+       }
+       return 1;
+    }
+
+    i = annosbr (fd, file, comp, text, inplace, datesw);
+    lkclose (fd, file);
+    return i;
+}
+
+
+static int
+annosbr (int fd, char *file, char *comp, char *text, int inplace, int datesw)
+{
+    int mode, tmpfd;
+    char *cp, *sp;
+    char buffer[BUFSIZ], tmpfil[BUFSIZ];
+    struct stat st;
+    FILE *tmp;
+
+    mode = fstat (fd, &st) != NOTOK ? (st.st_mode & 0777) : m_gmprot ();
+
+    strncpy (tmpfil, m_scratch (file, "annotate"), sizeof(tmpfil));
+
+    if ((tmp = fopen (tmpfil, "w")) == NULL) {
+       admonish (tmpfil, "unable to create");
+       return 1;
+    }
+    chmod (tmpfil, mode);
+
+    if (datesw)
+       fprintf (tmp, "%s: %s\n", comp, dtimenow (0));
+    if ((cp = text)) {
+       do {
+           while (*cp == ' ' || *cp == '\t')
+               cp++;
+           sp = cp;
+           while (*cp && *cp++ != '\n')
+               continue;
+           if (cp - sp)
+               fprintf (tmp, "%s: %*.*s", comp, cp - sp, cp - sp, sp);
+       } while (*cp);
+       if (cp[-1] != '\n' && cp != text)
+           putc ('\n', tmp);
+    }
+    fflush (tmp);
+    cpydata (fd, fileno (tmp), file, tmpfil);
+    fclose (tmp);
+
+    if (inplace) {
+       if ((tmpfd = open (tmpfil, O_RDONLY)) == NOTOK)
+           adios (tmpfil, "unable to open for re-reading");
+       lseek (fd, (off_t) 0, SEEK_SET);
+       cpydata (tmpfd, fd, tmpfil, file);
+       close (tmpfd);
+       unlink (tmpfil);
+    } else {
+       strncpy (buffer, m_backup (file), sizeof(buffer));
+       if (rename (file, buffer) == NOTOK) {
+           switch (errno) {
+               case ENOENT:    /* unlinked early - no annotations */
+                   unlink (tmpfil);
+                   break;
+
+               default:
+                   admonish (buffer, "unable to rename %s to", file);
+                   break;
+           }
+           return 1;
+       }
+       if (rename (tmpfil, file) == NOTOK) {
+           admonish (file, "unable to rename %s to", tmpfil);
+           return 1;
+       }
+    }
+
+    return 0;
+}
diff --git a/uip/ap.c b/uip/ap.c
new file mode 100644 (file)
index 0000000..7e16b92
--- /dev/null
+++ b/uip/ap.c
@@ -0,0 +1,205 @@
+
+/*
+ * ap.c -- parse addresses 822-style
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/addrsbr.h>
+#include <h/fmt_scan.h>
+
+#define        NADDRS  100
+
+#define        WIDTH   78
+#define        WBUFSIZ BUFSIZ
+
+#define        FORMAT  "%<{error}%{error}: %{text}%|%(putstr(proper{text}))%>"
+
+static struct swit switches[] = {
+#define        FORMSW  0
+    { "form formatfile", 0 },
+#define        FMTSW   1
+    { "format string", 5 },
+#define        NORMSW  2
+    { "normalize", 0 },
+#define        NNORMSW 3
+    { "nonormalize", 0 },
+#define        WIDTHSW 4
+    { "width columns", 0 },
+#define VERSIONSW 5
+    { "version", 0 },
+#define        HELPSW  6
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+static struct format *fmt;
+
+static int dat[5];
+
+/*
+ * prototypes
+ */
+int sc_width (void);  /* from termsbr.c */
+
+/*
+ * static prototypes
+ */
+static int process (char *, int, int);
+
+
+int
+main (int argc, char **argv)
+{
+    int addrp = 0, normalize = AD_HOST;
+    int width = 0, status = 0;
+    char *cp, *form = NULL, *format = NULL, *nfs;
+    char buf[BUFSIZ], **argp;
+    char **arguments, *addrs[NADDRS];
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    mts_init (invo_name);
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown", cp);
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf), "%s [switches] addrs ...",
+                       invo_name);
+                   print_help (buf, switches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version (invo_name);
+                   done (1);
+
+               case FORMSW: 
+                   if (!(form = *argp++) || *form == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   format = NULL;
+                   continue;
+               case FMTSW: 
+                   if (!(format = *argp++) || *format == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   form = NULL;
+                   continue;
+
+               case WIDTHSW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   width = atoi (cp);
+                   continue;
+
+               case NORMSW: 
+                   normalize = AD_HOST;
+                   continue;
+               case NNORMSW: 
+                   normalize = AD_NHST;
+                   continue;
+           }
+       }
+       if (addrp > NADDRS)
+           adios (NULL, "more than %d addresses", NADDRS);
+       else
+           addrs[addrp++] = cp;
+    }
+    addrs[addrp] = NULL;
+
+    if (addrp == 0)
+       adios (NULL, "usage: %s [switches] addrs ...", invo_name);
+
+    /* get new format string */
+    nfs = new_fs (form, format, FORMAT);
+
+    if (width == 0) {
+       if ((width = sc_width ()) < WIDTH / 2)
+           width = WIDTH / 2;
+       width -= 2;
+    }
+    if (width > WBUFSIZ)
+       width = WBUFSIZ;
+    fmt_norm = normalize;
+    fmt_compile (nfs, &fmt);
+
+    dat[0] = 0;
+    dat[1] = 0;
+    dat[2] = 0;
+    dat[3] = width;
+    dat[4] = 0;
+
+    for (addrp = 0; addrs[addrp]; addrp++)
+       status += process (addrs[addrp], width, normalize);
+
+    done (status);
+}
+
+struct pqpair {
+    char *pq_text;
+    char *pq_error;
+    struct pqpair *pq_next;
+};
+
+
+static int
+process (char *arg, int length, int norm)
+{
+    int        status = 0;
+    register char *cp;
+    char buffer[WBUFSIZ + 1], error[BUFSIZ];
+    register struct comp *cptr;
+    register struct pqpair *p, *q;
+    struct pqpair pq;
+    register struct mailname *mp;
+
+    (q = &pq)->pq_next = NULL;
+    while ((cp = getname (arg))) {
+       if ((p = (struct pqpair *) calloc ((size_t) 1, sizeof(*p))) == NULL)
+           adios (NULL, "unable to allocate pqpair memory");
+       if ((mp = getm (cp, NULL, 0, norm, error)) == NULL) {
+           p->pq_text = getcpy (cp);
+           p->pq_error = getcpy (error);
+           status++;
+       }
+       else {
+           p->pq_text = getcpy (mp->m_text);
+           mnfree (mp);
+       }
+       q = (q->pq_next = p);
+    }
+
+    for (p = pq.pq_next; p; p = q) {
+       FINDCOMP (cptr, "text");
+       if (cptr)
+           cptr->c_text = p->pq_text;
+       FINDCOMP (cptr, "error");
+       if (cptr)
+           cptr->c_text = p->pq_error;
+
+       fmt_scan (fmt, buffer, length, dat);
+       fputs (buffer, stdout);
+
+       free (p->pq_text);
+       if (p->pq_error)
+           free (p->pq_error);
+       q = p->pq_next;
+       free ((char *) p);
+    }
+
+    return status;
+}
diff --git a/uip/burst.c b/uip/burst.c
new file mode 100644 (file)
index 0000000..d13b076
--- /dev/null
@@ -0,0 +1,406 @@
+
+/*
+ * burst.c -- explode digests into individual messages
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+static struct swit switches[] = {
+#define        INPLSW  0
+    { "inplace", 0 },
+#define        NINPLSW 1
+    { "noinplace", 0 },
+#define        QIETSW  2
+    { "quiet", 0 },
+#define        NQIETSW 3
+    { "noquiet", 0 },
+#define        VERBSW  4
+    { "verbose", 0 },
+#define        NVERBSW 5
+    { "noverbose", 0 },
+#define VERSIONSW 6
+    { "version", 0 },
+#define        HELPSW  7
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+static char delim3[] = "-------";
+
+struct smsg {
+    long s_start;
+    long s_stop;
+};
+
+/*
+ * static prototypes
+ */
+static int find_delim (int, struct smsg *);
+static void burst (struct msgs **, int, struct smsg *, int, int, int);
+static void cpybrst (FILE *, FILE *, char *, char *, int);
+
+
+int
+main (int argc, char **argv)
+{
+    int inplace = 0, quietsw = 0, verbosw = 0;
+    int msgp = 0, hi, msgnum, numburst;
+    char *cp, *maildir, *folder = NULL, buf[BUFSIZ];
+    char **argp, **arguments, *msgs[MAXARGS];
+    struct smsg *smsgs;
+    struct msgs *mp;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+           case AMBIGSW: 
+               ambigsw (cp, switches);
+               done (1);
+           case UNKWNSW: 
+               adios (NULL, "-%s unknown\n", cp);
+
+           case HELPSW: 
+               snprintf (buf, sizeof(buf), "%s [+folder] [msgs] [switches]",
+                       invo_name);
+               print_help (buf, switches, 1);
+               done (1);
+           case VERSIONSW:
+               print_version(invo_name);
+               done (1);
+
+           case INPLSW: 
+               inplace++;
+               continue;
+           case NINPLSW: 
+               inplace = 0;
+               continue;
+
+           case QIETSW: 
+               quietsw++;
+               continue;
+           case NQIETSW: 
+               quietsw = 0;
+               continue;
+
+           case VERBSW: 
+               verbosw++;
+               continue;
+           case NVERBSW: 
+               verbosw = 0;
+               continue;
+           }
+       }
+       if (*cp == '+' || *cp == '@') {
+           if (folder)
+               adios (NULL, "only one folder at a time!");
+           else
+               folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       } else {
+           msgs[msgp++] = cp;
+       }
+    }
+
+    if (!context_find ("path"))
+       free (path ("./", TFOLDER));
+    if (!msgp)
+       msgs[msgp++] = "cur";
+    if (!folder)
+       folder = getfolder (1);
+    maildir = m_maildir (folder);
+
+    if (chdir (maildir) == NOTOK)
+       adios (maildir, "unable to change directory to");
+
+    /* read folder and create message structure */
+    if (!(mp = folder_read (folder)))
+       adios (NULL, "unable to read folder %s", folder);
+
+    /* check for empty folder */
+    if (mp->nummsg == 0)
+       adios (NULL, "no messages in %s", folder);
+
+    /* parse all the message ranges/sequences and set SELECTED */
+    for (msgnum = 0; msgnum < msgp; msgnum++)
+       if (!m_convert (mp, msgs[msgnum]))
+           done (1);
+    seq_setprev (mp);  /* set the previous-sequence */
+
+    smsgs = (struct smsg *)
+       calloc ((size_t) (MAXFOLDER + 2), sizeof(*smsgs));
+    if (smsgs == NULL)
+       adios (NULL, "unable to allocate burst storage");
+
+    hi = mp->hghmsg + 1;
+
+    /* burst all the SELECTED messages */
+    for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
+       if (is_selected (mp, msgnum)) {
+           if ((numburst = find_delim (msgnum, smsgs)) >= 1) {
+               if (verbosw)
+                   printf ("%d message%s exploded from digest %d\n",
+                           numburst, numburst > 1 ? "s" : "", msgnum);
+               burst (&mp, msgnum, smsgs, numburst, inplace, verbosw);
+           } else {
+               if (numburst == 0)
+                   if (!quietsw)
+                       admonish (NULL, "message %d not in digest format", msgnum);
+               else
+                   adios (NULL, "burst() botch -- you lose big");
+           }
+       }
+    }
+
+    free ((char *) smsgs);
+    context_replace (pfolder, folder); /* update current folder */
+
+    /*
+     * If -inplace is given, then the first message burst becomes
+     * the current message (which will now show a table of contents).
+     * Otherwise, the first message extracted from the first digest
+     * becomes the current message.
+     */
+    if (inplace) {
+       if (mp->lowsel != mp->curmsg)
+           seq_setcur (mp, mp->lowsel);
+    } else {
+       if (hi <= mp->hghmsg)
+           seq_setcur (mp, hi);
+    }
+
+    seq_save (mp);     /* synchronize message sequences */
+    context_save ();   /* save the context file         */
+    folder_free (mp);  /* free folder/message structure */
+    done (0);
+}
+
+
+/*
+ * Scan the message and find the beginning and
+ * end of all the messages in the digest.
+ */
+
+static int
+find_delim (int msgnum, struct smsg *smsgs)
+{
+    int ld3, wasdlm, msgp;
+    long pos;
+    char c, *msgnam;
+    int cc;
+    char buffer[BUFSIZ];
+    FILE *in;
+
+    ld3 = strlen (delim3);
+
+    if ((in = fopen (msgnam = m_name (msgnum), "r")) == NULL)
+       adios (msgnam, "unable to read message");
+
+    for (msgp = 0, pos = 0L; msgp <= MAXFOLDER;) {
+       while (fgets (buffer, sizeof(buffer), in) && buffer[0] == '\n')
+           pos += (long) strlen (buffer);
+       if (feof (in))
+           break;
+       fseek (in, pos, SEEK_SET);
+       smsgs[msgp].s_start = pos;
+
+       for (c = 0; fgets (buffer, sizeof(buffer), in); c = buffer[0]) {
+           if (strncmp (buffer, delim3, ld3) == 0
+                   && (msgp == 1 || c == '\n')
+                   && ((cc = peekc (in)) == '\n' || cc == EOF))
+               break;
+           else
+               pos += (long) strlen (buffer);
+       }
+
+       wasdlm = strncmp (buffer, delim3, ld3) == 0;
+       if (smsgs[msgp].s_start != pos)
+           smsgs[msgp++].s_stop = (c == '\n' && wasdlm) ? pos - 1 : pos;
+       if (feof (in)) {
+#if 0
+           if (wasdlm) {
+               smsgs[msgp - 1].s_stop -= ((long) strlen (buffer) + 1);
+               msgp++;         /* fake "End of XXX Digest" */
+           }
+#endif
+           break;
+       }
+       pos += (long) strlen (buffer);
+    }
+
+    fclose (in);
+    return (msgp - 1);         /* toss "End of XXX Digest" */
+}
+
+
+/*
+ * Burst out the messages in the digest into the folder
+ */
+
+static void
+burst (struct msgs **mpp, int msgnum, struct smsg *smsgs, int numburst,
+       int inplace, int verbosw)
+{
+    int i, j, mode;
+    char *msgnam;
+    char f1[BUFSIZ], f2[BUFSIZ], f3[BUFSIZ];
+    FILE *in, *out;
+    struct stat st;
+    struct msgs *mp;
+
+    if ((in = fopen (msgnam = m_name (msgnum), "r")) == NULL)
+       adios (msgnam, "unable to read message");
+
+    mode = fstat (fileno(in), &st) != NOTOK ? (st.st_mode & 0777) : m_gmprot();
+    mp = *mpp;
+
+    /*
+     * See if we have enough space in the folder
+     * structure for all the new messages.
+     */
+    if ((mp->hghmsg + numburst > mp->hghoff) &&
+       !(mp = folder_realloc (mp, mp->lowoff, mp->hghmsg + numburst)))
+       adios (NULL, "unable to allocate folder storage");
+    *mpp = mp;
+
+    j = mp->hghmsg;            /* old value */
+    mp->hghmsg += numburst;
+    mp->nummsg += numburst;
+
+    /*
+     * If this is not the highest SELECTED message, then
+     * increment mp->hghsel by numburst, since the highest
+     * SELECTED is about to be slid down by that amount.
+     */
+    if (msgnum < mp->hghsel)
+       mp->hghsel += numburst;
+
+    /*
+     * If -inplace is given, renumber the messages after the
+     * source message, to make room for each of the messages
+     * contained within the digest.
+     */
+    if (inplace) {
+       for (i = mp->hghmsg; j > msgnum; i--, j--) {
+           strncpy (f1, m_name (i), sizeof(f1));
+           strncpy (f2, m_name (j), sizeof(f2));
+           if (does_exist (mp, j)) {
+               if (verbosw)
+                   printf ("message %d becomes message %d\n", j, i);
+
+               if (rename (f2, f1) == NOTOK)
+                   admonish (f1, "unable to rename %s to", f2);
+               copy_msg_flags (mp, i, j);
+               clear_msg_flags (mp, j);
+               mp->msgflags |= SEQMOD;
+           }
+       }
+    }
+    
+    unset_selected (mp, msgnum);
+
+    /* new hghmsg is hghmsg + numburst */
+    i = inplace ? msgnum + numburst : mp->hghmsg;
+    for (j = numburst; j >= (inplace ? 0 : 1); i--, j--) {
+       strncpy (f1, m_name (i), sizeof(f1));
+       strncpy (f2, m_scratch ("", invo_name), sizeof(f2));
+       if (verbosw && i != msgnum)
+           printf ("message %d of digest %d becomes message %d\n", j, msgnum, i);
+
+       if ((out = fopen (f2, "w")) == NULL)
+           adios (f2, "unable to write message");
+       chmod (f2, mode);
+       fseek (in, smsgs[j].s_start, SEEK_SET);
+       cpybrst (in, out, msgnam, f2,
+               (int) (smsgs[j].s_stop - smsgs[j].s_start));
+       fclose (out);
+
+       if (i == msgnum) {
+           strncpy (f3, m_backup (f1), sizeof(f3));
+           if (rename (f1, f3) == NOTOK)
+               admonish (f3, "unable to rename %s to", f1);
+       }
+       if (rename (f2, f1) == NOTOK)
+           admonish (f1, "unable to rename %s to", f2);
+       copy_msg_flags (mp, i, msgnum);
+       mp->msgflags |= SEQMOD;
+    }
+
+    fclose (in);
+}
+
+
+#define        S1  0
+#define        S2  1
+#define        S3  2
+
+/*
+ * Copy a mesage which is being burst out of a digest.
+ * It will remove any "dashstuffing" in the message.
+ */
+
+static void
+cpybrst (FILE *in, FILE *out, char *ifile, char *ofile, int len)
+{
+    register int c, state;
+
+    for (state = S1; (c = fgetc (in)) != EOF && len > 0; len--) {
+       if (c == 0)
+           continue;
+       switch (state) {
+           case S1: 
+               switch (c) {
+                   case '-': 
+                       state = S3;
+                       break;
+
+                   default: 
+                       state = S2;
+                   case '\n': 
+                       fputc (c, out);
+                       break;
+               }
+               break;
+
+           case S2: 
+               switch (c) {
+                   case '\n': 
+                       state = S1;
+                   default: 
+                       fputc (c, out);
+                       break;
+               }
+               break;
+
+           case S3: 
+               switch (c) {
+                   case ' ': 
+                       state = S2;
+                       break;
+
+                   default: 
+                       state = (c == '\n') ? S1 : S2;
+                       fputc ('-', out);
+                       fputc (c, out);
+                       break;
+               }
+               break;
+       }
+    }
+
+    if (ferror (in) && !feof (in))
+       adios (ifile, "error reading");
+    if (ferror (out))
+       adios (ofile, "error writing");
+}
diff --git a/uip/comp.c b/uip/comp.c
new file mode 100644 (file)
index 0000000..e40c968
--- /dev/null
@@ -0,0 +1,306 @@
+
+/*
+ * comp.c -- compose a message
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+
+static struct swit switches[] = {
+#define        DFOLDSW                0
+    { "draftfolder +folder", 0 },
+#define        DMSGSW                 1
+    { "draftmessage msg", 0 },
+#define        NDFLDSW                2
+    { "nodraftfolder", 0 },
+#define        EDITRSW                3
+    { "editor editor", 0 },
+#define        NEDITSW                4
+    { "noedit", 0 },
+#define        FILESW                 5
+    { "file file", 0 },
+#define        FORMSW                 6
+    { "form formfile", 0 },
+#define        USESW                  7
+    { "use", 0 },
+#define        NUSESW                 8
+    { "nouse", 0 },
+#define        WHATSW                 9
+    { "whatnowproc program", 0 },
+#define        NWHATSW               10
+    { "nowhatnowproc", 0 },
+#define VERSIONSW             11
+    { "version", 0 },
+#define        HELPSW                12
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+static struct swit aqrunl[] = {
+#define        NOSW          0
+    { "quit", 0 },
+#define        YESW          1
+    { "replace", 0 },
+#define        USELSW        2
+    { "use", 0 },
+#define        LISTDSW       3
+    { "list", 0 },
+#define        REFILSW       4
+    { "refile +folder", 0 },
+#define NEWSW         5
+    { "new", 0 },
+    { NULL, 0 }
+};
+
+static struct swit aqrul[] = {
+    { "quit", 0 },
+    { "replace", 0 },
+    { "use", 0 },
+    { "list", 0 },
+    { "refile", 0 },
+    { NULL, 0 }
+};
+
+
+int
+main (int argc, char **argv)
+{
+    int use = NOUSE, nedit = 0, nwhat = 0;
+    int i, in, isdf = 0, out;
+    char *cp, *cwd, *maildir, *dfolder = NULL;
+    char *ed = NULL, *file = NULL, *form = NULL;
+    char *folder = NULL, *msg = NULL, buf[BUFSIZ];
+    char drft[BUFSIZ], **argp, **arguments;
+    struct msgs *mp = NULL;
+    struct stat st;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown", cp);
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf), "%s [+folder] [msg] [switches]",
+                       invo_name);
+                   print_help (buf, switches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case EDITRSW: 
+                   if (!(ed = *argp++) || *ed == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   nedit = 0;
+                   continue;
+               case NEDITSW: 
+                   nedit++;
+                   continue;
+
+               case WHATSW: 
+                   if (!(whatnowproc = *argp++) || *whatnowproc == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   nwhat = 0;
+                   continue;
+               case NWHATSW: 
+                   nwhat++;
+                   continue;
+
+               case FORMSW: 
+                   if (!(form = *argp++) || *form == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+
+               case USESW: 
+                   use++;
+                   continue;
+               case NUSESW: 
+                   use = NOUSE;
+                   continue;
+
+               case FILESW:    /* compatibility */
+                   if (file)
+                       adios (NULL, "only one file at a time!");
+                   if (!(file = *argp++) || *file == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   isdf = NOTOK;
+                   continue;
+
+               case DFOLDSW: 
+                   if (dfolder)
+                       adios (NULL, "only one draft folder at a time!");
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   dfolder = path (*cp == '+' || *cp == '@' ? cp + 1 : cp,
+                           *cp != '@' ? TFOLDER : TSUBCWF);
+                   continue;
+               case DMSGSW: 
+                   if (file)
+                       adios (NULL, "only one draft message at a time!");
+                   if (!(file = *argp++) || *file == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+               case NDFLDSW: 
+                   dfolder = NULL;
+                   isdf = NOTOK;
+                   continue;
+           }
+       }
+       if (*cp == '+' || *cp == '@') {
+           if (folder)
+               adios (NULL, "only one folder at a time!");
+           else
+               folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       } else {
+           if (msg)
+               adios (NULL, "only one message at a time!");
+           else
+               msg = cp;
+       }
+    }
+
+    cwd = getcpy (pwd ());
+
+    if (!context_find ("path"))
+       free (path ("./", TFOLDER));
+
+    /*
+     * Check if we are using a draft folder
+     * and have specified a message in it.
+     */
+    if ((dfolder || context_find ("Draft-Folder")) && !folder && msg && !file) {
+       file = msg;
+       msg = NULL;
+    }
+    if (form && (folder || msg))
+           adios (NULL, "can't mix forms and folders/msgs");
+
+    if (folder || msg) {
+       /*
+        * Use a message as the "form" for the new message.
+        */
+       if (!msg)
+           msg = "cur";
+       if (!folder)
+           folder = getfolder (1);
+       maildir = m_maildir (folder);
+
+       if (chdir (maildir) == NOTOK)
+           adios (maildir, "unable to change directory to");
+
+       /* read folder and create message structure */
+       if (!(mp = folder_read (folder)))
+           adios (NULL, "unable to read folder %s", folder);
+
+       /* check for empty folder */
+       if (mp->nummsg == 0)
+           adios (NULL, "no messages in %s", folder);
+
+       /* parse the message range/sequence/name and set SELECTED */
+       if (!m_convert (mp, msg))
+           done (1);
+       seq_setprev (mp);       /* set the previous-sequence */
+
+       if (mp->numsel > 1)
+           adios (NULL, "only one message at a time!");
+
+       if ((in = open (form = getcpy (m_name (mp->lowsel)), O_RDONLY)) == NOTOK)
+           adios (form, "unable to open message");
+    } else {
+       /*
+        * Open a component or forms file
+        */
+       if (form) {
+           if ((in = open (etcpath (form), O_RDONLY)) == NOTOK)
+               adios (form, "unable to open form file");
+       } else {
+           if ((in = open (etcpath (components), O_RDONLY)) == NOTOK)
+               adios (components, "unable to open default components file");
+           form = components;
+       }
+    }
+
+try_it_again:
+    strncpy (drft, m_draft (dfolder, file, use, &isdf), sizeof(drft));
+
+    /*
+     * Check if we have an existing draft
+     */
+    if ((out = open (drft, O_RDONLY)) != NOTOK) {
+       i = fdcompare (in, out);
+       close (out);
+
+       /*
+        * If we have given -use flag, or if the
+        * draft is just the same as the components
+        * file, then no need to ask any questions.
+        */
+       if (use || i)
+           goto edit_it;
+
+       if (stat (drft, &st) == NOTOK)
+           adios (drft, "unable to stat");
+       printf ("Draft \"%s\" exists (%ld bytes).", drft, (long) st.st_size);
+       for (i = LISTDSW; i != YESW;) {
+           if (!(argp = getans ("\nDisposition? ", isdf ? aqrunl : aqrul)))
+               done (1);
+           switch (i = smatch (*argp, isdf ? aqrunl : aqrul)) {
+               case NOSW: 
+                   done (0);
+               case NEWSW: 
+                   file = NULL;
+                   use = NOUSE;
+                   goto try_it_again;
+               case YESW: 
+                   break;
+               case USELSW:
+                   use++;
+                   goto edit_it;
+               case LISTDSW: 
+                   showfile (++argp, drft);
+                   break;
+               case REFILSW: 
+                   if (refile (++argp, drft) == 0)
+                       i = YESW;
+                   break;
+               default: 
+                   advise (NULL, "say what?");
+                   break;
+           }
+       }
+    } else {
+       if (use)
+           adios (drft, "unable to open");
+    }
+
+    if ((out = creat (drft, m_gmprot ())) == NOTOK)
+       adios (drft, "unable to create");
+    cpydata (in, out, form, drft);
+    close (in);
+    close (out);
+
+edit_it:
+    context_save ();   /* save the context file */
+
+    if (nwhat)
+       done (0);
+    what_now (ed, nedit, use, drft, NULL, 0, NULLMP, NULL, 0, cwd);
+    done (1);
+}
diff --git a/uip/conflict.c b/uip/conflict.c
new file mode 100644 (file)
index 0000000..29a12e7
--- /dev/null
@@ -0,0 +1,545 @@
+
+/*
+ * conflict.c -- check for conflicts in mail system
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+#include <h/aliasbr.h>
+#include <zotnet/mts/mts.h>
+#include <grp.h>
+#include <pwd.h>
+
+/*
+ * maximum number of directories that can
+ * be specified using -search switch.
+ */
+#define        NDIRS   100
+
+/*
+ * Add space for group names, 100 at a time
+ */
+#define        NGRPS   100
+
+static struct swit switches[] = {
+#define        MAILSW         0
+    { "mail name", 0 },
+#define        SERCHSW        1
+    { "search directory", 0 },
+#define VERSIONSW      2
+    { "version", 0 },
+#define        HELPSW         3
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+static char *mail = NULL;
+static char *dirs[NDIRS];
+static FILE *out = NULL;
+
+extern struct aka  *akahead;
+extern struct home *homehead;
+
+/*
+ * prototypes
+ */
+void alias_files (int, char **);
+void pwd_names (void);
+void grp_names (void);
+void grp_members (void);
+void grp_ids (void);
+void maildrops (void);
+void mdrop(char *);
+int check (char *);
+void setup (void);
+
+
+int
+main (int argc, char **argv)
+{
+    int        akp = 0, dp = 0;
+    char *cp, **argp, **arguments;
+    char buf[BUFSIZ], *akv[50];
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* foil search of user profile/context */
+    if (context_foil (NULL) == -1)
+       done (1);
+
+    mts_init (invo_name);
+    arguments = getarguments (invo_name, argc, argv, 0);
+    argp = arguments;
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown", cp);
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf), "%s [switches] [aliasfiles ...]",
+                       invo_name);
+                   print_help (buf, switches, 0);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case MAILSW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   if (mail)
+                       adios (NULL, "mail to one address only");
+                   else
+                       mail = cp;
+                   continue;
+
+               case SERCHSW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   if (dp >= NDIRS)
+                       adios (NULL, "more than %d directories", NDIRS);
+                   dirs[dp++] = cp;
+                   continue;
+           }
+       }
+       akv[akp++] = cp;
+    }
+
+    if (akp == 0)
+       akv[akp++] = AliasFile;
+    if (!homehead)
+       init_pw ();
+    if (!mail)
+       out = stdout;
+    dirs[dp] = NULL;
+
+    alias_files (akp, akv);
+    pwd_names ();
+    grp_names ();
+    grp_members ();
+    grp_ids ();
+#ifdef UCI
+    ldr_names ();
+    ldr_ship ();
+#endif /* UCI */
+    maildrops ();
+
+    done (0);
+}
+
+
+void
+alias_files (int akp, char **akv)
+{
+    register int i, err;
+
+    for (i = 0; i < akp; i++)
+       if ((err = alias (akv[i])) != AK_OK) {
+           setup ();
+           fprintf (out, "aliasing error in %s - %s\n", akv[i], akerror (err));
+       }
+       else
+           if (out && !mail)
+               fprintf (out, "alias file %s is ok\n", akv[i]);
+}
+
+
+void
+pwd_names (void)
+{
+    int hit = 0;
+    register struct home *hm, *lm;
+
+    for (hm = homehead; hm; hm = hm->h_next)
+       for (lm = hm->h_next; lm; lm = lm->h_next)
+           if (strcmp (hm->h_name, lm->h_name) == 0) {
+               setup ();
+               fprintf (out, "duplicate user %s(uid=%d)\n",
+                       lm->h_name, (int) lm->h_uid);
+               hit++;
+           }
+
+    if (!hit && out && !mail)
+       fprintf (out, "no duplicate users\n");
+}
+
+
+void
+grp_names (void)
+{
+    int numgroups, maxgroups;
+    int i, hit = 0;
+    char **grps;
+    struct group *gr;
+
+    /* allocate space NGRPS at a time */
+    numgroups = 0;
+    maxgroups = NGRPS;
+    if (!(grps = (char **) malloc((size_t) (maxgroups * sizeof(*grps)))))
+       adios (NULL, "unable to allocate group name storage");
+
+    setgrent ();
+    while ((gr = getgrent ())) {
+       for (i = 0; i < numgroups; i++)
+           if (!strcmp (grps[i], gr->gr_name)) {
+               setup ();
+               fprintf (out, "duplicate group %s(gid=%d)\n",
+                       gr->gr_name, (int) gr->gr_gid);
+               hit++;
+               break;
+           }
+       if (i >= numgroups) {
+           if (numgroups >= maxgroups) {
+               maxgroups += NGRPS;
+               if (!(grps = (char **) realloc(grps,
+                       (size_t) (maxgroups * sizeof(*grps)))))
+                   adios (NULL, "unable to reallocate group name storage");
+           }
+           grps[numgroups++] = getcpy (gr->gr_name);
+       }
+    }
+    endgrent ();
+
+    for (i = 0; i < numgroups; i++)
+       free (grps[i]);
+    free (grps);
+
+    if (!hit && out && !mail)
+       fprintf (out, "no duplicate groups\n");
+}
+
+
+void
+grp_members (void)
+{
+    register int hit = 0;
+    register char **cp, **dp;
+    register struct group *gr;
+    register struct home  *hm;
+
+    setgrent ();
+    while ((gr = getgrent ())) {
+       for (cp = gr->gr_mem; *cp; cp++) {
+           for (hm = homehead; hm; hm = hm->h_next)
+               if (!strcmp (*cp, hm->h_name))
+                   break;
+           if (hm == NULL) {
+               setup ();
+               fprintf (out, "group %s(gid=%d) has unknown member %s\n",
+                       gr->gr_name, (int) gr->gr_gid, *cp);
+               hit++;
+           } else {
+               hm->h_ngrps++;
+           }
+
+           for (dp = cp + 1; *dp; dp++)
+               if (strcmp (*cp, *dp) == 0) {
+                   setup ();
+                   fprintf (out, "group %s(gid=%d) has duplicate member %s\n",
+                           gr->gr_name, (int) gr->gr_gid, *cp);
+                   hit++;
+               }
+       }
+    }
+    endgrent ();
+
+    for (hm = homehead; hm; hm = hm->h_next)
+       if (hm->h_ngrps > NGROUPS_MAX) {
+           setup ();
+           fprintf (out, "user %s is a member of %d groups (max %d)\n",
+                   hm->h_name, hm->h_ngrps, NGROUPS_MAX);
+           hit++;
+       }
+
+    if (!hit && out && !mail)
+       fprintf (out, "all group members accounted for\n");
+}
+
+
+void
+grp_ids (void)
+{              /* -DRAND not implemented at most places */
+    register int hit = 0;
+    register struct home *hm;
+
+    for (hm = homehead; hm; hm = hm->h_next)
+       if (getgrgid (hm->h_gid) == NULL) {
+           setup ();
+           fprintf (out, "user %s(uid=%d) has unknown group-id %d\n",
+                   hm->h_name, (int) hm->h_uid, (int) hm->h_gid);
+           hit++;
+       }
+
+    if (!hit && out && !mail)
+       fprintf (out, "all group-id users accounted for\n");
+}
+
+
+void
+maildrops (void) 
+{
+    register int i;
+
+    if (mmdfldir && *mmdfldir)
+       mdrop (mmdfldir);
+    if (uucpldir && *uucpldir)
+       mdrop (uucpldir);
+    for (i = 0; dirs[i]; i++)
+       mdrop (dirs[i]);
+}
+
+
+void
+mdrop(char *drop)
+{
+    register int hit = 0;
+    register struct dirent *dp;
+    register DIR *dd = opendir (drop);
+
+    if (!dd) {
+       setup ();
+       fprintf (out, "unable to open maildrop area %s\n", drop);
+       return;
+    }
+
+    while ((dp = readdir (dd)))
+       if (dp->d_name[0] != '.' && !check (dp->d_name)) {
+           setup ();
+           fprintf (out,
+                   "there is a maildrop for the unknown user %s in %s\n",
+                   dp->d_name, drop);
+           hit++;
+       }
+
+    closedir (dd);
+    if (!hit && out && !mail)
+       fprintf (out, "all maildrops accounted for in %s\n", drop);
+}
+
+
+int
+check (char *s)
+{
+    register struct home *hm;
+
+    for (hm = homehead; hm; hm = hm->h_next)
+       if (!strcmp (s, hm->h_name))
+           return 1;
+    return 0;
+}
+
+void
+setup (void)
+{
+    int fd, pd[2];
+
+    if (out)
+       return;
+
+    if (mail) {
+       if (pipe (pd) == NOTOK)
+           adios ("pipe", "unable to");
+
+       switch (fork ()) {
+           case NOTOK: 
+               adios ("fork", "unable to");
+
+           case OK: 
+               close (pd[1]);
+               if (pd[0] != 0) {
+                   dup2 (pd[0], 0);
+                   close (pd[0]);
+               }
+               if ((fd = open ("/dev/null", O_WRONLY)) != NOTOK)
+                   if (fd != 1) {
+                       dup2 (fd, 1);
+                       close (fd);
+                   }
+               execlp (mailproc, r1bindex (mailproc, '/'),
+                       mail, "-subject", invo_name, NULL);
+               adios (mailproc, "unable to exec ");
+
+           default: 
+               close (pd[0]);
+               out = fdopen (pd[1], "w");
+               fprintf (out, "%s: the following is suspicious\n\n",
+                       invo_name);
+       }
+    }
+}
+
+#ifdef UCI
+/*
+ * UCI specific stuff for conflict
+ */
+
+/* taken from <grpldr.h> */
+
+#define        GLDRS   "/admin/etc/GroupLeaders"
+
+struct grpldr {
+    char *gl_name;
+    char **gl_ldr;
+};
+
+int setglent (), endglent ();
+struct grpldr *getglent (), *getglnam ();
+
+
+/* taken from the getglent() routines */
+
+#define        MAXGLS  100
+
+static FILE *glp = NULL;
+static char line[BUFSIZ+1];
+static struct grpldr grpldr;
+static char *gl_ldr[MAXGLS + 1];
+
+
+setglent() {
+    if (glp == NULL)
+       glp = fopen (GLDRS, "r");
+    else
+       rewind (glp);
+
+    return (glp != NULL);
+}
+
+
+endglent() {
+    if (glp != NULL) {
+       fclose (glp);
+       glp = NULL;
+    }
+
+    return 1;
+}
+
+struct grpldr  *getglent () {
+    register char  *cp,
+                  **q;
+
+    if (glp == NULL && !setglent ())
+       return NULL;
+    if ((cp = fgets (line, BUFSIZ, glp)) == NULL)
+       return NULL;
+
+    grpldr.gl_name = cp;
+    grpldr.gl_ldr = q = gl_ldr;
+
+    while (*cp) {
+       while (*cp && !isspace (*cp))
+           cp++;
+       while (*cp && isspace (*cp))
+           *cp++ = '\0';
+       if (*cp == '\0')
+           break;
+       if (q < gl_ldr + MAXGLS)
+           *q++ = cp;
+       else
+           break;
+    }
+    *q = NULL;
+
+    return (&grpldr);
+}
+
+struct grpldr  *getglnam (name)
+char   *name;
+{
+    register struct grpldr  *gl = NULL;
+
+    setglent ();
+    while (gl = getglent ())
+       if (strcmp (name, gl->gl_name) == 0)
+           break;
+    endglent ();
+
+    return gl;
+}
+
+ldr_names () {
+    register int     gp,
+                    hit = 0;
+    char   *gldrs[NGRPS];
+    register struct grpldr  *gl;
+
+    gldrs[0] = NULL;
+    setglent ();
+    while (gl = getglent ()) {
+       if (getgrnam (gl->gl_name) == NULL) {
+           setup ();
+           fprintf (out, "unknown group %s in group leaders file\n",
+                   gl->gl_name);
+           hit++;
+       }
+       for (gp = 0; gldrs[gp]; gp++)
+           if (strcmp (gldrs[gp], gl->gl_name) == 0) {
+               setup ();
+               fprintf (out, "duplicate group %s in group leaders file\n",
+                       gl->gl_name);
+               hit++;
+               break;
+           }
+       if (gldrs[gp] == NULL)
+           if (gp < NGRPS) {
+               gldrs[gp++] = getcpy (gl->gl_name);
+               gldrs[gp] = NULL;
+           }
+           else {
+               setup ();
+               fprintf (out, "more than %d groups in group leaders file%s\n",
+                       " (time to recompile)", NGRPS - 1);
+               hit++;
+           }
+    }
+    endglent ();
+
+    for (gp = 0; gldrs[gp]; gp++)
+       free (gldrs[gp]);
+
+    if (!hit && out && !mail)
+       fprintf (out, "all groups in group leaders file accounted for\n");
+}
+
+
+ldr_ship () {
+    register int     hit = 0;
+    register char  **cp,
+                  **dp;
+    register struct grpldr  *gl;
+
+    setglent ();
+    while (gl = getglent ())
+       for (cp = gl->gl_ldr; *cp; cp++) {
+           if (!check (*cp)) {
+               setup ();
+               fprintf (out, "group %s has unknown leader %s\n",
+                       gl->gl_name, *cp);
+               hit++;
+           }
+
+           for (dp = cp + 1; *dp; dp++)
+               if (strcmp (*cp, *dp) == 0) {
+                   setup ();
+                   fprintf (out, "group %s had duplicate leader %s\n",
+                           gl->gl_name, *cp);
+                   hit++;
+               }
+       }
+    endglent ();
+
+    if (!hit && out && !mail)
+       fprintf (out, "all group leaders accounted for\n");
+}
+#endif /* UCI */
diff --git a/uip/dist.c b/uip/dist.c
new file mode 100644 (file)
index 0000000..1fb1741
--- /dev/null
@@ -0,0 +1,291 @@
+
+/*
+ * dist.c -- re-distribute a message
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+
+static struct swit switches[] = {
+#define        ANNOSW  0
+    { "annotate", 0 },
+#define        NANNOSW 1
+    { "noannotate", 0 },
+#define        DFOLDSW 2
+    { "draftfolder +folder", 0 },
+#define        DMSGSW  3
+    { "draftmessage msg", 0 },
+#define        NDFLDSW 4
+    { "nodraftfolder", 0 },
+#define        EDITRSW 5
+    { "editor editor", 0 },
+#define        NEDITSW 6
+    { "noedit", 0 },
+#define        FORMSW  7
+    { "form formfile", 0 },
+#define        INPLSW  8
+    { "inplace", 0 },
+#define        NINPLSW 9
+    { "noinplace", 0 },
+#define        WHATSW  10
+    { "whatnowproc program", 0 },
+#define        NWHATSW 11
+    { "nowhatnowproc", 0 },
+#define VERSIONSW 12
+    { "version", 0 },
+#define        HELPSW  13
+    { "help", 4 },
+#define        FILESW  14
+    { "file file", -4 },       /* interface from msh */
+    { NULL, 0 }
+};
+
+static struct swit aqrnl[] = {
+#define        NOSW    0
+    { "quit", 0 },
+#define        YESW    1
+    { "replace", 0 },
+#define        LISTDSW 2
+    { "list", 0 },
+#define        REFILSW 3
+    { "refile +folder", 0 },
+#define NEWSW  4
+    { "new", 0 },
+    { NULL, 0 }
+};
+
+
+static struct swit aqrl[] = {
+    { "quit", 0 },
+    { "replace", 0 },
+    { "list", 0 },
+    { "refile +folder", 0 },
+    { NULL, 0 }
+};
+
+
+int
+main (int argc, char **argv)
+{
+    int anot = 0, inplace = 1, nedit = 0;
+    int nwhat = 0, i, in, isdf = 0, out;
+    char *cp, *cwd, *maildir, *msgnam, *dfolder = NULL;
+    char *dmsg = NULL, *ed = NULL, *file = NULL, *folder = NULL;
+    char *form = NULL, *msg = NULL, buf[BUFSIZ], drft[BUFSIZ];
+    char **argp, **arguments;
+    struct msgs *mp = NULL;
+    struct stat st;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown", cp);
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf), "%s [+folder] [msg] [switches]",
+                       invo_name);
+                   print_help (buf, switches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case ANNOSW: 
+                   anot++;
+                   continue;
+               case NANNOSW: 
+                   anot = 0;
+                   continue;
+
+               case EDITRSW: 
+                   if (!(ed = *argp++) || *ed == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   nedit = 0;
+                   continue;
+               case NEDITSW:
+                   nedit++;
+                   continue;
+                   
+               case WHATSW: 
+                   if (!(whatnowproc = *argp++) || *whatnowproc == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   nwhat = 0;
+                   continue;
+               case NWHATSW: 
+                   nwhat++;
+                   continue;
+
+               case FILESW: 
+                   if (file)
+                       adios (NULL, "only one file at a time!");
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   file = path (cp, TFILE);
+                   continue;
+               case FORMSW: 
+                   if (!(form = *argp++) || *form == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+
+               case INPLSW: 
+                   inplace++;
+                   continue;
+               case NINPLSW: 
+                   inplace = 0;
+                   continue;
+
+               case DFOLDSW: 
+                   if (dfolder)
+                       adios (NULL, "only one draft folder at a time!");
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   dfolder = path (*cp == '+' || *cp == '@' ? cp + 1 : cp,
+                           *cp != '@' ? TFOLDER : TSUBCWF);
+                   continue;
+               case DMSGSW: 
+                   if (dmsg)
+                       adios (NULL, "only one draft message at a time!");
+                   if (!(dmsg = *argp++) || *dmsg == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+               case NDFLDSW: 
+                   dfolder = NULL;
+                   isdf = NOTOK;
+                   continue;
+           }
+       }
+       if (*cp == '+' || *cp == '@') {
+           if (folder)
+               adios (NULL, "only one folder at a time!");
+           else
+               folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       } else {
+           if (msg)
+               adios (NULL, "only one message at a time!");
+           else
+               msg = cp;
+       }
+    }
+
+    cwd = getcpy (pwd ());
+
+    if (!context_find ("path"))
+       free (path ("./", TFOLDER));
+    if (file && (msg || folder))
+       adios (NULL, "can't mix files and folders/msgs");
+
+    if (form) {
+       if ((in = open (etcpath (form), O_RDONLY)) == NOTOK)
+           adios (form, "unable to open form file");
+    } else {
+       if ((in = open (etcpath (distcomps), O_RDONLY)) == NOTOK)
+           adios (distcomps, "unable to open default components file");
+       form = distcomps;
+    }
+
+try_it_again:
+    strncpy (drft, m_draft (dfolder, dmsg, NOUSE, &isdf), sizeof(drft));
+
+    /* Check if draft already exists */
+    if (stat (drft, &st) != NOTOK) {
+       printf ("Draft \"%s\" exists (%ld bytes).", drft, (long) st.st_size);
+       for (i = LISTDSW; i != YESW;) {
+           if (!(argp = getans ("\nDisposition? ", isdf ? aqrnl : aqrl)))
+               done (1);
+           switch (i = smatch (*argp, isdf ? aqrnl : aqrl)) {
+               case NOSW: 
+                   done (0);
+               case NEWSW: 
+                   dmsg = NULL;
+                   goto try_it_again;
+               case YESW: 
+                   break;
+               case LISTDSW: 
+                   showfile (++argp, drft);
+                   break;
+               case REFILSW: 
+                   if (refile (++argp, drft) == 0)
+                       i = YESW;
+                   break;
+               default: 
+                   advise (NULL, "say what?");
+                   break;
+           }
+       }
+    }
+    if ((out = creat (drft, m_gmprot ())) == NOTOK)
+       adios (drft, "unable to create");
+
+    cpydata (in, out, form, drft);
+    close (in);
+    close (out);
+
+    if (file) {
+       /*
+        * Dist a file
+        */
+       anot = 0;       /* don't want to annotate a file */
+    } else {
+       /*
+        * Dist a message
+        */
+       if (!msg)
+           msg = "cur";
+       if (!folder)
+           folder = getfolder (1);
+       maildir = m_maildir (folder);
+
+       if (chdir (maildir) == NOTOK)
+           adios (maildir, "unable to change directory to");
+
+       /* read folder and create message structure */
+       if (!(mp = folder_read (folder)))
+           adios (NULL, "unable to read folder %s", folder);
+
+       /* check for empty folder */
+       if (mp->nummsg == 0)
+           adios (NULL, "no messages in %s", folder);
+
+       /* parse the message range/sequence/name and set SELECTED */
+       if (!m_convert (mp, msg))
+           done (1);
+       seq_setprev (mp);       /* set the previous-sequence */
+
+       if (mp->numsel > 1)
+           adios (NULL, "only one message at a time!");
+    }
+
+    msgnam = file ? file : getcpy (m_name (mp->lowsel));
+    if ((in = open (msgnam, O_RDONLY)) == NOTOK)
+       adios (msgnam, "unable to open message");
+
+    if (!file) {
+       context_replace (pfolder, folder);/* update current folder  */
+       seq_setcur (mp, mp->lowsel);      /* update current message */
+       seq_save (mp);                    /* synchronize sequences  */
+       context_save ();                  /* save the context file  */
+    }
+
+    if (nwhat)
+       done (0);
+    what_now (ed, nedit, NOUSE, drft, msgnam, 1, mp,
+       anot ? "Resent" : NULL, inplace, cwd);
+    done (1);
+}
diff --git a/uip/distsbr.c b/uip/distsbr.c
new file mode 100644 (file)
index 0000000..be2716b
--- /dev/null
@@ -0,0 +1,194 @@
+
+/*
+ * distsbr.c -- routines to do additional "dist-style" processing
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+
+static int  hdrfd = NOTOK;
+static int  txtfd = NOTOK;
+
+#define        BADHDR  "please re-edit %s to remove the ``%s'' header!"
+#define        BADTXT  "please re-edit %s to consist of headers only!"
+#define        BADMSG  "please re-edit %s to include a ``Resent-To:''!"
+#define        BADRFT  "please re-edit %s and fix that header!"
+
+/*
+ * static prototypes
+ */
+static void ready_msg(char *);
+
+int
+distout (char *drft, char *msgnam, char *backup)
+{
+    int state;
+    register char *dp, *resent;
+    char name[NAMESZ], buffer[BUFSIZ];
+    register FILE *ifp, *ofp;
+
+    if (rename (drft, strcpy (backup, m_backup (drft))) == NOTOK)
+       adios (backup, "unable to rename %s to",drft);
+    if ((ifp = fopen (backup, "r")) == NULL)
+       adios (backup, "unable to read");
+
+    if ((ofp = fopen (drft, "w")) == NULL)
+       adios (drft, "unable to create temporary file");
+    chmod (drft, m_gmprot ());
+
+    ready_msg (msgnam);
+    lseek (hdrfd, (off_t) 0, SEEK_SET); /* msgnam not accurate */
+    cpydata (hdrfd, fileno (ofp), msgnam, drft);
+
+    for (state = FLD, resent = NULL;;)
+       switch (state =
+               m_getfld (state, name, buffer, sizeof buffer, ifp)) {
+           case FLD: 
+           case FLDPLUS: 
+           case FLDEOF: 
+               if (uprf (name, "distribute-"))
+                   snprintf (name, sizeof(name), "%s%s", "Resent", &name[10]);
+               if (uprf (name, "distribution-"))
+                   snprintf (name, sizeof(name), "%s%s", "Resent", &name[12]);
+               if (!uprf (name, "resent")) {
+                   advise (NULL, BADHDR, "draft", name);
+                   goto leave_bad;
+               }
+               if (state == FLD)
+                   resent = add (":", add (name, resent));
+               resent = add (buffer, resent);
+               fprintf (ofp, "%s: %s", name, buffer);
+               while (state == FLDPLUS) {
+                   state = m_getfld (state, name,
+                           buffer, sizeof buffer, ifp);
+                   resent = add (buffer, resent);
+                   fputs (buffer, ofp);
+               }
+               if (state == FLDEOF)
+                   goto process;
+               break;
+
+           case BODY: 
+           case BODYEOF: 
+               for (dp = buffer; *dp; dp++)
+                   if (!isspace (*dp)) {
+                       advise (NULL, BADTXT, "draft");
+                       goto leave_bad;
+                   }
+
+           case FILEEOF: 
+               goto process;
+
+           case LENERR: 
+           case FMTERR: 
+               advise (NULL, BADRFT, "draft");
+       leave_bad: ;
+               fclose (ifp);
+               fclose (ofp);
+               unlink (drft);
+               if (rename (backup, drft) == NOTOK)
+                   adios (drft, "unable to rename %s to", backup);
+               return NOTOK;
+
+           default: 
+               adios (NULL, "getfld() returned %d", state);
+       }
+process: ;
+    fclose (ifp);
+    fflush (ofp);
+
+    if (!resent) {
+       advise (NULL, BADMSG, "draft");
+       fclose (ofp);
+       unlink (drft);
+       if (rename (backup, drft) == NOTOK)
+           adios (drft, "unable to rename %s to", backup);
+       return NOTOK;
+    }
+    free (resent);
+
+    if (txtfd != NOTOK) {
+       lseek (txtfd, (off_t) 0, SEEK_SET); /* msgnam not accurate */
+       cpydata (txtfd, fileno (ofp), msgnam, drft);
+    }
+
+    fclose (ofp);
+
+    return OK;
+}
+
+
+static void
+ready_msg (char *msgnam)
+{
+    int state, out;
+    char name[NAMESZ], buffer[BUFSIZ], tmpfil[BUFSIZ];
+    register FILE *ifp, *ofp;
+
+    if (hdrfd != NOTOK)
+       close (hdrfd), hdrfd = NOTOK;
+    if (txtfd != NOTOK)
+       close (txtfd), txtfd = NOTOK;
+
+    if ((ifp = fopen (msgnam, "r")) == NULL)
+       adios (msgnam, "unable to open message");
+
+    strncpy (tmpfil, m_tmpfil ("dist"), sizeof(tmpfil));
+    if ((hdrfd = open (tmpfil, O_RDWR | O_CREAT | O_TRUNC, 0600)) == NOTOK)
+       adios (tmpfil, "unable to re-open temporary file");
+    if ((out = dup (hdrfd)) == NOTOK
+           || (ofp = fdopen (out, "w")) == NULL)
+       adios (NULL, "no file descriptors -- you lose big");
+    unlink (tmpfil);
+
+    for (state = FLD;;)
+       switch (state =
+               m_getfld (state, name, buffer, sizeof buffer, ifp)) {
+           case FLD: 
+           case FLDPLUS: 
+           case FLDEOF: 
+               if (uprf (name, "resent"))
+                   fprintf (ofp, "Prev-");
+               fprintf (ofp, "%s: %s", name, buffer);
+               while (state == FLDPLUS) {
+                   state = m_getfld (state, name,
+                           buffer, sizeof buffer, ifp);
+                   fputs (buffer, ofp);
+               }
+               if (state == FLDEOF)
+                   goto process;
+               break;
+
+           case BODY: 
+           case BODYEOF: 
+               fclose (ofp);
+
+               strncpy (tmpfil, m_tmpfil ("dist"), sizeof(tmpfil));
+               if ((txtfd = open (tmpfil, O_RDWR | O_CREAT | O_TRUNC, 0600)) == NOTOK)
+                   adios (tmpfil, "unable to open temporary file");
+               if ((out = dup (txtfd)) == NOTOK
+                       || (ofp = fdopen (out, "w")) == NULL)
+                   adios (NULL, "no file descriptors -- you lose big");
+               unlink (tmpfil);
+               fprintf (ofp, "\n%s", buffer);
+               while (state == BODY) {
+                   state = m_getfld (state, name,
+                           buffer, sizeof buffer, ifp);
+                   fputs (buffer, ofp);
+               }
+           case FILEEOF: 
+               goto process;
+
+           case LENERR: 
+           case FMTERR: 
+               adios (NULL, "format error in message %s", msgnam);
+
+           default: 
+               adios (NULL, "getfld() returned %d", state);
+       }
+process: ;
+    fclose (ifp);
+    fclose (ofp);
+}
diff --git a/uip/dp.c b/uip/dp.c
new file mode 100644 (file)
index 0000000..0041198
--- /dev/null
+++ b/uip/dp.c
@@ -0,0 +1,153 @@
+
+/*
+ * dp.c -- parse dates 822-style
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/fmt_scan.h>
+#include <zotnet/tws/tws.h>
+
+#define        NDATES 100
+
+#define        WIDTH 78
+#define        WBUFSIZ BUFSIZ
+
+#define        FORMAT "%<(nodate{text})error: %{text}%|%(putstr(pretty{text}))%>"
+
+static struct swit switches[] = {
+#define        FORMSW                0
+    { "form formatfile", 0 },
+#define        FMTSW                 1
+    { "format string", 5 },
+#define        WIDTHSW               2
+    { "width columns", 0 },
+#define VERSIONSW             3
+    { "version", 0 },
+#define        HELPSW                4
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+static struct format *fmt;
+
+static int dat[5];
+
+/*
+ * prototypes
+ */
+int sc_width (void);  /* from termsbr.c */
+
+/*
+ * static prototypes
+ */
+static int process (char *, int);
+
+
+int
+main (int argc, char **argv)
+{
+    int datep = 0, width = 0, status = 0;
+    char *cp, *form = NULL, *format = NULL, *nfs;
+    char buf[BUFSIZ], **argp, **arguments;
+    char *dates[NDATES];
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown", cp);
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf), "%s [switches] dates ...",
+                       invo_name);
+                   print_help (buf, switches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case FORMSW: 
+                   if (!(form = *argp++) || *form == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   format = NULL;
+                   continue;
+               case FMTSW: 
+                   if (!(format = *argp++) || *format == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   form = NULL;
+                   continue;
+
+               case WIDTHSW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   width = atoi (cp);
+                   continue;
+           }
+       }
+       if (datep > NDATES)
+           adios (NULL, "more than %d dates", NDATES);
+       else
+           dates[datep++] = cp;
+    }
+    dates[datep] = NULL;
+
+    if (datep == 0)
+       adios (NULL, "usage: %s [switches] dates ...", invo_name);
+
+    /* get new format string */
+    nfs = new_fs (form, format, FORMAT);
+
+    if (width == 0) {
+       if ((width = sc_width ()) < WIDTH / 2)
+           width = WIDTH / 2;
+       width -= 2;
+    }
+    if (width > WBUFSIZ)
+       width = WBUFSIZ;
+    fmt_compile (nfs, &fmt);
+
+    dat[0] = 0;
+    dat[1] = 0;
+    dat[2] = 0;
+    dat[3] = width;
+    dat[4] = 0;
+
+    for (datep = 0; dates[datep]; datep++)
+       status += process (dates[datep], width);
+
+    context_save ();   /* save the context file */
+    done (status);
+}
+
+
+static int
+process (char *date, int length)
+{
+    int status = 0;
+    char buffer[WBUFSIZ + 1];
+    register struct comp *cptr;
+
+    FINDCOMP (cptr, "text");
+    if (cptr)
+       cptr->c_text = date;
+    fmt_scan (fmt, buffer, length, dat);
+    fputs (buffer, stdout);
+
+    return status;
+}
diff --git a/uip/dropsbr.c b/uip/dropsbr.c
new file mode 100644 (file)
index 0000000..857263a
--- /dev/null
@@ -0,0 +1,720 @@
+
+/*
+ * dropsbr.c -- create/read/manipulate mail drops
+ *
+ * $Id$
+ */
+
+#include <h/nmh.h>
+
+#ifndef        MMDFONLY
+# include <h/mh.h>
+# include <h/dropsbr.h>
+# include <zotnet/mts/mts.h>
+# include <zotnet/tws/tws.h>
+#else
+# include "dropsbr.h"
+# include "strings.h"
+# include "mmdfonly.h"
+#endif
+
+#ifdef HAVE_ERRNO_H
+# include <errno.h>
+#endif
+
+#ifdef NTOHLSWAP
+# include <netinet/in.h>
+#else
+# undef ntohl
+# define ntohl(n) (n)
+#endif
+
+#include <fcntl.h>
+
+extern int errno;
+
+/*
+ * static prototypes
+ */
+static int mbx_chk_mbox (int);
+static int mbx_chk_mmdf (int);
+static int map_open (char *, int *, int);
+
+
+/*
+ * Main entry point to open/create and lock
+ * a file or maildrop.
+ */
+
+int
+mbx_open (char *file, int mbx_style, uid_t uid, gid_t gid, mode_t mode)
+{
+    int j, count, fd;
+    struct stat st;
+
+    j = 0;
+
+    /* attempt to open and lock file */
+    for (count = 4; count > 0; count--) {
+       if ((fd = lkopen (file, O_RDWR | O_CREAT | O_NONBLOCK, mode)) == NOTOK) {
+           switch (errno) {
+#if defined(FCNTL_LOCKING) || defined(LOCKF_LOCKING)
+               case EACCES:
+               case EAGAIN:
+#endif
+
+#ifdef FLOCK_LOCKING
+               case EWOULDBLOCK:
+#endif
+               case ETXTBSY: 
+                   j = errno;
+                   sleep (5);
+                   break;
+
+               default: 
+                   /* just return error */
+                   return NOTOK;
+           }
+       }
+
+       /* good file descriptor */
+       break;
+    }
+
+    errno = j;
+
+    /*
+     * Return if we still failed after 4 attempts,
+     * or we just want to skip the sanity checks.
+     */
+    if (fd == NOTOK || mbx_style == OTHER_FORMAT)
+       return fd;
+
+    /*
+     * Do sanity checks on maildrop.
+     */
+    if (fstat (fd, &st) == NOTOK) {
+       /*
+        * The stat failed.  So we make sure file
+        * has right ownership/modes
+        */
+       chown (file, uid, gid);
+       chmod (file, mode);
+    } else if (st.st_size > (off_t) 0) {
+       int status;
+
+       /* check the maildrop */
+       switch (mbx_style) {
+           case MMDF_FORMAT: 
+           default: 
+               status = mbx_chk_mmdf (fd);
+               break;
+
+           case MBOX_FORMAT: 
+               status = mbx_chk_mbox (fd);
+               break;
+       }
+
+       /* if error, attempt to close it */
+       if (status == NOTOK) {
+           close (fd);
+           return NOTOK;
+       }
+    }
+
+    return fd;
+}
+
+
+/*
+ * Check/prepare MBOX style maildrop for appending.
+ */
+
+static int
+mbx_chk_mbox (int fd)
+{
+    /* just seek to the end */
+    if (lseek (fd, (off_t) 0, SEEK_END) == (off_t) NOTOK)
+       return NOTOK;
+
+    return OK;
+}
+
+
+/*
+ * Check/prepare MMDF style maildrop for appending.
+ */
+
+static int
+mbx_chk_mmdf (int fd)
+{
+    size_t count;
+    char ldelim[BUFSIZ];
+
+    count = strlen (mmdlm2);
+
+    /* casting -count to off_t, seem to break FreeBSD 2.2.6 */
+    if (lseek (fd, (long) (-count), SEEK_END) == (off_t) NOTOK)
+       return NOTOK;
+    if (read (fd, ldelim, count) != count)
+       return NOTOK;
+
+    ldelim[count] = 0;
+
+    if (strcmp (ldelim, mmdlm2)
+           && write (fd, "\n", 1) != 1
+           && write (fd, mmdlm2, count) != count)
+       return NOTOK;
+
+    return OK;
+}
+
+
+int
+mbx_read (FILE *fp, long pos, struct drop **drops, int noisy)
+{
+    register int len, size;
+    register long ld1, ld2;
+    register char *bp;
+    char buffer[BUFSIZ];
+    register struct drop *cp, *dp, *ep, *pp;
+
+    pp = (struct drop *) calloc ((size_t) (len = MAXFOLDER), sizeof(*dp));
+    if (pp == NULL) {
+       if (noisy)
+           admonish (NULL, "unable to allocate drop storage");
+       return NOTOK;
+    }
+
+    ld1 = (long) strlen (mmdlm1);
+    ld2 = (long) strlen (mmdlm2);
+
+    fseek (fp, pos, SEEK_SET);
+    for (ep = (dp = pp) + len - 1; fgets (buffer, sizeof(buffer), fp);) {
+       size = 0;
+       if (strcmp (buffer, mmdlm1) == 0)
+           pos += ld1, dp->d_start = (long) pos;
+       else {
+           dp->d_start = (long)pos , pos += (long) strlen (buffer);
+           for (bp = buffer; *bp; bp++, size++)
+               if (*bp == '\n')
+                   size++;
+       }
+
+       while (fgets (buffer, sizeof(buffer), fp) != NULL)
+           if (strcmp (buffer, mmdlm2) == 0)
+               break;
+           else {
+               pos += (long) strlen (buffer);
+               for (bp = buffer; *bp; bp++, size++)
+                   if (*bp == '\n')
+                       size++;
+           }
+
+       if (dp->d_start != (long) pos) {
+           dp->d_id = 0;
+           dp->d_size = (long) size;
+           dp->d_stop = pos;
+           dp++;
+       }
+       pos += ld2;
+
+       if (dp >= ep) {
+           register int    curlen = dp - pp;
+
+           cp = (struct drop *) realloc ((char *) pp,
+                                   (size_t) (len += MAXFOLDER) * sizeof(*pp));
+           if (cp == NULL) {
+               if (noisy)
+                   admonish (NULL, "unable to allocate drop storage");
+               free ((char *) pp);
+               return 0;
+           }
+           dp = cp + curlen, ep = (pp = cp) + len - 1;
+       }
+    }
+
+    if (dp == pp)
+       free ((char *) pp);
+    else
+       *drops = pp;
+    return (dp - pp);
+}
+
+
+int
+mbx_write(char *mailbox, int md, FILE *fp, int id, long last,
+           long pos, off_t stop, int mapping, int noisy)
+{
+    register int i, j, size;
+    off_t start;
+    long off;
+    register char *cp;
+    char buffer[BUFSIZ];
+
+    off = (long) lseek (md, (off_t) 0, SEEK_CUR);
+    j = strlen (mmdlm1);
+    if (write (md, mmdlm1, j) != j)
+       return NOTOK;
+    start = lseek (md, (off_t) 0, SEEK_CUR);
+    size = 0;
+
+    fseek (fp, pos, SEEK_SET);
+    while (fgets (buffer, sizeof(buffer), fp) && (pos < stop)) {
+       i = strlen (buffer);
+       for (j = 0; (j = stringdex (mmdlm1, buffer)) >= 0; buffer[j]++)
+           continue;
+       for (j = 0; (j = stringdex (mmdlm2, buffer)) >= 0; buffer[j]++)
+           continue;
+       if (write (md, buffer, i) != i)
+           return NOTOK;
+       pos += (long) i;
+       if (mapping)
+           for (cp = buffer; i-- > 0; size++)
+               if (*cp++ == '\n')
+                   size++;
+    }
+
+    stop = lseek (md, (off_t) 0, SEEK_CUR);
+    j = strlen (mmdlm2);
+    if (write (md, mmdlm2, j) != j)
+       return NOTOK;
+    if (mapping)
+       map_write (mailbox, md, id, last, start, stop, off, size, noisy);
+
+    return OK;
+}
+
+
+/*
+ * Append message to end of file or maildrop.
+ */
+
+int
+mbx_copy (char *mailbox, int mbx_style, int md, int fd,
+          int mapping, char *text, int noisy)
+{
+    int i, j, size;
+    off_t start, stop;
+    long pos;
+    char *cp, buffer[BUFSIZ];
+    FILE *fp;
+
+    pos = (long) lseek (md, (off_t) 0, SEEK_CUR);
+    size = 0;
+
+    switch (mbx_style) {
+       case MMDF_FORMAT: 
+       default: 
+           j = strlen (mmdlm1);
+           if (write (md, mmdlm1, j) != j)
+               return NOTOK;
+           start = lseek (md, (off_t) 0, SEEK_CUR);
+
+           if (text) {
+               i = strlen (text);
+               if (write (md, text, i) != i)
+                   return NOTOK;
+               for (cp = text; *cp++; size++)
+                   if (*cp == '\n')
+                       size++;
+           }
+                   
+           while ((i = read (fd, buffer, sizeof(buffer))) > 0) {
+               for (j = 0;
+                       (j = stringdex (mmdlm1, buffer)) >= 0;
+                       buffer[j]++)
+                   continue;
+               for (j = 0;
+                       (j = stringdex (mmdlm2, buffer)) >= 0;
+                       buffer[j]++)
+                   continue;
+               if (write (md, buffer, i) != i)
+                   return NOTOK;
+               if (mapping)
+                   for (cp = buffer; i-- > 0; size++)
+                       if (*cp++ == '\n')
+                           size++;
+           }
+
+           stop = lseek (md, (off_t) 0, SEEK_CUR);
+           j = strlen (mmdlm2);
+           if (write (md, mmdlm2, j) != j)
+               return NOTOK;
+           if (mapping)
+               map_write (mailbox, md, 0, (long) 0, start, stop, pos, size, noisy);
+
+           return (i != NOTOK ? OK : NOTOK);
+
+       case MBOX_FORMAT:
+           if ((j = dup (fd)) == NOTOK)
+               return NOTOK;
+           if ((fp = fdopen (j, "r")) == NULL) {
+               close (j);
+               return NOTOK;
+           }
+           start = lseek (md, (off_t) 0, SEEK_CUR);
+
+           /* If text is given, we add it to top of message */
+           if (text) {
+               i = strlen (text);
+               if (write (md, text, i) != i)
+                   return NOTOK;
+               for (cp = text; *cp++; size++)
+                   if (*cp == '\n')
+                       size++;
+           }
+                   
+           for (j = 0; fgets (buffer, sizeof(buffer), fp) != NULL; j++) {
+
+               /*
+                * Check the first line, and make some changes.
+                */
+               if (j == 0 && !text) {
+                   /*
+                    * Change the "Return-Path:" field (if in first line)
+                    * back to "From ".
+                    */
+                   if (!strncmp (buffer, "Return-Path:", 12)) {
+                       char tmpbuffer[BUFSIZ];
+                       char *tp, *ep, *fp;
+
+                       strncpy(tmpbuffer, buffer, sizeof(tmpbuffer));
+                       ep = tmpbuffer + 13;
+                       if (!(fp = strchr(ep + 1, ' ')))
+                           fp = strchr(ep + 1, '\n');
+                       tp = dctime(dlocaltimenow());
+                       snprintf (buffer, sizeof(buffer), "From %.*s  %s",
+                               fp - ep, ep, tp);
+                   } else if (!strncmp (buffer, "X-Envelope-From:", 16)) {
+                       /*
+                        * Change the "X-Envelope-From:" field
+                        * (if first line) back to "From ".
+                        */
+                       char tmpbuffer[BUFSIZ];
+                       char *ep;
+
+                       strncpy(tmpbuffer, buffer, sizeof(tmpbuffer));
+                       ep = tmpbuffer + 17;
+                       snprintf (buffer, sizeof(buffer), "From %s", ep);
+                   } else if (strncmp (buffer, "From ", 5)) {
+                       /*
+                        * If there is already a "From " line,
+                        * then leave it alone.  Else we add one.
+                        */
+                       char tmpbuffer[BUFSIZ];
+                       char *tp, *ep;
+
+                       strncpy(tmpbuffer, buffer, sizeof(tmpbuffer));
+                       ep = "nobody@nowhere";
+                       tp = dctime(dlocaltimenow());
+                       snprintf (buffer, sizeof(buffer), "From %s  %s", ep, tp);
+                       strcat (buffer, tmpbuffer);
+                   }
+               }
+
+               /*
+                * If this is not first line, and begins with
+                * "From ", then prepend line with ">".
+                */
+               if (j != 0 && strncmp (buffer, "From ", 5) == 0) {
+                   write (md, ">", 1);
+                   size++;
+               }
+               i = strlen (buffer);
+               if (write (md, buffer, i) != i) {
+                   fclose (fp);
+                   return NOTOK;
+               }
+               if (mapping)
+                   for (cp = buffer; i-- > 0; size++)
+                       if (*cp++ == '\n')
+                           size++;
+           }
+           if (write (md, "\n", 1) != 1) {
+               fclose (fp);
+               return NOTOK;
+           }
+           if (mapping)
+               size += 2;
+
+           fclose (fp);
+           lseek (fd, (off_t) 0, SEEK_END);
+           stop = lseek (md, (off_t) 0, SEEK_CUR);
+           if (mapping)
+               map_write (mailbox, md, 0, (long) 0, start, stop, pos, size, noisy);
+
+           return OK;
+    }
+}
+
+
+int
+mbx_size (int md, off_t start, off_t stop)
+{
+    register int i, fd;
+    register long pos;
+    register FILE *fp;
+
+    if ((fd = dup (md)) == NOTOK || (fp = fdopen (fd, "r")) == NULL) {
+       if (fd != NOTOK)
+           close (fd);
+       return NOTOK;
+    }
+
+    fseek (fp, start, SEEK_SET);
+    for (i = 0, pos = stop - start; pos-- > 0; i++)
+       if (fgetc (fp) == '\n')
+           i++;
+
+    fclose (fp);
+    return i;
+}
+
+
+/*
+ * Close and unlock file/maildrop.
+ */
+
+int
+mbx_close (char *mailbox, int md)
+{
+    lkclose (md, mailbox);
+    return OK;
+}
+
+
+/*
+ * This function is performed implicitly by getbbent.c:
+ *     bb->bb_map = map_name (bb->bb_file);
+ */
+
+char *
+map_name (char *file)
+{
+    register char *cp, *dp;
+    static char buffer[BUFSIZ];
+
+    if ((dp = strchr(cp = r1bindex (file, '/'), '.')) == NULL)
+       dp = cp + strlen (cp);
+    if (cp == file)
+       snprintf (buffer, sizeof(buffer), ".%.*s%s", dp - cp, cp, ".map");
+    else
+       snprintf (buffer, sizeof(buffer), "%.*s.%.*s%s",
+               cp - file, file, dp - cp, cp, ".map");
+
+    return buffer;
+}
+
+
+int
+map_read (char *file, long pos, struct drop **drops, int noisy)
+{
+    register int i, md, msgp;
+    register char *cp;
+    struct drop d;
+    register struct drop *mp, *dp;
+
+    if ((md = open (cp = map_name (file), O_RDONLY)) == NOTOK
+           || map_chk (cp, md, mp = &d, pos, noisy)) {
+       if (md != NOTOK)
+           close (md);
+       return 0;
+    }
+
+    msgp = mp->d_id;
+    dp = (struct drop *) calloc ((size_t) (msgp + 1), sizeof(*dp));
+    if (dp == NULL) {
+       close (md);
+       return 0;
+    }
+
+    memcpy((char *) dp, (char *) mp, sizeof(*dp));
+
+    lseek (md, (off_t) sizeof(*mp), SEEK_SET);
+    if ((i = read (md, (char *) (dp + 1), msgp * sizeof(*dp))) < sizeof(*dp)) {
+       i = 0;
+       free ((char *) dp);
+    } else {
+#ifdef NTOHLSWAP
+       register struct drop *tdp;
+       int j;
+
+       for (j = 0, tdp = dp; j < i / sizeof(*dp); j++, tdp++) {
+           tdp->d_id = ntohl(tdp->d_id);
+           tdp->d_size = ntohl(tdp->d_size);
+           tdp->d_start = ntohl(tdp->d_start);
+           tdp->d_stop = ntohl(tdp->d_stop);
+       }
+#endif
+       *drops = dp;
+    }
+
+    close (md);
+
+    return (i / sizeof(*dp));
+}
+
+
+int
+map_write (char *mailbox, int md, int id, long last, off_t start,
+           off_t stop, long pos, int size, int noisy)
+{
+    register int i;
+    int clear, fd, td;
+    char *file;
+    register struct drop *dp;
+    struct drop d1, d2, *rp;
+    register FILE *fp;
+
+    if ((fd = map_open (file = map_name (mailbox), &clear, md)) == NOTOK)
+       return NOTOK;
+
+    if (!clear && map_chk (file, fd, &d1, pos, noisy)) {
+       unlink (file);
+       mbx_close (file, fd);
+       if ((fd = map_open (file, &clear, md)) == NOTOK)
+           return NOTOK;
+       clear++;
+    }
+
+    if (clear) {
+       if ((td = dup (md)) == NOTOK || (fp = fdopen (td, "r")) == NULL) {
+           if (noisy)
+               admonish (file, "unable to %s", td != NOTOK ? "fdopen" : "dup");
+           if (td != NOTOK)
+               close (td);
+           mbx_close (file, fd);
+           return NOTOK;
+       }
+
+       switch (i = mbx_read (fp, 0, &rp, noisy)) {
+           case NOTOK:
+               fclose (fp);
+               mbx_close (file, fd);
+               return NOTOK;
+
+           case OK:
+               break;
+
+           default:
+               d1.d_id = 0;
+               for (dp = rp; i-- >0; dp++) {
+                   if (dp->d_start == start)
+                       dp->d_id = id;
+                   lseek (fd, (off_t) (++d1.d_id * sizeof(*dp)), SEEK_SET);
+                   if (write (fd, (char *) dp, sizeof(*dp)) != sizeof(*dp)) {
+                       if (noisy)
+                           admonish (file, "write error");
+                       mbx_close (file, fd);
+                       fclose (fp);
+                       return NOTOK;
+                   }
+               }
+               free ((char *) rp);
+               break;
+       }
+    }
+    else {
+       if (last == 0)
+           last = d1.d_start;
+       dp = &d2;
+       dp->d_id = id;
+       dp->d_size = (long) (size ? size : mbx_size (fd, start, stop));
+       dp->d_start = start;
+       dp->d_stop = stop;
+       lseek (fd, (off_t) (++d1.d_id * sizeof(*dp)), SEEK_SET);
+       if (write (fd, (char *) dp, sizeof(*dp)) != sizeof(*dp)) {
+           if (noisy)
+               admonish (file, "write error");
+           mbx_close (file, fd);
+           return NOTOK;
+       }
+    }
+
+    dp = &d1;
+    dp->d_size = DRVRSN;
+    dp->d_start = (long) last;
+    dp->d_stop = lseek (md, (off_t) 0, SEEK_CUR);
+
+    lseek (fd, (off_t) 0, SEEK_SET);
+    if (write (fd, (char *) dp, sizeof(*dp)) != sizeof(*dp)) {
+       if (noisy)
+           admonish (file, "write error");
+       mbx_close (file, fd);
+       return NOTOK;
+    }
+
+    mbx_close (file, fd);
+
+    return OK;
+}
+
+
+static int
+map_open (char *file, int *clear, int md)
+{
+    mode_t mode;
+    struct stat st;
+
+    mode = fstat (md, &st) != NOTOK ? (mode_t) (st.st_mode & 0777) : m_gmprot ();
+    return mbx_open (file, OTHER_FORMAT, st.st_uid, st.st_gid, mode);
+}
+
+
+int
+map_chk (char *file, int fd, struct drop *dp, long pos, int noisy)
+{
+    long count;
+    struct drop d, tmpd;
+    register struct drop *dl;
+
+    if (read (fd, (char *) &tmpd, sizeof(*dp)) != sizeof(*dp)) {
+#ifdef notdef
+       admonish (NULL, "%s: missing or partial index", file);
+#endif /* notdef */
+       return NOTOK;
+    }
+#ifndef        NTOHLSWAP
+    *dp = tmpd;                /* if ntohl(n)=(n), can use struct assign */
+#else
+    dp->d_id    = ntohl(tmpd.d_id);
+    dp->d_size  = ntohl(tmpd.d_size);
+    dp->d_start = ntohl(tmpd.d_start);
+    dp->d_stop  = ntohl(tmpd.d_stop);
+#endif
+    
+    if (dp->d_size != DRVRSN) {
+       if (noisy)
+           admonish (NULL, "%s: version mismatch (%d != %d)", file,
+                               dp->d_size, DRVRSN);
+       return NOTOK;
+    }
+
+    if (dp->d_stop != pos) {
+       if (noisy && pos != (long) 0)
+           admonish (NULL,
+                   "%s: pointer mismatch or incomplete index (%ld!=%ld)", 
+                   file, dp->d_stop, (long) pos);
+       return NOTOK;
+    }
+
+    if ((long) ((dp->d_id + 1) * sizeof(*dp)) != (long) lseek (fd, (off_t) 0, SEEK_END)) {
+       if (noisy)
+           admonish (NULL, "%s: corrupt index(1)", file);
+       return NOTOK;
+    }
+
+    dl = &d;
+    count = (long) strlen (mmdlm2);
+    lseek (fd, (off_t) (dp->d_id * sizeof(*dp)), SEEK_SET);
+    if (read (fd, (char *) dl, sizeof(*dl)) != sizeof(*dl)
+           || (ntohl(dl->d_stop) != dp->d_stop
+               && ntohl(dl->d_stop) + count != dp->d_stop)) {
+       if (noisy)
+           admonish (NULL, "%s: corrupt index(2)", file);
+       return NOTOK;
+    }
+
+    return OK;
+}
diff --git a/uip/flist.c b/uip/flist.c
new file mode 100644 (file)
index 0000000..0e2b161
--- /dev/null
@@ -0,0 +1,709 @@
+/*
+ * flist.c -- list nmh folders containing messages
+ *         -- in a given sequence
+ *
+ * originally by
+ * David Nichols, Xerox-PARC, November, 1992
+ *
+ * Copyright (c) 1994 Xerox Corporation.
+ * Use and copying of this software and preparation of derivative works based
+ * upon this software are permitted. Any distribution of this software or
+ * derivative works must comply with all applicable United States export
+ * control laws. This software is made available AS IS, and Xerox Corporation
+ * makes no warranty about the software, its performance or its conformity to
+ * any specification.
+ *
+ *  $Id$
+ */
+
+#include <h/mh.h>
+
+#define FALSE   0
+#define TRUE    1
+
+/*
+ * We allocate space to record the names of folders
+ * (foldersToDo array), this number of elements at a time.
+ */
+#define MAXFOLDERS  100
+
+
+static struct swit switches[] = {
+#define SEQSW           0
+    { "sequence name", 0 },
+#define ALLSW           1
+    { "all", 0 },
+#define NOALLSW         2
+    { "noall", 0 },
+#define RECURSE         3
+    { "recurse", 0 },
+#define NORECURSE       4
+    { "norecurse", 0 },
+#define SHOWZERO        5
+    { "showzero", 0 },
+#define NOSHOWZERO      6
+    { "noshowzero", 0 },
+#define ALPHASW         7
+    { "alpha", 0 },
+#define NOALPHASW       8
+    { "noalpha", 0 },
+#define FASTSW          9
+    { "fast", 0 },
+#define NOFASTSW        10
+    { "nofast", 0 },
+#define TOTALSW         11
+    { "total", -5 },
+#define NOTOTALSW       12
+    { "nototal", -7 },
+#define VERSIONSW      13
+    { "version", 0 },
+#define HELPSW          14
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+struct Folder {
+    char *name;                        /* name of folder */
+    int priority;
+    int error;                 /* error == 1 for unreadable folder     */
+    int nMsgs;                 /* number of messages in folder         */
+    int nSeq[NUMATTRS];                /* number of messages in each sequence  */
+    int private[NUMATTRS];     /* is given sequence, public or private */
+};
+
+static struct Folder *orders = NULL;
+static int nOrders = 0;
+static int nOrdersAlloced = 0;
+static struct Folder *folders = NULL;
+static int nFolders = 0;
+static int nFoldersAlloced = 0;
+
+/* info on folders to search */
+static char **foldersToDo;
+static int numfolders;
+static int maxfolders;
+
+/* info on sequences to search for */
+static char *sequencesToDo[NUMATTRS];
+static int numsequences;
+
+static int all        = FALSE;  /* scan all folders in top level?           */
+static int alphaOrder = FALSE; /* want alphabetical order only             */
+static int recurse    = FALSE; /* show nested folders?                     */
+static int showzero   = TRUE;  /* show folders even if no messages in seq? */
+static int Total      = TRUE;  /* display info on number of messages in    *
+                                * sequence found, and total num messages   */
+
+static char curfolder[BUFSIZ]; /* name of the current folder */
+static char *nmhdir;           /* base nmh mail directory    */
+
+/*
+ * Type for a compare function for qsort.  This keeps
+ * the compiler happy.
+ */
+typedef int (*qsort_comp) (const void *, const void *);
+
+/*
+ * prototypes
+ */
+int CompareFolders(struct Folder *, struct Folder *);
+void GetFolderOrder(void);
+void ScanFolders(void);
+int AddFolder(char *, int);
+void BuildFolderList(char *, int);
+void BuildFolderListRecurse(char *, struct stat *, int);
+void PrintFolders(void);
+static int num_digits (int);
+void AllocFolders(struct Folder **, int *, int);
+int AssignPriority(char *);
+static void do_readonly_folders(void);
+
+
+
+int
+main(int argc, char **argv)
+{
+    char *cp, **argp;
+    char **arguments;
+    char buf[BUFSIZ];
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex(argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    /*
+     * If program was invoked with name ending
+     * in `s', then add switch `-all'.
+     */
+    if (argv[0][strlen (argv[0]) - 1] == 's')
+       all = TRUE;
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    /* allocate the initial space to record the folder names */
+    numfolders = 0;
+    maxfolders = MAXFOLDERS;
+    if (!(foldersToDo = (char **) malloc ((size_t) (maxfolders * sizeof(*foldersToDo)))))
+       adios (NULL, "unable to allocate folder storage");
+
+    /* no sequences yet */
+    numsequences = 0;
+
+    /* parse arguments */
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch(++cp, switches)) {
+           case AMBIGSW:
+               ambigsw(cp, switches);
+               done(1);
+           case UNKWNSW:
+               adios(NULL, "-%s unknown", cp);
+
+           case HELPSW:
+               snprintf(buf, sizeof(buf), "%s [+folder1 [+folder2 ...]][switches]",
+                       invo_name);
+               print_help(buf, switches, 1);
+               done(1);
+           case VERSIONSW:
+               print_version(invo_name);
+               done (1);
+
+           case SEQSW:
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+
+               /* check if too many sequences specified */
+               if (numsequences >= NUMATTRS)
+                   adios (NULL, "too many sequences (more than %d) specified", NUMATTRS);
+               sequencesToDo[numsequences++] = cp;
+               break;
+
+           case ALLSW:
+               all = TRUE;
+               break;
+           case NOALLSW:
+               all = FALSE;
+               break;
+
+           case SHOWZERO:
+               showzero = TRUE;
+               break;
+           case NOSHOWZERO:
+               showzero = FALSE;
+               break;
+
+           case ALPHASW:
+               alphaOrder = TRUE;
+               break;
+           case NOALPHASW:
+               alphaOrder = FALSE;
+               break;
+
+           case NOFASTSW:
+           case TOTALSW:
+               Total = TRUE;
+               break;
+
+           case FASTSW:
+           case NOTOTALSW:
+               Total = FALSE;
+               break;
+
+           case RECURSE:
+               recurse = TRUE;
+               break;
+           case NORECURSE:
+               recurse = FALSE;
+               break;
+           }
+       } else {
+           /*
+            * Check if we need to allocate more space
+            * for folder names.
+            */
+           if (numfolders >= maxfolders) {
+               maxfolders += MAXFOLDERS;
+               if (!(foldersToDo = (char **) realloc (foldersToDo,
+                       (size_t) (maxfolders * sizeof(*foldersToDo)))))
+                   adios (NULL, "unable to reallocate folder name storage");
+           }
+           if (*cp == '+')
+               ++cp;
+           foldersToDo[numfolders++] = cp;
+       }
+    }
+
+    if (!context_find ("path"))
+        free (path ("./", TFOLDER));
+
+    /* get current folder */
+    strncpy (curfolder, getfolder(1), sizeof(curfolder));
+
+    /* get nmh base directory */
+    nmhdir = m_maildir ("");
+
+    /*
+     * If we didn't specify any sequences, we search
+     * for the "Unseen-Sequence" profile entry and use
+     * all the sequences defined there.  We check to
+     * make sure that the Unseen-Sequence entry doesn't
+     * contain more than NUMATTRS sequences.
+     */
+    if (numsequences == 0) {
+       if ((cp = context_find(usequence)) && *cp) {
+           char **ap, *dp;
+
+           dp = getcpy(cp);
+           ap = brkstring (dp, " ", "\n");
+           for (; ap && *ap; ap++) {
+               if (numsequences >= NUMATTRS)
+                   adios (NULL, "too many sequences (more than %d) in %s profile entry",
+                          NUMATTRS, usequence);
+               else
+                   sequencesToDo[numsequences++] = *ap;
+           }
+       } else {
+           adios (NULL, "no sequence specified or %s profile entry found", usequence);
+       }
+    }
+
+    GetFolderOrder();
+    ScanFolders();
+    qsort(folders, nFolders, sizeof(struct Folder), (qsort_comp) CompareFolders);
+    PrintFolders();
+    done (0);
+}
+
+/*
+ * Read the Flist-Order profile entry to determine
+ * how to sort folders for output.
+ */
+
+void
+GetFolderOrder(void)
+{
+    char *p, *s;
+    int priority = 1;
+    struct Folder *o;
+
+    if (!(p = context_find("Flist-Order")))
+       return;
+    for (;;) {
+       while (isspace(*p))
+           ++p;
+       s = p;
+       while (*p && !isspace(*p))
+           ++p;
+       if (p != s) {
+           /* Found one. */
+           AllocFolders(&orders, &nOrdersAlloced, nOrders + 1);
+           o = &orders[nOrders++];
+           o->priority = priority++;
+           o->name = (char *) malloc(p - s + 1);
+           strncpy(o->name, s, p - s);
+           o->name[p - s] = 0;
+       } else
+           break;
+    }
+}
+
+/*
+ * Scan all the necessary folders
+ */
+
+void
+ScanFolders(void)
+{
+    int i;
+
+    /*
+     * change directory to base of nmh directory
+     */
+    if (chdir (nmhdir) == NOTOK)
+       adios (nmhdir, "unable to change directory to");
+
+    if (numfolders > 0) {
+       /* Update context */
+       strncpy (curfolder, foldersToDo[numfolders - 1], sizeof(curfolder));
+       context_replace (pfolder, curfolder);/* update current folder */
+       context_save ();                     /* save the context file */
+
+       /*
+        * Scan each given folder.  If -all is given,
+        * then also scan the 1st level subfolders under
+        * each given folder.
+        */
+       for (i = 0; i < numfolders; ++i)
+           BuildFolderList(foldersToDo[i], all ? 1 : 0);
+    } else {
+       if (all) {
+           /*
+            * Do the readonly folders
+            */
+           do_readonly_folders();
+
+           /*
+            * Now scan the entire nmh directory for folders
+            */
+           BuildFolderList(".", 0);
+       } else {
+           /*
+            * Else scan current folder
+            */
+           BuildFolderList(curfolder, 0);
+       }
+    }
+}
+
+/*
+ * Initial building of folder list for
+ * the top of our search tree.
+ */
+
+void
+BuildFolderList(char *dirName, int searchdepth)
+{
+    struct stat st;
+
+    /* Make sure we have a directory */
+    if ((stat(dirName, &st) == -1) || !S_ISDIR(st.st_mode))
+       return;
+
+    /*
+     * If base directory, don't add it to the
+     * folder list. We just recurse into it.
+     */
+    if (!strcmp (dirName, ".")) {
+       BuildFolderListRecurse (".", &st, 0);
+       return;
+    }
+
+    /*
+     * Add this folder to the list.
+     * If recursing and directory has subfolders,
+     * then build folder list for subfolders.
+     */
+    if (AddFolder(dirName, showzero) && (recurse || searchdepth) && st.st_nlink > 2)
+       BuildFolderListRecurse(dirName, &st, searchdepth - 1);
+}
+
+/*
+ * Recursive building of folder list
+ */
+
+void
+BuildFolderListRecurse(char *dirName, struct stat *s, int searchdepth)
+{
+    char *base, name[PATH_MAX];
+    int nlinks;
+    DIR *dir;
+    struct dirent *dp;
+    struct stat st;
+
+    /*
+     * Keep track of number of directories we've seen so we can
+     * stop stat'ing entries in this directory once we've seen
+     * them all.  This optimization will fail if you have extra
+     * directories beginning with ".", since we don't bother to
+     * stat them.  But that shouldn't generally be a problem.
+     */
+    nlinks = s->st_nlink;
+
+    if (!(dir = opendir(dirName)))
+       adios(dirName, "can't open directory");
+
+    /*
+     * A hack so that we don't see a
+     * leading "./" in folder names.
+     */
+    base = strcmp (dirName, ".") ? dirName : dirName + 1;
+
+    while (nlinks && (dp = readdir(dir))) {
+       if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) {
+           nlinks--;
+           continue;
+       }
+       if (dp->d_name[0] == '.')
+           continue;
+       strncpy (name, base, sizeof(name) - 2);
+       if (*base)
+           strcat(name, "/");
+       strncat(name, dp->d_name, sizeof(name) - strlen(name) - 1);
+       if ((stat(name, &st) != -1) && S_ISDIR(st.st_mode)) {
+           /*
+            * Check if this was really a symbolic link pointing
+            * to a directory.  If not, then decrement link count.
+            */
+           if (lstat (name, &st) == -1)
+               nlinks--;
+           /* Add this folder to the list */
+           if (AddFolder(name, showzero) &&
+                       (recurse || searchdepth) && st.st_nlink > 2)
+               BuildFolderListRecurse(name, &st, searchdepth - 1);
+       }
+    }
+    closedir(dir);
+}
+
+/*
+ * Add this folder to our list, counting the total number of
+ * messages and the number of messages in each sequence.
+ */
+
+int
+AddFolder(char *name, int force)
+{
+    int i, msgnum, nonzero;
+    int seqnum[NUMATTRS], nSeq[NUMATTRS];
+    struct Folder *f;
+    struct msgs *mp;
+
+    /* Read folder and create message structure */
+    if (!(mp = folder_read (name))) {
+       /* Oops, error occurred.  Record it and continue. */
+       AllocFolders(&folders, &nFoldersAlloced, nFolders + 1);
+       f = &folders[nFolders++];
+       f->name = getcpy(name);
+       f->error = 1;
+       f->priority = AssignPriority(f->name);
+       return 0;
+    }
+
+    for (i = 0; i < numsequences; i++) {
+       /* Convert sequences to their sequence numbers */
+       if (sequencesToDo[i])
+           seqnum[i] = seq_getnum(mp, sequencesToDo[i]);
+       else
+           seqnum[i] = -1;
+
+       /* Now count messages in this sequence */
+       nSeq[i] = 0;
+       if (mp->nummsg > 0 && seqnum[i] != -1) {
+           for (msgnum = mp->lowmsg; msgnum <= mp->hghmsg; msgnum++) {
+               if (in_sequence(mp, seqnum[i], msgnum))
+                   nSeq[i]++;
+           }
+       }
+    }
+
+    /* Check if any of the sequence checks were nonzero */
+    nonzero = 0;
+    for (i = 0; i < numsequences; i++) {
+       if (nSeq[i] > 0) {
+           nonzero = 1;
+           break;
+       }
+    }
+
+    if (nonzero || force) {
+       /* save general folder information */
+       AllocFolders(&folders, &nFoldersAlloced, nFolders + 1);
+       f = &folders[nFolders++];
+       f->name = getcpy(name);
+       f->nMsgs = mp->nummsg;
+       f->error = 0;
+       f->priority = AssignPriority(f->name);
+
+       /* record the sequence information */
+       for (i = 0; i < numsequences; i++) {
+           f->nSeq[i] = nSeq[i];
+           f->private[i] = (seqnum[i] != -1) ? is_seq_private(mp, seqnum[i]) : 0;
+       }
+    }
+
+    folder_free (mp);  /* free folder/message structure */
+    return 1;
+}
+
+/*
+ * Print the folder/sequence information
+ */
+
+void
+PrintFolders(void)
+{
+    char tmpname[BUFSIZ];
+    int i, j, len, has_private = 0;
+    int maxfolderlen = 0, maxseqlen = 0;
+    int maxnum = 0, maxseq = 0;
+
+    if (!Total) {
+       for (i = 0; i < nFolders; i++)
+           printf("%s\n", folders[i].name);
+       return;
+    }
+
+    /*
+     * Find the width we need for various fields
+     */
+    for (i = 0; i < nFolders; ++i) {
+       /* find the length of longest folder name */
+       len = strlen(folders[i].name);
+       if (len > maxfolderlen)
+           maxfolderlen = len;
+
+       /* If folder had error, skip the rest */
+       if (folders[i].error)
+           continue;
+
+       /* find the maximum total messages */
+       if (folders[i].nMsgs > maxnum)
+           maxnum = folders[i].nMsgs;
+
+       for (j = 0; j < numsequences; j++) {
+           /* find maximum width of sequence name */
+           len = strlen (sequencesToDo[j]);
+           if ((folders[i].nSeq[j] > 0 || showzero) && (len > maxseqlen))
+               maxseqlen = len;
+
+           /* find the maximum number of messages in sequence */
+           if (folders[i].nSeq[j] > maxseq)
+               maxseq = folders[i].nSeq[j];
+
+           /* check if this sequence is private in any of the folders */
+           if (folders[i].private[j])
+               has_private = 1;
+       }
+    }
+
+    /* Now print all the folder/sequence information */
+    for (i = 0; i < nFolders; i++) {
+       for (j = 0; j < numsequences; j++) {
+           if (folders[i].nSeq[j] > 0 || showzero) {
+               /* Add `+' to end of name of current folder */
+               if (strcmp(curfolder, folders[i].name))
+                   snprintf(tmpname, sizeof(tmpname), "%s", folders[i].name);
+               else
+                   snprintf(tmpname, sizeof(tmpname), "%s+", folders[i].name);
+
+               if (folders[i].error) {
+                   printf("%-*s is unreadable\n", maxfolderlen+1, tmpname);
+                   continue;
+               }
+
+               printf("%-*s has %*d in sequence %-*s%s; out of %*d\n",
+                      maxfolderlen+1, tmpname,
+                      num_digits(maxseq), folders[i].nSeq[j],
+                      maxseqlen, sequencesToDo[j],
+                      !has_private ? "" : folders[i].private[j] ? " (private)" : "          ",
+                      num_digits(maxnum), folders[i].nMsgs);
+           }
+       }
+    }
+}
+
+/*
+ * Calculate the number of digits in a nonnegative integer
+ */
+static int
+num_digits (int n)
+{
+    int ndigits = 0;
+
+    /* Sanity check */
+    if (n < 0)
+       adios (NULL, "oops, num_digits called with negative value");
+
+    if (n == 0)
+       return 1;
+
+    while (n) {
+       n /= 10;
+       ndigits++;
+    }
+
+    return ndigits;
+}
+
+/*
+ * Put them in priority order.
+ */
+
+int
+CompareFolders(struct Folder *f1, struct Folder *f2)
+{
+    if (!alphaOrder && f1->priority != f2->priority)
+       return f1->priority - f2->priority;
+    else
+       return strcmp(f1->name, f2->name);
+}
+
+/*
+ * Make sure we have at least n folders allocated.
+ */
+
+void
+AllocFolders(struct Folder **f, int *nfa, int n)
+{
+    if (n <= *nfa)
+       return;
+    if (*f == NULL) {
+       *nfa = 10;
+       *f = (struct Folder *) malloc (*nfa * (sizeof(struct Folder)));
+    } else {
+       *nfa *= 2;
+       *f = (struct Folder *) realloc (*f, *nfa * (sizeof(struct Folder)));
+    }
+}
+
+/*
+ * Return the priority for a name.  The highest comes from an exact match.
+ * After that, the longest match (then first) assigns the priority.
+ */
+int
+AssignPriority(char *name)
+{
+    int i, ol, nl;
+    int best = nOrders;
+    int bestLen = 0;
+    struct Folder *o;
+
+    nl = strlen(name);
+    for (i = 0; i < nOrders; ++i) {
+       o = &orders[i];
+       if (!strcmp(name, o->name))
+           return o->priority;
+       ol = strlen(o->name);
+       if (nl < ol - 1)
+           continue;
+       if (ol < bestLen)
+           continue;
+       if (o->name[0] == '*' && !strcmp(o->name + 1, name + (nl - ol + 1))) {
+           best = o->priority;
+           bestLen = ol;
+       } else if (o->name[ol - 1] == '*' && strncmp(o->name, name, ol - 1) == 0) {
+           best = o->priority;
+           bestLen = ol;
+       }
+    }
+    return best;
+}
+
+/*
+ * Do the read only folders
+ */
+
+static void
+do_readonly_folders (void)
+{
+    int atrlen;
+    char atrcur[BUFSIZ];
+    register struct node *np;
+
+    /* sanity check - check that context has been read */
+    if (defpath == NULL)
+       adios (NULL, "oops, context hasn't been read yet");
+
+    snprintf (atrcur, sizeof(atrcur), "atr-%s-", current);
+    atrlen = strlen (atrcur);
+
+    for (np = m_defs; np; np = np->n_next)
+        if (ssequal (atrcur, np->n_name)
+                && !ssequal (nmhdir, np->n_name + atrlen))
+            BuildFolderList (np->n_name + atrlen, 0);
+}
diff --git a/uip/fmtdump.c b/uip/fmtdump.c
new file mode 100644 (file)
index 0000000..89e70ef
--- /dev/null
@@ -0,0 +1,506 @@
+
+/*
+ * fmtdump.c -- compile format file and dump out instructions
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/fmt_scan.h>
+#include <h/fmt_compile.h>
+#include <h/scansbr.h>
+
+static struct swit switches[] = {
+#define        FORMSW              0
+    { "form formatfile", 0 },
+#define        FMTSW               1
+    { "format string", 5 },
+#define VERSIONSW           2
+    { "version", 0 },
+#define        HELPSW              3
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+/* for assignlabel */
+static struct format *lvec[128];
+static lused = 0;
+
+/*
+ * static prototypes
+ */
+static void fmt_dump (struct format *);
+static void dumpone(struct format *);
+static int findlabel(struct format *);
+static void assignlabel(struct format *);
+static char *f_typestr(int);
+static char *c_typestr(int);
+static void litputs(char *);
+static void litputc(char);
+
+
+int
+main (int argc, char **argv)
+{
+    int ncomps;
+    char *cp, *form = NULL, *format = NULL;
+    char buf[BUFSIZ], *nfs, **argp, **arguments;
+    struct format *fmt;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown", cp);
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf), "%s [switches]", invo_name);
+                   print_help (buf, switches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case FORMSW: 
+                   if (!(form = *argp++) || *form == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   format = NULL;
+                   continue;
+               case FMTSW: 
+                   if (!(format = *argp++) || *format == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   form = NULL;
+                   continue;
+
+           }
+       }
+       if (form)
+           adios (NULL, "only one form at a time!");
+       else
+           form = cp;
+    }
+
+    /*
+     * Get new format string.  Must be before chdir().
+     */
+    nfs = new_fs (form, format, FORMAT);
+    ncomps = fmt_compile(nfs, &fmt);
+
+    fmt_dump(fmt);
+    done(0);
+}
+
+static void
+fmt_dump (struct format *fmth)
+{
+       int i;
+       register struct format *fmt, *addr;
+
+       /* Assign labels */
+       for (fmt = fmth; fmt; ++fmt) {
+               i = fmt->f_type;
+               if (i == FT_IF_S ||
+                   i == FT_IF_S_NULL ||
+                   i == FT_IF_V_EQ ||
+                   i == FT_IF_V_NE ||
+                   i == FT_IF_V_GT ||
+                   i == FT_IF_MATCH ||
+                   i == FT_IF_AMATCH ||
+                   i == FT_GOTO) {
+                       addr = fmt + fmt->f_skip;
+                       if (findlabel(addr) < 0)
+                               assignlabel(addr);
+               }
+               if (fmt->f_type == FT_DONE && fmt->f_value == 0)
+                       break;
+       }
+
+       /* Dump them out! */
+       for (fmt = fmth; fmt; ++fmt) {
+               dumpone(fmt);
+               if (fmt->f_type == FT_DONE && fmt->f_value == 0)
+                       break;
+       }
+}
+
+static void
+dumpone(struct format *fmt)
+{
+       register int i;
+
+       if ((i = findlabel(fmt)) >= 0)
+               printf("L%d:", i);
+       putchar('\t');
+
+       fputs(f_typestr((int)fmt->f_type), stdout);
+
+       switch (fmt->f_type) {
+
+       case FT_COMP:
+       case FT_LS_COMP:
+       case FT_LV_COMPFLAG:
+       case FT_LV_COMP:
+               printf(", comp ");
+               litputs(fmt->f_comp->c_name);
+               if (fmt->f_comp->c_type)
+                       printf(", c_type %s", c_typestr(fmt->f_comp->c_type));
+               if (fmt->f_comp->c_flags)
+                       printf(", c_flags %d", fmt->f_comp->c_flags);
+               break;
+
+       case FT_LV_SEC:
+       case FT_LV_MIN:
+       case FT_LV_HOUR:
+       case FT_LV_MDAY:
+       case FT_LV_MON:
+       case FT_LS_MONTH:
+       case FT_LS_LMONTH:
+       case FT_LS_ZONE:
+       case FT_LV_YEAR:
+       case FT_LV_WDAY:
+       case FT_LS_DAY:
+       case FT_LS_WEEKDAY:
+       case FT_LV_YDAY:
+       case FT_LV_ZONE:
+       case FT_LV_CLOCK:
+       case FT_LV_RCLOCK:
+       case FT_LV_DAYF:
+       case FT_LV_ZONEF:
+       case FT_LV_DST:
+       case FT_LS_822DATE:
+       case FT_LS_PRETTY:
+       case FT_LOCALDATE:
+       case FT_GMTDATE:
+       case FT_PARSEDATE:
+               printf(", c_name ");
+               litputs(fmt->f_comp->c_name);
+               if (fmt->f_comp->c_type)
+                       printf(", c_type %s", c_typestr(fmt->f_comp->c_type));
+               if (fmt->f_comp->c_flags)
+                       printf(", c_flags %d", fmt->f_comp->c_flags);
+               break;
+
+       case FT_LS_ADDR:
+       case FT_LS_PERS:
+       case FT_LS_MBOX:
+       case FT_LS_HOST:
+       case FT_LS_PATH:
+       case FT_LS_GNAME:
+       case FT_LS_NOTE:
+       case FT_LS_822ADDR:
+       case FT_LV_HOSTTYPE:
+       case FT_LV_INGRPF:
+       case FT_LV_NOHOSTF:
+       case FT_LS_FRIENDLY:
+       case FT_PARSEADDR:
+       case FT_MYMBOX:
+               printf(", c_name ");
+               litputs(fmt->f_comp->c_name);
+               if (fmt->f_comp->c_type)
+                       printf(", c_type %s", c_typestr(fmt->f_comp->c_type));
+               if (fmt->f_comp->c_flags)
+                       printf(", c_flags %d", fmt->f_comp->c_flags);
+               break;
+
+       case FT_COMPF:
+               printf(", width %d, fill '", fmt->f_width);
+               litputc(fmt->f_fill);
+               printf("' name ");
+               litputs(fmt->f_comp->c_name);
+               if (fmt->f_comp->c_type)
+                       printf(", c_type %s", c_typestr(fmt->f_comp->c_type));
+               if (fmt->f_comp->c_flags)
+                       printf(", c_flags %d", fmt->f_comp->c_flags);
+               break;
+
+       case FT_STRF:
+       case FT_NUMF:
+               printf(", width %d, fill '", fmt->f_width);
+               litputc(fmt->f_fill);
+               putchar('\'');
+               break;
+
+       case FT_LIT:
+#ifdef FT_LIT_FORCE
+       case FT_LIT_FORCE:
+#endif
+               putchar(' ');
+               litputs(fmt->f_text);
+               break;
+
+       case FT_LITF:
+               printf(", width %d, fill '", fmt->f_width);
+               litputc(fmt->f_fill);
+               printf("' ");
+               litputs(fmt->f_text);
+               break;
+
+       case FT_CHAR:
+               putchar(' ');
+               putchar('\'');
+               litputc(fmt->f_char);
+               putchar('\'');
+               break;
+
+
+       case FT_IF_S:
+       case FT_IF_S_NULL:
+       case FT_IF_MATCH:
+       case FT_IF_AMATCH:
+               printf(" continue else goto");
+       case FT_GOTO:
+               i = findlabel(fmt + fmt->f_skip);
+               printf(" L%d", i);
+               break;
+
+       case FT_IF_V_EQ:
+       case FT_IF_V_NE:
+       case FT_IF_V_GT:
+               i = findlabel(fmt + fmt->f_skip);
+               printf(" %d continue else goto L%d", fmt->f_value, i);
+               break;
+
+       case FT_V_EQ:
+       case FT_V_NE:
+       case FT_V_GT:
+       case FT_LV_LIT:
+       case FT_LV_PLUS_L:
+       case FT_LV_MINUS_L:
+       case FT_LV_DIVIDE_L:
+       case FT_LV_MODULO_L:
+               printf(" value %d", fmt->f_value);
+               break;
+
+       case FT_LS_LIT:
+               printf(" str ");
+               litputs(fmt->f_text);
+               break;
+
+       case FT_LS_GETENV:
+               printf(" getenv ");
+               litputs(fmt->f_text);
+               break;
+
+       case FT_LS_DECODECOMP:
+               printf(", comp ");
+               litputs(fmt->f_comp->c_name);
+               break;
+
+       case FT_LS_DECODE:
+               break;
+
+       case FT_LS_TRIM:
+               printf(", width %d", fmt->f_width);
+               break;
+
+       case FT_LV_DAT:
+               printf(", value dat[%d]", fmt->f_value);
+               break;
+       }
+       putchar('\n');
+}
+
+static int
+findlabel(struct format *addr)
+{
+       register int i;
+
+       for (i = 0; i < lused; ++i)
+               if (addr == lvec[i])
+                       return(i);
+       return(-1);
+}
+
+static void
+assignlabel(struct format *addr)
+{
+       lvec[lused++] = addr;
+}
+
+static char *
+f_typestr(int t)
+{
+       static char buf[32];
+
+       switch (t) {
+       case FT_COMP: return("COMP");
+       case FT_COMPF: return("COMPF");
+       case FT_LIT: return("LIT");
+       case FT_LITF: return("LITF");
+#ifdef FT_LIT_FORCE
+       case FT_LIT_FORCE: return("LIT_FORCE");
+#endif
+       case FT_CHAR: return("CHAR");
+       case FT_NUM: return("NUM");
+       case FT_NUMF: return("NUMF");
+       case FT_STR: return("STR");
+       case FT_STRF: return("STRF");
+       case FT_STRFW: return("STRFW");
+       case FT_PUTADDR: return("PUTADDR");
+       case FT_LS_COMP: return("LS_COMP");
+       case FT_LS_LIT: return("LS_LIT");
+       case FT_LS_GETENV: return("LS_GETENV");
+       case FT_LS_DECODECOMP: return("FT_LS_DECODECOMP");
+       case FT_LS_DECODE: return("FT_LS_DECODE");
+       case FT_LS_TRIM: return("LS_TRIM");
+       case FT_LV_COMP: return("LV_COMP");
+       case FT_LV_COMPFLAG: return("LV_COMPFLAG");
+       case FT_LV_LIT: return("LV_LIT");
+       case FT_LV_DAT: return("LV_DAT");
+       case FT_LV_STRLEN: return("LV_STRLEN");
+       case FT_LV_PLUS_L: return("LV_PLUS_L");
+       case FT_LV_MINUS_L: return("LV_MINUS_L");
+       case FT_LV_DIVIDE_L: return("LV_DIVIDE_L");
+       case FT_LV_MODULO_L: return("LV_MODULO_L");
+       case FT_LV_CHAR_LEFT: return("LV_CHAR_LEFT");
+       case FT_LS_MONTH: return("LS_MONTH");
+       case FT_LS_LMONTH: return("LS_LMONTH");
+       case FT_LS_ZONE: return("LS_ZONE");
+       case FT_LS_DAY: return("LS_DAY");
+       case FT_LS_WEEKDAY: return("LS_WEEKDAY");
+       case FT_LS_822DATE: return("LS_822DATE");
+       case FT_LS_PRETTY: return("LS_PRETTY");
+       case FT_LV_SEC: return("LV_SEC");
+       case FT_LV_MIN: return("LV_MIN");
+       case FT_LV_HOUR: return("LV_HOUR");
+       case FT_LV_MDAY: return("LV_MDAY");
+       case FT_LV_MON: return("LV_MON");
+       case FT_LV_YEAR: return("LV_YEAR");
+       case FT_LV_YDAY: return("LV_YDAY");
+       case FT_LV_WDAY: return("LV_WDAY");
+       case FT_LV_ZONE: return("LV_ZONE");
+       case FT_LV_CLOCK: return("LV_CLOCK");
+       case FT_LV_RCLOCK: return("LV_RCLOCK");
+       case FT_LV_DAYF: return("LV_DAYF");
+       case FT_LV_DST: return("LV_DST");
+       case FT_LV_ZONEF: return("LV_ZONEF");
+       case FT_LS_ADDR: return("LS_ADDR");
+       case FT_LS_PERS: return("LS_PERS");
+       case FT_LS_MBOX: return("LS_MBOX");
+       case FT_LS_HOST: return("LS_HOST");
+       case FT_LS_PATH: return("LS_PATH");
+       case FT_LS_GNAME: return("LS_GNAME");
+       case FT_LS_NOTE: return("LS_NOTE");
+       case FT_LS_822ADDR: return("LS_822ADDR");
+       case FT_LS_FRIENDLY: return("LS_FRIENDLY");
+       case FT_LV_HOSTTYPE: return("LV_HOSTTYPE");
+       case FT_LV_INGRPF: return("LV_INGRPF");
+       case FT_LV_NOHOSTF: return("LV_NOHOSTF");
+       case FT_LOCALDATE: return("LOCALDATE");
+       case FT_GMTDATE: return("GMTDATE");
+       case FT_PARSEDATE: return("PARSEDATE");
+       case FT_PARSEADDR: return("PARSEADDR");
+       case FT_FORMATADDR: return("FORMATADDR");
+       case FT_MYMBOX: return("MYMBOX");
+#ifdef FT_ADDTOSEQ
+       case FT_ADDTOSEQ: return("ADDTOSEQ");
+#endif
+       case FT_SAVESTR: return("SAVESTR");
+#ifdef FT_PAUSE
+       case FT_PAUSE: return ("PAUSE");
+#endif
+       case FT_DONE: return("DONE");
+       case FT_NOP: return("NOP");
+       case FT_GOTO: return("GOTO");
+       case FT_IF_S_NULL: return("IF_S_NULL");
+       case FT_IF_S: return("IF_S");
+       case FT_IF_V_EQ: return("IF_V_EQ");
+       case FT_IF_V_NE: return("IF_V_NE");
+       case FT_IF_V_GT: return("IF_V_GT");
+       case FT_IF_MATCH: return("IF_MATCH");
+       case FT_IF_AMATCH: return("IF_AMATCH");
+       case FT_S_NULL: return("S_NULL");
+       case FT_S_NONNULL: return("S_NONNULL");
+       case FT_V_EQ: return("V_EQ");
+       case FT_V_NE: return("V_NE");
+       case FT_V_GT: return("V_GT");
+       case FT_V_MATCH: return("V_MATCH");
+       case FT_V_AMATCH: return("V_AMATCH");
+       default:
+               printf(buf, "/* ??? #%d */", t);
+               return(buf);
+       }
+}
+
+#define FNORD(v, s) if (t & (v)) { \
+       if (i++ > 0) \
+               strcat(buf, "|"); \
+       strcat(buf, s); }
+
+static char *
+c_typestr(int t)
+{
+       register int i;
+       static char buf[64];
+
+       buf[0] = '\0';
+       if (t & ~(CT_ADDR|CT_DATE|CT_MYMBOX|CT_ADDRPARSE))
+               printf(buf, "0x%x ", t);
+       strcat(buf, "<");
+       i = 0;
+       FNORD(CT_ADDR, "ADDR");
+       FNORD(CT_DATE, "DATE");
+       FNORD(CT_MYMBOX, "MYMBOX");
+       FNORD(CT_ADDRPARSE, "ADDRPARSE");
+       strcat(buf, ">");
+       return(buf);
+#undef FNORD
+}
+
+static void
+litputs(char *s)
+{
+       if (s) {
+               putc('"', stdout);
+               while (*s)
+                       litputc(*s++);
+               putc('"', stdout);
+       } else
+               fputs("<nil>", stdout);
+}
+
+static void
+litputc(char c)
+{
+       if (c & ~ 0177) {
+               putc('M', stdout);
+               putc('-', stdout);
+               c &= 0177;
+       }
+       if (c < 0x20 || c == 0177) {
+               if (c == '\b') {
+                       putc('\\', stdout);
+                       putc('b', stdout);
+               } else if (c == '\f') {
+                       putc('\\', stdout);
+                       putc('f', stdout);
+               } else if (c == '\n') {
+                       putc('\\', stdout);
+                       putc('n', stdout);
+               } else if (c == '\r') {
+                       putc('\\', stdout);
+                       putc('r', stdout);
+               } else if (c == '\t') {
+                       putc('\\', stdout);
+                       putc('t', stdout);
+               } else {
+                       putc('^', stdout);
+                       putc(c ^ 0x40, stdout); /* DEL to ?, others to alpha */
+               }
+       } else
+               putc(c, stdout);
+}
diff --git a/uip/folder.c b/uip/folder.c
new file mode 100644 (file)
index 0000000..c023e11
--- /dev/null
@@ -0,0 +1,828 @@
+
+/*
+ * folder(s).c -- set/list the current message and/or folder
+ *             -- push/pop a folder onto/from the folder stack
+ *             -- list the folder stack
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <errno.h>
+
+static struct swit switches[] = {
+#define        ALLSW           0
+    { "all", 0 },
+#define NALLSW         1
+    { "noall", 0 },
+#define        CREATSW         2
+    { "create", 0 },
+#define        NCREATSW        3
+    { "nocreate", 0 },
+#define        FASTSW          4
+    { "fast", 0 },
+#define        NFASTSW         5
+    { "nofast", 0 },
+#define        HDRSW           6
+    { "header", 0 },
+#define        NHDRSW          7
+    { "noheader", 0 },
+#define        PACKSW          8
+    { "pack", 0 },
+#define        NPACKSW         9
+    { "nopack", 0 },
+#define        VERBSW         10
+    { "verbose", 0 },
+#define        NVERBSW        11
+    { "noverbose", 0 },
+#define        RECURSW        12
+    { "recurse", 0 },
+#define        NRECRSW        13
+    { "norecurse", 0 },
+#define        TOTALSW        14
+    { "total", 0 },
+#define        NTOTLSW        15
+    { "nototal", 0 },
+#define        LISTSW         16
+    { "list", 0 },
+#define        NLISTSW        17
+    { "nolist", 0 },
+#define        PRNTSW         18
+    { "print", 0 },
+#define        NPRNTSW        19
+    { "noprint", -4 },
+#define        PUSHSW         20
+    { "push", 0 },
+#define        POPSW          21
+    { "pop", 0 },
+#define VERSIONSW      22
+    { "version", 0 },
+#define        HELPSW         23
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+extern int errno;
+
+static int fshort   = 0;       /* output only folder names                 */
+static int fcreat   = 0;       /* should we ask to create new folders?     */
+static int fpack    = 0;       /* are we packing the folder?               */
+static int fverb    = 0;       /* print actions taken while packing folder */
+static int fheader  = 0;       /* should we output a header?               */
+static int frecurse = 0;       /* recurse through subfolders               */
+static int ftotal   = 0;       /* should we output the totals?             */
+static int all      = 0;       /* should we output all folders             */
+
+static int total_folders = 0;  /* total number of folders                  */
+
+static int start = 0;
+static int foldp = 0;
+
+static char *nmhdir;
+static char *stack = "Folder-Stack";
+static char folder[BUFSIZ];
+
+#define NUMFOLDERS 100
+
+/*
+ * This is how many folders we currently can hold in the array
+ * `folds'.  We increase this amount by NUMFOLDERS at a time.
+ */
+static int maxfolders;
+static char **folds;
+
+/*
+ * Structure to hold information about
+ * folders as we scan them.
+ */
+struct FolderInfo {
+    char *name;
+    int nummsg;
+    int curmsg;
+    int lowmsg;
+    int hghmsg;
+    int others;                /* others == 1 if other files in folder */
+    int error;         /* error == 1 for unreadable folder     */
+};
+
+/*
+ * Dynamically allocated space to hold
+ * all the folder information.
+ */
+static struct FolderInfo *fi;
+static int maxFolderInfo;
+
+/*
+ * static prototypes
+ */
+static void dodir (char *);
+static int get_folder_info (char *, char *);
+static void print_folders (void);
+static int num_digits (int);
+static int sfold (struct msgs *, char *);
+static void addir (char *);
+static void addfold (char *);
+static int compare (char *, char *);
+static void readonly_folders (void);
+
+
+int
+main (int argc, char **argv)
+{
+    int printsw = 0, listsw = 0;
+    int pushsw = 0, popsw = 0;
+    char *cp, *dp, *msg = NULL, *argfolder = NULL;
+    char **ap, **argp, buf[BUFSIZ], **arguments;
+    struct stat st;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    /*
+     * If program was invoked with name ending
+     * in `s', then add switch `-all'.
+     */
+    if (argv[0][strlen (argv[0]) - 1] == 's')
+       all = 1;
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown", cp);
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf), "%s [+folder] [msg] [switches]",
+                       invo_name);
+                   print_help (buf, switches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case ALLSW: 
+                   all = 1;
+                   continue;
+
+               case NALLSW:
+                   all = 0;
+                   continue;
+
+               case CREATSW: 
+                   fcreat = 1;
+                   continue;
+               case NCREATSW: 
+                   fcreat = -1;
+                   continue;
+
+               case FASTSW: 
+                   fshort++;
+                   continue;
+               case NFASTSW: 
+                   fshort = 0;
+                   continue;
+
+               case HDRSW: 
+                   fheader = 1;
+                   continue;
+               case NHDRSW: 
+                   fheader = -1;
+                   continue;
+
+               case PACKSW: 
+                   fpack++;
+                   continue;
+               case NPACKSW: 
+                   fpack = 0;
+                   continue;
+
+               case VERBSW:
+                   fverb++;
+                   continue;
+               case NVERBSW:
+                   fverb = 0;
+                   continue;
+
+               case RECURSW: 
+                   frecurse++;
+                   continue;
+               case NRECRSW: 
+                   frecurse = 0;
+                   continue;
+
+               case TOTALSW: 
+                   ftotal = 1;
+                   continue;
+               case NTOTLSW: 
+                   ftotal = -1;
+                   continue;
+
+               case PRNTSW: 
+                   printsw = 1;
+                   continue;
+               case NPRNTSW: 
+                   printsw = 0;
+                   continue;
+
+               case LISTSW: 
+                   listsw = 1;
+                   continue;
+               case NLISTSW: 
+                   listsw = 0;
+                   continue;
+
+               case PUSHSW: 
+                   pushsw = 1;
+                   listsw = 1;
+                   popsw  = 0;
+                   continue;
+               case POPSW: 
+                   popsw  = 1;
+                   listsw = 1;
+                   pushsw = 0;
+                   continue;
+           }
+       }
+       if (*cp == '+' || *cp == '@') {
+           if (argfolder)
+               adios (NULL, "only one folder at a time!");
+           else
+               argfolder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       } else {
+           if (msg)
+               adios (NULL, "only one (current) message at a time!");
+           else
+               msg = cp;
+       }
+    }
+
+    if (!context_find ("path"))
+       free (path ("./", TFOLDER));
+    nmhdir = concat (m_maildir (""), "/", NULL);
+
+    /*
+     * If we aren't working with the folder stack
+     * (-push, -pop, -list) then the default is to print.
+     */
+    if (pushsw == 0 && popsw == 0 && listsw == 0)
+       printsw++;
+
+    /* Pushing a folder onto the folder stack */
+    if (pushsw) {
+       if (!argfolder) {
+           /* If no folder is given, the current folder and */
+           /* the top of the folder stack are swapped.      */
+           if ((cp = context_find (stack))) {
+               dp = getcpy (cp);
+               ap = brkstring (dp, " ", "\n");
+               argfolder = getcpy(*ap++);
+           } else {
+               adios (NULL, "no other folder");
+           }
+           for (cp = getcpy (getfolder (1)); *ap; ap++)
+               cp = add (*ap, add (" ", cp));
+           free (dp);
+           context_replace (stack, cp);        /* update folder stack */
+       } else {
+           /* update folder stack */
+           context_replace (stack,
+                   (cp = context_find (stack))
+                   ? concat (getfolder (1), " ", cp, NULL)
+                   : getcpy (getfolder (1)));
+       }
+    }
+
+    /* Popping a folder off of the folder stack */
+    if (popsw) {
+       if (argfolder)
+           adios (NULL, "sorry, no folders allowed with -pop");
+       if ((cp = context_find (stack))) {
+           dp = getcpy (cp);
+           ap = brkstring (dp, " ", "\n");
+           argfolder = getcpy(*ap++);
+       } else {
+           adios (NULL, "folder stack empty");
+       }
+       if (*ap) {
+           /* if there's anything left in the stack */
+           cp = getcpy (*ap++);
+           for (; *ap; ap++)
+               cp = add (*ap, add (" ", cp));
+           context_replace (stack, cp);        /* update folder stack */
+       } else {
+           context_del (stack);        /* delete folder stack entry from context */
+       }
+       free (dp);
+    }
+    if (pushsw || popsw) {
+       cp = m_maildir(argfolder);
+       if (access (cp, F_OK) == NOTOK)
+           adios (cp, "unable to find folder");
+       context_replace (pfolder, argfolder);   /* update current folder   */
+       context_save ();                /* save the context file   */
+       argfolder = NULL;
+    }
+
+    /* Listing the folder stack */
+    if (listsw) {
+       printf ("%s", argfolder ? argfolder : getfolder (1));
+       if ((cp = context_find (stack))) {
+           dp = getcpy (cp);
+           for (ap = brkstring (dp, " ", "\n"); *ap; ap++)
+               printf (" %s", *ap);
+           free (dp);
+       }
+       printf ("\n");
+
+       if (!printsw)
+           done (0);
+    }
+
+    /* Allocate initial space to record folder names */
+    maxfolders = NUMFOLDERS;
+    if ((folds = malloc (maxfolders * sizeof(char *))) == NULL)
+       adios (NULL, "unable to allocate storage for folder names");
+
+    /* Allocate initial space to record folder information */
+    maxFolderInfo = NUMFOLDERS;
+    if ((fi = malloc (maxFolderInfo * sizeof(*fi))) == NULL)
+       adios (NULL, "unable to allocate storage for folder info");
+
+    /*
+     * Scan the folders
+     */
+    if (all || ftotal > 0) {
+       /*
+        * If no folder is given, do them all
+        */
+       if (!argfolder) {
+           if (msg)
+               admonish (NULL, "no folder given for message %s", msg);
+           readonly_folders (); /* do any readonly folders */
+           strncpy (folder, (cp = context_find (pfolder)) ? cp : "", sizeof(folder));
+           dodir (".");
+       } else {
+           strncpy (folder, argfolder, sizeof(folder));
+           if (get_folder_info (argfolder, msg)) {
+               context_replace (pfolder, argfolder);/* update current folder */
+               context_save ();                     /* save the context file */
+           }
+           /*
+            * Since recurse wasn't done in get_folder_info(),
+            * we still need to list all level-1 sub-folders.
+            */
+           if (!frecurse)
+               dodir (folder);
+       }
+    } else {
+       strncpy (folder, argfolder ? argfolder : getfolder (1), sizeof(folder));
+
+       /*
+        * Check if folder exists.  If not, then see if
+        * we should create it, or just exit.
+        */
+       if (stat (strncpy (buf, m_maildir (folder), sizeof(buf)), &st) == -1) {
+           if (errno != ENOENT)
+               adios (buf, "error on folder");
+           if (fcreat == 0) {
+               /* ask before creating folder */
+               cp = concat ("Create folder \"", buf, "\"? ", NULL);
+               if (!getanswer (cp))
+                   done (1);
+               free (cp);
+           } else if (fcreat == -1) {
+               /* do not create, so exit */
+               done (1);
+           }
+           if (!makedir (buf))
+               adios (NULL, "unable to create folder %s", buf);
+       }
+
+       if (get_folder_info (folder, msg) && argfolder) {
+           /* update current folder */
+           context_replace (pfolder, argfolder);
+           }
+    }
+
+    /*
+     * Print out folder information
+     */
+    print_folders();
+
+    context_save ();   /* save the context file */
+    done (0);
+}
+
+/*
+ * Base routine for scanning a folder
+ */
+
+static void
+dodir (char *dir)
+{
+    int i;
+    int os = start;
+    int of = foldp;
+    char buffer[BUFSIZ];
+
+    start = foldp;
+
+    /* change directory to base of nmh directory */
+    if (chdir (nmhdir) == NOTOK)
+       adios (nmhdir, "unable to change directory to");
+
+    addir (strncpy (buffer, dir, sizeof(buffer)));
+
+    for (i = start; i < foldp; i++) {
+       get_folder_info (folds[i], NULL);
+       fflush (stdout);
+    }
+
+    start = os;
+    foldp = of;
+}
+
+static int
+get_folder_info (char *fold, char *msg)
+{
+    int        i, retval = 1;
+    char *mailfile;
+    struct msgs *mp = NULL;
+
+    i = total_folders++;
+
+    /*
+     * if necessary, reallocate the space
+     * for folder information
+     */
+    if (total_folders >= maxFolderInfo) {
+       maxFolderInfo += NUMFOLDERS;
+       if ((fi = realloc (fi, maxFolderInfo * sizeof(*fi))) == NULL)
+           adios (NULL, "unable to re-allocate storage for folder info");
+    }
+
+    fi[i].name   = fold;
+    fi[i].nummsg = 0;
+    fi[i].curmsg = 0;
+    fi[i].lowmsg = 0;
+    fi[i].hghmsg = 0;
+    fi[i].others = 0;
+    fi[i].error  = 0;
+
+    mailfile = m_maildir (fold);
+
+    if (!chdir (mailfile)) {
+       if ((ftotal > 0) || !fshort || msg || fpack) {
+           /*
+            * create message structure and get folder info
+            */
+           if (!(mp = folder_read (fold))) {
+               admonish (NULL, "unable to read folder %s", fold);
+               return 0;
+           }
+
+           /* set the current message */
+           if (msg && !sfold (mp, msg))
+               retval = 0;
+
+           if (fpack) {
+               if (folder_pack (&mp, fverb) == -1)
+                   done (1);
+               seq_save (mp);          /* synchronize the sequences */
+               context_save ();        /* save the context file     */
+           }
+
+           /* record info for this folder */
+           if ((ftotal > 0) || !fshort) {
+               fi[i].nummsg = mp->nummsg;
+               fi[i].curmsg = mp->curmsg;
+               fi[i].lowmsg = mp->lowmsg;
+               fi[i].hghmsg = mp->hghmsg;
+               fi[i].others = other_files (mp);
+           }
+
+           folder_free (mp); /* free folder/message structure */
+       }
+    } else {
+       fi[i].error = 1;
+    }
+
+    if (frecurse && (fshort || fi[i].others) && (fi[i].error == 0))
+       dodir (fold);
+    return retval;
+}
+
+/*
+ * Print folder information
+ */
+
+static void
+print_folders (void)
+{
+    int i, len, hasempty = 0, curprinted;
+    int maxlen = 0, maxnummsg = 0, maxlowmsg = 0;
+    int maxhghmsg = 0, maxcurmsg = 0, total_msgs = 0;
+    int nummsgdigits, lowmsgdigits;
+    int hghmsgdigits, curmsgdigits;
+    char tmpname[BUFSIZ];
+
+    /*
+     * compute a few values needed to for
+     * printing various fields
+     */
+    for (i = 0; i < total_folders; i++) {
+       /* length of folder name */
+       len = strlen (fi[i].name);
+       if (len > maxlen)
+           maxlen = len;
+
+       /* If folder has error, skip the rest */
+       if (fi[i].error)
+           continue;
+
+       /* calculate total number of messages */
+       total_msgs += fi[i].nummsg;
+
+       /* maximum number of messages */
+       if (fi[i].nummsg > maxnummsg)
+           maxnummsg = fi[i].nummsg;
+
+       /* maximum low message */
+       if (fi[i].lowmsg > maxlowmsg)
+           maxlowmsg = fi[i].lowmsg;
+
+       /* maximum high message */
+       if (fi[i].hghmsg > maxhghmsg)
+           maxhghmsg = fi[i].hghmsg;
+
+       /* maximum current message */
+       if (fi[i].curmsg >= fi[i].lowmsg &&
+           fi[i].curmsg <= fi[i].hghmsg &&
+           fi[i].curmsg > maxcurmsg)
+           maxcurmsg = fi[i].curmsg;
+
+       /* check for empty folders */
+       if (fi[i].nummsg == 0)
+           hasempty = 1;
+    }
+    nummsgdigits = num_digits (maxnummsg);
+    lowmsgdigits = num_digits (maxlowmsg);
+    hghmsgdigits = num_digits (maxhghmsg);
+    curmsgdigits = num_digits (maxcurmsg);
+
+    if (hasempty && nummsgdigits < 2)
+       nummsgdigits = 2;
+
+    /*
+     * Print the header
+     */
+    if (fheader > 0 || (all && !fshort && fheader >= 0))
+       printf ("%-*s %*s %-*s; %-*s %*s\n",
+               maxlen+1, "FOLDER",
+               nummsgdigits + 13, "# MESSAGES",
+               lowmsgdigits + hghmsgdigits + 4, " RANGE",
+               curmsgdigits + 4, "CUR",
+               9, "(OTHERS)");
+
+    /*
+     * Print folder information
+     */
+    if (all || fshort || ftotal < 1) {
+       for (i = 0; i < total_folders; i++) {
+           if (fshort) {
+               printf ("%s\n", fi[i].name);
+               continue;
+           }
+
+           /* Add `+' to end of name, if folder is current */
+           if (strcmp (folder, fi[i].name))
+               snprintf (tmpname, sizeof(tmpname), "%s", fi[i].name);
+           else
+               snprintf (tmpname, sizeof(tmpname), "%s+", fi[i].name);
+
+           if (fi[i].error) {
+               printf ("%-*s is unreadable\n", maxlen+1, tmpname);
+               continue;
+           }
+
+           printf ("%-*s ", maxlen+1, tmpname);
+
+           curprinted = 0; /* remember if we print cur */
+           if (fi[i].nummsg == 0) {
+               printf ("has %*s messages%*s",
+                       nummsgdigits, "no",
+                       fi[i].others ? lowmsgdigits + hghmsgdigits + 5 : 0, "");
+           } else {
+               printf ("has %*d message%s  (%*d-%*d)",
+                       nummsgdigits, fi[i].nummsg,
+                       (fi[i].nummsg == 1) ? " " : "s",
+                       lowmsgdigits, fi[i].lowmsg,
+                       hghmsgdigits, fi[i].hghmsg);
+               if (fi[i].curmsg >= fi[i].lowmsg && fi[i].curmsg <= fi[i].hghmsg) {
+                   curprinted = 1;
+                   printf ("; cur=%*d", curmsgdigits, fi[i].curmsg);
+               }
+           }
+
+           if (fi[i].others)
+               printf (";%*s (others)", curprinted ? 0 : curmsgdigits + 6, "");
+           printf (".\n");
+       }
+    }
+
+    /*
+     * Print folder/message totals
+     */
+    if (ftotal > 0 || (all && !fshort && ftotal >= 0)) {
+       if (all)
+           printf ("\n");
+       printf ("TOTAL = %d message%c in %d folder%s.\n",
+               total_msgs, total_msgs != 1 ? 's' : ' ',
+               total_folders, total_folders != 1 ? "s" : "");
+    }
+
+    fflush (stdout);
+}
+
+/*
+ * Calculate the number of digits in a nonnegative integer
+ */
+int
+num_digits (int n)
+{
+    int ndigits = 0;
+
+    /* Sanity check */
+    if (n < 0)
+       adios (NULL, "oops, num_digits called with negative value");
+
+    if (n == 0)
+       return 1;
+
+    while (n) {
+       n /= 10;
+       ndigits++;
+    }
+
+    return ndigits;
+}
+
+/*
+ * Set the current message and sychronize sequences
+ */
+
+static int
+sfold (struct msgs *mp, char *msg)
+{
+    /* parse the message range/sequence/name and set SELECTED */
+    if (!m_convert (mp, msg))
+       return 0;
+
+    if (mp->numsel > 1) {
+       admonish (NULL, "only one message at a time!");
+       return 0;
+    }
+    seq_setprev (mp);          /* set the previous-sequence     */
+    seq_setcur (mp, mp->lowsel);/* set current message           */
+    seq_save (mp);             /* synchronize message sequences */
+    context_save ();           /* save the context file         */
+
+    return 1;
+}
+
+
+static void
+addir (char *name)
+{
+    int nlink;
+    char *base, *cp;
+    struct stat st;
+    struct dirent *dp;
+    DIR * dd;
+
+    cp = name + strlen (name);
+    *cp++ = '/';
+    *cp = '\0';
+
+    /*
+     * A hack to skip over a leading
+     * "./" in folder names.
+     */
+    base = strcmp (name, "./") ? name : name + 2;
+
+   /* short-cut to see if directory has any sub-directories */
+    if (stat (name, &st) != -1 && st.st_nlink == 2)
+        return;
+    if (!(dd = opendir (name))) {
+       admonish (name, "unable to read directory ");
+       return;
+    }
+
+    /*
+     * Keep track of the number of directories we've seen
+     * so we can quit stat'ing early, if we've seen them all.
+     */
+    nlink = st.st_nlink;
+
+    while (nlink && (dp = readdir (dd))) {
+       if (!strcmp (dp->d_name, ".") || !strcmp (dp->d_name, "..")) {
+           nlink--;
+           continue;
+       }
+       if (cp + NLENGTH(dp) + 2 >= name + BUFSIZ)
+           continue;
+       strcpy (cp, dp->d_name);
+       if (stat (name, &st) != -1 && S_ISDIR(st.st_mode)) {
+           /*
+            * Check if this was really a symbolic link pointing at
+            * a directory.  If not, then decrement link count.
+            */
+           if (lstat (name, &st) == -1)
+               nlink--;
+           addfold (base);
+       }
+    }
+
+    closedir (dd);
+    *--cp = '\0';
+}
+
+/*
+ * Add the folder name into the
+ * list in a sorted fashion.
+ */
+
+static void
+addfold (char *fold)
+{
+    register int i, j;
+    register char *cp;
+
+    /* if necessary, reallocate the space for folder names */
+    if (foldp >= maxfolders) {
+       maxfolders += NUMFOLDERS;
+       if ((folds = realloc (folds, maxfolders * sizeof(char *))) == NULL)
+           adios (NULL, "unable to re-allocate storage for folder names");
+    }
+
+    cp = getcpy (fold);
+    for (i = start; i < foldp; i++)
+       if (compare (cp, folds[i]) < 0) {
+           for (j = foldp - 1; j >= i; j--)
+               folds[j + 1] = folds[j];
+           foldp++;
+           folds[i] = cp;
+           return;
+       }
+
+    folds[foldp++] = cp;
+}
+
+
+static int
+compare (char *s1, char *s2)
+{
+    register int i;
+
+    while (*s1 || *s2)
+       if ((i = *s1++ - *s2++))
+           return i;
+
+    return 0;
+}
+
+/*
+ * Do the read only folders
+ */
+
+static void
+readonly_folders (void)
+{
+    int        atrlen;
+    char atrcur[BUFSIZ];
+    register struct node *np;
+
+    /* sanity check - check that context has been read */
+    if (defpath == NULL)
+       adios (NULL, "oops, context hasn't been read yet");
+
+    snprintf (atrcur, sizeof(atrcur), "atr-%s-", current);
+    atrlen = strlen (atrcur);
+
+    for (np = m_defs; np; np = np->n_next)
+       if (ssequal (atrcur, np->n_name)
+               && !ssequal (nmhdir, np->n_name + atrlen))
+           get_folder_info (np->n_name + atrlen, NULL);
+}
diff --git a/uip/forw.c b/uip/forw.c
new file mode 100644 (file)
index 0000000..ade775e
--- /dev/null
@@ -0,0 +1,697 @@
+
+/*
+ * forw.c -- forward a message, or group of messages.
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+#include <h/fmt_scan.h>
+#include <zotnet/tws/tws.h>
+
+
+#define        IFORMAT "digest-issue-%s"
+#define        VFORMAT "digest-volume-%s"
+
+static struct swit switches[] = {
+#define        ANNOSW                 0
+    { "annotate", 0 },
+#define        NANNOSW                1
+    { "noannotate", 0 },
+#define        DFOLDSW                2
+    { "draftfolder +folder", 0 },
+#define        DMSGSW                 3
+    { "draftmessage msg", 0 },
+#define        NDFLDSW                4
+    { "nodraftfolder", 0 },
+#define        EDITRSW                5
+    { "editor editor", 0 },
+#define        NEDITSW                6
+    { "noedit", 0 },
+#define        FILTSW                 7
+    { "filter filterfile", 0 },
+#define        FORMSW                 8
+    { "form formfile", 0 },
+#define        FRMTSW                 9
+    { "format", 5 },
+#define        NFRMTSW               10
+    { "noformat", 7 },
+#define        INPLSW                11
+    { "inplace", 0 },
+#define        NINPLSW               12
+    { "noinplace", 0 },
+#define MIMESW                13
+    { "mime", 0 },
+#define NMIMESW               14
+    { "nomime", 0 },
+#define        DGSTSW                15
+    { "digest list", 0 },
+#define        ISSUESW               16
+    { "issue number", 0 },
+#define        VOLUMSW               17
+    { "volume number", 0 },
+#define        WHATSW                18
+    { "whatnowproc program", 0 },
+#define        NWHATSW               19
+    { "nowhatnowproc", 0 },
+#define        BITSTUFFSW            20
+    { "dashstuffing", 0 },             /* interface to mhl */
+#define        NBITSTUFFSW           21
+    { "nodashstuffing", 0 },
+#define VERSIONSW             22
+    { "version", 0 },
+#define        HELPSW                23
+    { "help", 4 },
+#define        FILESW                24
+    { "file file", -4 },               /* interface from msh */
+
+#ifdef MHE
+#define        BILDSW                25
+    { "build", -5 },                   /* interface from mhe */
+#endif /* MHE */
+
+    { NULL, 0 }
+};
+
+static struct swit aqrnl[] = {
+#define        NOSW         0
+    { "quit", 0 },
+#define        YESW         1
+    { "replace", 0 },
+#define        LISTDSW      2
+    { "list", 0 },
+#define        REFILSW      3
+    { "refile +folder", 0 },
+#define NEWSW        4
+    { "new", 0 },
+    { NULL, 0 }
+};
+
+static struct swit aqrl[] = {
+    { "quit", 0 },
+    { "replace", 0 },
+    { "list", 0 },
+    { "refile +folder", 0 },
+    { NULL, 0 }
+};
+
+static char drft[BUFSIZ];
+
+static char delim3[] =
+    "\n------------------------------------------------------------\n\n";
+static char delim4[] = "\n------------------------------\n\n";
+
+
+static struct msgs *mp = NULL;         /* used a lot */
+
+
+/*
+ * static prototypes
+ */
+static void mhl_draft (int, char *, int, int, char *, char *, int);
+static void copy_draft (int, char *, char *, int, int, int);
+static void copy_mime_draft (int);
+static int build_form (char *, char *, int, int);
+
+
+int
+main (int argc, char **argv)
+{
+    int msgp = 0, anot = 0, inplace = 1, mime = 0;
+    int issue = 0, volume = 0, dashstuff = 0;
+    int nedit = 0, nwhat = 0, i, in;
+    int out, isdf = 0, msgnum;
+    char *cp, *cwd, *maildir, *dfolder = NULL;
+    char *dmsg = NULL, *digest = NULL, *ed = NULL;
+    char *file = NULL, *filter = NULL, *folder = NULL;
+    char *form = NULL, buf[BUFSIZ], value[10];
+    char **argp, **arguments, *msgs[MAXARGS];
+    struct stat st;
+
+#ifdef MHE
+    int buildsw = 0;
+#endif /* MHE */
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown", cp);
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf), "%s [+folder] [msgs] [switches]",
+                       invo_name);
+                   print_help (buf, switches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case ANNOSW: 
+                   anot++;
+                   continue;
+               case NANNOSW: 
+                   anot = 0;
+                   continue;
+
+               case EDITRSW: 
+                   if (!(ed = *argp++) || *ed == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   nedit = 0;
+                   continue;
+               case NEDITSW:
+                   nedit++;
+                   continue;
+
+               case WHATSW: 
+                   if (!(whatnowproc = *argp++) || *whatnowproc == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   nwhat = 0;
+                   continue;
+#ifdef MHE
+               case BILDSW:
+                   buildsw++;  /* fall... */
+#endif /* MHE */
+               case NWHATSW: 
+                   nwhat++;
+                   continue;
+
+               case FILESW: 
+                   if (file)
+                       adios (NULL, "only one file at a time!");
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   file = path (cp, TFILE);
+                   continue;
+               case FILTSW:
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   filter = getcpy (etcpath (cp));
+                   mime = 0;
+                   continue;
+               case FORMSW: 
+                   if (!(form = *argp++) || *form == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+
+               case FRMTSW:
+                   filter = getcpy (etcpath (mhlforward));
+                   continue;
+               case NFRMTSW:
+                   filter = NULL;
+                   continue;
+
+               case INPLSW: 
+                   inplace++;
+                   continue;
+               case NINPLSW: 
+                   inplace = 0;
+                   continue;
+
+               case MIMESW:
+                   mime++;
+                   filter = NULL;
+                   continue;
+               case NMIMESW: 
+                   mime = 0;
+                   continue;
+
+               case DGSTSW: 
+                   if (!(digest = *argp++) || *digest == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   mime = 0;
+                   continue;
+               case ISSUESW:
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   if ((issue = atoi (cp)) < 1)
+                       adios (NULL, "bad argument %s %s", argp[-2], cp);
+                   continue;
+               case VOLUMSW:
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   if ((volume = atoi (cp)) < 1)
+                       adios (NULL, "bad argument %s %s", argp[-2], cp);
+                   continue;
+
+               case DFOLDSW: 
+                   if (dfolder)
+                       adios (NULL, "only one draft folder at a time!");
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   dfolder = path (*cp == '+' || *cp == '@' ? cp + 1 : cp,
+                                   *cp != '@' ? TFOLDER : TSUBCWF);
+                   continue;
+               case DMSGSW:
+                   if (dmsg)
+                       adios (NULL, "only one draft message at a time!");
+                   if (!(dmsg = *argp++) || *dmsg == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+               case NDFLDSW: 
+                   dfolder = NULL;
+                   isdf = NOTOK;
+                   continue;
+
+               case BITSTUFFSW: 
+                   dashstuff = 1;      /* trinary logic */
+                   continue;
+               case NBITSTUFFSW: 
+                   dashstuff = -1;     /* trinary logic */
+                   continue;
+           }
+       }
+       if (*cp == '+' || *cp == '@') {
+           if (folder)
+               adios (NULL, "only one folder at a time!");
+           else
+               folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       } else {
+           msgs[msgp++] = cp;
+       }
+    }
+
+    cwd = getcpy (pwd ());
+
+    if (!context_find ("path"))
+       free (path ("./", TFOLDER));
+    if (file && (msgp || folder))
+       adios (NULL, "can't mix files and folders/msgs");
+
+try_it_again:
+
+#ifdef MHE
+    strncpy (drft, buildsw ? m_maildir ("draft")
+                         : m_draft (dfolder, NULL, NOUSE, &isdf), sizeof(drft));
+
+    /* Check if a draft already exists */
+    if (!buildsw && stat (drft, &st) != NOTOK) {
+#else
+    strncpy (drft, m_draft (dfolder, dmsg, NOUSE, &isdf), sizeof(drft));
+
+    /* Check if a draft already exists */
+    if (stat (drft, &st) != NOTOK) {
+#endif /* MHE */
+       printf ("Draft \"%s\" exists (%ld bytes).", drft, (long) st.st_size);
+       for (i = LISTDSW; i != YESW;) {
+           if (!(argp = getans ("\nDisposition? ", isdf ? aqrnl : aqrl)))
+               done (1);
+           switch (i = smatch (*argp, isdf ? aqrnl : aqrl)) {
+               case NOSW: 
+                   done (0);
+               case NEWSW: 
+                   dmsg = NULL;
+                   goto try_it_again;
+               case YESW: 
+                   break;
+               case LISTDSW: 
+                   showfile (++argp, drft);
+                   break;
+               case REFILSW: 
+                   if (refile (++argp, drft) == 0)
+                       i = YESW;
+                   break;
+               default: 
+                   advise (NULL, "say what?");
+                   break;
+           }
+       }
+    }
+
+    if (file) {
+       /*
+        * Forwarding a file.
+         */
+       anot = 0;       /* don't want to annotate a file */
+    } else {
+       /*
+        * Forwarding a message.
+        */
+       if (!msgp)
+           msgs[msgp++] = "cur";
+       if (!folder)
+           folder = getfolder (1);
+       maildir = m_maildir (folder);
+
+       if (chdir (maildir) == NOTOK)
+           adios (maildir, "unable to change directory to");
+
+       /* read folder and create message structure */
+       if (!(mp = folder_read (folder)))
+           adios (NULL, "unable to read folder %s", folder);
+
+       /* check for empty folder */
+       if (mp->nummsg == 0)
+           adios (NULL, "no messages in %s", folder);
+
+       /* parse all the message ranges/sequences and set SELECTED */
+       for (msgnum = 0; msgnum < msgp; msgnum++)
+           if (!m_convert (mp, msgs[msgnum]))
+               done (1);
+       seq_setprev (mp);       /* set the previous sequence */
+    }
+
+    if (filter && access (filter, R_OK) == NOTOK)
+       adios (filter, "unable to read");
+
+    /*
+     * Open form (component) file.
+     */
+    if (digest) {
+       if (issue == 0) {
+           snprintf (buf, sizeof(buf), IFORMAT, digest);
+           if (volume == 0
+                   && (cp = context_find (buf))
+                   && ((issue = atoi (cp)) < 0))
+               issue = 0;
+           issue++;
+       }
+       if (volume == 0)
+           snprintf (buf, sizeof(buf), VFORMAT, digest);
+       if ((cp = context_find (buf)) == NULL || (volume = atoi (cp)) <= 0)
+           volume = 1;
+       if (!form)
+           form = digestcomps;
+       in = build_form (form, digest, volume, issue);
+    } else {
+       if (form) {
+           if ((in = open (etcpath (form), O_RDONLY)) == NOTOK)
+               adios (form, "unable to open form file");
+       } else {
+           if ((in = open (etcpath (forwcomps), O_RDONLY)) == NOTOK)
+               adios (forwcomps, "unable to open default components file");
+           form = forwcomps;
+       }
+    }
+
+    if ((out = creat (drft, m_gmprot ())) == NOTOK)
+       adios (drft, "unable to create");
+
+    /*
+     * copy the components into the draft
+     */
+    cpydata (in, out, form, drft);
+    close (in);
+
+    if (file) {
+       /* just copy the file into the draft */
+       if ((in = open (file, O_RDONLY)) == NOTOK)
+           adios (file, "unable to open");
+       cpydata (in, out, file, drft);
+       close (in);
+       close (out);
+    } else {
+       /*
+        * If filter file is defined, then format the
+        * messages into the draft using mhlproc.
+        */
+       if (filter)
+           mhl_draft (out, digest, volume, issue, drft, filter, dashstuff);
+       else if (mime)
+           copy_mime_draft (out);
+       else
+           copy_draft (out, digest, drft, volume, issue, dashstuff);
+       close (out);
+
+       if (digest) {
+           snprintf (buf, sizeof(buf), IFORMAT, digest);
+           snprintf (value, sizeof(value), "%d", issue);
+           context_replace (buf, getcpy (value));
+           snprintf (buf, sizeof(buf), VFORMAT, digest);
+           snprintf (value, sizeof(value), "%d", volume);
+           context_replace (buf, getcpy (value));
+       }
+
+       context_replace (pfolder, folder);      /* update current folder   */
+       seq_setcur (mp, mp->lowsel);            /* update current message  */
+       seq_save (mp);                          /* synchronize sequences   */
+       context_save ();                        /* save the context file   */
+    }
+
+    if (nwhat)
+       done (0);
+    what_now (ed, nedit, NOUSE, drft, NULL, 0, mp,
+       anot ? "Forwarded" : NULL, inplace, cwd);
+    done (1);
+}
+
+
+/*
+ * Filter the messages you are forwarding, into the
+ * draft calling the mhlproc, and reading its output
+ * from a pipe.
+ */
+
+static void
+mhl_draft (int out, char *digest, int volume, int issue,
+            char *file, char *filter, int dashstuff)
+{
+    pid_t child_id;
+    int i, msgnum, pd[2];
+    char *vec[MAXARGS];
+    char buf1[BUFSIZ];
+    char buf2[BUFSIZ];
+    
+    if (pipe (pd) == NOTOK)
+       adios ("pipe", "unable to create");
+
+    vec[0] = r1bindex (mhlproc, '/');
+
+    for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
+       sleep (5);
+    switch (child_id) {
+       case NOTOK: 
+           adios ("fork", "unable to");
+
+       case OK: 
+           close (pd[0]);
+           dup2 (pd[1], 1);
+           close (pd[1]);
+
+           i = 1;
+           vec[i++] = "-forwall";
+           vec[i++] = "-form";
+           vec[i++] = filter;
+
+           if (digest) {
+               vec[i++] = "-digest";
+               vec[i++] = digest;
+               vec[i++] = "-issue";
+               snprintf (buf1, sizeof(buf1), "%d", issue);
+               vec[i++] = buf1;
+               vec[i++] = "-volume";
+               snprintf (buf2, sizeof(buf2), "%d", volume);
+               vec[i++] = buf2;
+           }
+
+           /*
+            * Are we dashstuffing (quoting) the lines that begin
+            * with `-'.  We use the mhl default (don't add any flag)
+            * unless the user has specified a specific flag.
+            */
+           if (dashstuff > 0)
+               vec[i++] = "-dashstuffing";
+           else if (dashstuff < 0)
+               vec[i++] = "-nodashstuffing";
+
+           if (mp->numsel >= MAXARGS - i)
+               adios (NULL, "more than %d messages for %s exec",
+                       vec[0], MAXARGS - i);
+
+           /*
+            * Now add the message names to filter.  We can only
+            * handle about 995 messages (because vec is fixed size),
+            * but that should be plenty.
+            */
+           for (msgnum = mp->lowsel; msgnum <= mp->hghsel && i < sizeof(vec) - 1;
+                       msgnum++)
+               if (is_selected (mp, msgnum))
+                   vec[i++] = getcpy (m_name (msgnum));
+           vec[i] = NULL;
+
+           execvp (mhlproc, vec);
+           fprintf (stderr, "unable to exec ");
+           perror (mhlproc);
+           _exit (-1);
+
+       default: 
+           close (pd[1]);
+           cpydata (pd[0], out, vec[0], file);
+           close (pd[0]);
+           pidXwait(child_id, mhlproc);
+           break;
+    }
+}
+
+
+/*
+ * Copy the messages into the draft.  The messages are
+ * not filtered through the mhlproc.  Do dashstuffing if
+ * necessary.
+ */
+
+static void
+copy_draft (int out, char *digest, char *file, int volume, int issue, int dashstuff)
+{
+    int fd,i, msgcnt, msgnum;
+    int len, buflen;
+    register char *bp, *msgnam;
+    char buffer[BUFSIZ];
+
+    msgcnt = 1;
+    for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
+       if (is_selected (mp, msgnum)) {
+           if (digest) {
+               strncpy (buffer, msgnum == mp->lowsel ? delim3 : delim4,
+                       sizeof(buffer));
+           } else {
+               /* Get buffer ready to go */
+               bp = buffer;
+               buflen = sizeof(buffer);
+
+               strncpy (bp, "\n-------", buflen);
+               len = strlen (bp);
+               bp += len;
+               buflen -= len;
+
+               if (msgnum == mp->lowsel) {
+                   snprintf (bp, buflen, " Forwarded Message%s",
+                       mp->numsel > 1 ? "s" : "");
+               } else {
+                   snprintf (bp, buflen, " Message %d", msgcnt);
+               }
+               len = strlen (bp);
+               bp += len;
+               buflen -= len;
+
+               strncpy (bp, "\n\n", buflen);
+           }
+           write (out, buffer, strlen (buffer));
+
+           if ((fd = open (msgnam = m_name (msgnum), O_RDONLY)) == NOTOK) {
+               admonish (msgnam, "unable to read message");
+               continue;
+           }
+
+           /*
+            * Copy the message.  Add RFC934 quoting (dashstuffing)
+            * unless given the -nodashstuffing flag.
+            */
+           if (dashstuff >= 0)
+               cpydgst (fd, out, msgnam, file);
+           else
+               cpydata (fd, out, msgnam, file);
+
+           close (fd);
+           msgcnt++;
+       }
+    }
+
+    if (digest) {
+       strncpy (buffer, delim4, sizeof(buffer));
+    } else {
+       snprintf (buffer, sizeof(buffer), "\n------- End of Forwarded Message%s\n\n",
+               mp->numsel > 1 ? "s" : "");
+    }
+    write (out, buffer, strlen (buffer));
+
+    if (digest) {
+       snprintf (buffer, sizeof(buffer), "End of %s Digest [Volume %d Issue %d]\n",
+               digest, volume, issue);
+       i = strlen (buffer);
+       for (bp = buffer + i; i > 1; i--)
+           *bp++ = '*';
+       *bp++ = '\n';
+       *bp = 0;
+       write (out, buffer, strlen (buffer));
+    }
+}
+
+
+/*
+ * Create a mhn composition file for forwarding message.
+ */
+
+static void
+copy_mime_draft (int out)
+{
+    int msgnum;
+    char buffer[BUFSIZ];
+
+    snprintf (buffer, sizeof(buffer), "#forw [forwarded message%s] +%s",
+       mp->numsel == 1 ? "" : "s", mp->foldpath);
+    write (out, buffer, strlen (buffer));
+    for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
+       if (is_selected (mp, msgnum)) {
+           snprintf (buffer, sizeof(buffer), " %s", m_name (msgnum));
+           write (out, buffer, strlen (buffer));
+       }
+    write (out, "\n", 1);
+}
+
+
+static int
+build_form (char *form, char *digest, int volume, int issue)
+{
+    int        in;
+    int fmtsize;
+    register char *nfs;
+    char *line, tmpfil[BUFSIZ];
+    register FILE *tmp;
+    register struct comp *cptr;
+    struct format *fmt;
+    int dat[5];
+
+    /* Get new format string */
+    nfs = new_fs (form, NULL, NULL);
+    fmtsize = strlen (nfs) + 256;
+
+    /* Compile format string */
+    fmt_compile (nfs, &fmt);
+
+    FINDCOMP (cptr, "digest");
+    if (cptr)
+       cptr->c_text = digest;
+    FINDCOMP (cptr, "date");
+    if (cptr)
+       cptr->c_text = getcpy(dtimenow (0));
+
+    dat[0] = issue;
+    dat[1] = volume;
+    dat[2] = 0;
+    dat[3] = fmtsize;
+    dat[4] = 0;
+
+    strncpy (tmpfil, m_tmpfil (invo_name), sizeof(tmpfil));
+    if ((tmp = fopen (tmpfil, "w+")) == NULL)
+       adios (tmpfil, "unable to create");
+    unlink (tmpfil);
+    if ((in = dup (fileno (tmp))) == NOTOK)
+       adios ("dup", "unable to");
+
+    if ((line = malloc ((unsigned) fmtsize)) == NULL)
+       adios (NULL, "unable to allocate format line storage");
+    fmt_scan (fmt, line, fmtsize, dat);
+    fputs (line, tmp);
+    free (line);
+    if (fclose (tmp))
+       adios (tmpfil, "error writing");
+
+    lseek (in, (off_t) 0, SEEK_SET);
+    return in;
+}
diff --git a/uip/ftpsbr.c b/uip/ftpsbr.c
new file mode 100644 (file)
index 0000000..d36e0e5
--- /dev/null
@@ -0,0 +1,523 @@
+
+/*
+ * ftpsbr.c -- simple FTP client library
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/mime.h>
+
+#ifdef HAVE_ARPA_FTP_H
+# include <arpa/ftp.h>
+#endif
+
+#define        v_debug debugsw
+#define        v_verbose verbosw
+
+static int ftp_fd = NOTOK;
+static int data_fd = NOTOK;
+
+static int v_noise;
+
+extern int v_debug;
+extern int v_verbose;
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#if defined(BIND) && !defined(h_addr)
+# define h_addr        h_addr_list[0]
+#endif
+
+#define        inaddr_copy(hp,sin) \
+    memcpy((char *) &((sin)->sin_addr), (hp)->h_addr, (hp)->h_length)
+
+extern int errno;
+
+#define        start_tcp_client(sock,priv) \
+       socket (AF_INET, SOCK_STREAM, 0)
+
+#define        join_tcp_server(fd, sock) \
+       connect ((fd), (struct sockaddr *) (sock), sizeof *(sock))
+
+/*
+ * prototypes
+ */
+struct hostent *gethostbystring ();
+int ftp_get (char *, char *, char *, char *, char *, char *, int, int);
+int ftp_trans (char *, char *, char *, char *, char *, char *, char *, int, int);
+
+/*
+ * static prototypes
+ */
+static int start_tcp_server (struct sockaddr_in *, int, int, int);
+static void _asnprintf (char *, int, char *, va_list);
+static int ftp_quit (void);
+static int ftp_read (char *, char *, char *, int);
+static int initconn (void);
+static int dataconn (void);
+static int command (int arg1, ...);
+static int vcommand (int, va_list);
+static int getreply (int, int);
+
+
+static int
+start_tcp_server (struct sockaddr_in *sock, int backlog, int opt1, int opt2)
+{
+    int eindex, sd;
+
+    if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == NOTOK)
+       return NOTOK;
+
+    if (bind (sd, (struct sockaddr *) sock, sizeof *sock) == NOTOK) {
+       eindex = errno;
+       close (sd);
+       errno = eindex;
+    } else {
+       listen (sd, backlog);
+    }
+
+    return sd;
+}
+
+
+static int __len__;
+
+#define        join_tcp_client(fd,sock) \
+       accept ((fd), (struct sockaddr *) (sock), \
+               (__len__ = sizeof *(sock), &__len__))
+
+#define        read_tcp_socket  read
+#define        write_tcp_socket write
+#define        close_tcp_socket close
+
+static void
+_asnprintf (char *bp, int len_bp, char *what, va_list ap)
+{
+    int eindex, len;
+    char *fmt;
+
+    eindex = errno;
+
+    *bp = '\0';
+    fmt = va_arg (ap, char *);
+
+    if (fmt) {
+       vsnprintf(bp, len_bp, fmt, ap);
+       len = strlen(bp);
+       bp += len;
+       len_bp -= len;
+    }
+
+    if (what) {
+       char *s;
+
+       if (*what) {
+           snprintf (bp, len_bp, " %s: ", what);
+           len = strlen (bp);
+           bp += len;
+           len_bp -= len;
+       }
+       if ((s = strerror(eindex)))
+           strncpy (bp, s, len_bp);
+       else
+           snprintf (bp, len_bp, "Error %d", eindex);
+       bp += strlen (bp);
+    }
+
+    errno = eindex;
+}
+
+
+int
+ftp_get (char *host, char *user, char *password, char *cwd,
+         char *remote, char *local, int ascii, int stayopen)
+{
+    return ftp_trans (host, user, password, cwd, remote, local,
+                      "RETR", ascii, stayopen);
+}
+
+
+int
+ftp_trans (char *host, char *user, char *password, char *cwd, char *remote,
+           char *local, char *cmd, int ascii, int stayopen)
+{
+    int        result;
+
+    if (stayopen <= 0) {
+       result = ftp_quit ();
+       if (host == NULL)
+           return result;
+    }
+
+    if (ftp_fd == NOTOK) {
+       struct sockaddr_in in_socket;
+       register struct hostent *hp;
+       register struct servent *sp;
+
+       if ((sp = getservbyname ("ftp", "tcp")) == NULL) {
+           fprintf (stderr, "tcp/ftp: unknown service");
+           return NOTOK;
+       }
+       if ((hp = gethostbystring (host)) == NULL) {
+           fprintf (stderr, "%s: unknown host\n", host);
+           return NOTOK;
+       }
+       in_socket.sin_family = hp->h_addrtype;
+       inaddr_copy (hp, &in_socket);
+       in_socket.sin_port = sp->s_port;
+
+       if ((ftp_fd = start_tcp_client ((struct sockaddr_in *) NULL, 0))
+               == NOTOK) {
+           perror (host);
+           return NOTOK;
+       }
+       if (join_tcp_server (ftp_fd, &in_socket) == NOTOK) {
+           perror (host);
+           close_tcp_socket (ftp_fd), ftp_fd = NOTOK;
+           return NOTOK;
+       }
+       getreply (1, 0);
+
+       if (v_verbose) {
+           fprintf (stdout, "Connected to %s\n", host);
+           fflush (stdout);
+       }
+
+       if (user) {
+           if ((result = command (0, "USER %s", user)) == CONTINUE)
+               result = command (1, "PASS %s", password);
+           if (result != COMPLETE) {
+               result = NOTOK;
+               goto out;
+           }
+       }
+
+       if (remote == NULL)
+           return OK;
+    }
+
+    if (cwd && ((result = command (0, "CWD %s", cwd)) != COMPLETE
+                   && result != CONTINUE)) {
+       result = NOTOK;
+       goto out;
+    }
+
+    if (command (1, ascii ? "TYPE A" : "TYPE I") != COMPLETE) {
+       result = NOTOK;
+       goto out;
+    }
+
+    result = ftp_read (remote, local, cmd, ascii);
+
+out: ;
+    if (result != OK || !stayopen)
+       ftp_quit ();
+
+    return result;
+}
+
+
+static int
+ftp_quit (void)
+{
+    int        n;
+
+    if (ftp_fd == NOTOK)
+       return OK;
+
+    n = command (1, "QUIT");
+    close_tcp_socket (ftp_fd), ftp_fd = NOTOK;
+    return (n == 0 || n == COMPLETE ? OK : NOTOK);
+}
+
+static int
+ftp_read (char *remote, char *local, char *cmd, int ascii)
+{
+    int        istdio = 0, istore;
+    register int cc;
+    int        expectingreply = 0;
+    char buffer[BUFSIZ];
+    FILE *fp = NULL;
+
+    if (initconn () == NOTOK)
+       goto bad;
+
+    v_noise = v_verbose;
+    if (command (-1, *remote ? "%s %s" : "%s", cmd, remote) != PRELIM)
+       goto bad;
+
+    expectingreply++;
+    if (dataconn () == NOTOK) {
+bad: ;
+        if (fp && !istdio)
+           fclose (fp);
+       if (data_fd != NOTOK)
+           close_tcp_socket (data_fd), data_fd = NOTOK;
+       if (expectingreply)
+           getreply (-2, 0);
+
+       return NOTOK;
+    }
+
+    istore = !strcmp (cmd, "STOR");
+
+    if ((istdio = !strcmp (local, "-")))
+       fp = istore ? stdin : stdout;
+    else
+       if ((fp = fopen (local, istore ? "r" : "w")) == NULL) {
+           perror (local);
+           goto bad;
+       }
+
+    if (istore) {
+       if (ascii) {
+           int c;
+           FILE *out;
+
+           if (!(out = fdopen (data_fd, "w"))) {
+               perror ("fdopen");
+               goto bad;
+           }
+
+           while ((c = getc (fp)) != EOF) {
+               if (c == '\n')
+                   putc ('\r', out);
+               if (putc (c, out) == EOF) {
+                   perror ("putc");
+                   fclose (out);
+                   data_fd = NOTOK;
+                   goto bad;
+               }
+           }
+
+           fclose (out);
+           data_fd = NOTOK;
+       }
+       else {
+           while ((cc = fread (buffer, sizeof *buffer, sizeof buffer, fp)) > 0)
+               if (write_tcp_socket (data_fd, buffer, cc) != cc) {
+                   perror ("write_tcp_socket");
+                   goto bad;
+               }
+
+           close_tcp_socket (data_fd), data_fd = NOTOK;
+       }
+    }
+    else {
+       if (ascii) {
+           int c;
+           FILE *in;
+
+           if (!(in = fdopen (data_fd, "r"))) {
+               perror ("fdopen");
+               goto bad;
+           }
+
+           while ((c = getc (in)) != EOF) {
+               if (c == '\r')
+                   switch (c = getc (in)) {
+                       case EOF:
+                       case '\0':
+                           c = '\r';
+                           break;
+
+                       case '\n':
+                           break;
+
+                       default:
+                           putc ('\r', fp);
+                           break;
+                       }
+
+               if (putc (c, fp) == EOF) {
+                   perror ("putc");
+                   fclose (in);
+                   data_fd = NOTOK;
+                   goto bad;
+               }
+           }
+
+           fclose (in);
+           data_fd = NOTOK;
+       }
+       else {
+           while ((cc = read_tcp_socket (data_fd, buffer, sizeof buffer)) > 0)
+               if (fwrite (buffer, sizeof *buffer, cc, fp) == 0) {
+                   perror ("fwrite");
+                   goto bad;
+               }
+           if (cc < 0) {
+               perror ("read_tcp_socket");
+               goto bad;
+           }
+
+           close_tcp_socket (data_fd), data_fd = NOTOK;
+       }
+    }
+
+    if (!istdio)
+       fclose (fp);
+
+    v_noise = v_verbose;
+    return (getreply (1, 0) == COMPLETE ? OK : NOTOK);
+}
+
+
+#define        UC(b) (((int) b) & 0xff)
+
+static int
+initconn (void)
+{
+    int        len;
+    register char *a, *p;
+    struct sockaddr_in in_socket;
+
+    if (getsockname (ftp_fd, (struct sockaddr *) &in_socket,
+                    (len = sizeof(in_socket), &len)) == NOTOK) {
+       perror ("getsockname");
+       return NOTOK;
+    }
+    in_socket.sin_port = 0;
+    if ((data_fd = start_tcp_server (&in_socket, 1, 0, 0)) == NOTOK) {
+       perror ("start_tcp_server");
+       return NOTOK;
+    }
+
+    if (getsockname (data_fd, (struct sockaddr *) &in_socket,
+                    (len = sizeof in_socket, &len)) == NOTOK) {
+       perror ("getsockname");
+       return NOTOK;
+    }
+
+    a = (char *) &in_socket.sin_addr;
+    p = (char *) &in_socket.sin_port;
+
+    if (command (1, "PORT %d,%d,%d,%d,%d,%d",
+                     UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+                     UC(p[0]), UC(p[1])) == COMPLETE)
+       return OK;
+
+    return NOTOK;
+}
+
+static int
+dataconn (void)
+{
+    int        fd;
+    struct sockaddr_in in_socket;
+    
+    if ((fd = join_tcp_client (data_fd, &in_socket)) == NOTOK) {
+       perror ("join_tcp_client");
+       return NOTOK;
+    }
+    close_tcp_socket (data_fd);
+    data_fd = fd;
+
+    return OK;
+}
+
+
+static int
+command (int arg1, ...)
+{
+    int        val;
+    va_list ap;
+
+    va_start (ap, arg1);
+    val = vcommand (arg1, ap);
+    va_end (ap);
+
+    return val;
+}
+
+static int
+vcommand (int complete, va_list ap)
+{
+    int        len;
+    char buffer[BUFSIZ];
+
+    if (ftp_fd == NOTOK)
+       return NOTOK;
+
+    _asnprintf (buffer, sizeof(buffer), NULL, ap);
+    if (v_debug)
+       fprintf (stderr, "<--- %s\n", buffer);
+
+    strcat (buffer, "\r\n");
+    len = strlen (buffer);
+
+    if (write_tcp_socket (ftp_fd, buffer, len) != len) {
+       perror ("write_tcp_socket");
+       return NOTOK;
+    }
+
+    return (getreply (complete, !strcmp (buffer, "QUIT")));
+}
+
+
+static int
+getreply (int complete, int expecteof)
+{
+    for (;;) {
+       register int code, dig, n;
+       int continuation;
+       register char *bp;
+       char buffer[BUFSIZ];
+
+       code = dig = n = continuation = 0;
+       bp = buffer;
+
+       for (;;) {
+           char c;
+
+           if (read_tcp_socket (ftp_fd, &c, 1) < 1) {
+               if (expecteof)
+                   return OK;
+
+               perror ("read_tcp_socket");
+               return DONE;
+           }
+           if (c == '\n')
+               break;
+           *bp++ = c != '\r' ? c : '\0';
+
+           dig++;
+           if (dig < 4) {
+               if (isdigit(c))
+                   code = code * 10 + (c - '0');
+               else                            /* XXX: naughty FTP... */
+                   if (isspace (c))
+                       continuation++;
+           }
+           else
+               if (dig == 4 && c == '-')
+                   continuation++;
+           if (n == 0)
+               n = c;
+       }
+
+       if (v_debug)
+           fprintf (stderr, "---> %s\n", buffer);
+       if (continuation)
+           continue;
+
+       n -= '0';
+
+       if (v_noise) {
+           fprintf (stdout, "%s\n", buffer);
+           fflush (stdout);
+           v_noise = 0;
+       }
+       else
+           if ((complete == -1 && n != PRELIM)
+                   || (complete == 0 && n != CONTINUE && n != COMPLETE)
+                   || (complete == 1 && n != COMPLETE))
+               fprintf (stderr, "%s\n", buffer);
+
+       return n;
+    }
+}
diff --git a/uip/inc.c b/uip/inc.c
new file mode 100644 (file)
index 0000000..9752b5a
--- /dev/null
+++ b/uip/inc.c
@@ -0,0 +1,939 @@
+
+/*
+ * inc.c -- incorporate messages from a maildrop into a folder
+ *
+ * $Id$
+ */
+
+#ifdef MAILGROUP
+/* Revised: Sat Apr 14 17:08:17 PDT 1990 (marvit@hplabs)
+ *    Added hpux hacks to set and reset gid to be "mail" as needed. The reset
+ *    is necessary so inc'ed mail is the group of the inc'er, rather than
+ *    "mail". We setgid to egid only when [un]locking the mail file. This
+ *    is also a major security precaution which will not be explained here.
+ *
+ * Fri Feb  7 16:04:57 PST 1992                John Romine <bug-mh@ics.uci.edu>
+ *   NB: I'm not 100% sure that this setgid stuff is secure even now.
+ */
+#endif
+
+#include <h/mh.h>
+#include <fcntl.h>
+
+#ifdef POP
+# include <h/dropsbr.h>
+# include <h/popsbr.h>
+#endif
+
+#ifdef HESIOD
+# include <hesiod.h>
+#endif
+
+#include <h/fmt_scan.h>
+#include <h/scansbr.h>
+#include <h/signals.h>
+#include <zotnet/tws/tws.h>
+#include <zotnet/mts/mts.h>
+#include <errno.h>
+#include <signal.h>
+
+#ifndef        POP
+# define POPminc(a) (a)
+#else
+# define POPminc(a)  0
+#endif
+
+#ifndef        RPOP
+# define RPOPminc(a) (a)
+#else
+# define RPOPminc(a)  0
+#endif
+
+#ifndef        APOP
+# define APOPminc(a) (a)
+#else
+# define APOPminc(a)  0
+#endif
+
+static struct swit switches[] = {
+#define        AUDSW                      0
+    { "audit audit-file", 0 },
+#define        NAUDSW                     1
+    { "noaudit", 0 },
+#define        CHGSW                      2
+    { "changecur", 0 },
+#define        NCHGSW                     3
+    { "nochangecur", 0 },
+#define        FILESW                     4
+    { "file name", 0 },
+#define        FORMSW                     5
+    { "form formatfile", 0 },
+#define        FMTSW                      6
+    { "format string", 5 },
+#define        HOSTSW                     7
+    { "host hostname", POPminc (-4) },
+#define        USERSW                     8
+    { "user username", POPminc (-4) },
+#define        PACKSW                     9
+    { "pack file", POPminc (-4) },
+#define        NPACKSW                   10
+    { "nopack", POPminc (-6) },
+#define        APOPSW                    11
+    { "apop", APOPminc (-4) },
+#define        NAPOPSW                   12
+    { "noapop", APOPminc (-6) },
+#define        RPOPSW                    13
+    { "rpop", RPOPminc (-4) },
+#define        NRPOPSW                   14
+    { "norpop", RPOPminc (-6) },
+#define        SILSW                     15
+    { "silent", 0 },
+#define        NSILSW                    16
+    { "nosilent", 0 },
+#define        TRNCSW                    17
+    { "truncate", 0 },
+#define        NTRNCSW                   18
+    { "notruncate", 0 },
+#define        WIDTHSW                   19
+    { "width columns", 0 },
+#define VERSIONSW                 20
+    { "version", 0 },
+#define        HELPSW                    21
+    { "help", 4 },
+#define SNOOPSW                   22
+    { "snoop", -5 },
+    { NULL, 0 }
+};
+
+extern int errno;
+
+/*
+ * flags for the mail source
+ */
+#define INC_FILE  0
+#define INC_POP   1
+
+static int inc_type;
+static int snoop = 0;
+
+#ifdef POP
+extern char response[];
+
+static char *packfile = NULL;
+static int size;
+static long pos;
+static long start;
+static long stop;
+
+static int mbx_style = MMDF_FORMAT;
+static int pd = NOTOK;
+static FILE *pf = NULL;
+#endif /* POP */
+
+
+/*
+ * For setting and returning to "mail" gid
+ */
+#ifdef MAILGROUP
+static int return_gid;
+#endif
+
+/*
+ * prototypes
+ */
+char *map_name(char *);
+
+#ifdef POP
+void done(int);
+static int pop_action(char *);
+static int pop_pack(char *);
+static int map_count(void);
+#endif
+
+
+int
+main (int argc, char **argv)
+{
+    int chgflag = 1, trnflag = 1;
+    int noisy = 1, width = 0, locked = 0;
+    int rpop, i, hghnum, msgnum;
+    char *cp, *maildir, *folder = NULL;
+    char *format = NULL, *form = NULL;
+    char *newmail, *host = NULL, *user = NULL;
+    char *audfile = NULL, *from = NULL;
+    char buf[BUFSIZ], **argp, *nfs, **arguments;
+    struct msgs *mp;
+    struct stat st, s1;
+    FILE *in, *aud = NULL;
+
+#ifdef POP
+    int nmsgs, nbytes, p = 0;
+    char *pass = NULL;
+#endif
+
+#ifdef MHE
+    FILE *mhe = NULL;
+#endif
+
+#ifdef HESIOD
+    struct hes_postoffice *po;
+    char *tmphost;
+#endif
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    mts_init (invo_name);
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+#ifdef POP
+# ifdef HESIOD
+    /*
+     * Scheme is:
+     *        use MAILHOST environment variable if present,
+     *  else try Hesiod.
+     *  If that fails, use the default (if any)
+     *  provided by mts.conf in mts_init()
+     */
+    if ((tmphost = getenv("MAILHOST")) != NULL)
+       pophost = tmphost;
+    else if ((po = hes_getmailhost(getusername())) != NULL &&
+            strcmp(po->po_type, "POP") == 0)
+       pophost = po->po_host;
+# endif /* HESIOD */
+    /*
+     * If there is a valid "pophost" entry in mts.conf,
+     * then use it as the default host.
+     */
+    if (pophost && *pophost)
+       host = pophost;
+
+    if ((cp = getenv ("MHPOPDEBUG")) && *cp)
+       snoop++;
+#endif /* POP */
+
+#ifdef KPOP
+    rpop = 1;
+#else
+    rpop = 0;
+#endif
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+           case AMBIGSW: 
+               ambigsw (cp, switches);
+               done (1);
+           case UNKWNSW: 
+               adios (NULL, "-%s unknown", cp);
+
+           case HELPSW: 
+               snprintf (buf, sizeof(buf), "%s [+folder] [switches]", invo_name);
+               print_help (buf, switches, 1);
+               done (1);
+           case VERSIONSW:
+               print_version(invo_name);
+               done (1);
+
+           case AUDSW: 
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               audfile = getcpy (m_maildir (cp));
+               continue;
+           case NAUDSW: 
+               audfile = NULL;
+               continue;
+
+           case CHGSW: 
+               chgflag++;
+               continue;
+           case NCHGSW: 
+               chgflag = 0;
+               continue;
+
+           /*
+            * The flag `trnflag' has the value:
+            *
+            * 2 if -truncate is given
+            * 1 by default (truncating is default)
+            * 0 if -notruncate is given
+            */
+           case TRNCSW: 
+               trnflag = 2;
+               continue;
+           case NTRNCSW: 
+               trnflag = 0;
+               continue;
+
+           case FILESW: 
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               from = path (cp, TFILE);
+
+               /*
+                * If the truncate file is in default state,
+                * change to not truncate.
+                */
+               if (trnflag == 1)
+                   trnflag = 0;
+               continue;
+
+           case SILSW: 
+               noisy = 0;
+               continue;
+           case NSILSW: 
+               noisy++;
+               continue;
+
+           case FORMSW: 
+               if (!(form = *argp++) || *form == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               format = NULL;
+               continue;
+           case FMTSW: 
+               if (!(format = *argp++) || *format == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               form = NULL;
+               continue;
+
+           case WIDTHSW: 
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               width = atoi (cp);
+               continue;
+
+           case HOSTSW:
+               if (!(host = *argp++) || *host == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               continue;
+           case USERSW:
+               if (!(user = *argp++) || *user == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               continue;
+
+           case PACKSW:
+#ifndef        POP
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+#else /* POP */
+               if (!(packfile = *argp++) || *packfile == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+#endif /* POP */
+               continue;
+           case NPACKSW:
+#ifdef POP
+               packfile = NULL;
+#endif /* POP */
+               continue;
+
+           case APOPSW:
+               rpop = -1;
+               continue;
+           case NAPOPSW:
+               rpop = 0;
+               continue;
+
+           case RPOPSW:
+               rpop = 1;
+               continue;
+           case NRPOPSW:
+               rpop = 0;
+               continue;
+
+           case SNOOPSW:
+               snoop++;
+               continue;
+           }
+       }
+       if (*cp == '+' || *cp == '@') {
+           if (folder)
+               adios (NULL, "only one folder at a time!");
+           else
+               folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       } else {
+           adios (NULL, "usage: %s [+folder] [switches]", invo_name);
+       }
+    }
+
+#ifdef MAILGROUP
+    return_gid = getegid();  /* Save effective gid, assuming we'll use it */
+    setgid(getgid());        /* Turn off extraordinary privileges         */
+#endif /* MAILGROUP */
+
+#ifdef POP
+    if (host && !*host)
+       host = NULL;
+    if (from || !host || rpop <= 0)
+       setuid (getuid ());
+#endif /* POP */
+
+    /*
+     * Where are we getting the new mail?
+     */
+    if (from)
+       inc_type = INC_FILE;
+#ifdef POP
+    else if (host)
+       inc_type = INC_POP;
+#endif
+    else
+       inc_type = INC_FILE;
+
+#ifdef POP
+    /*
+     * Are we getting the mail from
+     * a POP server?
+     */
+    if (inc_type == INC_POP) {
+       if (user == NULL)
+           user = getusername ();
+       if (rpop > 0)
+           pass = getusername ();
+       else
+           ruserpass (host, &user, &pass);
+
+       /*
+        * initialize POP connection
+        */
+       if (pop_init (host, user, pass, snoop, rpop) == NOTOK)
+           adios (NULL, "%s", response);
+
+       /* Check if there are any messages */
+       if (pop_stat (&nmsgs, &nbytes) == NOTOK)
+           adios (NULL, "%s", response);
+
+       if (rpop > 0)
+           setuid (getuid ());
+       if (nmsgs == 0) {
+           pop_quit();
+           adios (NULL, "no mail to incorporate");
+       }
+    }
+#endif /* POP */
+
+    /*
+     * We will get the mail from a file
+     * (typically the standard maildrop)
+     */
+
+    if (inc_type == INC_FILE) {
+       if (from)
+           newmail = from;
+       else if ((newmail = getenv ("MAILDROP")) && *newmail)
+           newmail = m_mailpath (newmail);
+       else if ((newmail = context_find ("maildrop")) && *newmail)
+           newmail = m_mailpath (newmail);
+       else {
+           newmail = concat (MAILDIR, "/", MAILFIL, NULL);
+       }
+       if (stat (newmail, &s1) == NOTOK || s1.st_size == 0)
+           adios (NULL, "no mail to incorporate");
+    }
+
+#ifdef POP
+    /* skip the folder setup */
+    if ((inc_type == INC_POP) && packfile)
+       goto go_to_it;
+#endif /* POP */
+
+    if (!context_find ("path"))
+       free (path ("./", TFOLDER));
+    if (!folder)
+       folder = getfolder (0);
+    maildir = m_maildir (folder);
+
+    if (stat (maildir, &st) == NOTOK) {
+       if (errno != ENOENT)
+           adios (maildir, "error on folder");
+       cp = concat ("Create folder \"", maildir, "\"? ", NULL);
+       if (noisy && !getanswer (cp))
+           done (1);
+       free (cp);
+       if (!makedir (maildir))
+           adios (NULL, "unable to create folder %s", maildir);
+    }
+
+    if (chdir (maildir) == NOTOK)
+       adios (maildir, "unable to change directory to");
+
+    /* read folder and create message structure */
+    if (!(mp = folder_read (folder)))
+       adios (NULL, "unable to read folder %s", folder);
+
+#ifdef POP
+go_to_it:
+#endif /* POP */
+
+    if (inc_type == INC_FILE) {
+       if (access (newmail, W_OK) != NOTOK) {
+           locked++;
+           if (trnflag) {
+               SIGNAL (SIGHUP, SIG_IGN);
+               SIGNAL (SIGINT, SIG_IGN);
+               SIGNAL (SIGQUIT, SIG_IGN);
+               SIGNAL (SIGTERM, SIG_IGN);
+           }
+
+#ifdef MAILGROUP
+           setgid(return_gid); /* Reset gid to lock mail file */
+#endif /* MAILGROUP */
+
+           /* lock and fopen the mail spool */
+           if ((in = lkfopen (newmail, "r")) == NULL)
+               adios (NULL, "unable to lock and fopen %s", newmail);
+
+#ifdef MAILGROUP
+           setgid(getgid());   /* Return us to normal privileges */
+#endif /* MAILGROUP */
+           fstat (fileno(in), &s1);
+       } else {
+           trnflag = 0;
+           if ((in = fopen (newmail, "r")) == NULL)
+               adios (newmail, "unable to read");
+       }
+    }
+
+#ifdef MAILGROUP
+    setgid(getgid());  /* Return us to normal privileges */
+#endif /* MAILGROUP */
+
+    if (audfile) {
+       if ((i = stat (audfile, &st)) == NOTOK)
+           advise (NULL, "Creating Receive-Audit: %s", audfile);
+       if ((aud = fopen (audfile, "a")) == NULL)
+           adios (audfile, "unable to append to");
+       else if (i == NOTOK)
+           chmod (audfile, m_gmprot ());
+
+#ifdef POP
+       fprintf (aud, from ? "<<inc>> %s -ms %s\n"
+                : host ? "<<inc>> %s -host %s -user %s%s\n"
+                : "<<inc>> %s\n",
+                dtimenow (0), from ? from : host, user,
+                rpop < 0 ? " -apop" : rpop > 0 ? " -rpop" : "");
+#else /* POP */
+       fprintf (aud, from ? "<<inc>> %s  -ms %s\n" : "<<inc>> %s\n",
+                dtimenow (0), from);
+#endif /* POP */
+    }
+
+#ifdef MHE
+    if (context_find ("mhe")) {
+       cp = concat (maildir, "/++", NULL);
+       i = stat (cp, &st);
+       if ((mhe = fopen (cp, "a")) == NULL)
+           admonish (cp, "unable to append to");
+       else
+           if (i == NOTOK)
+               chmod (cp, m_gmprot ());
+       free (cp);
+    }
+#endif /* MHE */
+
+    /* Get new format string */
+    nfs = new_fs (form, format, FORMAT);
+
+    if (noisy) {
+       printf ("Incorporating new mail into %s...\n\n", folder);
+       fflush (stdout);
+    }
+
+#ifdef POP
+    /*
+     * Get the mail from a POP server
+     */
+    if (inc_type == INC_POP) {
+       if (packfile) {
+           packfile = path (packfile, TFILE);
+           if (stat (packfile, &st) == NOTOK) {
+               if (errno != ENOENT)
+                   adios (packfile, "error on file");
+               cp = concat ("Create file \"", packfile, "\"? ", NULL);
+               if (noisy && !getanswer (cp))
+                   done (1);
+               free (cp);
+           }
+           msgnum = map_count ();
+           if ((pd = mbx_open (packfile, mbx_style, getuid(), getgid(), m_gmprot()))
+               == NOTOK)
+               adios (packfile, "unable to open");
+           if ((pf = fdopen (pd, "w+")) == NULL)
+               adios (NULL, "unable to fdopen %s", packfile);
+       } else {
+           hghnum = msgnum = mp->hghmsg;
+           /*
+            * Check if we have enough message space for all the new
+            * messages.  If not, then realloc the folder and add enough
+            * space for all new messages plus 10 additional slots.
+            */
+           if (mp->hghmsg + nmsgs >= mp->hghoff
+               && !(mp = folder_realloc (mp, mp->lowoff, mp->hghmsg + nmsgs + 10)))
+               adios (NULL, "unable to allocate folder storage");
+       }
+
+       for (i = 1; i <= nmsgs; i++) {
+           msgnum++;
+           if (packfile) {
+               fseek (pf, 0L, SEEK_CUR);
+               pos = ftell (pf);
+               size = 0;
+               fwrite (mmdlm1, 1, strlen (mmdlm1), pf);
+               start = ftell (pf);
+
+               if (pop_retr (i, pop_pack) == NOTOK)
+                   adios (NULL, "%s", response);
+
+               fseek (pf, 0L, SEEK_CUR);
+               stop = ftell (pf);
+               if (fflush (pf))
+                   adios (packfile, "write error on");
+               fseek (pf, start, SEEK_SET);
+           } else {
+               cp = getcpy (m_name (msgnum));
+               if ((pf = fopen (cp, "w+")) == NULL)
+                   adios (cp, "unable to write");
+               chmod (cp, m_gmprot ());
+               start = stop = 0L;
+
+               if (pop_retr (i, pop_action) == NOTOK)
+                   adios (NULL, "%s", response);
+
+               if (fflush (pf))
+                   adios (cp, "write error on");
+               fseek (pf, 0L, SEEK_SET);
+           }
+           switch (p = scan (pf, msgnum, 0, nfs, width,
+                             packfile ? 0 : msgnum == mp->hghmsg + 1 && chgflag,
+                             1, NULL, stop - start, noisy)) {
+           case SCNEOF: 
+               printf ("%*d  empty\n", DMAXFOLDER, msgnum);
+               break;
+
+           case SCNFAT:
+               trnflag = 0;
+               noisy++;
+               /* advise (cp, "unable to read"); already advised */
+               /* fall thru */
+
+           case SCNERR:
+           case SCNNUM: 
+               break;
+
+           case SCNMSG: 
+           case SCNENC:
+           default: 
+               if (aud)
+                   fputs (scanl, aud);
+# ifdef MHE
+               if (mhe)
+                   fputs (scanl, mhe);
+# endif /* MHE */
+               if (noisy)
+                   fflush (stdout);
+               if (!packfile) {
+                   clear_msg_flags (mp, msgnum);
+                   set_exists (mp, msgnum);
+                   set_unseen (mp, msgnum);
+                   mp->msgflags |= SEQMOD;
+               }
+               break;
+           }
+           if (packfile) {
+               fseek (pf, stop, SEEK_SET);
+               fwrite (mmdlm2, 1, strlen (mmdlm2), pf);
+               if (fflush (pf) || ferror (pf)) {
+                   int e = errno;
+                   pop_quit ();
+                   errno = e;
+                   adios (packfile, "write error on");
+               }
+               map_write (packfile, pd, 0, 0L, start, stop, pos, size, noisy);
+           } else {
+               if (ferror(pf) || fclose (pf)) {
+                   int e = errno;
+                   unlink (cp);
+                   pop_quit ();
+                   errno = e;
+                   adios (cp, "write error on");
+               }
+               free (cp);
+           }
+
+           if (trnflag && pop_dele (i) == NOTOK)
+               adios (NULL, "%s", response);
+       }
+
+       if (pop_quit () == NOTOK)
+           adios (NULL, "%s", response);
+       if (packfile) {
+           mbx_close (packfile, pd);
+           pd = NOTOK;
+       }
+    }
+#endif /* POP */
+
+    /*
+     * Get the mail from file (usually mail spool)
+     */
+    if (inc_type == INC_FILE) {
+       m_unknown (in);         /* the MAGIC invocation... */
+       hghnum = msgnum = mp->hghmsg;
+       for (i = 0;;) {
+           /*
+            * Check if we need to allocate more space for message status.
+            * If so, then add space for an additional 100 messages.
+            */
+           if (msgnum >= mp->hghoff
+               && !(mp = folder_realloc (mp, mp->lowoff, mp->hghoff + 100))) {
+               advise (NULL, "unable to allocate folder storage");
+               i = NOTOK;
+               break;
+           }
+
+#if 0
+           /* copy file from spool to tmp file */
+           tmpfilenam = m_scratch ("", invo_name);
+           if ((fd = creat (tmpfilenam, m_gmprot ())) == NOTOK)
+               adios (tmpfilenam, "unable to create");
+           chmod (tmpfilenam, m_gmprot ());
+           if (!(in2 = fdopen (fd, "r+")))
+               adios (tmpfilenam, "unable to access");
+           cpymsg (in, in2);
+
+           /* link message into folder */
+           newmsg = folder_addmsg(mp, tmpfilenam);
+#endif
+
+           /* create scanline for new message */
+           switch (i = scan (in, msgnum + 1, msgnum + 1, nfs, width,
+                             msgnum == hghnum && chgflag, 1, NULL, 0L, noisy)) {
+           case SCNFAT:
+           case SCNEOF: 
+               break;
+
+           case SCNERR:
+               if (aud)
+                   fputs ("inc aborted!\n", aud);
+               advise (NULL, "aborted!");      /* doesn't clean up locks! */
+               break;
+
+           case SCNNUM: 
+               advise (NULL, "BUG in %s, number out of range", invo_name);
+               break;
+
+           default: 
+               advise (NULL, "BUG in %s, scan() botch (%d)", invo_name, i);
+               break;
+
+           case SCNMSG:
+           case SCNENC:
+               if (aud)
+                   fputs (scanl, aud);
+#ifdef MHE
+               if (mhe)
+                   fputs (scanl, mhe);
+#endif /* MHE */
+               if (noisy)
+                   fflush (stdout);
+
+               msgnum++;
+               mp->hghmsg++;
+               clear_msg_flags (mp, msgnum);
+               set_exists (mp, msgnum);
+               set_unseen (mp, msgnum);
+               mp->msgflags |= SEQMOD;
+               continue;
+           }
+           break;
+       }
+    }
+
+#ifdef POP
+    if (p < 0) {               /* error */
+#else
+    if (i < 0) {               /* error */
+#endif
+       if (locked) {
+#ifdef MAILGROUP
+           /* Be sure we can unlock mail file */
+           setgid(return_gid);
+#endif /* MAILGROUP */
+
+           lkfclose (in, newmail);
+
+#ifdef MAILGROUP
+           /* And then return us to normal privileges */
+           setgid(getgid());
+#endif /* MAILGROUP */
+       } else {
+           fclose (in);
+       }
+       adios (NULL, "failed");
+    }
+
+    if (aud)
+       fclose (aud);
+
+#ifdef MHE
+    if (mhe)
+       fclose (mhe);
+#endif /* MHE */
+
+    if (noisy)
+       fflush (stdout);
+
+#ifdef POP
+    if ((inc_type == INC_POP) && packfile)
+       done (0);
+#endif /* POP */
+
+    /*
+     * truncate file we are incorporating from
+     */
+    if (inc_type == INC_FILE) {
+       if (trnflag) {
+           if (stat (newmail, &st) != NOTOK && s1.st_mtime != st.st_mtime)
+               advise (NULL, "new messages have arrived!\007");
+           else {
+               if ((i = creat (newmail, 0600)) != NOTOK)
+                   close (i);
+               else
+                   admonish (newmail, "error zero'ing");
+               unlink(map_name(newmail));
+           }
+       } else {
+           if (noisy)
+               printf ("%s not zero'd\n", newmail);
+       }
+    }
+
+    if (msgnum == hghnum) {
+       admonish (NULL, "no messages incorporated");
+    } else {
+       context_replace (pfolder, folder);      /* update current folder */
+       if (chgflag)
+           mp->curmsg = hghnum + 1;
+       mp->hghmsg = msgnum;
+       if (mp->lowmsg == 0)
+           mp->lowmsg = 1;
+       if (chgflag)            /* sigh... */
+           seq_setcur (mp, mp->curmsg);
+    }
+
+    /*
+     * unlock the mail spool
+     */
+    if (inc_type == INC_FILE) {
+       if (locked) {
+#ifdef MAILGROUP
+           setgid(return_gid); /* Be sure we can unlock mail file */
+#endif /* MAILGROUP */
+
+           lkfclose (in, newmail);
+
+#ifdef MAILGROUP
+           setgid(getgid());   /* And then return us to normal privileges */
+#endif /* MAILGROUP */
+       } else {
+           fclose (in);
+       }
+    }
+
+    seq_setunseen (mp, 0);     /* set the Unseen-Sequence */
+    seq_save (mp);             /* synchronize sequences   */
+    context_save ();           /* save the context file   */
+    done (0);
+}
+
+
+#if 0
+
+/*
+ * Copy message message from spool into
+ * temporary file.  Massage the "From " line
+ * while copying.
+ */
+
+cpymsg (FILE *in, FILE *out)
+{
+    int state;
+    char *tmpbuf, name[NAMESZ];
+
+    for (;;) {
+       state = m_getfld (state, name, tmpbuf, rlwidth, in);
+       switch (state) {
+       case FLD:
+       case FLDPLUS:
+           break;
+       case BODY:
+           break;
+       case LENERR:
+       case FMTERR:
+           break;
+       case FILEEOF:
+           break;
+       default:
+       }
+    }
+}
+#endif /* if 0 */
+
+
+#ifdef POP
+void
+done (int status)
+{
+    if (packfile && pd != NOTOK)
+       mbx_close (packfile, pd);
+
+    exit (status);
+}
+
+static int
+pop_action (char *s)
+{
+    fprintf (pf, "%s\n", s);
+    stop += strlen (s) + 1;
+}
+
+static int
+pop_pack (char *s)
+{
+    int j;
+    char buffer[BUFSIZ];
+
+    snprintf (buffer, sizeof(buffer), "%s\n", s);
+    for (j = 0; (j = stringdex (mmdlm1, buffer)) >= 0; buffer[j]++)
+       continue;
+    for (j = 0; (j = stringdex (mmdlm2, buffer)) >= 0; buffer[j]++)
+       continue;
+    fputs (buffer, pf);
+    size += strlen (buffer) + 1;
+}
+
+static int
+map_count (void)
+{
+    int md;
+    char *cp;
+    struct drop d;
+    struct stat st;
+
+    if (stat (packfile, &st) == NOTOK)
+       return 0;
+    if ((md = open (cp = map_name (packfile), O_RDONLY)) == NOTOK
+           || map_chk (cp, md, &d, (long) st.st_size, 1)) {
+       if (md != NOTOK)
+           close (md);
+       return 0;
+    }
+    close (md);
+    return (d.d_id);
+}
+#endif /* POP */
diff --git a/uip/install-mh.c b/uip/install-mh.c
new file mode 100644 (file)
index 0000000..b2d2486
--- /dev/null
@@ -0,0 +1,209 @@
+
+/*
+ * install-mh.c -- initialize the nmh environment of a new user
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <pwd.h>
+
+static struct swit switches[] = {
+#define AUTOSW     0
+    { "auto", 0 },
+#define VERSIONSW  1
+    { "version", 0 },
+#define HELPSW     2
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+static char *message[] = {
+    "Prior to using nmh, it is necessary to have a file in your login",
+    "directory (%s) named %s which contains information",
+    "to direct certain nmh operations.  The only item which is required",
+    "is the path to use for all nmh folder operations.  The suggested nmh",
+    "path for you is %s/Mail...",
+    NULL
+};
+
+/*
+ * static prototypes
+ */
+static char *geta(void);
+
+
+int
+main (int argc, char **argv)
+{
+    int i, autof = 0;
+    char *cp, *path, buf[BUFSIZ];
+    char *dp, **arguments, **argp;
+    struct node *np;
+    struct passwd *pw;
+    struct stat st;
+    FILE *in, *out;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+    arguments = getarguments (invo_name, argc, argv, 0);
+    argp = arguments;
+
+    while ((dp = *argp++)) {
+       if (*dp == '-') {
+           switch (smatch (++dp, switches)) {
+               case AMBIGSW:
+                   ambigsw (dp, switches);
+                   done (1);
+               case UNKWNSW:
+                   adios (NULL, "-%s unknown\n", dp);
+
+               case HELPSW:
+                   snprintf (buf, sizeof(buf), "%s [switches]", invo_name);
+                   print_help (buf, switches, 0);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case AUTOSW:
+                   autof++;
+                   continue;
+           }
+       } else {
+           adios (NULL, "%s is invalid argument", dp);
+       }
+    }
+
+    /* straight from context_read ... */
+    if (mypath == NULL) {
+       if ((mypath = getenv ("HOME"))) {
+           mypath = getcpy (mypath);
+       } else {
+           if ((pw = getpwuid (getuid ())) == NULL
+                   || pw->pw_dir == NULL
+                   || *pw->pw_dir == 0)
+               adios (NULL, "no HOME envariable");
+           else
+               mypath = getcpy (pw->pw_dir);
+       }
+       if ((cp = mypath + strlen (mypath) - 1) > mypath && *cp == '/')
+           *cp = 0;
+    }
+    defpath = concat (mypath, "/", mh_profile, NULL);
+
+    if (stat (defpath, &st) != NOTOK) {
+       if (autof)
+           adios (NULL, "invocation error");
+       else
+           adios (NULL,
+                   "You already have an nmh profile, use an editor to modify it");
+    }
+
+    if (!autof && gans ("Do you want help? ", anoyes)) {
+       putchar ('\n');
+       for (i = 0; message[i]; i++) {
+           printf (message[i], mypath, mh_profile);
+           putchar ('\n');
+       }
+       putchar ('\n');
+    }
+
+    cp = concat (mypath, "/", "Mail", NULL);
+    if (stat (cp, &st) != NOTOK) {
+       if (S_ISDIR(st.st_mode)) {
+           cp = concat ("You already have the standard nmh directory \"",
+                   cp, "\".\nDo you want to use it for nmh? ", NULL);
+           if (gans (cp, anoyes))
+               path = "Mail";
+           else
+               goto query;
+       } else {
+           goto query;
+       }
+    } else {
+       if (autof)
+           printf ("I'm going to create the standard nmh path for you.\n");
+       else
+           cp = concat ("Do you want the standard nmh path \"",
+                   mypath, "/", "Mail\"? ", NULL);
+       if (autof || gans (cp, anoyes))
+           path = "Mail";
+       else {
+query:
+           if (gans ("Do you want a path below your login directory? ",
+                       anoyes)) {
+               printf ("What is the path?  %s/", mypath);
+               path = geta ();
+           } else {
+               printf ("What is the whole path?  /");
+               path = concat ("/", geta (), NULL);
+           }
+       }
+    }
+
+    chdir (mypath);
+    if (chdir (path) == NOTOK) {
+       cp = concat ("\"", path, "\" doesn't exist; Create it? ", NULL);
+       if (autof || gans (cp, anoyes))
+           if (makedir (path) == 0)
+               adios (NULL, "unable to create %s", path);
+    } else {
+       printf ("[Using existing directory]\n");
+    }
+
+    /*
+     * Add some initial elements to the profile/context list
+     */
+    if (!(m_defs = (struct node *) malloc (sizeof *np)))
+       adios (NULL, "unable to allocate profile storage");
+    np = m_defs;
+    np->n_name = getcpy ("Path");
+    np->n_field = getcpy (path);
+    np->n_context = 0;
+    np->n_next = NULL;
+
+    /*
+     * If there is a default profile file in the
+     * nmh `etc' directory, then read it also.
+     */
+    if ((in = fopen (mh_defaults, "r"))) {
+       readconfig (&np->n_next, in, mh_defaults, 0);
+       fclose (in);
+    }
+
+    ctxpath = getcpy (m_maildir (context = "context"));
+
+    /* Initialize current folder to default */
+    context_replace (pfolder, defaultfolder);
+    context_save ();
+
+    /*
+     * Now write out the initial .mh_profile
+     */
+    if ((out = fopen (defpath, "w")) == NULL)
+       adios (defpath, "unable to write");
+    for (np = m_defs; np; np = np->n_next) {
+       if (!np->n_context)
+           fprintf (out, "%s: %s\n", np->n_name, np->n_field);
+    }
+    fclose (out);
+    done (0);
+}
+
+
+static char *
+geta (void)
+{
+    char *cp;
+    static char line[BUFSIZ];
+
+    fflush(stdout);
+    if (fgets(line, sizeof(line), stdin) == NULL)
+       done (1);
+    if ((cp = strchr(line, '\n')))
+       *cp = 0;
+    return line;
+}
diff --git a/uip/mark.c b/uip/mark.c
new file mode 100644 (file)
index 0000000..2bbba6d
--- /dev/null
@@ -0,0 +1,298 @@
+
+/*
+ * mark.c -- add message(s) to sequences in given folder
+ *        -- delete messages (s) from sequences in given folder
+ *        -- list sequences in given folder
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+/*
+ * We allocate space for messages (msgs array)
+ * this number of elements at a time.
+ */
+#define MAXMSGS  256
+
+
+static struct swit switches[] = {
+#define        ADDSW               0
+    { "add", 0 },
+#define        DELSW               1
+    { "delete", 0 },
+#define        LSTSW               2
+    { "list", 0 },
+#define        SEQSW               3
+    { "sequence name", 0 },
+#define        PUBLSW              4
+    { "public", 0 },
+#define        NPUBLSW             5
+    { "nopublic", 0 },
+#define        ZEROSW              6
+    { "zero", 0 },
+#define        NZEROSW             7
+    { "nozero", 0 },
+#define VERSIONSW           8
+    { "version", 0 },
+#define        HELPSW              9
+    { "help", 4 },
+#define        DEBUGSW            10
+    { "debug", -5 },
+    { NULL, 0 }
+};
+
+/*
+ * static prototypes
+ */
+static void print_debug (struct msgs *);
+static void seq_printdebug (struct msgs *);
+
+
+int
+main (int argc, char **argv)
+{
+    int addsw = 0, deletesw = 0, debugsw = 0;
+    int listsw = 0, publicsw = -1, zerosw = 0;
+    int seqp = 0, msgnum, nummsgs, maxmsgs;
+    char *cp, *maildir, *folder = NULL, buf[BUFSIZ];
+    char **argp, **arguments;
+    char *seqs[NUMATTRS + 1], **msgs;
+    struct msgs *mp;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    /*
+     * Allocate the initial space to record message
+     * names, ranges, and sequences.
+     */
+    nummsgs = 0;
+    maxmsgs = MAXMSGS;
+    if (!(msgs = (char **) malloc ((size_t) (maxmsgs * sizeof(*msgs)))))
+       adios (NULL, "unable to allocate storage");
+
+    /*
+     * Parse arguments
+     */
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+           case AMBIGSW: 
+               ambigsw (cp, switches);
+               done (1);
+           case UNKWNSW: 
+               adios (NULL, "-%s unknown\n", cp);
+
+           case HELPSW: 
+               snprintf (buf, sizeof(buf), "%s [+folder] [msgs] [switches]",
+                         invo_name);
+               print_help (buf, switches, 1);
+               done (1);
+           case VERSIONSW:
+               print_version(invo_name);
+               done (1);
+
+           case ADDSW: 
+               addsw++;
+               deletesw = listsw = 0;
+               continue;
+           case DELSW: 
+               deletesw++;
+               addsw = listsw = 0;
+               continue;
+           case LSTSW: 
+               listsw++;
+               addsw = deletesw = 0;
+               continue;
+
+           case SEQSW: 
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+
+               /* check if too many sequences specified */
+               if (seqp >= NUMATTRS)
+                   adios (NULL, "too many sequences (more than %d) specified", NUMATTRS);
+               seqs[seqp++] = cp;
+               continue;
+
+           case PUBLSW: 
+               publicsw = 1;
+               continue;
+           case NPUBLSW: 
+               publicsw = 0;
+               continue;
+
+           case DEBUGSW: 
+               debugsw++;
+               continue;
+
+           case ZEROSW: 
+               zerosw++;
+               continue;
+           case NZEROSW: 
+               zerosw = 0;
+               continue;
+           }
+       }
+       if (*cp == '+' || *cp == '@') {
+           if (folder)
+               adios (NULL, "only one folder at a time!");
+           else
+               folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       } else {
+           /*
+            * Check if we need to allocate more space
+            * for message names/ranges/sequences.
+            */
+           if (nummsgs >= maxmsgs) {
+               maxmsgs += MAXMSGS;
+               if (!(msgs = (char **) realloc (msgs,
+                                               (size_t) (maxmsgs * sizeof(*msgs)))))
+                   adios (NULL, "unable to reallocate msgs storage");
+           }
+           msgs[nummsgs++] = cp;
+       }
+    }
+
+    /*
+     * If we haven't specified -add, -delete, or -list,
+     * then use -add if a sequence was specified, else
+     * use -list.
+     */
+    if (!addsw && !deletesw && !listsw) {
+       if (seqp)
+           addsw++;
+       else
+           listsw++;
+    }
+
+    if (!context_find ("path"))
+       free (path ("./", TFOLDER));
+    if (!nummsgs)
+       msgs[nummsgs++] = listsw ? "all" :"cur";
+    if (!folder)
+       folder = getfolder (1);
+    maildir = m_maildir (folder);
+
+    if (chdir (maildir) == NOTOK)
+       adios (maildir, "unable to change directory to");
+
+    /* read folder and create message structure */
+    if (!(mp = folder_read (folder)))
+       adios (NULL, "unable to read folder %s", folder);
+
+    /* print some general debugging info */
+    if (debugsw)
+       print_debug(mp);
+
+    /* check for empty folder */
+    if (mp->nummsg == 0)
+       adios (NULL, "no messages in %s", folder);
+
+    /* parse all the message ranges/sequences and set SELECTED */
+    for (msgnum = 0; msgnum < nummsgs; msgnum++)
+       if (!m_convert (mp, msgs[msgnum]))
+           done (1);
+
+    if (publicsw == 1 && is_readonly(mp))
+       adios (NULL, "folder %s is read-only, so -public not allowed", folder);
+
+    /*
+     * Make sure at least one sequence has been
+     * specified if we are adding or deleting.
+     */
+    if (seqp == 0 && (addsw || deletesw))
+       adios (NULL, "-%s requires at least one -sequence argument",
+              addsw ? "add" : "delete");
+    seqs[seqp] = NULL;
+
+    /* Adding messages to sequences */
+    if (addsw) {
+       for (seqp = 0; seqs[seqp]; seqp++)
+           if (!seq_addsel (mp, seqs[seqp], publicsw, zerosw))
+               done (1);
+    }
+
+    /* Deleting messages from sequences */
+    if (deletesw) {
+       for (seqp = 0; seqs[seqp]; seqp++)
+           if (!seq_delsel (mp, seqs[seqp], publicsw, zerosw))
+               done (1);
+    }
+
+    /* Listing messages in sequences */
+    if (listsw) {
+       if (seqp) {
+           /* print the sequences given */
+           for (seqp = 0; seqs[seqp]; seqp++)
+               seq_print (mp, seqs[seqp]);
+       } else {
+           /* else print them all */
+           seq_printall (mp);
+       }
+
+       /* print debugging info about SELECTED messages */
+       if (debugsw)
+           seq_printdebug (mp);
+    }
+
+    seq_save (mp);                     /* synchronize message sequences */
+    context_replace (pfolder, folder); /* update current folder         */
+    context_save ();                   /* save the context file         */
+    folder_free (mp);                  /* free folder/message structure */
+    done (0);
+}
+
+
+/*
+ * Print general debugging info
+ */
+static void
+print_debug (struct msgs *mp)
+{
+    char buf[100];
+
+    printf ("invo_name     = %s\n", invo_name);
+    printf ("mypath        = %s\n", mypath);
+    printf ("defpath       = %s\n", defpath);
+    printf ("ctxpath       = %s\n", ctxpath);
+    printf ("context flags = %s\n", snprintb (buf, sizeof(buf),
+               (unsigned) ctxflags, DBITS));
+    printf ("foldpath      = %s\n", mp->foldpath);
+    printf ("folder flags  = %s\n\n", snprintb(buf, sizeof(buf),
+               (unsigned) mp->msgflags, FBITS));
+    printf ("lowmsg=%d hghmsg=%d nummsg=%d curmsg=%d\n",
+       mp->lowmsg, mp->hghmsg, mp->nummsg, mp->curmsg);
+    printf ("lowsel=%d hghsel=%d numsel=%d\n",
+       mp->lowsel, mp->hghsel, mp->numsel);
+    printf ("lowoff=%d hghoff=%d\n\n", mp->lowoff, mp->hghoff);
+}
+
+
+/*
+ * Print debugging info about all the SELECTED
+ * messages and the sequences they are in.
+ */
+static void
+seq_printdebug (struct msgs *mp)
+{
+    int msgnum;
+    char buf[100];
+
+    printf ("\n");
+    for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
+       if (is_selected (mp, msgnum))
+           printf ("%*d: %s\n", DMAXFOLDER, msgnum,
+               snprintb (buf, sizeof(buf),
+               (unsigned) mp->msgstats[msgnum - mp->lowoff], seq_bits (mp)));
+    }
+}
diff --git a/uip/md5.c b/uip/md5.c
new file mode 100644 (file)
index 0000000..2cb6cb5
--- /dev/null
+++ b/uip/md5.c
@@ -0,0 +1,308 @@
+
+/*
+ * md5.c -- md5 message digest algorithm
+ *          taken from RFC-1321/Appendix A.3
+ *
+ * $Id$
+ */
+
+/*
+ * MD5C.C -- RSA Data Security, Inc., MD5 message-digest algorithm
+ */
+
+/*
+ * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+ * rights reserved.
+ *
+ * License to copy and use this software is granted provided that it
+ * is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+ * Algorithm" in all material mentioning or referencing this software
+ * or this function.
+ *
+ * License is also granted to make and use derivative works provided
+ * that such works are identified as "derived from the RSA Data
+ * Security, Inc. MD5 Message-Digest Algorithm" in all material
+ * mentioning or referencing the derived work.
+ *
+ * RSA Data Security, Inc. makes no representations concerning either
+ * the merchantability of this software or the suitability of this
+ * software for any particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ */
+
+#include <h/md5.h>
+
+/*
+ * Constants for MD5Transform routine.
+ */
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64]));
+static void Encode PROTO_LIST ((unsigned char *, UINT4 *, unsigned int));
+static void Decode PROTO_LIST ((UINT4 *, unsigned char *, unsigned int));
+
+static unsigned char PADDING[64] = {
+  0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G, H and I are basic MD5 functions.
+ */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+Rotation is separate from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s, ac) { \
+ (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+#define GG(a, b, c, d, x, s, ac) { \
+ (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+#define HH(a, b, c, d, x, s, ac) { \
+ (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+#define II(a, b, c, d, x, s, ac) { \
+ (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+
+/* MD5 initialization. Begins an MD5 operation, writing a new context.
+ */
+void MD5Init (context)
+MD5_CTX *context;                                        /* context */
+{
+  context->count[0] = context->count[1] = 0;
+  /* Load magic initialization constants.
+*/
+  context->state[0] = 0x67452301;
+  context->state[1] = 0xefcdab89;
+  context->state[2] = 0x98badcfe;
+  context->state[3] = 0x10325476;
+}
+
+/* MD5 block update operation. Continues an MD5 message-digest
+  operation, processing another message block, and updating the
+  context.
+ */
+void MD5Update (context, input, inputLen)
+MD5_CTX *context;                                        /* context */
+unsigned char *input;                                /* input block */
+unsigned int inputLen;                     /* length of input block */
+{
+  unsigned int i, index, partLen;
+
+  /* Compute number of bytes mod 64 */
+  index = (unsigned int)((context->count[0] >> 3) & 0x3F);
+
+  /* Update number of bits */
+  if ((context->count[0] += ((UINT4)inputLen << 3))
+   < ((UINT4)inputLen << 3))
+ context->count[1]++;
+  context->count[1] += ((UINT4)inputLen >> 29);
+
+  partLen = 64 - index;
+
+  /* Transform as many times as possible. */
+  if (inputLen >= partLen) {
+ memcpy ((POINTER)&context->buffer[index], (POINTER)input, partLen);
+ MD5Transform (context->state, context->buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+   MD5Transform (context->state, &input[i]);
+
+ index = 0;
+  }
+  else
+ i = 0;
+
+  /* Buffer remaining input */
+  memcpy ((POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i);
+}
+
+/*
+ * MD5 finalization. Ends an MD5 message-digest operation, writing the
+ * the message digest and zeroizing the context.
+ */
+void MD5Final (digest, context)
+unsigned char digest[16];                         /* message digest */
+MD5_CTX *context;                                       /* context */
+{
+  unsigned char bits[8];
+  unsigned int index, padLen;
+
+  /* Save number of bits */
+  Encode (bits, context->count, 8);
+
+  /* Pad out to 56 mod 64.
+*/
+  index = (unsigned int)((context->count[0] >> 3) & 0x3f);
+  padLen = (index < 56) ? (56 - index) : (120 - index);
+  MD5Update (context, PADDING, padLen);
+
+  /* Append length (before padding) */
+  MD5Update (context, bits, 8);
+  /* Store state in digest */
+  Encode (digest, context->state, 16);
+
+  /* Zeroize sensitive information. */
+  memset ((POINTER)context, 0, sizeof(*context));
+}
+
+/* MD5 basic transformation. Transforms state based on block.
+ */
+static void MD5Transform (state, block)
+UINT4 state[4];
+unsigned char block[64];
+{
+  UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+  Decode (x, block, 64);
+
+  /* Round 1 */
+  FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
+  FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
+  FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
+  FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
+  FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
+  FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
+  FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
+  FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
+  FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
+  FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
+  FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+  FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+  FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+  FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+  FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+  FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+  GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
+  GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
+  GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+  GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
+  GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
+  GG (d, a, b, c, x[10], S22,  0x2441453); /* 22 */
+  GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+  GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
+  GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
+  GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+  GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
+  GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
+  GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+  GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
+  GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
+  GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+  /* Round 3 */
+  HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
+  HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
+  HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+  HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+  HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
+  HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
+  HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
+  HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+  HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+  HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
+  HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
+  HH (b, c, d, a, x[ 6], S34,  0x4881d05); /* 44 */
+  HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
+  HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+  HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+  HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
+
+  /* Round 4 */
+  II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
+  II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
+  II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+  II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
+  II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+  II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
+  II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+  II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
+  II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
+  II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+  II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
+  II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+  II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
+  II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+  II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
+  II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
+
+  state[0] += a;
+  state[1] += b;
+  state[2] += c;
+  state[3] += d;
+
+  /* Zeroize sensitive information. */
+  memset ((POINTER)x, 0, sizeof(x));
+}
+
+/* Encodes input (UINT4) into output (unsigned char). Assumes len is
+  a multiple of 4.
+ */
+static void Encode (output, input, len)
+unsigned char *output;
+UINT4 *input;
+unsigned int len;
+{
+  unsigned int i, j;
+
+  for (i = 0, j = 0; j < len; i++, j += 4) {
+ output[j] = (unsigned char)(input[i] & 0xff);
+ output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
+ output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
+ output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
+  }
+}
+
+/* Decodes input (unsigned char) into output (UINT4). Assumes len is
+  a multiple of 4.
+ */
+static void Decode (output, input, len)
+UINT4 *output;
+unsigned char *input;
+unsigned int len;
+{
+  unsigned int i, j;
+
+  for (i = 0, j = 0; j < len; i++, j += 4)
+ output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
+   (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
+}
+
diff --git a/uip/mhbuild.c b/uip/mhbuild.c
new file mode 100644 (file)
index 0000000..bacec10
--- /dev/null
@@ -0,0 +1,384 @@
+
+/*
+ * mhbuild.c -- expand/translate MIME composition files
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+#include <h/signals.h>
+#include <h/md5.h>
+#include <errno.h>
+#include <signal.h>
+#include <zotnet/mts/mts.h>
+#include <zotnet/tws/tws.h>
+#include <h/mime.h>
+#include <h/mhparse.h>
+#include <h/mhcachesbr.h>
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+static struct swit switches[] = {
+#define        CHECKSW                 0
+    { "check", 0 },
+#define        NCHECKSW                1
+    { "nocheck", 0 },
+#define        EBCDICSW                2
+    { "ebcdicsafe", 0 },
+#define        NEBCDICSW               3
+    { "noebcdicsafe", 0 },
+#define        HEADSW                  4
+    { "headers", 0 },
+#define        NHEADSW                 5
+    { "noheaders", 0 },
+#define        LISTSW                  6
+    { "list", 0 },
+#define        NLISTSW                 7
+    { "nolist", 0 },
+#define        SIZESW                  8
+    { "realsize", 0 },
+#define        NSIZESW                 9
+    { "norealsize", 0 },
+#define        RFC934SW               10
+    { "rfc934mode", 0 },
+#define        NRFC934SW              11
+    { "norfc934mode", 0 },
+#define        VERBSW                 12
+    { "verbose", 0 },
+#define        NVERBSW                13
+    { "noverbose", 0 },
+#define        RCACHESW               14
+    { "rcache policy", 0 },
+#define        WCACHESW               15
+    { "wcache policy", 0 },
+#define VERSIONSW              16
+    { "version", 0 },
+#define        HELPSW                 17
+    { "help", 4 },
+#define        DEBUGSW                18
+    { "debug", -5 },
+    { NULL, 0 }
+};
+
+
+extern int errno;
+
+/* mhbuildsbr.c */
+extern int checksw;
+extern char *tmp;      /* directory to place temp files */
+
+/* mhcachesbr.c */
+extern int rcachesw;
+extern int wcachesw;
+extern char *cache_public;
+extern char *cache_private;
+
+int debugsw = 0;
+int verbosw = 0;
+
+int ebcdicsw = 0;
+int listsw   = 0;
+int rfc934sw = 0;
+
+/*
+ * Temporary files
+ */
+static char infile[BUFSIZ];
+static int unlink_infile  = 0;
+
+static char outfile[BUFSIZ];
+static int unlink_outfile = 0;
+
+
+/* mhbuildsbr.c */
+CT build_mime (char *);
+int output_message (CT, char *);
+
+/* mhlistsbr.c */
+int list_all_messages (CT *, int, int, int, int);
+
+/* mhmisc.c */
+void set_endian (void);
+
+/* mhfree.c */
+void free_content (CT);
+
+
+int
+main (int argc, char **argv)
+{
+    int sizesw = 1, headsw = 1;
+    int *icachesw;
+    char *cp, buf[BUFSIZ];
+    char buffer[BUFSIZ], *compfile = NULL;
+    char **argp, **arguments;
+    CT ct, cts[2];
+    FILE *fp;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    while ((cp = *argp++)) {
+       if (cp[0] == '-' && cp[1] == '\0') {
+           if (compfile)
+               adios (NULL, "cannot specify both standard input and a file");
+           else
+               compfile = cp;
+           listsw = 0;         /* turn off -list if using standard in/out */
+           verbosw = 0;        /* turn off -verbose listings */
+           break;
+       }
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+           case AMBIGSW: 
+               ambigsw (cp, switches);
+               done (1);
+           case UNKWNSW: 
+               adios (NULL, "-%s unknown", cp);
+
+           case HELPSW: 
+               snprintf (buf, sizeof(buf), "%s [switches] file", invo_name);
+               print_help (buf, switches, 1);
+               done (1);
+           case VERSIONSW:
+               print_version(invo_name);
+               done (1);
+
+           case RCACHESW:
+               icachesw = &rcachesw;
+               goto do_cache;
+           case WCACHESW:
+               icachesw = &wcachesw;
+           do_cache: ;
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               switch (*icachesw = smatch (cp, caches)) {
+               case AMBIGSW:
+                   ambigsw (cp, caches);
+                   done (1);
+               case UNKWNSW:
+                   adios (NULL, "%s unknown", cp);
+               default:
+                   break;
+               }
+               continue;
+
+           case CHECKSW:
+               checksw++;
+               continue;
+           case NCHECKSW:
+               checksw = 0;
+               continue;
+
+           case EBCDICSW:
+               ebcdicsw++;
+               continue;
+           case NEBCDICSW:
+               ebcdicsw = 0;
+               continue;
+
+           case HEADSW:
+               headsw++;
+               continue;
+           case NHEADSW:
+               headsw = 0;
+               continue;
+
+           case LISTSW:
+               listsw++;
+               continue;
+           case NLISTSW:
+               listsw = 0;
+               continue;
+
+           case RFC934SW:
+               rfc934sw++;
+               continue;
+           case NRFC934SW:
+               rfc934sw = 0;
+               continue;
+
+           case SIZESW:
+               sizesw++;
+               continue;
+           case NSIZESW:
+               sizesw = 0;
+               continue;
+
+           case VERBSW: 
+               verbosw++;
+               continue;
+           case NVERBSW: 
+               verbosw = 0;
+               continue;
+           case DEBUGSW:
+               debugsw = 1;
+               continue;
+           }
+       }
+       if (compfile)
+           adios (NULL, "only one composition file allowed");
+       else
+           compfile = cp;
+    }
+
+    set_endian ();
+
+    if ((cp = getenv ("MM_NOASK")) && !strcmp (cp, "1"))
+       listsw  = 0;
+
+    /*
+     * Check if we've specified an additional profile
+     */
+    if ((cp = getenv ("MHBUILD"))) {
+       if ((fp = fopen (cp, "r"))) {
+           readconfig ((struct node **) 0, fp, cp, 0);
+           fclose (fp);
+       } else {
+           admonish ("", "unable to read $MHBUILD profile (%s)", cp);
+       }
+    }
+
+    /*
+     * Read the standard profile setup
+     */
+    if ((fp = fopen (cp = etcpath ("mhn.defaults"), "r"))) {
+       readconfig ((struct node **) 0, fp, cp, 0);
+       fclose (fp);
+    }
+
+    /* Check for public cache location */
+    if ((cache_public = context_find (nmhcache)) && *cache_public != '/')
+       cache_public = NULL;
+
+    /* Check for private cache location */
+    if (!(cache_private = context_find (nmhprivcache)))
+       cache_private = ".cache";
+    cache_private = getcpy (m_maildir (cache_private));
+
+    /*
+     * Check for storage directory.  If defined, we
+     * will store temporary files there.  Else we
+     * store them in standard nmh directory.
+     */
+    if ((cp = context_find (nmhstorage)) && *cp)
+       tmp = concat (cp, "/", invo_name, NULL);
+    else
+       tmp = add (m_maildir (invo_name), NULL);
+
+    if (!context_find ("path"))
+       free (path ("./", TFOLDER));
+
+    /* Check if we have a file to process */
+    if (!compfile)
+       adios (NULL, "need to specify a %s composition file", invo_name);
+
+    /*
+     * Process the composition file from standard input.
+     */
+    if (compfile[0] == '-' && compfile[1] == '\0') {
+
+       /* copy standard input to temporary file */
+       strncpy (infile, m_scratch ("", invo_name), sizeof(infile));
+       if ((fp = fopen (infile, "w")) == NULL)
+           adios (infile, "unable to open");
+       while (fgets (buffer, BUFSIZ, stdin))
+           fputs (buffer, fp);
+       fclose (fp);
+       unlink_infile = 1;
+
+       /* build the content structures for MIME message */
+       ct = build_mime (infile);
+       cts[0] = ct;
+       cts[1] = NULL;
+
+       /* output MIME message to this temporary file */
+       strncpy (outfile, m_scratch ("", invo_name), sizeof(outfile));
+       unlink_outfile = 1;
+
+       /* output the message */
+       output_message (ct, outfile);
+
+       /* output the temp file to standard output */
+       if ((fp = fopen (outfile, "r")) == NULL)
+           adios (outfile, "unable to open");
+       while (fgets (buffer, BUFSIZ, fp))
+           fputs (buffer, stdout);
+       fclose (fp);
+
+       unlink (infile);
+       unlink_infile = 0;
+
+       unlink (outfile);
+       unlink_outfile = 0;
+
+       free_content (ct);
+       done (0);
+    }
+
+    /*
+     * Process the composition file from a file.
+     */
+
+    /* build the content structures for MIME message */
+    ct = build_mime (compfile);
+    cts[0] = ct;
+    cts[1] = NULL;
+
+    /* output MIME message to this temporary file */
+    strncpy (outfile, m_scratch (compfile, invo_name), sizeof(outfile));
+    unlink_outfile = 1;
+
+    /* output the message */
+    output_message (ct, outfile);
+
+    /*
+     * List the message info
+     */
+    if (listsw)
+       list_all_messages (cts, headsw, sizesw, verbosw, debugsw);
+
+    /* Rename composition draft */
+    snprintf (buffer, sizeof(buffer), "%s.orig", m_backup (compfile));
+    if (rename (compfile, buffer) == NOTOK)
+       adios (compfile, "unable to rename %s to", buffer);
+
+    /* Rename output file to take its place */
+    if (rename (outfile, compfile) == NOTOK) {
+       advise (outfile, "unable to rename %s to", compfile);
+       rename (buffer, compfile);
+       done (1);
+    }
+    unlink_outfile = 0;
+
+    free_content (ct);
+    done (0);
+    /* NOT REACHED */
+}
+
+
+void
+done (int status)
+{
+    /*
+     * Check if we need to remove stray
+     * temporary files.
+     */
+    if (unlink_infile)
+       unlink (infile);
+    if (unlink_outfile)
+       unlink (outfile);
+
+    exit (status);
+}
diff --git a/uip/mhbuildsbr.c b/uip/mhbuildsbr.c
new file mode 100644 (file)
index 0000000..73907ab
--- /dev/null
@@ -0,0 +1,4216 @@
+
+/*
+ * mhbuildsbr.c -- routines to expand/translate MIME composition files
+ *
+ * $Id$
+ */
+
+/*
+ * This code was originally part of mhn.c.  I split it into
+ * a separate program (mhbuild.c) and then later split it
+ * again (mhbuildsbr.c).  But the code still has some of
+ * the mhn.c code in it.  This program needs additional
+ * streamlining and removal of unneeded code.
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+#include <h/signals.h>
+#include <h/md5.h>
+#include <errno.h>
+#include <signal.h>
+#include <zotnet/mts/mts.h>
+#include <zotnet/tws/tws.h>
+#include <h/mime.h>
+#include <h/mhparse.h>
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+
+extern int errno;
+
+extern int debugsw;
+extern int verbosw;
+
+extern int ebcdicsw;
+extern int listsw;
+extern int rfc934sw;
+
+extern int endian;     /* mhmisc.c */
+
+/* cache policies */
+extern int rcachesw;   /* mhcachesbr.c */
+extern int wcachesw;   /* mhcachesbr.c */
+
+int checksw = 0;       /* Add Content-MD5 field */
+
+/*
+ * Directory to place tmp files.  This must
+ * be set before these routines are called.
+ */
+char *tmp;
+
+pid_t xpid = 0;
+
+static char prefix[] = "----- =_aaaaaaaaaa";
+
+/*
+ * Structure for mapping types to their internal flags
+ */
+struct k2v {
+    char *kv_key;
+    int          kv_value;
+};
+
+/*
+ * Structures for TEXT messages
+ */
+static struct k2v SubText[] = {
+    { "plain",    TEXT_PLAIN },
+    { "richtext", TEXT_RICHTEXT },  /* defined in RFC-1341    */
+    { "enriched", TEXT_ENRICHED },  /* defined in RFC-1896    */
+    { NULL,       TEXT_UNKNOWN }    /* this one must be last! */
+};
+
+static struct k2v Charset[] = {
+    { "us-ascii",   CHARSET_USASCII },
+    { "iso-8859-1", CHARSET_LATIN },
+    { NULL,         CHARSET_UNKNOWN }  /* this one must be last! */
+};
+
+/*
+ * Structures for MULTIPART messages
+ */
+static struct k2v SubMultiPart[] = {
+    { "mixed",       MULTI_MIXED },
+    { "alternative", MULTI_ALTERNATE },
+    { "digest",      MULTI_DIGEST },
+    { "parallel",    MULTI_PARALLEL },
+    { NULL,          MULTI_UNKNOWN }    /* this one must be last! */
+};
+
+/*
+ * Structures for MESSAGE messages
+ */
+static struct k2v SubMessage[] = {
+    { "rfc822",        MESSAGE_RFC822 },
+    { "partial",       MESSAGE_PARTIAL },
+    { "external-body", MESSAGE_EXTERNAL },
+    { NULL,            MESSAGE_UNKNOWN }       /* this one must be last! */
+};
+
+/*
+ * Structure for APPLICATION messages
+ */
+static struct k2v SubApplication[] = {
+    { "octet-stream", APPLICATION_OCTETS },
+    { "postscript",   APPLICATION_POSTSCRIPT },
+    { NULL,           APPLICATION_UNKNOWN }    /* this one must be last! */
+};
+
+
+/* mhmisc.c */
+int make_intermediates (char *);
+void content_error (char *, CT, char *, ...);
+
+/* mhcachesbr.c */
+int find_cache (CT, int, int *, char *, char *, int);
+
+/* ftpsbr.c */
+int ftp_get (char *, char *, char *, char *, char *, char *, int, int);
+
+/* mhfree.c */
+void free_content (CT);
+void free_ctinfo (CT);
+void free_encoding (CT, int);
+
+/*
+ * prototypes
+ */
+CT build_mime (char *);
+int pidcheck (int);
+
+/*
+ * static prototypes
+ */
+static CT get_content (FILE *, char *, int);
+static int add_header (CT, char *, char *);
+static int get_ctinfo (char *, CT, int);
+static int get_comment (CT, char **, int);
+static int InitGeneric (CT);
+static int InitText (CT);
+static int InitMultiPart (CT);
+static void reverse_parts (CT);
+static int InitMessage (CT);
+static int params_external (CT, int);
+static int InitApplication (CT);
+static int init_decoded_content (CT);
+static int init_encoding (CT, OpenCEFunc);
+static void close_encoding (CT);
+static unsigned long size_encoding (CT);
+static int InitBase64 (CT);
+static int openBase64 (CT, char **);
+static int InitQuoted (CT);
+static int openQuoted (CT, char **);
+static int Init7Bit (CT);
+static int open7Bit (CT, char **);
+static int openExternal (CT, CT, CE, char **, int *);
+static int InitFile (CT);
+static int openFile (CT, char **);
+static int InitFTP (CT);
+static int openFTP (CT, char **);
+static int InitMail (CT);
+static int openMail (CT, char **);
+static char *fgetstr (char *, int, FILE *);
+static int user_content (FILE *, char *, char *, CT *);
+static void set_id (CT, int);
+static int compose_content (CT);
+static int scan_content (CT);
+static int build_headers (CT);
+static char *calculate_digest (CT, int);
+static int readDigest (CT, char *);
+
+/*
+ * Structures for mapping (content) types to
+ * the functions to handle them.
+ */
+struct str2init {
+    char *si_key;
+    int          si_val;
+    InitFunc si_init;
+};
+
+static struct str2init str2cts[] = {
+    { "application", CT_APPLICATION, InitApplication },
+    { "audio",      CT_AUDIO,       InitGeneric },
+    { "image",      CT_IMAGE,       InitGeneric },
+    { "message",     CT_MESSAGE,     InitMessage },
+    { "multipart",   CT_MULTIPART,   InitMultiPart },
+    { "text",       CT_TEXT,        InitText },
+    { "video",      CT_VIDEO,       InitGeneric },
+    { NULL,         CT_EXTENSION,   NULL },  /* these two must be last! */
+    { NULL,         CT_UNKNOWN,     NULL },
+};
+
+static struct str2init str2ces[] = {
+    { "base64",                  CE_BASE64,    InitBase64 },
+    { "quoted-printable", CE_QUOTED,   InitQuoted },
+    { "8bit",            CE_8BIT,      Init7Bit },
+    { "7bit",            CE_7BIT,      Init7Bit },
+    { "binary",                  CE_BINARY,    NULL },
+    { NULL,              CE_EXTENSION, NULL },  /* these two must be last! */
+    { NULL,              CE_UNKNOWN,   NULL },
+};
+
+/*
+ * NOTE WELL: si_key MUST NOT have value of NOTOK
+ *
+ * si_key is 1 if access method is anonymous.
+ */
+static struct str2init str2methods[] = {
+    { "afs",         1,        InitFile },
+    { "anon-ftp",    1,        InitFTP },
+    { "ftp",         0,        InitFTP },
+    { "local-file",  0,        InitFile },
+    { "mail-server", 0,        InitMail },
+    { NULL,          0, NULL }
+};
+
+
+int
+pidcheck (int status)
+{
+    if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
+       return status;
+
+    fflush (stdout);
+    fflush (stderr);
+    done (1);
+    /* NOTREACHED */
+}
+
+
+/*
+ * Main routine for translating composition file
+ * into valid MIME message.  It translates the draft
+ * into a content structure (actually a tree of content
+ * structures).  This message then can be manipulated
+ * in various ways, including being output via
+ * output_message().
+ */
+
+CT
+build_mime (char *infile)
+{
+    int        compnum, state;
+    char buf[BUFSIZ], name[NAMESZ];
+    char *cp, *np, *vp;
+    struct multipart *m;
+    struct part **pp;
+    CT ct;
+    FILE *in;
+
+    umask (~m_gmprot ());
+
+    /* open the composition draft */
+    if ((in = fopen (infile, "r")) == NULL)
+       adios (infile, "unable to open for reading");
+
+    /*
+     * Allocate space for primary (outside) content
+     */
+    if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
+       adios (NULL, "out of memory");
+
+    /*
+     * Allocate structure for handling decoded content
+     * for this part.  We don't really need this, but
+     * allocate it to remain consistent.
+     */
+    init_decoded_content (ct);
+
+    /*
+     * Parse some of the header fields in the composition
+     * draft into the linked list of header fields for
+     * the new MIME message.
+     */
+    for (compnum = 1, state = FLD;;) {
+       switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
+       case FLD:
+       case FLDPLUS:
+       case FLDEOF:
+           compnum++;
+
+           /* abort if draft has Mime-Version header field */
+           if (!strcasecmp (name, VRSN_FIELD))
+               adios (NULL, "draft shouldn't contain %s: field", VRSN_FIELD);
+
+           /* abort if draft has Content-Transfer-Encoding header field */
+           if (!strcasecmp (name, ENCODING_FIELD))
+               adios (NULL, "draft shouldn't contain %s: field", ENCODING_FIELD);
+
+           /* ignore any Content-Type fields in the header */
+           if (!strcasecmp (name, TYPE_FIELD)) {
+               while (state == FLDPLUS)
+                   state = m_getfld (state, name, buf, sizeof(buf), in);
+               goto finish_field;
+           }
+
+           /* get copies of the buffers */
+           np = add (name, NULL);
+           vp = add (buf, NULL);
+
+           /* if necessary, get rest of field */
+           while (state == FLDPLUS) {
+               state = m_getfld (state, name, buf, sizeof(buf), in);
+               vp = add (buf, vp);     /* add to previous value */
+           }
+
+           /* Now add the header data to the list */
+           add_header (ct, np, vp);
+
+finish_field:
+           /* if this wasn't the last header field, then continue */
+           if (state != FLDEOF)
+               continue;
+           /* else fall... */
+
+       case FILEEOF:
+           adios (NULL, "draft has empty body -- no directives!");
+           /* NOTREACHED */
+
+       case BODY:
+       case BODYEOF:
+           fseek (in, (long) (-strlen (buf)), SEEK_CUR);
+           break;
+
+       case LENERR:
+       case FMTERR:
+           adios (NULL, "message format error in component #%d", compnum);
+
+       default:
+           adios (NULL, "getfld() returned %d", state);
+       }
+       break;
+    }
+
+    /*
+     * Now add the MIME-Version header field
+     * to the list of header fields.
+     */
+    np = add (VRSN_FIELD, NULL);
+    vp = concat (" ", VRSN_VALUE, "\n", NULL);
+    add_header (ct, np, vp);
+
+    /*
+     * We initally assume we will find multiple contents in the
+     * draft.  So create a multipart/mixed content to hold everything.
+     * We can remove this later, if it is not needed.
+     */
+    if (get_ctinfo ("multipart/mixed", ct, 0) == NOTOK)
+       done (1);
+    ct->c_type = CT_MULTIPART;
+    ct->c_subtype = MULTI_MIXED;
+    ct->c_file = add (infile, NULL);
+
+    if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
+       adios (NULL, "out of memory");
+    ct->c_ctparams = (void *) m;
+    pp = &m->mp_parts;
+
+    /*
+     * read and parse the composition file
+     * and the directives it contains.
+     */
+    while (fgetstr (buf, sizeof(buf) - 1, in)) {
+       struct part *part;
+       CT p;
+
+       if (user_content (in, infile, buf, &p) == DONE) {
+           admonish (NULL, "ignoring spurious #end");
+           continue;
+       }
+       if (!p)
+           continue;
+
+       if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
+           adios (NULL, "out of memory");
+       *pp = part;
+       pp = &part->mp_next;
+       part->mp_part = p;
+    }
+
+    /*
+     * close the composition draft since
+     * it's not needed any longer.
+     */
+    fclose (in);
+
+    /* check if any contents were found */
+    if (!m->mp_parts)
+       adios (NULL, "no content directives found");
+
+    /*
+     * If only one content was found, then remove and
+     * free the outer multipart content.
+     */
+    if (!m->mp_parts->mp_next) {
+       CT p;
+
+       p = m->mp_parts->mp_part;
+       m->mp_parts->mp_part = NULL;
+
+       /* move header fields */
+       p->c_first_hf = ct->c_first_hf;
+       p->c_last_hf = ct->c_last_hf;
+       ct->c_first_hf = NULL;
+       ct->c_last_hf = NULL;
+
+       free_content (ct);
+       ct = p;
+    } else {
+       set_id (ct, 1);
+    }
+
+    /*
+     * Fill out, or expand directives.  Parse and execute
+     * commands specified by profile composition strings.
+     */
+    compose_content (ct);
+
+    if ((cp = strchr(prefix, 'a')) == NULL)
+       adios (NULL, "internal error(4)");
+
+    /*
+     * Scan the contents.  Choose a transfer encoding, and
+     * check if prefix for multipart boundary clashes with
+     * any of the contents.
+     */
+    while (scan_content (ct) == NOTOK) {
+       if (*cp < 'z') {
+           (*cp)++;
+        } else {
+           if (*++cp == 0)
+               adios (NULL, "giving up trying to find a unique delimiter string");
+           else
+               (*cp)++;
+       }
+    }
+
+    /* Build the rest of the header field structures */
+    build_headers (ct);
+
+    return ct;
+}
+
+
+/*
+ * Main routine for reading/parsing the headers
+ * of a message content.
+ *
+ * toplevel =  1   # we are at the top level of the message
+ * toplevel =  0   # we are inside message type or multipart type
+ *                 # other than multipart/digest
+ * toplevel = -1   # we are inside multipart/digest
+ */
+
+static CT
+get_content (FILE *in, char *file, int toplevel)
+{
+    int compnum, state;
+    char buf[BUFSIZ], name[NAMESZ];
+    CT ct;
+
+    if (!(ct = (CT) calloc (1, sizeof(*ct))))
+       adios (NULL, "out of memory");
+
+    ct->c_fp = in;
+    ct->c_file = add (file, NULL);
+    ct->c_begin = ftell (ct->c_fp) + 1;
+
+    /*
+     * Read the content headers
+     */
+    for (compnum = 1, state = FLD;;) {
+       switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
+       case FLD:
+       case FLDPLUS:
+       case FLDEOF:
+           compnum++;
+
+           /* Get MIME-Version field */
+           if (!strcasecmp (name, VRSN_FIELD)) {
+               int ucmp;
+               char c, *cp, *dp;
+
+               cp = add (buf, NULL);
+               while (state == FLDPLUS) {
+                   state = m_getfld (state, name, buf, sizeof(buf), in);
+                   cp = add (buf, cp);
+               }
+
+               if (ct->c_vrsn) {
+                   advise (NULL, "message %s has multiple %s: fields (%s)",
+                           ct->c_file, VRSN_FIELD, dp = trimcpy (cp));
+                   free (dp);
+                   free (cp);
+                   goto out;
+               }
+
+               ct->c_vrsn = cp;
+               while (isspace (*cp))
+                   cp++;
+               for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
+                   *dp++ = ' ';
+               for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
+                   if (!isspace (*dp))
+                       break;
+               *++dp = '\0';
+               if (debugsw)
+                   fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
+
+               if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
+                   goto out;
+
+               for (dp = cp; istoken (*dp); dp++)
+                   continue;
+               c = *dp, *dp = '\0';
+               ucmp = !strcasecmp (cp, VRSN_VALUE);
+               *dp = c;
+               if (!ucmp)
+                   admonish (NULL,
+                             "message %s has unknown value for %s: field (%s)",
+                             ct->c_file, VRSN_FIELD, cp);
+               goto got_header;
+           }
+
+           /* Get Content-Type field */
+           if (!strcasecmp (name, TYPE_FIELD)) {
+               char *cp;
+               struct str2init *s2i;
+               CI ci = &ct->c_ctinfo;
+
+               cp = add (buf, NULL);
+               while (state == FLDPLUS) {
+                   state = m_getfld (state, name, buf, sizeof(buf), in);
+                   cp = add (buf, cp);
+               }
+
+               /* Check if we've already seen a Content-Type header */
+               if (ct->c_ctline) {
+                   char *dp = trimcpy (cp);
+
+                   advise (NULL, "message %s has multiple %s: fields (%s)",
+                           ct->c_file, TYPE_FIELD, dp);
+                   free (dp);
+                   free (cp);
+                   goto out;
+               }
+
+               /* Parse the Content-Type field */
+               if (get_ctinfo (cp, ct, 0) == NOTOK)
+                   goto out;
+
+               /*
+                * Set the Init function and the internal
+                * flag for this content type.
+                */
+               for (s2i = str2cts; s2i->si_key; s2i++)
+                   if (!strcasecmp (ci->ci_type, s2i->si_key))
+                       break;
+               if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
+                   s2i++;
+               ct->c_type = s2i->si_val;
+               ct->c_ctinitfnx = s2i->si_init;
+               goto got_header;
+           }
+
+           /* Get Content-Transfer-Encoding field */
+           if (!strcasecmp (name, ENCODING_FIELD)) {
+               char *cp, *dp;
+               char c;
+               struct str2init *s2i;
+
+               cp = add (buf, NULL);
+               while (state == FLDPLUS) {
+                   state = m_getfld (state, name, buf, sizeof(buf), in);
+                   cp = add (buf, cp);
+               }
+
+               /*
+                * Check if we've already seen the
+                * Content-Transfer-Encoding field
+                */
+               if (ct->c_celine) {
+                   advise (NULL, "message %s has multiple %s: fields (%s)",
+                           ct->c_file, ENCODING_FIELD, dp = trimcpy (cp));
+                   free (dp);
+                   free (cp);
+                   goto out;
+               }
+
+               ct->c_celine = cp;      /* Save copy of this field */
+               while (isspace (*cp))
+                   cp++;
+               for (dp = cp; istoken (*dp); dp++)
+                   continue;
+               c = *dp;
+               *dp = '\0';
+
+               /*
+                * Find the internal flag and Init function
+                * for this transfer encoding.
+                */
+               for (s2i = str2ces; s2i->si_key; s2i++)
+                   if (!strcasecmp (cp, s2i->si_key))
+                       break;
+               if (!s2i->si_key && !uprf (cp, "X-"))
+                   s2i++;
+               *dp = c;
+               ct->c_encoding = s2i->si_val;
+
+               /* Call the Init function for this encoding */
+               if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
+                   goto out;
+               goto got_header;
+           }
+
+           /* Get Content-ID field */
+           if (!strcasecmp (name, ID_FIELD)) {
+               ct->c_id = add (buf, ct->c_id);
+               while (state == FLDPLUS) {
+                   state = m_getfld (state, name, buf, sizeof(buf), in);
+                   ct->c_id = add (buf, ct->c_id);
+               }
+               goto got_header;
+           }
+
+           /* Get Content-Description field */
+           if (!strcasecmp (name, DESCR_FIELD)) {
+               ct->c_descr = add (buf, ct->c_descr);
+               while (state == FLDPLUS) {
+                   state = m_getfld (state, name, buf, sizeof(buf), in);
+                   ct->c_descr = add (buf, ct->c_descr);
+               }
+               goto got_header;
+           }
+
+           /* Get Content-MD5 field */
+           if (!strcasecmp (name, MD5_FIELD)) {
+               char *cp, *dp, *ep;
+
+               cp = add (buf, NULL);
+               while (state == FLDPLUS) {
+                   state = m_getfld (state, name, buf, sizeof(buf), in);
+                   cp = add (buf, cp);
+               }
+
+               if (!checksw) {
+                   free (cp);
+                   goto got_header;
+               }
+
+               if (ct->c_digested) {
+                   advise (NULL, "message %s has multiple %s: fields (%s)",
+                           ct->c_file, MD5_FIELD, dp = trimcpy (cp));
+                   free (dp);
+                   free (cp);
+                   goto out;
+               }
+
+               ep = cp;
+               while (isspace (*cp))
+                   cp++;
+               for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
+                   *dp++ = ' ';
+               for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
+                   if (!isspace (*dp))
+                       break;
+               *++dp = '\0';
+               if (debugsw)
+                   fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
+
+               if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
+                   free (ep);
+                   goto out;
+               }
+
+               for (dp = cp; *dp && !isspace (*dp); dp++)
+                   continue;
+               *dp = '\0';
+
+               readDigest (ct, cp);
+               free (ep);
+               ct->c_digested++;
+               goto got_header;
+           }
+
+#if 0
+           if (uprf (name, XXX_FIELD_PRF))
+               advise (NULL, "unknown field (%s) in message %s",
+                       name, ct->c_file);
+           /* and fall... */
+#endif
+
+           while (state == FLDPLUS)
+               state = m_getfld (state, name, buf, sizeof(buf), in);
+
+got_header:
+           if (state != FLDEOF) {
+               ct->c_begin = ftell (in) + 1;
+               continue;
+           }
+           /* else fall... */
+
+       case BODY:
+       case BODYEOF:
+           ct->c_begin = ftell (in) - strlen (buf);
+           break;
+
+       case FILEEOF:
+           ct->c_begin = ftell (in);
+           break;
+
+       case LENERR:
+       case FMTERR:
+           adios (NULL, "message format error in component #%d", compnum);
+
+       default:
+           adios (NULL, "getfld() returned %d", state);
+       }
+       break;
+    }
+
+    /*
+     * Check if we saw a Content-Type field.
+     * If not, then assign a default value for
+     * it, and the Init function.
+     */
+    if (!ct->c_ctline) {
+       /*
+        * If we are inside a multipart/digest message,
+        * so default type is message/rfc822
+        */
+       if (toplevel < 0) {
+           if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
+               goto out;
+           ct->c_type = CT_MESSAGE;
+           ct->c_ctinitfnx = InitMessage;
+       } else {
+           /*
+            * Else default type is text/plain
+            */
+           if (get_ctinfo ("text/plain", ct, 0) == NOTOK)
+               goto out;
+           ct->c_type = CT_TEXT;
+           ct->c_ctinitfnx = InitText;
+       }
+    }
+
+    /* Use default Transfer-Encoding, if necessary */
+    if (!ct->c_celine) {
+       ct->c_encoding = CE_7BIT;
+       Init7Bit (ct);
+    }
+
+    return ct;
+
+out:
+    free_content (ct);
+    return NULL;
+}
+
+
+/*
+ * small routine to add header field to list
+ */
+
+static int
+add_header (CT ct, char *name, char *value)
+{
+    HF hp;
+
+    /* allocate header field structure */
+    if (!(hp = malloc (sizeof(*hp))))
+       adios (NULL, "out of memory");
+
+    /* link data into header structure */
+    hp->name = name;
+    hp->value = value;
+    hp->next = NULL;
+
+    /* link header structure into the list */
+    if (ct->c_first_hf == NULL) {
+       ct->c_first_hf = hp;            /* this is the first */
+       ct->c_last_hf = hp;
+    } else {
+       ct->c_last_hf->next = hp;       /* add it to the end */
+       ct->c_last_hf = hp;
+    }
+
+    return 0;
+}
+
+
+/*
+ * Used to parse both:
+ *   1) Content-Type line
+ *   2) composition directives
+ *
+ * and fills in the information of the CTinfo structure.
+ */
+
+static int
+get_ctinfo (char *cp, CT ct, int magic)
+{
+    int        i;
+    char *dp, **ap, **ep;
+    char c;
+    CI ci;
+
+    ci = &ct->c_ctinfo;
+    i = strlen (invo_name) + 2;
+
+    /* store copy of Content-Type line */
+    cp = ct->c_ctline = add (cp, NULL);
+
+    while (isspace (*cp))      /* trim leading spaces */
+       cp++;
+
+    /* change newlines to spaces */
+    for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
+       *dp++ = ' ';
+
+    /* trim trailing spaces */
+    for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
+       if (!isspace (*dp))
+           break;
+    *++dp = '\0';
+
+    if (debugsw)
+       fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
+
+    if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
+       return NOTOK;
+
+    for (dp = cp; istoken (*dp); dp++)
+       continue;
+    c = *dp, *dp = '\0';
+    ci->ci_type = add (cp, NULL);      /* store content type */
+    *dp = c, cp = dp;
+
+    if (!*ci->ci_type) {
+       advise (NULL, "invalid %s: field in message %s (empty type)", 
+               TYPE_FIELD, ct->c_file);
+       return NOTOK;
+    }
+
+    /* down case the content type string */
+    for (dp = ci->ci_type; *dp; dp++)
+       if (isalpha(*dp) && isupper (*dp))
+           *dp = tolower (*dp);
+
+    while (isspace (*cp))
+       cp++;
+
+    if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
+       return NOTOK;
+
+    if (*cp != '/') {
+       if (!magic)
+           ci->ci_subtype = add ("", NULL);
+       goto magic_skip;
+    }
+
+    cp++;
+    while (isspace (*cp))
+       cp++;
+
+    if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
+       return NOTOK;
+
+    for (dp = cp; istoken (*dp); dp++)
+       continue;
+    c = *dp, *dp = '\0';
+    ci->ci_subtype = add (cp, NULL);   /* store the content subtype */
+    *dp = c, cp = dp;
+
+    if (!*ci->ci_subtype) {
+       advise (NULL,
+               "invalid %s: field in message %s (empty subtype for \"%s\")",
+               TYPE_FIELD, ct->c_file, ci->ci_type);
+       return NOTOK;
+    }
+
+    /* down case the content subtype string */
+    for (dp = ci->ci_subtype; *dp; dp++)
+       if (isalpha(*dp) && isupper (*dp))
+           *dp = tolower (*dp);
+
+magic_skip:
+    while (isspace (*cp))
+       cp++;
+
+    if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
+       return NOTOK;
+
+    /*
+     * Parse attribute/value pairs given with Content-Type
+     */
+    ep = (ap = ci->ci_attrs) + NPARMS;
+    while (*cp == ';') {
+       char *vp, *up;
+
+       if (ap >= ep) {
+           advise (NULL,
+                   "too many parameters in message %s's %s: field (%d max)",
+                   ct->c_file, TYPE_FIELD, NPARMS);
+           return NOTOK;
+       }
+
+       cp++;
+       while (isspace (*cp))
+           cp++;
+
+       if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
+           return NOTOK;
+
+       if (*cp == 0) {
+           advise (NULL,
+                   "extraneous trailing ';' in message %s's %s: parameter list",
+                   ct->c_file, TYPE_FIELD);
+           return OK;
+       }
+
+       /* down case the attribute name */
+       for (dp = cp; istoken (*dp); dp++)
+           if (isalpha(*dp) && isupper (*dp))
+               *dp = tolower (*dp);
+
+       for (up = dp; isspace (*dp); )
+           dp++;
+       if (dp == cp || *dp != '=') {
+           advise (NULL,
+                   "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
+                   ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
+           return NOTOK;
+       }
+
+       vp = (*ap = add (cp, NULL)) + (up - cp);
+       *vp = '\0';
+       for (dp++; isspace (*dp); )
+           dp++;
+
+       /* now add the attribute value */
+       ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
+
+       if (*dp == '"') {
+           for (cp = ++dp, dp = vp;;) {
+               switch (c = *cp++) {
+                   case '\0':
+bad_quote:
+                       advise (NULL,
+                               "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
+                               ct->c_file, TYPE_FIELD, i, i, "", *ap);
+                       return NOTOK;
+
+                   case '\\':
+                       *dp++ = c;
+                       if ((c = *cp++) == '\0')
+                           goto bad_quote;
+                       /* else fall... */
+
+                   default:
+                       *dp++ = c;
+                       continue;
+
+                   case '"':
+                       *dp = '\0';
+                       break;
+               }
+               break;
+           }
+       } else {
+           for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
+               continue;
+           *dp = '\0';
+       }
+       if (!*vp) {
+           advise (NULL,
+                   "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
+                   ct->c_file, TYPE_FIELD, i, i, "", *ap);
+           return NOTOK;
+       }
+       ap++;
+
+       while (isspace (*cp))
+           cp++;
+
+       if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
+           return NOTOK;
+    }
+
+    /*
+     * Get any <Content-Id> given in buffer
+     */
+    if (magic && *cp == '<') {
+       if (ct->c_id) {
+           free (ct->c_id);
+           ct->c_id = NULL;
+       }
+       if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
+           advise (NULL, "invalid ID in message %s", ct->c_file);
+           return NOTOK;
+       }
+       c = *dp;
+       *dp = '\0';
+       if (*ct->c_id)
+           ct->c_id = concat ("<", ct->c_id, ">\n", NULL);
+       else
+           ct->c_id = NULL;
+       *dp++ = c;
+       cp = dp;
+
+       while (isspace (*cp))
+           cp++;
+    }
+
+    /*
+     * Get any [Content-Description] given in buffer.
+     */
+    if (magic && *cp == '[') {
+       ct->c_descr = ++cp;
+       for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
+           if (*dp == ']')
+               break;
+       if (dp < cp) {
+           advise (NULL, "invalid description in message %s", ct->c_file);
+           ct->c_descr = NULL;
+           return NOTOK;
+       }
+       
+       c = *dp;
+       *dp = '\0';
+       if (*ct->c_descr)
+           ct->c_descr = concat (ct->c_descr, "\n", NULL);
+       else
+           ct->c_descr = NULL;
+       *dp++ = c;
+       cp = dp;
+
+       while (isspace (*cp))
+           cp++;
+    }
+
+    /*
+     * Check if anything is left over
+     */
+    if (*cp) {
+       if (magic)
+           ci->ci_magic = add (cp, NULL);
+       else
+           advise (NULL,
+                   "extraneous information in message %s's %s: field\n%*.*s(%s)",
+               ct->c_file, TYPE_FIELD, i, i, "", cp);
+    }
+
+    return OK;
+}
+
+
+static int
+get_comment (CT ct, char **ap, int istype)
+{
+    int i;
+    char *bp, *cp;
+    char c, buffer[BUFSIZ], *dp;
+    CI ci;
+
+    ci = &ct->c_ctinfo;
+    cp = *ap;
+    bp = buffer;
+    cp++;
+
+    for (i = 0;;) {
+       switch (c = *cp++) {
+       case '\0':
+invalid:
+       advise (NULL, "invalid comment in message %s's %s: field",
+               ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
+       return NOTOK;
+
+       case '\\':
+           *bp++ = c;
+           if ((c = *cp++) == '\0')
+               goto invalid;
+           *bp++ = c;
+           continue;
+
+       case '(':
+           i++;
+           /* and fall... */
+       default:
+           *bp++ = c;
+           continue;
+
+       case ')':
+           if (--i < 0)
+               break;
+           *bp++ = c;
+           continue;
+       }
+       break;
+    }
+    *bp = '\0';
+
+    if (istype) {
+       if ((dp = ci->ci_comment)) {
+           ci->ci_comment = concat (dp, " ", buffer, NULL);
+           free (dp);
+       } else {
+           ci->ci_comment = add (buffer, NULL);
+       }
+    }
+
+    while (isspace (*cp))
+       cp++;
+
+    *ap = cp;
+    return OK;
+}
+
+
+/*
+ * CONTENTS
+ *
+ * Handles content types audio, image, and video.
+ * There's not much to do right here.
+ */
+
+static int
+InitGeneric (CT ct)
+{
+    return OK;         /* not much to do here */
+}
+
+
+/*
+ * TEXT
+ */
+
+static int
+InitText (CT ct)
+{
+    char **ap, **ep;
+    struct k2v *kv;
+    struct text *t;
+    CI ci = &ct->c_ctinfo;
+
+    /* check for missing subtype */
+    if (!*ci->ci_subtype)
+       ci->ci_subtype = add ("plain", ci->ci_subtype);
+
+    /* match subtype */
+    for (kv = SubText; kv->kv_key; kv++)
+       if (!strcasecmp (ci->ci_subtype, kv->kv_key))
+           break;
+    ct->c_subtype = kv->kv_value;
+
+    /* allocate text character set structure */
+    if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
+       adios (NULL, "out of memory");
+    ct->c_ctparams = (void *) t;
+
+    /* initially mark character set as unspecified */
+    t->tx_charset = CHARSET_UNSPECIFIED;
+
+    /* scan for charset parameter */
+    for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
+       if (!strcasecmp (*ap, "charset"))
+           break;
+
+    /* check if content specified a character set */
+    if (*ap) {
+       /* match character set or set to CHARSET_UNKNOWN */
+       for (kv = Charset; kv->kv_key; kv++)
+           if (!strcasecmp (*ep, kv->kv_key))
+               break;
+       t->tx_charset = kv->kv_value;
+    }
+
+    return OK;
+}
+
+
+/*
+ * MULTIPART
+ */
+
+static int
+InitMultiPart (CT ct)
+{
+    int        inout;
+    long last, pos;
+    char *cp, *dp, **ap, **ep;
+    char *bp, buffer[BUFSIZ];
+    struct multipart *m;
+    struct k2v *kv;
+    struct part *part, **next;
+    CI ci = &ct->c_ctinfo;
+    CT p;
+    FILE *fp;
+
+    /*
+     * The encoding for multipart messages must be either
+     * 7bit, 8bit, or binary (per RFC2045).
+     */
+    if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
+       && ct->c_encoding != CE_BINARY) {
+       admonish (NULL,
+                 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
+                 ci->ci_type, ci->ci_subtype, ct->c_file);
+       return NOTOK;
+    }
+
+    /* match subtype */
+    for (kv = SubMultiPart; kv->kv_key; kv++)
+       if (!strcasecmp (ci->ci_subtype, kv->kv_key))
+           break;
+    ct->c_subtype = kv->kv_value;
+
+    /*
+     * Check for "boundary" parameter, which is
+     * required for multipart messages.
+     */
+    for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
+       if (!strcasecmp (*ap, "boundary")) {
+           bp = *ep;
+           break;
+       }
+    }
+
+    /* complain if boundary parameter is missing */
+    if (!*ap) {
+       advise (NULL,
+               "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
+               ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
+       return NOTOK;
+    }
+
+    /* allocate primary structure for multipart info */
+    if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
+       adios (NULL, "out of memory");
+    ct->c_ctparams = (void *) m;
+
+    /* check if boundary parameter contains only whitespace characters */
+    for (cp = bp; isspace (*cp); cp++)
+       continue;
+    if (!*cp) {
+       advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
+               ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
+       return NOTOK;
+    }
+
+    /* remove trailing whitespace from boundary parameter */
+    for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
+       if (!isspace (*dp))
+           break;
+    *++dp = '\0';
+
+    /* record boundary separators */
+    m->mp_start = concat (bp, "\n", NULL);
+    m->mp_stop = concat (bp, "--\n", NULL);
+
+    if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
+       advise (ct->c_file, "unable to open for reading");
+       return NOTOK;
+    }
+
+    fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
+    last = ct->c_end;
+    next = &m->mp_parts;
+    part = NULL;
+    inout = 1;
+
+    while (fgets (buffer, sizeof(buffer) - 1, fp)) {
+       if (pos > last)
+           break;
+
+       pos += strlen (buffer);
+       if (buffer[0] != '-' || buffer[1] != '-')
+           continue;
+       if (inout) {
+           if (strcmp (buffer + 2, m->mp_start))
+               continue;
+next_part:
+           if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
+               adios (NULL, "out of memory");
+           *next = part;
+           next = &part->mp_next;
+
+           if (!(p = get_content (fp, ct->c_file,
+               rfc934sw && ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
+               fclose (ct->c_fp);
+               ct->c_fp = NULL;
+               return NOTOK;
+           }
+           p->c_fp = NULL;
+           part->mp_part = p;
+           pos = p->c_begin;
+           fseek (fp, pos, SEEK_SET);
+           inout = 0;
+       } else {
+           if (strcmp (buffer + 2, m->mp_start) == 0) {
+               inout = 1;
+end_part:
+               p = part->mp_part;
+               p->c_end = ftell(fp) - (strlen(buffer) + 1);
+               if (p->c_end < p->c_begin)
+                   p->c_begin = p->c_end;
+               if (inout)
+                   goto next_part;
+               goto last_part;
+           } else {
+               if (strcmp (buffer + 2, m->mp_stop) == 0)
+                   goto end_part;
+           }
+       }
+    }
+
+    advise (NULL, "bogus multipart content in message %s", ct->c_file);
+    if (!inout && part) {
+       p = part->mp_part;
+       p->c_end = ct->c_end;
+
+       if (p->c_begin >= p->c_end) {
+           for (next = &m->mp_parts; *next != part;
+                    next = &((*next)->mp_next))
+               continue;
+           *next = NULL;
+           free_content (p);
+           free ((char *) part);
+       }
+    }
+
+last_part:
+    /* reverse the order of the parts for multipart/alternative */
+    if (ct->c_subtype == MULTI_ALTERNATE)
+       reverse_parts (ct);
+
+    /*
+     * label all subparts with part number, and
+     * then initialize the content of the subpart.
+     */
+    {
+       int partnum;
+       char *pp;
+       char partnam[BUFSIZ];
+
+       if (ct->c_partno) {
+           snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
+           pp = partnam + strlen (partnam);
+       } else {
+           pp = partnam;
+       }
+
+       for (part = m->mp_parts, partnum = 1; part;
+                part = part->mp_next, partnum++) {
+           p = part->mp_part;
+
+           sprintf (pp, "%d", partnum);
+           p->c_partno = add (partnam, NULL);
+
+           /* initialize the content of the subparts */
+           if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
+               fclose (ct->c_fp);
+               ct->c_fp = NULL;
+               return NOTOK;
+           }
+       }
+    }
+
+    fclose (ct->c_fp);
+    ct->c_fp = NULL;
+    return OK;
+}
+
+
+/*
+ * reverse the order of the parts of a multipart
+ */
+
+static void
+reverse_parts (CT ct)
+{
+    int i;
+    struct multipart *m;
+    struct part **base, **bmp, **next, *part;
+
+    m = (struct multipart *) ct->c_ctparams;
+
+    /* if only one part, just return */
+    if (!m->mp_parts || !m->mp_parts->mp_next)
+       return;
+
+    /* count number of parts */
+    i = 0;
+    for (part = m->mp_parts; part; part = part->mp_next)
+       i++;
+
+    /* allocate array of pointers to the parts */
+    if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
+       adios (NULL, "out of memory");
+    bmp = base;
+
+    /* point at all the parts */
+    for (part = m->mp_parts; part; part = part->mp_next)
+       *bmp++ = part;
+    *bmp = NULL;
+
+    /* reverse the order of the parts */
+    next = &m->mp_parts;
+    for (bmp--; bmp >= base; bmp--) {
+       part = *bmp;
+       *next = part;
+       next = &part->mp_next;
+    }
+    *next = NULL;
+
+    /* free array of pointers */
+    free ((char *) base);
+}
+
+
+/*
+ * MESSAGE
+ */
+
+static int
+InitMessage (CT ct)
+{
+    struct k2v *kv;
+    CI ci = &ct->c_ctinfo;
+
+    if (ct->c_encoding != CE_7BIT) {
+       admonish (NULL,
+                 "\"%s/%s\" type in message %s should be encoded in 7bit",
+                 ci->ci_type, ci->ci_subtype, ct->c_file);
+       return NOTOK;
+    }
+
+    /* check for missing subtype */
+    if (!*ci->ci_subtype)
+       ci->ci_subtype = add ("rfc822", ci->ci_subtype);
+
+    /* match subtype */
+    for (kv = SubMessage; kv->kv_key; kv++)
+       if (!strcasecmp (ci->ci_subtype, kv->kv_key))
+           break;
+    ct->c_subtype = kv->kv_value;
+
+    switch (ct->c_subtype) {
+       case MESSAGE_RFC822:
+           break;
+
+       case MESSAGE_PARTIAL:
+           {
+               char **ap, **ep;
+               struct partial *p;
+
+               if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
+                   adios (NULL, "out of memory");
+               ct->c_ctparams = (void *) p;
+
+               /* scan for parameters "id", "number", and "total" */
+               for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
+                   if (!strcasecmp (*ap, "id")) {
+                       p->pm_partid = add (*ep, NULL);
+                       continue;
+                   }
+                   if (!strcasecmp (*ap, "number")) {
+                       if (sscanf (*ep, "%d", &p->pm_partno) != 1
+                               || p->pm_partno < 1) {
+invalid_param:
+                           advise (NULL,
+                                   "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
+                                   *ap, ci->ci_type, ci->ci_subtype,
+                                   ct->c_file, TYPE_FIELD);
+                           return NOTOK;
+                       }
+                       continue;
+                   }
+                   if (!strcasecmp (*ap, "total")) {
+                       if (sscanf (*ep, "%d", &p->pm_maxno) != 1
+                               || p->pm_maxno < 1)
+                           goto invalid_param;
+                       continue;
+                   }
+               }
+
+               if (!p->pm_partid
+                       || !p->pm_partno
+                       || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
+                   advise (NULL,
+                           "invalid parameters for \"%s/%s\" type in message %s's %s field",
+                           ci->ci_type, ci->ci_subtype,
+                           ct->c_file, TYPE_FIELD);
+                   return NOTOK;
+               }
+           }
+           break;
+
+       case MESSAGE_EXTERNAL:
+           {
+               int exresult;
+               struct exbody *e;
+               CT p;
+               FILE *fp;
+
+               if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
+                   adios (NULL, "out of memory");
+               ct->c_ctparams = (void *) e;
+
+               if (!ct->c_fp
+                       && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
+                   advise (ct->c_file, "unable to open for reading");
+                   return NOTOK;
+               }
+
+               fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
+
+               if (!(p = get_content (fp, ct->c_file, 0))) {
+                   fclose (ct->c_fp);
+                   ct->c_fp = NULL;
+                   return NOTOK;
+               }
+
+               e->eb_parent = ct;
+               e->eb_content = p;
+               p->c_ctexbody = e;
+               if ((exresult = params_external (ct, 0)) != NOTOK
+                       && p->c_ceopenfnx == openMail) {
+                   int cc, size;
+                   char *bp;
+                   
+                   if ((size = ct->c_end - p->c_begin) <= 0) {
+                       if (!e->eb_subject)
+                           content_error (NULL, ct,
+                                          "empty body for access-type=mail-server");
+                       goto no_body;
+                   }
+                   
+                   if ((e->eb_body = bp = malloc ((unsigned) size)) == NULL)
+                       adios (NULL, "out of memory");
+                   fseek (p->c_fp, p->c_begin, SEEK_SET);
+                   while (size > 0)
+                       switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
+                           case NOTOK:
+                               adios ("failed", "fread");
+
+                           case OK:
+                               adios (NULL, "unexpected EOF from fread");
+
+                           default:
+                               bp += cc, size -= cc;
+                               break;
+                       }
+                   *bp = 0;
+               }
+no_body:
+               p->c_fp = NULL;
+               p->c_end = p->c_begin;
+
+               fclose (ct->c_fp);
+               ct->c_fp = NULL;
+
+               if (exresult == NOTOK)
+                   return NOTOK;
+               if (e->eb_flags == NOTOK)
+                   return OK;
+
+               switch (p->c_type) {
+                   case CT_MULTIPART:
+                       break;
+
+                   case CT_MESSAGE:
+                       if (p->c_subtype != MESSAGE_RFC822)
+                           break;
+                       /* else fall... */
+                   default:
+                       e->eb_partno = ct->c_partno;
+                       if (p->c_ctinitfnx)
+                           (*p->c_ctinitfnx) (p);
+                       break;
+               }
+           }
+           break;
+
+       default:
+           break;
+    }
+
+    return OK;
+}
+
+
+static int
+params_external (CT ct, int composing)
+{
+    char **ap, **ep;
+    struct exbody *e = (struct exbody *) ct->c_ctparams;
+    CI ci = &ct->c_ctinfo;
+
+    for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
+       if (!strcasecmp (*ap, "access-type")) {
+           struct str2init *s2i;
+           CT p = e->eb_content;
+
+           for (s2i = str2methods; s2i->si_key; s2i++)
+               if (!strcasecmp (*ep, s2i->si_key))
+                   break;
+
+           if (!s2i->si_key) {
+               e->eb_access = *ep;
+               e->eb_flags = NOTOK;
+               p->c_encoding = CE_EXTERNAL;
+               continue;
+           }
+           e->eb_access = s2i->si_key;
+           e->eb_flags = s2i->si_val;
+           p->c_encoding = CE_EXTERNAL;
+
+           /* Call the Init function for this external type */
+           if ((*s2i->si_init)(p) == NOTOK)
+               return NOTOK;
+           continue;
+       }
+       if (!strcasecmp (*ap, "name")) {
+           e->eb_name = *ep;
+           continue;
+       }
+       if (!strcasecmp (*ap, "permission")) {
+           e->eb_permission = *ep;
+           continue;
+       }
+       if (!strcasecmp (*ap, "site")) {
+           e->eb_site = *ep;
+           continue;
+       }
+       if (!strcasecmp (*ap, "directory")) {
+           e->eb_dir = *ep;
+           continue;
+       }
+       if (!strcasecmp (*ap, "mode")) {
+           e->eb_mode = *ep;
+           continue;
+       }
+       if (!strcasecmp (*ap, "size")) {
+           sscanf (*ep, "%lu", &e->eb_size);
+           continue;
+       }
+       if (!strcasecmp (*ap, "server")) {
+           e->eb_server = *ep;
+           continue;
+       }
+       if (!strcasecmp (*ap, "subject")) {
+           e->eb_subject = *ep;
+           continue;
+       }
+       if (composing && !strcasecmp (*ap, "body")) {
+           e->eb_body = getcpy (*ep);
+           continue;
+       }
+    }
+
+    if (!e->eb_access) {
+       advise (NULL,
+               "invalid parameters for \"%s/%s\" type in message %s's %s field",
+               ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
+       return NOTOK;
+    }
+
+    return OK;
+}
+
+
+/*
+ * APPLICATION
+ */
+
+static int
+InitApplication (CT ct)
+{
+    struct k2v *kv;
+    CI ci = &ct->c_ctinfo;
+
+    /* match subtype */
+    for (kv = SubApplication; kv->kv_key; kv++)
+       if (!strcasecmp (ci->ci_subtype, kv->kv_key))
+           break;
+    ct->c_subtype = kv->kv_value;
+
+    return OK;
+}
+
+
+/*
+ * Set up structures for placing unencoded
+ * content when building parts.
+ */
+
+static int
+init_decoded_content (CT ct)
+{
+    CE ce;
+
+    if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
+       adios (NULL, "out of memory");
+
+    ct->c_cefile     = ce;
+    ct->c_ceopenfnx  = open7Bit;       /* since unencoded */
+    ct->c_ceclosefnx = close_encoding;
+    ct->c_cesizefnx  = NULL;           /* since unencoded */
+
+    return OK;
+}
+
+
+/*
+ * TRANSFER ENCODINGS
+ */
+
+static int
+init_encoding (CT ct, OpenCEFunc openfnx)
+{
+    CE ce;
+
+    if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
+       adios (NULL, "out of memory");
+
+    ct->c_cefile     = ce;
+    ct->c_ceopenfnx  = openfnx;
+    ct->c_ceclosefnx = close_encoding;
+    ct->c_cesizefnx  = size_encoding;
+
+    return OK;
+}
+
+
+static void
+close_encoding (CT ct)
+{
+    CE ce;
+
+    if (!(ce = ct->c_cefile))
+       return;
+
+    if (ce->ce_fp) {
+       fclose (ce->ce_fp);
+       ce->ce_fp = NULL;
+    }
+}
+
+
+static unsigned long
+size_encoding (CT ct)
+{
+    int        fd;
+    unsigned long size;
+    char *file;
+    CE ce;
+    struct stat st;
+
+    if (!(ce = ct->c_cefile))
+       return (ct->c_end - ct->c_begin);
+
+    if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
+       return (long) st.st_size;
+
+    if (ce->ce_file) {
+       if (stat (ce->ce_file, &st) != NOTOK)
+           return (long) st.st_size;
+       else
+           return 0L;
+    }
+
+    if (ct->c_encoding == CE_EXTERNAL)
+       return (ct->c_end - ct->c_begin);       
+
+    file = NULL;
+    if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
+       return (ct->c_end - ct->c_begin);
+
+    if (fstat (fd, &st) != NOTOK)
+       size = (long) st.st_size;
+    else
+       size = 0L;
+
+    (*ct->c_ceclosefnx) (ct);
+    return size;
+}
+
+
+/*
+ * BASE64
+ */
+
+static unsigned char b642nib[0x80] = {
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
+    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
+    0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 
+    0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+    0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+    0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 
+    0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+    0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+    0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+
+static int
+InitBase64 (CT ct)
+{
+    return init_encoding (ct, openBase64);
+}
+
+
+static int
+openBase64 (CT ct, char **file)
+{
+    int        bitno, cc, digested;
+    int fd, len, skip;
+    unsigned long bits;
+    unsigned char value, *b, *b1, *b2, *b3;
+    char *cp, *ep, buffer[BUFSIZ];
+    CE ce;
+    MD5_CTX mdContext;
+
+    b  = (unsigned char *) &bits;
+    b1 = &b[endian > 0 ? 1 : 2];
+    b2 = &b[endian > 0 ? 2 : 1];
+    b3 = &b[endian > 0 ? 3 : 0];
+
+    ce = ct->c_cefile;
+    if (ce->ce_fp) {
+       fseek (ce->ce_fp, 0L, SEEK_SET);
+       goto ready_to_go;
+    }
+
+    if (ce->ce_file) {
+       if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
+           content_error (ce->ce_file, ct, "unable to fopen for reading");
+           return NOTOK;
+       }
+       goto ready_to_go;
+    }
+
+    if (*file == NULL) {
+       ce->ce_file = add (m_scratch ("", tmp), NULL);
+       ce->ce_unlink = 1;
+    } else {
+       ce->ce_file = add (*file, NULL);
+       ce->ce_unlink = 0;
+    }
+
+    if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
+       content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
+       return NOTOK;
+    }
+
+    if ((len = ct->c_end - ct->c_begin) < 0)
+       adios (NULL, "internal error(1)");
+
+    if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
+       content_error (ct->c_file, ct, "unable to open for reading");
+       return NOTOK;
+    }
+    
+    if ((digested = ct->c_digested))
+       MD5Init (&mdContext);
+
+    bitno = 18;
+    bits = 0L;
+    skip = 0;
+
+    lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
+    while (len > 0) {
+       switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
+       case NOTOK:
+           content_error (ct->c_file, ct, "error reading from");
+           goto clean_up;
+
+       case OK:
+           content_error (NULL, ct, "premature eof");
+           goto clean_up;
+
+       default:
+           if (cc > len)
+               cc = len;
+           len -= cc;
+
+           for (ep = (cp = buffer) + cc; cp < ep; cp++) {
+               switch (*cp) {
+               default:
+                   if (isspace (*cp))
+                       break;
+                   if (skip || (*cp & 0x80)
+                       || (value = b642nib[*cp & 0x7f]) > 0x3f) {
+                       if (debugsw) {
+                           fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
+                               *cp,
+                               (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
+                               skip);
+                       }
+                       content_error (NULL, ct,
+                                      "invalid BASE64 encoding -- continuing");
+                       continue;
+                   }
+
+                   bits |= value << bitno;
+test_end:
+                   if ((bitno -= 6) < 0) {
+                       putc ((char) *b1, ce->ce_fp);
+                       if (digested)
+                           MD5Update (&mdContext, b1, 1);
+                       if (skip < 2) {
+                           putc ((char) *b2, ce->ce_fp);
+                           if (digested)
+                               MD5Update (&mdContext, b2, 1);
+                           if (skip < 1) {
+                               putc ((char) *b3, ce->ce_fp);
+                               if (digested)
+                                   MD5Update (&mdContext, b3, 1);
+                           }
+                       }
+
+                       if (ferror (ce->ce_fp)) {
+                           content_error (ce->ce_file, ct,
+                                          "error writing to");
+                           goto clean_up;
+                       }
+                       bitno = 18, bits = 0L, skip = 0;
+                   }
+                   break;
+
+               case '=':
+                   if (++skip > 3)
+                       goto self_delimiting;
+                   goto test_end;
+               }
+           }
+       }
+    }
+
+    if (bitno != 18) {
+       if (debugsw)
+           fprintf (stderr, "premature ending (bitno %d)\n", bitno);
+
+       content_error (NULL, ct, "invalid BASE64 encoding");
+       goto clean_up;
+    }
+
+self_delimiting:
+    fseek (ct->c_fp, 0L, SEEK_SET);
+
+    if (fflush (ce->ce_fp)) {
+       content_error (ce->ce_file, ct, "error writing to");
+       goto clean_up;
+    }
+
+    if (digested) {
+       unsigned char digest[16];
+
+       MD5Final (digest, &mdContext);
+       if (memcmp((char *) digest, (char *) ct->c_digest,
+                  sizeof(digest) / sizeof(digest[0])))
+           content_error (NULL, ct,
+                          "content integrity suspect (digest mismatch) -- continuing");
+       else
+           if (debugsw)
+               fprintf (stderr, "content integrity confirmed\n");
+    }
+
+    fseek (ce->ce_fp, 0L, SEEK_SET);
+
+ready_to_go:
+    *file = ce->ce_file;
+    return fileno (ce->ce_fp);
+
+clean_up:
+    free_encoding (ct, 0);
+    return NOTOK;
+}
+
+
+/*
+ * QUOTED PRINTABLE
+ */
+
+static char hex2nib[0x80] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, 
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+
+static int 
+InitQuoted (CT ct)
+{
+    return init_encoding (ct, openQuoted);
+}
+
+
+static int
+openQuoted (CT ct, char **file)
+{
+    int        cc, digested, len, quoted;
+    char *cp, *ep;
+    char buffer[BUFSIZ];
+    unsigned char mask;
+    CE ce;
+    MD5_CTX mdContext;
+
+    ce = ct->c_cefile;
+    if (ce->ce_fp) {
+       fseek (ce->ce_fp, 0L, SEEK_SET);
+       goto ready_to_go;
+    }
+
+    if (ce->ce_file) {
+       if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
+           content_error (ce->ce_file, ct, "unable to fopen for reading");
+           return NOTOK;
+       }
+       goto ready_to_go;
+    }
+
+    if (*file == NULL) {
+       ce->ce_file = add (m_scratch ("", tmp), NULL);
+       ce->ce_unlink = 1;
+    } else {
+       ce->ce_file = add (*file, NULL);
+       ce->ce_unlink = 0;
+    }
+
+    if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
+       content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
+       return NOTOK;
+    }
+
+    if ((len = ct->c_end - ct->c_begin) < 0)
+       adios (NULL, "internal error(2)");
+
+    if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
+       content_error (ct->c_file, ct, "unable to open for reading");
+       return NOTOK;
+    }
+
+    if ((digested = ct->c_digested))
+       MD5Init (&mdContext);
+
+    quoted = 0;
+#ifdef lint
+    mask = 0;
+#endif
+
+    fseek (ct->c_fp, ct->c_begin, SEEK_SET);
+    while (len > 0) {
+       char *dp;
+
+       if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
+           content_error (NULL, ct, "premature eof");
+           goto clean_up;
+       }
+
+       if ((cc = strlen (buffer)) > len)
+           cc = len;
+       len -= cc;
+
+       for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
+           if (!isspace (*ep))
+               break;
+       *++ep = '\n', ep++;
+
+       for (; cp < ep; cp++) {
+           if (quoted) {
+               if (quoted > 1) {
+                   if (!isxdigit (*cp)) {
+invalid_hex:
+                       dp = "expecting hexidecimal-digit";
+                       goto invalid_encoding;
+                   }
+                   mask <<= 4;
+                   mask |= hex2nib[*cp & 0x7f];
+                   putc (mask, ce->ce_fp);
+                   if (digested)
+                       MD5Update (&mdContext, &mask, 1);
+               } else {
+                   switch (*cp) {
+                   case ':':
+                       putc (*cp, ce->ce_fp);
+                       if (digested)
+                           MD5Update (&mdContext, (unsigned char *) ":", 1);
+                       break;
+
+                   default:
+                       if (!isxdigit (*cp))
+                           goto invalid_hex;
+                       mask = hex2nib[*cp & 0x7f];
+                       quoted = 2;
+                       continue;
+                   }
+               }
+
+               if (ferror (ce->ce_fp)) {
+                   content_error (ce->ce_file, ct, "error writing to");
+                   goto clean_up;
+               }
+               quoted = 0;
+               continue;
+           }
+
+           switch (*cp) {
+           default:
+               if (*cp < '!' || *cp > '~') {
+                   int i;
+                   dp = "expecting character in range [!..~]";
+
+invalid_encoding:
+                   i = strlen (invo_name) + 2;
+                   content_error (NULL, ct,
+                                  "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x",
+                                  dp, i, i, "", *cp);
+                   goto clean_up;
+               }
+               /* and fall...*/
+           case ' ':
+           case '\t':
+           case '\n':
+               putc (*cp, ce->ce_fp);
+               if (digested) {
+                   if (*cp == '\n')
+                       MD5Update (&mdContext, (unsigned char *) "\r\n",2);
+                   else
+                       MD5Update (&mdContext, (unsigned char *) cp, 1);
+               }
+               if (ferror (ce->ce_fp)) {
+                   content_error (ce->ce_file, ct, "error writing to");
+                   goto clean_up;
+               }
+               break;
+
+           case '=':
+               if (*++cp != '\n') {
+                   quoted = 1;
+                   cp--;
+               }
+               break;
+           }
+       }
+    }
+    if (quoted) {
+       content_error (NULL, ct,
+                      "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
+       goto clean_up;
+    }
+
+    fseek (ct->c_fp, 0L, SEEK_SET);
+
+    if (fflush (ce->ce_fp)) {
+       content_error (ce->ce_file, ct, "error writing to");
+       goto clean_up;
+    }
+
+    if (digested) {
+       unsigned char digest[16];
+
+       MD5Final (digest, &mdContext);
+       if (memcmp((char *) digest, (char *) ct->c_digest,
+                  sizeof(digest) / sizeof(digest[0])))
+           content_error (NULL, ct,
+                          "content integrity suspect (digest mismatch) -- continuing");
+       else
+           if (debugsw)
+               fprintf (stderr, "content integrity confirmed\n");
+    }
+
+    fseek (ce->ce_fp, 0L, SEEK_SET);
+
+ready_to_go:
+    *file = ce->ce_file;
+    return fileno (ce->ce_fp);
+
+clean_up:
+    free_encoding (ct, 0);
+    return NOTOK;
+}
+
+
+/*
+ * 7BIT
+ */
+
+static int
+Init7Bit (CT ct)
+{
+    if (init_encoding (ct, open7Bit) == NOTOK)
+       return NOTOK;
+
+    ct->c_cesizefnx = NULL;    /* no need to decode for real size */
+    return OK;
+}
+
+
+static int
+open7Bit (CT ct, char **file)
+{
+    int        cc, fd, len;
+    char buffer[BUFSIZ];
+    CE ce;
+
+    ce = ct->c_cefile;
+    if (ce->ce_fp) {
+       fseek (ce->ce_fp, 0L, SEEK_SET);
+       goto ready_to_go;
+    }
+
+    if (ce->ce_file) {
+       if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
+           content_error (ce->ce_file, ct, "unable to fopen for reading");
+           return NOTOK;
+       }
+       goto ready_to_go;
+    }
+
+    if (*file == NULL) {
+       ce->ce_file = add (m_scratch ("", tmp), NULL);
+       ce->ce_unlink = 1;
+    } else {
+       ce->ce_file = add (*file, NULL);
+       ce->ce_unlink = 0;
+    }
+
+    if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
+       content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
+       return NOTOK;
+    }
+
+    if (ct->c_type == CT_MULTIPART) {
+       char **ap, **ep;
+       CI ci = &ct->c_ctinfo;
+
+       len = 0;
+       fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
+       len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
+           + 1 + strlen (ci->ci_subtype);
+       for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
+           putc (';', ce->ce_fp);
+           len++;
+
+           snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
+
+           if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
+               fputs ("\n\t", ce->ce_fp);
+               len = 8;
+           } else {
+               putc (' ', ce->ce_fp);
+               len++;
+           }
+           fprintf (ce->ce_fp, "%s", buffer);
+           len += cc;
+       }
+
+       if (ci->ci_comment) {
+           if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
+               fputs ("\n\t", ce->ce_fp);
+               len = 8;
+           }
+           else {
+               putc (' ', ce->ce_fp);
+               len++;
+           }
+           fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
+           len += cc;
+       }
+       fprintf (ce->ce_fp, "\n");
+       if (ct->c_id)
+           fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
+       if (ct->c_descr)
+           fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
+       fprintf (ce->ce_fp, "\n");
+    }
+
+    if ((len = ct->c_end - ct->c_begin) < 0)
+       adios (NULL, "internal error(3)");
+
+    if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
+       content_error (ct->c_file, ct, "unable to open for reading");
+       return NOTOK;
+    }
+
+    lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
+    while (len > 0)
+       switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
+       case NOTOK:
+           content_error (ct->c_file, ct, "error reading from");
+           goto clean_up;
+
+       case OK:
+           content_error (NULL, ct, "premature eof");
+           goto clean_up;
+
+       default:
+           if (cc > len)
+               cc = len;
+           len -= cc;
+
+           fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
+           if (ferror (ce->ce_fp)) {
+               content_error (ce->ce_file, ct, "error writing to");
+               goto clean_up;
+           }
+       }
+
+    fseek (ct->c_fp, 0L, SEEK_SET);
+
+    if (fflush (ce->ce_fp)) {
+       content_error (ce->ce_file, ct, "error writing to");
+       goto clean_up;
+    }
+
+    fseek (ce->ce_fp, 0L, SEEK_SET);
+
+ready_to_go:
+    *file = ce->ce_file;
+    return fileno (ce->ce_fp);
+
+clean_up:
+    free_encoding (ct, 0);
+    return NOTOK;
+}
+
+
+/*
+ * External
+ */
+
+static int
+openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
+{
+    char cachefile[BUFSIZ];
+
+    if (ce->ce_fp) {
+       fseek (ce->ce_fp, 0L, SEEK_SET);
+       goto ready_already;
+    }
+
+    if (ce->ce_file) {
+       if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
+           content_error (ce->ce_file, ct, "unable to fopen for reading");
+           return NOTOK;
+       }
+       goto ready_already;
+    }
+
+    if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
+               cachefile, sizeof(cachefile)) != NOTOK) {
+       if ((ce->ce_fp = fopen (cachefile, "r"))) {
+           ce->ce_file = getcpy (cachefile);
+           ce->ce_unlink = 0;
+           goto ready_already;
+       } else {
+           admonish (cachefile, "unable to fopen for reading");
+       }
+    }
+
+    return OK;
+
+ready_already:
+    *file = ce->ce_file;
+    *fd = fileno (ce->ce_fp);
+    return DONE;
+}
+
+/*
+ * File
+ */
+
+static int
+InitFile (CT ct)
+{
+    return init_encoding (ct, openFile);
+}
+
+
+static int
+openFile (CT ct, char **file)
+{
+    int        fd, cachetype;
+    char cachefile[BUFSIZ];
+    struct exbody *e = ct->c_ctexbody;
+    CE ce = ct->c_cefile;
+
+    switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
+       case NOTOK:
+           return NOTOK;
+
+       case OK:
+           break;
+
+       case DONE:
+           return fd;
+    }
+
+    if (!e->eb_name) {
+       content_error (NULL, ct, "missing name parameter");
+       return NOTOK;
+    }
+
+    ce->ce_file = getcpy (e->eb_name);
+    ce->ce_unlink = 0;
+
+    if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
+       content_error (ce->ce_file, ct, "unable to fopen for reading");
+       return NOTOK;
+    }
+
+    if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
+           && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
+               cachefile, sizeof(cachefile)) != NOTOK) {
+       int mask;
+       FILE *fp;
+
+       mask = umask (cachetype ? ~m_gmprot () : 0222);
+       if ((fp = fopen (cachefile, "w"))) {
+           int cc;
+           char buffer[BUFSIZ];
+           FILE *gp = ce->ce_fp;
+
+           fseek (gp, 0L, SEEK_SET);
+
+           while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
+                      > 0)
+               fwrite (buffer, sizeof(*buffer), cc, fp);
+           fflush (fp);
+
+           if (ferror (gp)) {
+               admonish (ce->ce_file, "error reading");
+               unlink (cachefile);
+           }
+           else
+               if (ferror (fp)) {
+                   admonish (cachefile, "error writing");
+                   unlink (cachefile);
+               }
+           fclose (fp);
+       }
+       umask (mask);
+    }
+
+    fseek (ce->ce_fp, 0L, SEEK_SET);
+    *file = ce->ce_file;
+    return fileno (ce->ce_fp);
+}
+
+/*
+ * FTP
+ */
+
+static int
+InitFTP (CT ct)
+{
+    return init_encoding (ct, openFTP);
+}
+
+
+static int
+openFTP (CT ct, char **file)
+{
+    int        cachetype, caching, fd;
+    int len, buflen;
+    char *bp, *ftp, *user, *pass;
+    char buffer[BUFSIZ], cachefile[BUFSIZ];
+    struct exbody *e;
+    CE ce;
+    static char *username = NULL;
+    static char *password = NULL;
+
+    e  = ct->c_ctexbody;
+    ce = ct->c_cefile;
+
+    if ((ftp = context_find (nmhaccessftp)) && !*ftp)
+       ftp = NULL;
+
+#ifndef BUILTIN_FTP
+    if (!ftp)
+       return NOTOK;
+#endif
+
+    switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
+       case NOTOK:
+           return NOTOK;
+
+       case OK:
+           break;
+
+       case DONE:
+           return fd;
+    }
+
+    if (!e->eb_name || !e->eb_site) {
+       content_error (NULL, ct, "missing %s parameter",
+                      e->eb_name ? "site": "name");
+       return NOTOK;
+    }
+
+    if (xpid) {
+       if (xpid < 0)
+           xpid = -xpid;
+       pidcheck (pidwait (xpid, NOTOK));
+       xpid = 0;
+    }
+
+    /* Get the buffer ready to go */
+    bp = buffer;
+    buflen = sizeof(buffer);
+
+    /*
+     * Construct the query message for user
+     */
+    snprintf (bp, buflen, "Retrieve %s", e->eb_name);
+    len = strlen (bp);
+    bp += len;
+    buflen -= len;
+
+    if (e->eb_partno) {
+       snprintf (bp, buflen, " (content %s)", e->eb_partno);
+       len = strlen (bp);
+       bp += len;
+       buflen -= len;
+    }
+
+    snprintf (bp, buflen, "\n    using %sFTP from site %s",
+                   e->eb_flags ? "anonymous " : "", e->eb_site);
+    len = strlen (bp);
+    bp += len;
+    buflen -= len;
+
+    if (e->eb_size > 0) {
+       snprintf (bp, buflen, " (%lu octets)", e->eb_size);
+       len = strlen (bp);
+       bp += len;
+       buflen -= len;
+    }
+    snprintf (bp, buflen, "? ");
+
+    /*
+     * Now, check the answer
+     */
+    if (!getanswer (buffer))
+       return NOTOK;
+
+    if (e->eb_flags) {
+       user = "anonymous";
+       snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
+       pass = buffer;
+    } else {
+       ruserpass (e->eb_site, &username, &password);
+       user = username;
+       pass = password;
+    }
+
+    ce->ce_unlink = (*file == NULL);
+    caching = 0;
+    cachefile[0] = '\0';
+    if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
+           && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
+               cachefile, sizeof(cachefile)) != NOTOK) {
+       if (*file == NULL) {
+           ce->ce_unlink = 0;
+           caching = 1;
+       }
+    }
+
+    if (*file)
+       ce->ce_file = add (*file, NULL);
+    else if (caching)
+       ce->ce_file = add (cachefile, NULL);
+    else
+       ce->ce_file = add (m_scratch ("", tmp), NULL);
+
+    if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
+       content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
+       return NOTOK;
+    }
+
+#ifdef BUILTIN_FTP
+    if (ftp)
+#endif
+    {
+       int child_id, i, vecp;
+       char *vec[9];
+
+       vecp = 0;
+       vec[vecp++] = r1bindex (ftp, '/');
+       vec[vecp++] = e->eb_site;
+       vec[vecp++] = user;
+       vec[vecp++] = pass;
+       vec[vecp++] = e->eb_dir;
+       vec[vecp++] = e->eb_name;
+       vec[vecp++] = ce->ce_file,
+       vec[vecp++] = e->eb_mode && !strcasecmp (e->eb_mode, "ascii")
+                       ? "ascii" : "binary";
+       vec[vecp] = NULL;
+
+       fflush (stdout);
+
+       for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
+           sleep (5);
+       switch (child_id) {
+           case NOTOK:
+               adios ("fork", "unable to");
+               /* NOTREACHED */
+
+           case OK:
+               close (fileno (ce->ce_fp));
+               execvp (ftp, vec);
+               fprintf (stderr, "unable to exec ");
+               perror (ftp);
+               _exit (-1);
+               /* NOTREACHED */
+
+           default:
+               if (pidXwait (child_id, NULL)) {
+#ifdef BUILTIN_FTP
+losing_ftp:
+#endif
+                   username = password = NULL;
+                   ce->ce_unlink = 1;
+                   return NOTOK;
+               }
+               break;
+       }
+    }
+#ifdef BUILTIN_FTP
+    else
+       if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
+                    ce->ce_file,
+                    e->eb_mode && !strcasecmp (e->eb_mode, "ascii"), 0)
+               == NOTOK)
+           goto losing_ftp;
+#endif
+
+    if (cachefile[0])
+       if (caching)
+           chmod (cachefile, cachetype ? m_gmprot () : 0444);
+       else {
+           int mask;
+           FILE *fp;
+
+           mask = umask (cachetype ? ~m_gmprot () : 0222);
+           if ((fp = fopen (cachefile, "w"))) {
+               int cc;
+               FILE *gp = ce->ce_fp;
+
+               fseek (gp, 0L, SEEK_SET);
+
+               while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
+                          > 0)
+                   fwrite (buffer, sizeof(*buffer), cc, fp);
+               fflush (fp);
+
+               if (ferror (gp)) {
+                   admonish (ce->ce_file, "error reading");
+                   unlink (cachefile);
+               }
+               else
+                   if (ferror (fp)) {
+                       admonish (cachefile, "error writing");
+                       unlink (cachefile);
+                   }
+               fclose (fp);
+           }
+           umask (mask);
+       }
+
+    fseek (ce->ce_fp, 0L, SEEK_SET);
+    *file = ce->ce_file;
+    return fileno (ce->ce_fp);
+}
+
+
+/*
+ * Mail
+ */
+
+static int
+InitMail (CT ct)
+{
+    return init_encoding (ct, openMail);
+}
+
+
+static int
+openMail (CT ct, char **file)
+{
+    int        child_id, fd, i, vecp;
+    int len, buflen;
+    char *bp, buffer[BUFSIZ], *vec[7];
+    struct exbody *e = ct->c_ctexbody;
+    CE ce = ct->c_cefile;
+
+    switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
+       case NOTOK:
+           return NOTOK;
+
+       case OK:
+           break;
+
+       case DONE:
+           return fd;
+    }
+
+    if (!e->eb_server) {
+       content_error (NULL, ct, "missing server parameter");
+       return NOTOK;
+    }
+
+    if (xpid) {
+       if (xpid < 0)
+           xpid = -xpid;
+       pidcheck (pidwait (xpid, NOTOK));
+       xpid = 0;
+    }
+
+    /* Get buffer ready to go */
+    bp = buffer;
+    buflen = sizeof(buffer);
+
+    /* Now construct query message */
+    snprintf (bp, buflen, "Retrieve content");
+    len = strlen (bp);
+    bp += len;
+    buflen -= len;
+
+    if (e->eb_partno) {
+       snprintf (bp, buflen, " %s", e->eb_partno);
+       len = strlen (bp);
+       bp += len;
+       buflen -= len;
+    }
+
+    snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
+                   e->eb_server,
+                   e->eb_subject ? e->eb_subject : e->eb_body);
+
+    /* Now, check answer */
+    if (!getanswer (buffer))
+       return NOTOK;
+
+    vecp = 0;
+    vec[vecp++] = r1bindex (mailproc, '/');
+    vec[vecp++] = e->eb_server;
+    vec[vecp++] = "-subject";
+    vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
+    vec[vecp++] = "-body";
+    vec[vecp++] = e->eb_body;
+    vec[vecp] = NULL;
+
+    for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
+       sleep (5);
+    switch (child_id) {
+       case NOTOK:
+           advise ("fork", "unable to");
+           return NOTOK;
+
+       case OK:
+           execvp (mailproc, vec);
+           fprintf (stderr, "unable to exec ");
+           perror (mailproc);
+           _exit (-1);
+           /* NOTREACHED */
+
+       default:
+           if (pidXwait (child_id, NULL) == OK)
+               advise (NULL, "request sent");
+           break;
+    }
+
+    if (*file == NULL) {
+       ce->ce_file = add (m_scratch ("", tmp), NULL);
+       ce->ce_unlink = 1;
+    } else {
+       ce->ce_file = add (*file, NULL);
+       ce->ce_unlink = 0;
+    }
+
+    if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
+       content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
+       return NOTOK;
+    }
+
+    fseek (ce->ce_fp, 0L, SEEK_SET);
+    *file = ce->ce_file;
+    return fileno (ce->ce_fp);
+}
+
+
+static char *
+fgetstr (char *s, int n, FILE *stream)
+{
+    char *cp, *ep;
+
+    for (ep = (cp = s) + n; cp < ep; ) {
+       int i;
+
+       if (!fgets (cp, n, stream))
+           return (cp != s ? s : NULL);
+       if (cp == s && *cp != '#')
+           return s;
+
+       cp += (i = strlen (cp)) - 1;
+       if (i <= 1 || *cp-- != '\n' || *cp != '\\')
+           break;
+       *cp = '\0';
+       n -= (i - 2);
+    }
+
+    return s;
+}
+
+
+/*
+ * Parse the composition draft for text and directives.
+ * Do initial setup of Content structure.
+ */
+
+static int
+user_content (FILE *in, char *file, char *buf, CT *ctp)
+{
+    int        extrnal, vrsn;
+    char *cp, **ap;
+    char buffer[BUFSIZ];
+    struct multipart *m;
+    struct part **pp;
+    struct stat st;
+    struct str2init *s2i;
+    CI ci;
+    CT ct;
+    CE ce;
+
+    if (buf[0] == '\n' || strcmp (buf, "#\n") == 0) {
+       *ctp = NULL;
+       return OK;
+    }
+
+    /* allocate basic Content structure */
+    if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
+       adios (NULL, "out of memory");
+    *ctp = ct;
+
+    /* allocate basic structure for handling decoded content */
+    init_decoded_content (ct);
+    ce = ct->c_cefile;
+
+    ci = &ct->c_ctinfo;
+    set_id (ct, 0);
+
+    /*
+     * Handle inline text.  Check if line
+     * is one of the following forms:
+     *
+     * 1) doesn't begin with '#'       (implicit directive)
+     * 2) begins with "##"             (implicit directive)
+     * 3) begins with "#<"
+     */
+    if (buf[0] != '#' || buf[1] == '#' || buf[1] == '<') {
+       int headers;
+       int inlineD;
+       long pos;
+       char content[BUFSIZ];
+       FILE *out;
+
+       /* use a temp file to collect the plain text lines */
+       ce->ce_file = add (m_tmpfil (invo_name), NULL);
+       ce->ce_unlink = 1;
+
+       if ((out = fopen (ce->ce_file, "w")) == NULL)
+           adios (ce->ce_file, "unable to open for writing");
+
+       if (buf[0] == '#' && buf[1] == '<') {
+           strncpy (content, buf + 2, sizeof(content));
+           inlineD = 1;
+           goto rock_and_roll;
+       } else {
+           inlineD = 0;
+       }
+
+       /* the directive is implicit */
+       strncpy (content, "text/plain", sizeof(content));
+       headers = 0;
+       strncpy (buffer, buf[0] != '#' ? buf : buf + 1, sizeof(buffer));
+       for (;;) {
+           int i;
+
+           if (headers >= 0 && uprf (buffer, DESCR_FIELD)
+               && buffer[i = strlen (DESCR_FIELD)] == ':') {
+               headers = 1;
+
+again_descr:
+               ct->c_descr = add (buffer + i + 1, ct->c_descr);
+               if (!fgetstr (buffer, sizeof(buffer) - 1, in))
+                   adios (NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD);
+               switch (buffer[0]) {
+               case ' ':
+               case '\t':
+                   i = -1;
+                   goto again_descr;
+
+               case '#':
+                   adios (NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
+                   /* NOTREACHED */
+
+               default:
+                   break;
+               }
+           }
+
+           if (headers != 1 || buffer[0] != '\n')
+               fputs (buffer, out);
+
+rock_and_roll:
+           headers = -1;
+           pos = ftell (in);
+           if ((cp = fgetstr (buffer, sizeof(buffer) - 1, in)) == NULL)
+               break;
+           if (buffer[0] == '#') {
+               char *bp;
+
+               if (buffer[1] != '#')
+                   break;
+               for (cp = (bp = buffer) + 1; *cp; cp++)
+                   *bp++ = *cp;
+               *bp = '\0';
+           }
+       }
+
+       if (listsw)
+           ct->c_end = ftell (out);
+       fclose (out);
+
+       /* parse content type */
+       if (get_ctinfo (content, ct, inlineD) == NOTOK)
+           done (1);
+
+       for (s2i = str2cts; s2i->si_key; s2i++)
+           if (!strcasecmp (ci->ci_type, s2i->si_key))
+               break;
+       if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
+           s2i++;
+
+       /*
+        * check type specified (possibly implicitly)
+        */
+       switch (ct->c_type = s2i->si_val) {
+       case CT_MESSAGE:
+           if (!strcasecmp (ci->ci_subtype, "rfc822")) {
+               ct->c_encoding = CE_7BIT;
+               goto call_init;
+           }
+           /* else fall... */
+       case CT_MULTIPART:
+           adios (NULL, "it doesn't make sense to define an in-line %s content",
+                  ct->c_type == CT_MESSAGE ? "message" : "multipart");
+           /* NOTREACHED */
+
+       default:
+call_init:
+           if ((ct->c_ctinitfnx = s2i->si_init))
+               (*ct->c_ctinitfnx) (ct);
+           break;
+       }
+
+       if (cp)
+           fseek (in, pos, SEEK_SET);
+       return OK;
+    }
+
+    /*
+     * If we've reached this point, the next line
+     * must be some type of explicit directive.
+     */
+
+    /* check if directive is external-type */
+    extrnal = (buf[1] == '@');
+
+    /* parse directive */
+    if (get_ctinfo (buf + (extrnal ? 2 : 1), ct, 1) == NOTOK)
+       done (1);
+
+    /* check directive against the list of MIME types */
+    for (s2i = str2cts; s2i->si_key; s2i++)
+       if (!strcasecmp (ci->ci_type, s2i->si_key))
+           break;
+
+    /*
+     * Check if the directive specified a valid type.
+     * This will happen if it was one of the following forms:
+     *
+     *    #type/subtype  (or)
+     *    #@type/subtype
+     */
+    if (s2i->si_key) {
+       if (!ci->ci_subtype)
+           adios (NULL, "missing subtype in \"#%s\"", ci->ci_type);
+
+       switch (ct->c_type = s2i->si_val) {
+       case CT_MULTIPART:
+           adios (NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"",
+                  ci->ci_type, ci->ci_subtype);
+           /* NOTREACHED */
+
+       case CT_MESSAGE:
+           if (!strcasecmp (ci->ci_subtype, "partial"))
+               adios (NULL, "sorry, \"#%s/%s\" isn't supported",
+                      ci->ci_type, ci->ci_subtype);
+           if (!strcasecmp (ci->ci_subtype, "external-body"))
+               adios (NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"",
+                      ci->ci_type, ci->ci_subtype);
+use_forw:
+           adios (NULL,
+                  "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"",
+                  ci->ci_type, ci->ci_subtype);
+           /* NOTREACHED */
+
+       default:
+           if ((ct->c_ctinitfnx = s2i->si_init))
+               (*ct->c_ctinitfnx) (ct);
+           break;
+       }
+
+       /*
+        * #@type/subtype (external types directive)
+        */
+       if (extrnal) {
+           struct exbody *e;
+           CT p;
+
+           if (!ci->ci_magic)
+               adios (NULL, "need external information for \"#@%s/%s\"",
+                      ci->ci_type, ci->ci_subtype);
+           p = ct;
+
+           snprintf (buffer, sizeof(buffer), "message/external-body; %s", ci->ci_magic);
+           free (ci->ci_magic);
+           ci->ci_magic = NULL;
+
+           /*
+            * Since we are using the current Content structure to
+            * hold information about the type of the external
+            * 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)
+               adios (NULL, "out of memory");
+           *ctp = ct;
+           ci = &ct->c_ctinfo;
+           if (get_ctinfo (buffer, ct, 0) == NOTOK)
+               done (1);
+           ct->c_type = CT_MESSAGE;
+           ct->c_subtype = MESSAGE_EXTERNAL;
+
+           if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
+               adios (NULL, "out of memory");
+           ct->c_ctparams = (void *) e;
+
+           e->eb_parent = ct;
+           e->eb_content = p;
+           p->c_ctexbody = e;
+
+           if (params_external (ct, 1) == NOTOK)
+               done (1);
+
+           return OK;
+       }
+
+       /* Handle [file] argument */
+       if (ci->ci_magic) {
+           /* check if specifies command to execute */
+           if (*ci->ci_magic == '|' || *ci->ci_magic == '!') {
+               for (cp = ci->ci_magic + 1; isspace (*cp); cp++)
+                   continue;
+               if (!*cp)
+                   adios (NULL, "empty pipe command for #%s directive", ci->ci_type);
+               cp = add (cp, NULL);
+               free (ci->ci_magic);
+               ci->ci_magic = cp;
+           } else {
+               /* record filename of decoded contents */
+               ce->ce_file = ci->ci_magic;
+               if (access (ce->ce_file, R_OK) == NOTOK)
+                   adios ("reading", "unable to access %s for", ce->ce_file);
+               if (listsw && stat (ce->ce_file, &st) != NOTOK)
+                   ct->c_end = (long) st.st_size;
+               ci->ci_magic = NULL;
+           }
+           return OK;
+       }
+
+       /*
+        * No [file] argument, so check profile for
+        * method to compose content.
+        */
+       snprintf (buffer, sizeof(buffer), "%s-compose-%s/%s",
+               invo_name, ci->ci_type, ci->ci_subtype);
+       if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
+           snprintf (buffer, sizeof(buffer), "%s-compose-%s", invo_name, ci->ci_type);
+           if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
+               content_error (NULL, ct, "don't know how to compose content");
+               done (1);
+           }
+       }
+       ci->ci_magic = add (cp, NULL);
+       return OK;
+    }
+
+    if (extrnal)
+       adios (NULL, "external definition not allowed for \"#%s\"", ci->ci_type);
+
+    /*
+     * Message directive
+     * #forw [+folder] [msgs]
+     */
+    if (!strcasecmp (ci->ci_type, "forw")) {
+       int msgnum;
+       char *folder, *arguments[MAXARGS];
+       struct msgs *mp;
+
+       if (ci->ci_magic) {
+           ap = brkstring (ci->ci_magic, " ", "\n");
+           copyip (ap, arguments, MAXARGS);
+       } else {
+           arguments[0] = "cur";
+           arguments[1] = NULL;
+       }
+       folder = NULL;
+
+       /* search the arguments for a folder name */
+       for (ap = arguments; *ap; ap++) {
+           cp = *ap;
+           if (*cp == '+' || *cp == '@')
+               if (folder)
+                   adios (NULL, "only one folder per #forw directive");
+               else
+                   folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       }
+
+       /* else, use the current folder */
+       if (!folder)
+           folder = add (getfolder (1), NULL);
+
+       if (!(mp = folder_read (folder)))
+           adios (NULL, "unable to read folder %s", folder);
+       for (ap = arguments; *ap; ap++) {
+           cp = *ap;
+           if (*cp != '+' && *cp != '@')
+               if (!m_convert (mp, cp))
+                   done (1);
+       }
+       free (folder);
+       free_ctinfo (ct);
+
+       /*
+        * If there is more than one message to include, make this
+        * a content of type "multipart/digest" and insert each message
+        * as a subpart.  If there is only one message, then make this
+        * a content of type "message/rfc822".
+        */
+       if (mp->numsel > 1) {
+           /* we are forwarding multiple messages */
+           if (get_ctinfo ("multipart/digest", ct, 0) == NOTOK)
+               done (1);
+           ct->c_type = CT_MULTIPART;
+           ct->c_subtype = MULTI_DIGEST;
+
+           if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
+               adios (NULL, "out of memory");
+           ct->c_ctparams = (void *) m;
+           pp = &m->mp_parts;
+
+           for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
+               if (is_selected(mp, msgnum)) {
+                   struct part *part;
+                   CT p;
+                   CE pe;
+
+                   if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
+                       adios (NULL, "out of memory");
+                   init_decoded_content (p);
+                   pe = p->c_cefile;
+                   if (get_ctinfo ("message/rfc822", p, 0) == NOTOK)
+                       done (1);
+                   p->c_type = CT_MESSAGE;
+                   p->c_subtype = MESSAGE_RFC822;
+
+                   snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
+                   pe->ce_file = add (buffer, NULL);
+                   if (listsw && stat (pe->ce_file, &st) != NOTOK)
+                       p->c_end = (long) st.st_size;
+
+                   if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
+                       adios (NULL, "out of memory");
+                   *pp = part;
+                   pp = &part->mp_next;
+                   part->mp_part = p;
+               }
+           }
+       } else {
+           /* we are forwarding one message */
+           if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
+               done (1);
+           ct->c_type = CT_MESSAGE;
+           ct->c_subtype = MESSAGE_RFC822;
+
+           msgnum = mp->lowsel;
+           snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
+           ce->ce_file = add (buffer, NULL);
+           if (listsw && stat (ce->ce_file, &st) != NOTOK)
+               ct->c_end = (long) st.st_size;
+       }
+
+       folder_free (mp);       /* free folder/message structure */
+       return OK;
+    }
+
+    /*
+     * #end
+     */
+    if (!strcasecmp (ci->ci_type, "end")) {
+       free_content (ct);
+       *ctp = NULL;
+       return DONE;
+    }
+
+    /*
+     * #begin [ alternative | parallel ]
+     */
+    if (!strcasecmp (ci->ci_type, "begin")) {
+       if (!ci->ci_magic) {
+           vrsn = MULTI_MIXED;
+           cp = SubMultiPart[vrsn - 1].kv_key;
+       } else if (!strcasecmp (ci->ci_magic, "alternative")) {
+           vrsn = MULTI_ALTERNATE;
+           cp = SubMultiPart[vrsn - 1].kv_key;
+       } else if (!strcasecmp (ci->ci_magic, "parallel")) {
+           vrsn = MULTI_PARALLEL;
+           cp = SubMultiPart[vrsn - 1].kv_key;
+       } else if (uprf (ci->ci_magic, "digest")) {
+           goto use_forw;
+       } else {
+           vrsn = MULTI_UNKNOWN;
+           cp = ci->ci_magic;
+       }
+
+       free_ctinfo (ct);
+       snprintf (buffer, sizeof(buffer), "multipart/%s", cp);
+       if (get_ctinfo (buffer, ct, 0) == NOTOK)
+           done (1);
+       ct->c_type = CT_MULTIPART;
+       ct->c_subtype = vrsn;
+
+       if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
+           adios (NULL, "out of memory");
+       ct->c_ctparams = (void *) m;
+
+       pp = &m->mp_parts;
+       while (fgetstr (buffer, sizeof(buffer) - 1, in)) {
+           struct part *part;
+           CT p;
+
+           if (user_content (in, file, buffer, &p) == DONE) {
+               if (!m->mp_parts)
+                   adios (NULL, "empty \"#begin ... #end\" sequence");
+               return OK;
+           }
+           if (!p)
+               continue;
+
+           if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
+               adios (NULL, "out of memory");
+           *pp = part;
+           pp = &part->mp_next;
+           part->mp_part = p;
+       }
+       admonish (NULL, "premature end-of-file, missing #end");
+       return OK;
+    }
+
+    /*
+     * Unknown directive
+     */
+    adios (NULL, "unknown directive \"#%s\"", ci->ci_type);
+    return NOTOK;      /* NOT REACHED */
+}
+
+
+static void
+set_id (CT ct, int top)
+{
+    char msgid[BUFSIZ];
+    static int partno;
+    static time_t clock = 0;
+    static char *msgfmt;
+
+    if (clock == 0) {
+       time (&clock);
+       snprintf (msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n",
+               (int) getpid(), (long) clock, LocalName());
+       partno = 0;
+       msgfmt = getcpy(msgid);
+    }
+    snprintf (msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno);
+    ct->c_id = getcpy (msgid);
+}
+
+
+static char ebcdicsafe[0x100] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
+    0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+
+/*
+ * Fill out, or expand the various contents in the composition
+ * draft.  Read-in any necessary files.  Parse and execute any
+ * commands specified by profile composition strings.
+ */
+
+static int
+compose_content (CT ct)
+{
+    CE ce = ct->c_cefile;
+
+    switch (ct->c_type) {
+    case CT_MULTIPART:
+    {
+       int partnum;
+       char *pp;
+       char partnam[BUFSIZ];
+       struct multipart *m = (struct multipart *) ct->c_ctparams;
+       struct part *part;
+
+       if (ct->c_partno) {
+           snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
+           pp = partnam + strlen (partnam);
+       } else {
+           pp = partnam;
+       }
+
+       /* first, we call compose_content on all the subparts */
+       for (part = m->mp_parts, partnum = 1; part; part = part->mp_next, partnum++) {
+           CT p = part->mp_part;
+
+           sprintf (pp, "%d", partnum);
+           p->c_partno = add (partnam, NULL);
+           if (compose_content (p) == NOTOK)
+               return NOTOK;
+       }
+
+       /*
+        * If the -rfc934mode switch is given, then check all
+        * the subparts of a multipart/digest.  If they are all
+        * message/rfc822, then mark this content and all
+        * subparts with the rfc934 compatibility mode flag.
+        */
+       if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
+           int is934 = 1;
+
+           for (part = m->mp_parts; part; part = part->mp_next) {
+               CT p = part->mp_part;
+
+               if (p->c_subtype != MESSAGE_RFC822) {
+                   is934 = 0;
+                   break;
+               }
+           }
+           ct->c_rfc934 = is934;
+           for (part = m->mp_parts; part; part = part->mp_next) {
+               CT p = part->mp_part;
+
+               if ((p->c_rfc934 = is934))
+                   p->c_end++;
+           }
+       }
+
+       if (listsw) {
+           ct->c_end = (partnum = strlen (prefix) + 2) + 2;
+           if (ct->c_rfc934)
+               ct->c_end += 1;
+
+           for (part = m->mp_parts; part; part = part->mp_next)
+               ct->c_end += part->mp_part->c_end + partnum;
+       }
+    }
+    break;
+
+    case CT_MESSAGE:
+       /* Nothing to do for type message */
+       break;
+
+    /*
+     * Discrete types (text/application/audio/image/video)
+     */
+    default:
+       if (!ce->ce_file) {
+           pid_t child_id;
+           int i, xstdout, len, buflen;
+           char *bp, **ap, *cp;
+           char *vec[4], buffer[BUFSIZ];
+           FILE *out;
+           CI ci = &ct->c_ctinfo;
+
+           if (!(cp = ci->ci_magic))
+               adios (NULL, "internal error(5)");
+
+           ce->ce_file = add (m_tmpfil (invo_name), NULL);
+           ce->ce_unlink = 1;
+
+           xstdout = 0;
+
+           /* Get buffer ready to go */
+           bp = buffer;
+           bp[0] = '\0';
+           buflen = sizeof(buffer);
+
+           /*
+            * Parse composition string into buffer
+            */
+           for ( ; *cp; cp++) {
+               if (*cp == '%') {
+                   switch (*++cp) {
+                   case 'a':
+                   {
+                       /* insert parameters from directive */
+                       char **ep;
+                       char *s = "";
+
+                       for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
+                           snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
+                           len = strlen (bp);
+                           bp += len;
+                           buflen -= len;
+                           s = " ";
+                       }
+                   }
+                   break;
+
+                   case 'F':
+                       /* %f, and stdout is not-redirected */
+                       xstdout = 1;
+                       /* and fall... */
+
+                   case 'f':
+                       /*
+                        * insert temporary filename where
+                        * content should be written
+                        */
+                       snprintf (bp, buflen, "%s", ce->ce_file);
+                       break;
+
+                   case 's':
+                       /* insert content subtype */
+                       strncpy (bp, ci->ci_subtype, buflen);
+                       break;
+
+                   case '%':
+                       /* insert character % */
+                       goto raw;
+
+                   default:
+                       *bp++ = *--cp;
+                       *bp = '\0';
+                       buflen--;
+                       continue;
+                   }
+                   len = strlen (bp);
+                   bp += len;
+                   buflen -= len;
+               } else {
+raw:
+               *bp++ = *cp;
+               *bp = '\0';
+               buflen--;
+               }
+           }
+
+           if (verbosw)
+               printf ("composing content %s/%s from command\n\t%s\n",
+                       ci->ci_type, ci->ci_subtype, buffer);
+
+           fflush (stdout);    /* not sure if need for -noverbose */
+
+           vec[0] = "/bin/sh";
+           vec[1] = "-c";
+           vec[2] = buffer;
+           vec[3] = NULL;
+
+           if ((out = fopen (ce->ce_file, "w")) == NULL)
+               adios (ce->ce_file, "unable to open for writing");
+
+           for (i = 0; (child_id = vfork()) == NOTOK && i > 5; i++)
+               sleep (5);
+           switch (child_id) {
+           case NOTOK:
+               adios ("fork", "unable to fork");
+               /* NOTREACHED */
+
+           case OK:
+               if (!xstdout)
+                   dup2 (fileno (out), 1);
+               close (fileno (out));
+               execvp ("/bin/sh", vec);
+               fprintf (stderr, "unable to exec ");
+               perror ("/bin/sh");
+               _exit (-1);
+               /* NOTREACHED */
+
+           default:
+               fclose (out);
+               if (pidXwait(child_id, NULL))
+                   done (1);
+               break;
+           }
+       }
+
+       /* Check size of file */
+       if (listsw && ct->c_end == 0L) {
+           struct stat st;
+
+           if (stat (ce->ce_file, &st) != NOTOK)
+               ct->c_end = (long) st.st_size;
+       }
+       break;
+    }
+
+    return OK;
+}
+
+
+/*
+ * Scan the content.
+ *
+ *    1) choose a transfer encoding.
+ *    2) check for clashes with multipart boundary string.
+ *    3) for text content, figure out which character set is being used.
+ *
+ * If there is a clash with one of the contents and the multipart boundary,
+ * this function will exit with NOTOK.  This will cause the scanning process
+ * to be repeated with a different multipart boundary.  It is possible
+ * (although highly unlikely) that this scan will be repeated multiple times.
+ */
+
+static int
+scan_content (CT ct)
+{
+    int len;
+    int check8bit, contains8bit = 0;     /* check if contains 8bit data                */
+    int checklinelen, linelen = 0;       /* check for long lines                       */
+    int checkboundary, boundaryclash = 0; /* check if clashes with multipart boundary   */
+    int checklinespace, linespace = 0;   /* check if any line ends with space          */
+    int checkebcdic, ebcdicunsafe = 0;   /* check if contains ebcdic unsafe characters */
+    char *cp, buffer[BUFSIZ];
+    struct text *t;
+    FILE *in;
+    CE ce = ct->c_cefile;
+
+    /*
+     * handle multipart by scanning all subparts
+     * and then checking their encoding.
+     */
+    if (ct->c_type == CT_MULTIPART) {
+       struct multipart *m = (struct multipart *) ct->c_ctparams;
+       struct part *part;
+
+       /* initially mark the domain of enclosing multipart as 7bit */
+       ct->c_encoding = CE_7BIT;
+
+       for (part = m->mp_parts; part; part = part->mp_next) {
+           CT p = part->mp_part;
+
+           if (scan_content (p) == NOTOK)      /* choose encoding for subpart */
+               return NOTOK;
+
+           /* if necessary, enlarge encoding for enclosing multipart */
+           if (p->c_encoding == CE_BINARY)
+               ct->c_encoding = CE_BINARY;
+           if (p->c_encoding == CE_8BIT && ct->c_encoding != CE_BINARY)
+               ct->c_encoding = CE_8BIT;
+       }
+
+       return OK;
+    }
+
+    /*
+     * Decide what to check while scanning this content.
+     */
+    switch (ct->c_type) {
+    case CT_TEXT:
+       check8bit = 1;
+       checkboundary = 1;
+       if (ct->c_subtype == TEXT_PLAIN) {
+           checkebcdic = 0;
+           checklinelen = 0;
+           checklinespace = 0;
+       } else {
+           checkebcdic = ebcdicsw;
+           checklinelen = 1;
+           checklinespace = 1;
+       }
+       break;
+
+    case CT_APPLICATION:
+       check8bit = 1;
+       checkebcdic = ebcdicsw;
+       checklinelen = 1;
+       checklinespace = 1;
+       checkboundary = 1;
+       break;
+
+    case CT_MESSAGE:
+       check8bit = 0;
+       checkebcdic = 0;
+       checklinelen = 0;
+       checklinespace = 0;
+
+       /* don't check anything for message/external */
+       if (ct->c_subtype == MESSAGE_EXTERNAL)
+           checkboundary = 0;
+       else
+           checkboundary = 1;
+       break;
+
+    case CT_AUDIO:
+    case CT_IMAGE:
+    case CT_VIDEO:
+       /*
+        * Don't check anything for these types,
+        * since we are forcing use of base64.
+        */
+       check8bit = 0;
+       checkebcdic = 0;
+       checklinelen = 0;
+       checklinespace = 0;
+       checkboundary = 0;
+       break;
+    }
+
+    /*
+     * Scan the unencoded content
+     */
+    if (check8bit || checklinelen || checklinespace || checkboundary) {
+       if ((in = fopen (ce->ce_file, "r")) == NULL)
+           adios (ce->ce_file, "unable to open for reading");
+       len = strlen (prefix);
+
+       while (fgets (buffer, sizeof(buffer) - 1, in)) {
+           /*
+            * Check for 8bit data.
+            */
+           if (check8bit) {
+               for (cp = buffer; *cp; cp++) {
+                   if (!isascii (*cp)) {
+                       contains8bit = 1;
+                       check8bit = 0;  /* no need to keep checking */
+                   }
+                   /*
+                    * Check if character is ebcdic-safe.  We only check
+                    * this if also checking for 8bit data.
+                    */
+                   if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
+                       ebcdicunsafe = 1;
+                       checkebcdic = 0; /* no need to keep checking */
+                   }
+               }
+           }
+
+           /*
+            * Check line length.
+            */
+           if (checklinelen && (strlen (buffer) > CPERLIN + 1)) {
+               linelen = 1;
+               checklinelen = 0;       /* no need to keep checking */
+           }
+
+           /*
+            * Check if line ends with a space.
+            */
+           if (checklinespace && (cp = buffer + strlen (buffer) - 2) > buffer && isspace (*cp)) {
+               linespace = 1;
+               checklinespace = 0;     /* no need to keep checking */
+           }
+
+           /*
+            * Check if content contains a line that clashes
+            * with our standard boundary for multipart messages.
+            */
+           if (checkboundary && buffer[0] == '-' && buffer[1] == '-') {
+               for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
+                   if (!isspace (*cp))
+                       break;
+               *++cp = '\0';
+               if (!strncmp(buffer + 2, prefix, len) && isdigit(buffer[2 + len])) {
+                   boundaryclash = 1;
+                   checkboundary = 0;  /* no need to keep checking */
+               }
+           }
+       }
+       fclose (in);
+    }
+
+    /*
+     * Decide which transfer encoding to use.
+     */
+    switch (ct->c_type) {
+    case CT_TEXT:
+       /*
+        * If the text content didn't specify a character
+        * set, we need to figure out which one was used.
+        */
+       t = (struct text *) ct->c_ctparams;
+       if (t->tx_charset == CHARSET_UNSPECIFIED) {
+           CI ci = &ct->c_ctinfo;
+           char **ap, **ep;
+
+           for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
+               continue;
+
+           if (contains8bit) {
+               t->tx_charset = CHARSET_UNKNOWN;
+               *ap = concat ("charset=", write_charset_8bit(), NULL);
+           } else {
+               t->tx_charset = CHARSET_USASCII;
+               *ap = add ("charset=us-ascii", NULL);
+           }
+
+           cp = strchr(*ap++, '=');
+           *ap = NULL;
+           *cp++ = '\0';
+           *ep = cp;
+       }
+
+       if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
+           ct->c_encoding = CE_QUOTED;
+       else
+           ct->c_encoding = CE_7BIT;
+       break;
+
+    case CT_APPLICATION:
+       /* For application type, use base64, except when postscript */
+       if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
+           ct->c_encoding = (ct->c_subtype == APPLICATION_POSTSCRIPT)
+               ? CE_QUOTED : CE_BASE64;
+       else
+           ct->c_encoding = CE_7BIT;
+       break;
+
+    case CT_MESSAGE:
+       ct->c_encoding = CE_7BIT;
+       break;
+
+    case CT_AUDIO:
+    case CT_IMAGE:
+    case CT_VIDEO:
+       /* For audio, image, and video contents, just use base64 */
+       ct->c_encoding = CE_BASE64;
+       break;
+    }
+
+    return (boundaryclash ? NOTOK : OK);
+}
+
+
+/*
+ * Scan the content structures, and build header
+ * fields that will need to be output into the
+ * message.
+ */
+
+static int
+build_headers (CT ct)
+{
+    int        cc, mailbody, len;
+    char **ap, **ep;
+    char *np, *vp, buffer[BUFSIZ];
+    CI ci = &ct->c_ctinfo;
+
+    /*
+     * If message is type multipart, then add the multipart
+     * boundary to the list of attribute/value pairs.
+     */
+    if (ct->c_type == CT_MULTIPART) {
+       char *cp;
+       static int level = 0;   /* store nesting level */
+
+       ap = ci->ci_attrs;
+       ep = ci->ci_values;
+       snprintf (buffer, sizeof(buffer), "boundary=%s%d", prefix, level++);
+       cp = strchr(*ap++ = add (buffer, NULL), '=');
+       *ap = NULL;
+       *cp++ = '\0';
+       *ep = cp;
+    }
+
+    /*
+     * Skip the output of Content-Type, parameters, content
+     * description, and Content-ID if the content is of type
+     * "message" and the rfc934 compatibility flag is set
+     * (which means we are inside multipart/digest and the
+     * switch -rfc934mode was given).
+     */
+    if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
+       goto skip_headers;
+
+    /*
+     * output the content type and subtype
+     */
+    np = add (TYPE_FIELD, NULL);
+    vp = concat (" ", ci->ci_type, "/", ci->ci_subtype, NULL);
+
+    /* keep track of length of line */
+    len = strlen (TYPE_FIELD) + strlen (ci->ci_type)
+               + strlen (ci->ci_subtype) + 3;
+
+    mailbody = ct->c_type == CT_MESSAGE
+       && ct->c_subtype == MESSAGE_EXTERNAL
+       && ((struct exbody *) ct->c_ctparams)->eb_body;
+
+    /*
+     * Append the attribute/value pairs to
+     * the end of the Content-Type line.
+     */
+    for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
+       if (mailbody && !strcasecmp (*ap, "body"))
+           continue;
+
+       vp = add (";", vp);
+       len++;
+
+       snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
+       if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
+           vp = add ("\n\t", vp);
+           len = 8;
+       } else {
+           vp = add (" ", vp);
+           len++;
+       }
+       vp = add (buffer, vp);
+       len += cc;
+    }
+
+    /*
+     * Append any RFC-822 comment to the end of
+     * the Content-Type line.
+     */
+    if (ci->ci_comment) {
+       snprintf (buffer, sizeof(buffer), "(%s)", ci->ci_comment);
+       if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
+           vp = add ("\n\t", vp);
+           len = 8;
+       } else {
+           vp = add (" ", vp);
+           len++;
+       }
+       vp = add (buffer, vp);
+       len += cc;
+    }
+    vp = add ("\n", vp);
+    add_header (ct, np, vp);
+
+    /*
+     * output the Content-ID
+     */
+    if (ct->c_id) {
+       np = add (ID_FIELD, NULL);
+       vp = concat (" ", ct->c_id, NULL);
+       add_header (ct, np, vp);
+    }
+
+    /*
+     * output the Content-Description
+     */
+    if (ct->c_descr) {
+       np = add (DESCR_FIELD, NULL);
+       vp = concat (" ", ct->c_descr, NULL);
+       add_header (ct, np, vp);
+    }
+
+skip_headers:
+    /*
+     * If this is the internal content structure for a
+     * "message/external", then we are done with the
+     * headers (since it has no body).
+     */
+    if (ct->c_ctexbody)
+       return OK;
+
+    /*
+     * output the Content-MD5
+     */
+    if (checksw) {
+       np = add (MD5_FIELD, NULL);
+       vp = calculate_digest (ct, (ct->c_encoding == CE_QUOTED) ? 1 : 0);
+       add_header (ct, np, vp);
+    }
+
+    /*
+     * output the Content-Transfer-Encoding
+     */
+    switch (ct->c_encoding) {
+    case CE_7BIT:
+       /* Nothing to output */
+#if 0
+       np = add (ENCODING_FIELD, NULL);
+       vp = concat (" ", "7bit", "\n", NULL);
+       add_header (ct, np, vp);
+#endif
+       break;
+
+    case CE_8BIT:
+       if (ct->c_type == CT_MESSAGE)
+           adios (NULL, "internal error, invalid encoding");
+
+       np = add (ENCODING_FIELD, NULL);
+       vp = concat (" ", "8bit", "\n", NULL);
+       add_header (ct, np, vp);
+       break;
+
+    case CE_QUOTED:
+       if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
+           adios (NULL, "internal error, invalid encoding");
+
+       np = add (ENCODING_FIELD, NULL);
+       vp = concat (" ", "quoted-printable", "\n", NULL);
+       add_header (ct, np, vp);
+       break;
+
+    case CE_BASE64:
+       if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
+           adios (NULL, "internal error, invalid encoding");
+
+       np = add (ENCODING_FIELD, NULL);
+       vp = concat (" ", "base64", "\n", NULL);
+       add_header (ct, np, vp);
+       break;
+
+    case CE_BINARY:
+       if (ct->c_type == CT_MESSAGE)
+           adios (NULL, "internal error, invalid encoding");
+
+       np = add (ENCODING_FIELD, NULL);
+       vp = concat (" ", "binary", "\n", NULL);
+       add_header (ct, np, vp);
+       break;
+
+    default:
+       adios (NULL, "unknown transfer encoding in content");
+       break;
+    }
+
+    /*
+     * Additional content specific header processing
+     */
+    switch (ct->c_type) {
+    case CT_MULTIPART:
+    {
+       struct multipart *m;
+       struct part *part;
+
+       m = (struct multipart *) ct->c_ctparams;
+       for (part = m->mp_parts; part; part = part->mp_next) {
+           CT p;
+
+           p = part->mp_part;
+           build_headers (p);
+       }
+    }
+       break;
+
+    case CT_MESSAGE:
+       if (ct->c_subtype == MESSAGE_EXTERNAL) {
+           struct exbody *e;
+
+           e = (struct exbody *) ct->c_ctparams;
+           build_headers (e->eb_content);
+       }
+       break;
+
+    default:
+       /* Nothing to do */
+       break;
+    }
+
+    return OK;
+}
+
+
+static char nib2b64[0x40+1] =
+       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static char *
+calculate_digest (CT ct, int asciiP)
+{
+    int        cc;
+    char buffer[BUFSIZ], *vp, *op;
+    unsigned char *dp;
+    unsigned char digest[16];
+    unsigned char outbuf[25];
+    FILE *in;
+    MD5_CTX mdContext;
+    CE ce = ct->c_cefile;
+
+    /* open content */
+    if ((in = fopen (ce->ce_file, "r")) == NULL)
+       adios (ce->ce_file, "unable to open for reading");
+
+    /* Initialize md5 context */
+    MD5Init (&mdContext);
+
+    /* calculate md5 message digest */
+    if (asciiP) {
+       while (fgets (buffer, sizeof(buffer) - 1, in)) {
+           char c, *cp;
+
+           cp = buffer + strlen (buffer) - 1;
+           if ((c = *cp) == '\n')
+               *cp = '\0';
+
+           MD5Update (&mdContext, (unsigned char *) buffer,
+                      (unsigned int) strlen (buffer));
+
+           if (c == '\n')
+               MD5Update (&mdContext, (unsigned char *) "\r\n", 2);
+       }
+    } else {
+       while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), in)) > 0)
+           MD5Update (&mdContext, (unsigned char *) buffer, (unsigned int) cc);
+    }
+
+    /* md5 finalization.  Write digest and zero md5 context */
+    MD5Final (digest, &mdContext);
+
+    /* close content */
+    fclose (in);
+
+    /* print debugging info */
+    if (debugsw) {
+       unsigned char *ep;
+
+       fprintf (stderr, "MD5 digest=");
+       for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]);
+                dp < ep; dp++)
+           fprintf (stderr, "%02x", *dp & 0xff);
+       fprintf (stderr, "\n");
+    }
+
+    /* encode the digest using base64 */
+    for (dp = digest, op = outbuf, cc = sizeof(digest) / sizeof(digest[0]);
+               cc > 0; cc -= 3, op += 4) {
+       unsigned long bits;
+       char *bp;
+
+       bits = (*dp++ & 0xff) << 16;
+       if (cc > 1) {
+           bits |= (*dp++ & 0xff) << 8;
+           if (cc > 2)
+               bits |= *dp++ & 0xff;
+       }
+
+       for (bp = op + 4; bp > op; bits >>= 6)
+           *--bp = nib2b64[bits & 0x3f];
+       if (cc < 3) {
+           *(op + 3) = '=';
+           if (cc < 2)
+               *(op + 2) = '=';
+       }
+    }
+
+    /* null terminate string */
+    outbuf[24] = '\0';
+
+    /* now make copy and return string */
+    vp = concat (" ", outbuf, "\n", NULL);
+    return vp;
+}
+
+
+static int
+readDigest (CT ct, char *cp)
+{
+    int        bitno, skip;
+    unsigned long bits;
+    char *bp = cp;
+    unsigned char *dp, value, *ep;
+    unsigned char *b, *b1, *b2, *b3;
+
+    b  = (unsigned char *) &bits,
+    b1 = &b[endian > 0 ? 1 : 2],
+    b2 = &b[endian > 0 ? 2 : 1],
+    b3 = &b[endian > 0 ? 3 : 0];
+    bitno = 18;
+    bits = 0L;
+    skip = 0;
+
+    for (ep = (dp = ct->c_digest)
+                + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
+       switch (*cp) {
+           default:
+               if (skip
+                       || (*cp & 0x80)
+                       || (value = b642nib[*cp & 0x7f]) > 0x3f) {
+                   if (debugsw)
+                       fprintf (stderr, "invalid BASE64 encoding\n");
+                   return NOTOK;
+               }
+
+               bits |= value << bitno;
+test_end:
+               if ((bitno -= 6) < 0) {
+                   if (dp + (3 - skip) > ep)
+                       goto invalid_digest;
+                   *dp++ = *b1;
+                   if (skip < 2) {
+                       *dp++ = *b2;
+                       if (skip < 1)
+                           *dp++ = *b3;
+                   }
+                   bitno = 18;
+                   bits = 0L;
+                   skip = 0;
+               }
+               break;
+
+           case '=':
+               if (++skip > 3)
+                   goto self_delimiting;
+               goto test_end;
+       }
+    if (bitno != 18) {
+       if (debugsw)
+           fprintf (stderr, "premature ending (bitno %d)\n", bitno);
+
+       return NOTOK;
+    }
+self_delimiting:
+    if (dp != ep) {
+invalid_digest:
+       if (debugsw) {
+           while (*cp)
+               cp++;
+           fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
+                    cp - bp);
+       }
+
+       return NOTOK;
+    }
+
+    if (debugsw) {
+       fprintf (stderr, "MD5 digest=");
+       for (dp = ct->c_digest; dp < ep; dp++)
+           fprintf (stderr, "%02x", *dp & 0xff);
+       fprintf (stderr, "\n");
+    }
+
+    return OK;
+}
diff --git a/uip/mhcachesbr.c b/uip/mhcachesbr.c
new file mode 100644 (file)
index 0000000..b372f95
--- /dev/null
@@ -0,0 +1,443 @@
+
+/*
+ * mhcachesbr.c -- routines to manipulate the MIME content cache
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+#include <h/signals.h>
+#include <h/md5.h>
+#include <errno.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <zotnet/mts/mts.h>
+#include <zotnet/tws/tws.h>
+#include <h/mime.h>
+#include <h/mhparse.h>
+#include <h/mhcachesbr.h>
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+
+extern int errno;
+extern int debugsw;
+
+extern pid_t xpid;     /* mhshowsbr.c or mhbuildsbr.c */
+
+/* cache policies */
+int rcachesw = CACHE_ASK;
+int wcachesw = CACHE_ASK;
+
+/*
+ * Location of public and private cache.  These must
+ * be set before these routines are called.
+ */
+char *cache_public;
+char *cache_private;
+
+
+/* mhparse.c (OR) mhbuildsbr.c */
+int pidcheck (int);
+
+/* mhmisc.c */
+int part_ok (CT, int);
+int type_ok (CT, int);
+int make_intermediates (char *);
+void content_error (char *, CT, char *, ...);
+void flush_errors (void);
+
+/*
+ * prototypes
+ */
+void cache_all_messages (CT *);
+int find_cache (CT, int, int *, char *, char *, int);
+
+/*
+ * static prototypes
+ */
+static void cache_content (CT);
+static int find_cache_aux (int, char *, char *, char *, int);
+static int find_cache_aux2 (char *, char *, char *, int);
+
+
+/*
+ * Top level entry point to cache content
+ * from a group of messages
+ */
+
+void
+cache_all_messages (CT *cts)
+{
+    CT ct, *ctp;
+
+    for (ctp = cts; *ctp; ctp++) {
+       ct = *ctp;
+       if (type_ok (ct, 1)) {
+           cache_content (ct);
+           if (ct->c_fp) {
+               fclose (ct->c_fp);
+               ct->c_fp = NULL;
+           }
+           if (ct->c_ceclosefnx)
+               (*ct->c_ceclosefnx) (ct);
+       }
+    }
+    flush_errors ();
+}
+
+
+/*
+ * Entry point to cache content from external sources.
+ */
+
+static void
+cache_content (CT ct)
+{
+    int        cachetype;
+    char *file, cachefile[BUFSIZ];
+    CE ce = ct->c_cefile;
+
+    if (!ct->c_id) {
+       advise (NULL, "no %s: field in %s", ID_FIELD, ct->c_file);
+       return;
+    }
+
+    if (!ce) {
+       advise (NULL, "unable to decode %s", ct->c_file);
+       return;
+    }
+
+/* THIS NEEDS TO BE FIXED */
+#if 0
+    if (ct->c_ceopenfnx == openMail) {
+       advise (NULL, "a radish may no know Greek, but I do...");
+       return;
+    }
+#endif
+
+    if (find_cache (NULL, wcachesw != CACHE_NEVER ? wcachesw : CACHE_ASK,
+                   &cachetype, ct->c_id, cachefile, sizeof(cachefile))
+           == NOTOK) {
+       advise (NULL, "unable to cache %s's contents", ct->c_file);
+       return;
+    }
+    if (wcachesw != CACHE_NEVER && wcachesw != CACHE_ASK) {
+       fflush (stdout);
+       fprintf (stderr, "caching message %s as file %s\n", ct->c_file,
+                cachefile);
+    }
+
+    if (ce->ce_file) {
+       int mask = umask (cachetype ? ~m_gmprot () : 0222);
+       FILE *fp;
+
+       if (debugsw)
+           fprintf (stderr, "caching by copying %s...\n", ce->ce_file);
+
+       file = NULL;
+       if ((*ct->c_ceopenfnx) (ct, &file) == NOTOK)
+           goto reset_umask;
+
+       if ((fp = fopen (cachefile, "w"))) {
+           int cc;
+           char buffer[BUFSIZ];
+           FILE *gp = ce->ce_fp;
+
+           fseek (gp, 0L, SEEK_SET);
+
+           while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
+                      > 0)
+               fwrite (buffer, sizeof(*buffer), cc, fp);
+           fflush (fp);
+
+           if (ferror (gp)) {
+               admonish (ce->ce_file, "error reading");
+               unlink (cachefile);
+           } else {
+               if (ferror (fp)) {
+                   admonish (cachefile, "error writing");
+                   unlink (cachefile);
+               }
+           }
+           fclose (fp);
+       } else
+           content_error (cachefile, ct, "unable to fopen for writing");
+reset_umask:
+       umask (mask);
+    } else {
+       if (debugsw)
+           fprintf (stderr, "in place caching...\n");
+
+       file = cachefile;
+       if ((*ct->c_ceopenfnx) (ct, &file) != NOTOK)
+           chmod (cachefile, cachetype ? m_gmprot () : 0444);
+    }
+}
+
+
+int
+find_cache (CT ct, int policy, int *writing, char *id,
+       char *buffer, int buflen)
+{
+    int        status = NOTOK;
+
+    if (id == NULL)
+       return NOTOK;
+    id = trimcpy (id);
+
+    if (debugsw)
+       fprintf (stderr, "find_cache %s(%d) %s %s\n", caches[policy].sw,
+                policy, writing ? "writing" : "reading", id);
+
+    switch (policy) {
+       case CACHE_NEVER:
+       default:
+           break;
+
+       case CACHE_ASK:
+       case CACHE_PUBLIC:
+           if (cache_private
+                   && !writing
+                   && find_cache_aux (writing ? 2 : 0, cache_private, id,
+                                      buffer, buflen) == OK) {
+               if (access (buffer, R_OK) != NOTOK) {
+got_private:
+                   if (writing)
+                       *writing = 1;
+got_it:
+                   status = OK;
+                   break;
+               }
+           }
+           if (cache_public
+                   && find_cache_aux (writing ? 1 : 0, cache_public, id,
+                                      buffer, buflen) == OK) {
+               if (writing || access (buffer, R_OK) != NOTOK) {
+                   if (writing)
+                       *writing = 0;
+                   goto got_it;
+               }
+           }
+           break;
+
+       case CACHE_PRIVATE:
+           if (cache_private
+                   && find_cache_aux (writing ? 2 : 0, cache_private, id,
+                                      buffer, buflen) == OK) {
+               if (writing || access (buffer, R_OK) != NOTOK)
+                   goto got_private;
+           }
+           break;
+
+    }
+
+    if (status == OK && policy == CACHE_ASK) {
+       int len, buflen;
+       char *bp, query[BUFSIZ];
+
+       if (xpid) {
+           if (xpid < 0)
+               xpid = -xpid;
+           pidcheck (pidwait (xpid, NOTOK));
+           xpid = 0;
+       }
+
+       /* Get buffer ready to go */
+       bp = query;
+       buflen = sizeof(query);
+
+       /* Now, construct query */
+       if (writing) {
+           snprintf (bp, buflen, "Make cached, publically-accessible copy");
+       } else {
+           struct stat st;
+
+           snprintf (bp, buflen, "Use cached copy");
+           len = strlen (bp);
+           bp += len;
+           buflen -= len;
+
+           if (ct->c_partno) {
+               snprintf (bp, buflen, " of content %s", ct->c_partno);
+               len = strlen (bp);
+               bp += len;
+               buflen -= len;
+           }
+
+           stat (buffer, &st);
+           snprintf (bp, buflen, " (size %lu octets)",
+                           (unsigned long) st.st_size);
+       }
+       len = strlen (bp);
+       bp += len;
+       buflen -= len;
+
+       snprintf (bp, buflen, "\n    in file %s? ", buffer);
+
+       /* Now, check answer */
+       if (!getanswer (query))
+           status = NOTOK;
+    }
+
+    if (status == OK && writing) {
+       if (*writing && strchr(buffer, '/'))
+           make_intermediates (buffer);
+       unlink (buffer);
+    }
+
+    free (id);
+    return status;
+}
+
+
+static int
+find_cache_aux (int writing, char *directory, char *id,
+       char *buffer, int buflen)
+{
+    int        mask, usemap;
+    char mapfile[BUFSIZ], mapname[BUFSIZ];
+    FILE *fp;
+    static int partno, pid;
+    static time_t clock = 0;
+
+#ifdef BSD42
+    usemap = strchr (id, '/') ? 1 : 0;
+#else
+    usemap = 1;
+#endif
+
+    if (debugsw)
+       fprintf (stderr, "find_cache_aux %s usemap=%d\n", directory, usemap);
+
+    snprintf (mapfile, sizeof(mapfile), "%s/cache.map", directory);
+    if (find_cache_aux2 (mapfile, id, mapname, sizeof(mapname)) == OK)
+       goto done_map;
+
+    if (!writing) {
+       if (usemap)
+           return NOTOK;
+
+use_raw:
+       snprintf (buffer, buflen, "%s/%s", directory, id);
+       return OK;
+    }
+
+    if (!usemap && access (mapfile, W_OK) == NOTOK)
+       goto use_raw;
+
+    if (clock != 0) {
+       time_t now;
+       
+       time (&now);
+       if (now > clock)
+           clock = 0;
+    } else {
+       pid = getpid ();
+    }
+
+    if (clock == 0) {
+       time (&clock);
+       partno = 0;
+    } else {
+       if (partno > 0xff) {
+           clock++;
+           partno = 0;
+       }
+    }
+
+    snprintf (mapname, sizeof(mapname), "%08x%04x%02x",
+               (unsigned int) (clock & 0xffffffff),
+               (unsigned int) (pid & 0xffff),
+               (unsigned int) (partno++ & 0xff));
+
+    if (debugsw)
+       fprintf (stderr, "creating mapping %s->%s\n", mapname, id);
+
+    make_intermediates (mapfile);
+    mask = umask (writing == 2 ? 0077 : 0);
+    if (!(fp = lkfopen (mapfile, "a")) && errno == ENOENT) {
+       int fd;
+
+       if ((fd = creat (mapfile, 0666)) != NOTOK) {
+           close (fd);
+           fp = lkfopen (mapfile, "a");
+       }
+    }
+    umask (mask);
+    if (!fp)
+       return NOTOK;
+    fprintf (fp, "%s: %s\n", mapname, id);
+    lkfclose (fp, mapfile);
+
+done_map:
+    if (*mapname == '/')
+       strncpy (buffer, mapname, buflen);
+    else
+       snprintf (buffer, buflen, "%s/%s", directory, mapname);
+    if (debugsw)
+       fprintf (stderr, "use %s\n", buffer);
+
+    return OK;
+}
+
+
+static int
+find_cache_aux2 (char *mapfile, char *id, char *mapname, int namelen)
+{
+    int        state;
+    char buf[BUFSIZ], name[NAMESZ];
+    FILE *fp;
+
+    if (!(fp = lkfopen (mapfile, "r")))
+       return NOTOK;
+
+    for (state = FLD;;) {
+       int result;
+       char *cp, *dp;
+
+       switch (state = m_getfld (state, name, buf, sizeof(buf), fp)) {
+           case FLD:
+           case FLDPLUS:
+           case FLDEOF:
+               strncpy (mapname, name, namelen);
+               if (state != FLDPLUS)
+                   cp = buf;
+               else {
+                   cp = add (buf, NULL);
+                   while (state == FLDPLUS) {
+                       state = m_getfld (state, name, buf, sizeof(buf), fp);
+                       cp = add (buf, cp);
+                   }
+               }
+               dp = trimcpy (cp);
+               if (cp != buf)
+                   free (cp);
+               if (debugsw)
+                   fprintf (stderr, "compare %s to %s <- %s\n", id, dp,
+                            mapname);
+               result = strcmp (id, dp);
+               free (dp);
+               if (result == 0) {
+                   lkfclose (fp, mapfile);
+                   return OK;
+               }
+               if (state != FLDEOF)
+                   continue;
+               /* else fall... */
+
+           case BODY:
+           case BODYEOF:
+           case FILEEOF:
+           default:
+               break;
+       }
+       break;
+    }
+
+    lkfclose (fp, mapfile);
+    return NOTOK;
+}
diff --git a/uip/mhfree.c b/uip/mhfree.c
new file mode 100644 (file)
index 0000000..aef86cd
--- /dev/null
@@ -0,0 +1,278 @@
+
+/*
+ * mhfree.c -- routines to free the data structures used to
+ *          -- represent MIME messages
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <errno.h>
+#include <h/mime.h>
+#include <h/mhparse.h>
+
+extern int errno;
+
+/*
+ * prototypes
+ */
+void free_content (CT);
+void free_header (CT);
+void free_ctinfo (CT);
+void free_encoding (CT, int);
+
+/*
+ * static prototypes
+ */
+static void free_text (CT);
+static void free_multi (CT);
+static void free_partial (CT);
+static void free_external (CT);
+
+
+/*
+ * Primary routine to free a MIME content structure
+ */
+
+void
+free_content (CT ct)
+{
+    if (!ct)
+       return;
+
+    /*
+     * free all the header fields
+     */
+    free_header (ct);
+
+    if (ct->c_partno)
+       free (ct->c_partno);
+
+    if (ct->c_vrsn)
+       free (ct->c_vrsn);
+
+    if (ct->c_ctline)
+       free (ct->c_ctline);
+
+    free_ctinfo (ct);
+
+    /*
+     * some of the content types have extra
+     * parts which need to be freed.
+     */
+    switch (ct->c_type) {
+       case CT_MULTIPART:
+           free_multi (ct);
+           break;
+
+       case CT_MESSAGE:
+           switch (ct->c_subtype) {
+               case MESSAGE_PARTIAL:
+                   free_partial (ct);
+                   break;
+
+               case MESSAGE_EXTERNAL:
+                   free_external (ct);
+                   break;
+           }
+           break;
+
+       case CT_TEXT:
+           free_text (ct);
+           break;
+    }
+
+    if (ct->c_showproc)
+       free (ct->c_showproc);
+    if (ct->c_termproc)
+       free (ct->c_termproc);
+    if (ct->c_storeproc)
+       free (ct->c_storeproc);
+
+    if (ct->c_celine)
+       free (ct->c_celine);
+
+    /* free structures for content encodings */
+    free_encoding (ct, 1);
+
+    if (ct->c_id)
+       free (ct->c_id);
+    if (ct->c_descr)
+       free (ct->c_descr);
+
+    if (ct->c_file) {
+       if (ct->c_unlink)
+           unlink (ct->c_file);
+       free (ct->c_file);
+    }
+    if (ct->c_fp)
+       fclose (ct->c_fp);
+
+    if (ct->c_storage)
+       free (ct->c_storage);
+    if (ct->c_folder)
+       free (ct->c_folder);
+
+    free (ct);
+}
+
+
+/*
+ * Free the linked list of header fields
+ * for this content.
+ */
+
+void
+free_header (CT ct)
+{
+    HF hp1, hp2;
+
+    hp1 = ct->c_first_hf;
+    while (hp1) {
+       hp2 = hp1->next;
+
+       free (hp1->name);
+       free (hp1->value);
+       free (hp1);
+
+       hp1 = hp2;
+    }
+
+    ct->c_first_hf = NULL;
+    ct->c_last_hf  = NULL;
+}
+
+
+void
+free_ctinfo (CT ct)
+{
+    char **ap;
+    CI ci;
+
+    ci = &ct->c_ctinfo;
+    if (ci->ci_type) {
+       free (ci->ci_type);
+       ci->ci_type = NULL;
+    }
+    if (ci->ci_subtype) {
+       free (ci->ci_subtype);
+       ci->ci_subtype = NULL;
+    }
+    for (ap = ci->ci_attrs; *ap; ap++) {
+       free (*ap);
+       *ap = NULL;
+    }
+    if (ci->ci_comment) {
+       free (ci->ci_comment);
+       ci->ci_comment = NULL;
+    }
+    if (ci->ci_magic) {
+       free (ci->ci_magic);
+       ci->ci_magic = NULL;
+    }
+}
+
+
+static void
+free_text (CT ct)
+{
+    struct text *t;
+
+    if (!(t = (struct text *) ct->c_ctparams))
+       return;
+
+    free ((char *) t);
+    ct->c_ctparams = NULL;
+}
+
+
+static void
+free_multi (CT ct)
+{
+    struct multipart *m;
+    struct part *part, *next;
+
+    if (!(m = (struct multipart *) ct->c_ctparams))
+       return;
+
+    if (m->mp_start)
+       free (m->mp_start);
+    if (m->mp_stop)
+       free (m->mp_stop);
+       
+    for (part = m->mp_parts; part; part = next) {
+       next = part->mp_next;
+       free_content (part->mp_part);
+       free ((char *) part);
+    }
+    m->mp_parts = NULL;
+
+    free ((char *) m);
+    ct->c_ctparams = NULL;
+}
+
+
+static void
+free_partial (CT ct)
+{
+    struct partial *p;
+
+    if (!(p = (struct partial *) ct->c_ctparams))
+       return;
+
+    if (p->pm_partid)
+       free (p->pm_partid);
+
+    free ((char *) p);
+    ct->c_ctparams = NULL;
+}
+
+
+static void
+free_external (CT ct)
+{
+    struct exbody *e;
+
+    if (!(e = (struct exbody *) ct->c_ctparams))
+       return;
+
+    free_content (e->eb_content);
+    if (e->eb_body)
+       free (e->eb_body);
+
+    free ((char *) e);
+    ct->c_ctparams = NULL;
+}
+
+
+/*
+ * Free data structures related to encoding/decoding
+ * Content-Transfer-Encodings.
+ */
+
+void
+free_encoding (CT ct, int toplevel)
+{
+    CE ce;
+
+    if (!(ce = ct->c_cefile))
+       return;
+
+    if (ce->ce_fp) {
+       fclose (ce->ce_fp);
+       ce->ce_fp = NULL;
+    }
+
+    if (ce->ce_file) {
+       if (ce->ce_unlink)
+           unlink (ce->ce_file);
+       free (ce->ce_file);
+    }
+
+    if (toplevel) {
+       free ((char *) ce);
+       ct->c_cefile = NULL;
+    } else {
+       ct->c_ceopenfnx = NULL;
+    }
+}
diff --git a/uip/mhl.c b/uip/mhl.c
new file mode 100644 (file)
index 0000000..4c9e5e0
--- /dev/null
+++ b/uip/mhl.c
@@ -0,0 +1,32 @@
+
+/*
+ * mhl.c -- the nmh message listing program
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+int
+main (int argc, char **argv)
+{
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    done (mhl (argc, argv));
+}
+
+
+/*
+ * Cheat: we are loaded with adrparse, which wants a routine called
+ * OfficialName().  We call adrparse:getm() with the correct arguments
+ * to prevent OfficialName() from being called.  Hence, the following
+ * is to keep the loader happy.
+ */
+
+char *
+OfficialName(char *name)
+{
+    return name;
+}
diff --git a/uip/mhlist.c b/uip/mhlist.c
new file mode 100644 (file)
index 0000000..d5f2908
--- /dev/null
@@ -0,0 +1,428 @@
+
+/*
+ * mhlist.c -- list the contents of MIME messages
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+#include <h/signals.h>
+#include <h/md5.h>
+#include <errno.h>
+#include <signal.h>
+#include <zotnet/mts/mts.h>
+#include <zotnet/tws/tws.h>
+#include <h/mime.h>
+#include <h/mhparse.h>
+#include <h/mhcachesbr.h>
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+/*
+ * We allocate space for message names (msgs array)
+ * this number of elements at a time.
+ */
+#define MAXMSGS  256
+
+
+static struct swit switches[] = {
+#define        CHECKSW                 0
+    { "check", 0 },
+#define        NCHECKSW                1
+    { "nocheck", 0 },
+#define        HEADSW                  2
+    { "headers", 0 },
+#define        NHEADSW                 3
+    { "noheaders", 0 },
+#define        SIZESW                  4
+    { "realsize", 0 },
+#define        NSIZESW                 5
+    { "norealsize", 0 },
+#define        VERBSW                  6
+    { "verbose", 0 },
+#define        NVERBSW                 7
+    { "noverbose", 0 },
+#define        FILESW                  8       /* interface from show */
+    { "file file", 0 },
+#define        PARTSW                  9
+    { "part number", 0 },
+#define        TYPESW                 10
+    { "type content", 0 },
+#define        RCACHESW               11
+    { "rcache policy", 0 },
+#define        WCACHESW               12
+    { "wcache policy", 0 },
+#define VERSIONSW              13
+    { "version", 0 },
+#define        HELPSW                 14
+    { "help", 4 },
+
+/*
+ * switches for debugging
+ */
+#define        DEBUGSW                15
+    { "debug", -5 },
+    { NULL, 0 }
+};
+
+
+extern int errno;
+
+/* mhparse.c */
+extern int checksw;
+extern char *tmp;      /* directory to place temp files */
+
+/* mhcachesbr.c */
+extern int rcachesw;
+extern int wcachesw;
+extern char *cache_public;
+extern char *cache_private;
+
+/* mhmisc.c */
+extern int npart;
+extern int ntype;
+extern char *parts[NPARTS + 1];
+extern char *types[NTYPES + 1];
+extern int userrs;
+
+/*
+ * This is currently needed to keep mhparse happy.
+ * This needs to be changed.
+ */
+pid_t xpid  = 0;
+
+int debugsw = 0;
+int verbosw = 0;
+
+/* The list of top-level contents to display */
+CT *cts = NULL;
+
+#define        quitser pipeser
+
+/* mhparse.c */
+CT parse_mime (char *);
+
+/* mhmisc.c */
+int part_ok (CT, int);
+int type_ok (CT, int);
+void set_endian (void);
+void flush_errors (void);
+
+/* mhlistsbr.c */
+void list_all_messages (CT *, int, int, int, int);
+
+/* mhfree.c */
+void free_content (CT);
+
+/*
+ * static prototypes
+ */
+static RETSIGTYPE pipeser (int);
+
+
+int
+main (int argc, char **argv)
+{
+    int sizesw = 1, headsw = 1;
+    int nummsgs, maxmsgs, msgnum, *icachesw;
+    char *cp, *file = NULL, *folder = NULL;
+    char *maildir, buf[100], **argp;
+    char **arguments, **msgs;
+    struct msgs *mp = NULL;
+    CT ct, *ctp;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    /*
+     * Allocate the initial space to record message
+     * names, ranges, and sequences.
+     */
+    nummsgs = 0;
+    maxmsgs = MAXMSGS;
+    if (!(msgs = (char **) malloc ((size_t) (maxmsgs * sizeof(*msgs)))))
+       adios (NULL, "unable to allocate storage");
+
+    /*
+     * Parse arguments
+     */
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+           case AMBIGSW: 
+               ambigsw (cp, switches);
+               done (1);
+           case UNKWNSW: 
+               adios (NULL, "-%s unknown", cp);
+
+           case HELPSW: 
+               snprintf (buf, sizeof(buf), "%s [+folder] [msgs] [switches]",
+                       invo_name);
+               print_help (buf, switches, 1);
+               done (1);
+           case VERSIONSW:
+               print_version(invo_name);
+               done (1);
+
+           case RCACHESW:
+               icachesw = &rcachesw;
+               goto do_cache;
+           case WCACHESW:
+               icachesw = &wcachesw;
+do_cache:
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               switch (*icachesw = smatch (cp, caches)) {
+               case AMBIGSW:
+                   ambigsw (cp, caches);
+                   done (1);
+               case UNKWNSW:
+                   adios (NULL, "%s unknown", cp);
+               default:
+                   break;
+               }
+               continue;
+
+           case CHECKSW:
+               checksw++;
+               continue;
+           case NCHECKSW:
+               checksw = 0;
+               continue;
+
+           case HEADSW:
+               headsw = 1;
+               continue;
+           case NHEADSW:
+               headsw = 0;
+               continue;
+
+           case SIZESW:
+               sizesw = 1;
+               continue;
+           case NSIZESW:
+               sizesw = 0;
+               continue;
+
+           case PARTSW:
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               if (npart >= NPARTS)
+                   adios (NULL, "too many parts (starting with %s), %d max",
+                          cp, NPARTS);
+               parts[npart++] = cp;
+               continue;
+
+           case TYPESW:
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               if (ntype >= NTYPES)
+                   adios (NULL, "too many types (starting with %s), %d max",
+                          cp, NTYPES);
+               types[ntype++] = cp;
+               continue;
+
+           case FILESW:
+               if (!(cp = *argp++) || (*cp == '-' && cp[1]))
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               file = *cp == '-' ? cp : path (cp, TFILE);
+               continue;
+
+           case VERBSW: 
+               verbosw = 1;
+               continue;
+           case NVERBSW: 
+               verbosw = 0;
+               continue;
+           case DEBUGSW:
+               debugsw = 1;
+               continue;
+           }
+       }
+       if (*cp == '+' || *cp == '@') {
+           if (folder)
+               adios (NULL, "only one folder at a time!");
+           else
+               folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       } else {
+           /*
+            * Check if we need to allocate more space
+            * for message names/ranges/sequences.
+            */
+           if (nummsgs >= maxmsgs) {
+               maxmsgs += MAXMSGS;
+               if (!(msgs = (char **) realloc (msgs,
+                       (size_t) (maxmsgs * sizeof(*msgs)))))
+                   adios (NULL, "unable to reallocate msgs storage");
+           }
+           msgs[nummsgs++] = cp;
+       }
+    }
+
+    /* null terminate the list of acceptable parts/types */
+    parts[npart] = NULL;
+    types[ntype] = NULL;
+
+    set_endian ();
+
+    /* Check for public cache location */
+    if ((cache_public = context_find (nmhcache)) && *cache_public != '/')
+       cache_public = NULL;
+
+    /* Check for private cache location */
+    if (!(cache_private = context_find (nmhprivcache)))
+       cache_private = ".cache";
+    cache_private = getcpy (m_maildir (cache_private));
+
+    /*
+     * Check for storage directory.  If specified,
+     * then store temporary files there.  Else we
+     * store them in standard nmh directory.
+     */
+    if ((cp = context_find (nmhstorage)) && *cp)
+       tmp = concat (cp, "/", invo_name, NULL);
+    else
+       tmp = add (m_maildir (invo_name), NULL);
+
+    if (!context_find ("path"))
+       free (path ("./", TFOLDER));
+
+    if (file && nummsgs)
+       adios (NULL, "cannot specify msg and file at same time!");
+
+    /*
+     * check if message is coming from file
+     */
+    if (file) {
+       if (!(cts = (CT *) calloc ((size_t) 2, sizeof(*cts))))
+           adios (NULL, "out of memory");
+       ctp = cts;
+
+       if ((ct = parse_mime (file)));
+           *ctp++ = ct;
+    } else {
+       /*
+        * message(s) are coming from a folder
+        */
+       if (!nummsgs)
+           msgs[nummsgs++] = "cur";
+       if (!folder)
+           folder = getfolder (1);
+       maildir = m_maildir (folder);
+
+       if (chdir (maildir) == NOTOK)
+           adios (maildir, "unable to change directory to");
+
+       /* read folder and create message structure */
+       if (!(mp = folder_read (folder)))
+           adios (NULL, "unable to read folder %s", folder);
+
+       /* check for empty folder */
+       if (mp->nummsg == 0)
+           adios (NULL, "no messages in %s", folder);
+
+       /* parse all the message ranges/sequences and set SELECTED */
+       for (msgnum = 0; msgnum < nummsgs; msgnum++)
+           if (!m_convert (mp, msgs[msgnum]))
+               done (1);
+       seq_setprev (mp);       /* set the previous-sequence */
+
+       if (!(cts = (CT *) calloc ((size_t) (mp->numsel + 1), sizeof(*cts))))
+           adios (NULL, "out of memory");
+       ctp = cts;
+
+       for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
+           if (is_selected(mp, msgnum)) {
+               char *msgnam;
+
+               msgnam = m_name (msgnum);
+               if ((ct = parse_mime (msgnam)))
+                   *ctp++ = ct;
+           }
+       }
+    }
+
+    if (!*cts)
+       done (1);
+
+    userrs = 1;
+    SIGNAL (SIGQUIT, quitser);
+    SIGNAL (SIGPIPE, pipeser);
+
+    /*
+     * Get the associated umask for the relevant contents.
+     */
+    for (ctp = cts; *ctp; ctp++) {
+       struct stat st;
+
+       ct = *ctp;
+       if (type_ok (ct, 1) && !ct->c_umask) {
+           if (stat (ct->c_file, &st) != NOTOK)
+               ct->c_umask = ~(st.st_mode & 0777);
+           else
+               ct->c_umask = ~m_gmprot();
+       }
+    }
+
+    /*
+     * List the message content
+     */
+    list_all_messages (cts, headsw, sizesw, verbosw, debugsw);
+
+    /* Now free all the structures for the content */
+    for (ctp = cts; *ctp; ctp++)
+       free_content (*ctp);
+
+    free ((char *) cts);
+    cts = NULL;
+
+    /* If reading from a folder, do some updating */
+    if (mp) {
+       context_replace (pfolder, folder);/* update current folder  */
+       seq_setcur (mp, mp->hghsel);      /* update current message */
+       seq_save (mp);                    /* synchronize sequences  */
+       context_save ();                  /* save the context file  */
+    }
+
+    done (0);
+    /* NOTREACHED */
+}
+
+
+static RETSIGTYPE
+pipeser (int i)
+{
+    if (i == SIGQUIT) {
+       unlink ("core");
+       fflush (stdout);
+       fprintf (stderr, "\n");
+       fflush (stderr);
+    }
+
+    done (1);
+    /* NOTREACHED */
+}
+
+
+void
+done (int status)
+{
+    CT *ctp;
+
+    if ((ctp = cts))
+       for (; *ctp; ctp++)
+           free_content (*ctp);
+
+    exit (status);
+}
diff --git a/uip/mhlistsbr.c b/uip/mhlistsbr.c
new file mode 100644 (file)
index 0000000..ca34902
--- /dev/null
@@ -0,0 +1,428 @@
+
+/*
+ * mhlistsbr.c -- routines to list information about the
+ *             -- contents of MIME messages
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+#include <h/signals.h>
+#include <errno.h>
+#include <signal.h>
+#include <zotnet/mts/mts.h>
+#include <zotnet/tws/tws.h>
+#include <h/mime.h>
+#include <h/mhparse.h>
+
+extern int errno;
+
+/* mhmisc.c */
+int part_ok (CT, int);
+int type_ok (CT, int);
+void flush_errors (void);
+
+/*
+ * prototypes
+ */
+void list_all_messages (CT *, int, int, int, int);
+int list_switch (CT, int, int, int, int);
+int list_content (CT, int, int, int, int);
+
+/*
+ * static prototypes
+ */
+static void list_single_message (CT, int, int, int);
+static int list_debug (CT);
+static int list_multi (CT, int, int, int, int);
+static int list_partial (CT, int, int, int, int);
+static int list_external (CT, int, int, int, int);
+static int list_application (CT, int, int, int, int);
+static int list_encoding (CT);
+
+
+/*
+ * various formats for -list option
+ */
+#define        LSTFMT1         "%4s %-5s %-24s %5s %-36s\n"
+#define        LSTFMT2a        "%4d "
+#define        LSTFMT2b        "%-5s %-24.24s "
+#define        LSTFMT2c1       "%5lu"
+#define        LSTFMT2c2       "%4lu%c"
+#define        LSTFMT2c3       "huge "
+#define        LSTFMT2c4       "     "
+#define        LSTFMT2d1       " %-36.36s"
+#define        LSTFMT2d2       "\t     %-65.65s\n"
+
+
+/*
+ * Top level entry point to list group of messages
+ */
+
+void
+list_all_messages (CT *cts, int headers, int realsize, int verbose, int debug)
+{
+    CT ct, *ctp;
+
+    if (headers)
+       printf (LSTFMT1, "msg", "part", "type/subtype", "size", "description");
+
+    for (ctp = cts; *ctp; ctp++) {
+       ct = *ctp;
+       list_single_message (ct, realsize, verbose, debug);
+    }
+
+    flush_errors ();
+}
+
+
+/*
+ * Entry point to list a single message
+ */
+
+static void
+list_single_message (CT ct, int realsize, int verbose, int debug)
+{
+    if (type_ok (ct, 1)) {
+       umask (ct->c_umask);
+       list_switch (ct, 1, realsize, verbose, debug);
+       if (ct->c_fp) {
+           fclose (ct->c_fp);
+           ct->c_fp = NULL;
+       }
+       if (ct->c_ceclosefnx)
+           (*ct->c_ceclosefnx) (ct);
+    }
+}
+
+
+/*
+ * Primary switching routine to list information about a content
+ */
+
+int
+list_switch (CT ct, int toplevel, int realsize, int verbose, int debug)
+{
+    switch (ct->c_type) {
+       case CT_MULTIPART:
+           return list_multi (ct, toplevel, realsize, verbose, debug);
+           break;
+
+       case CT_MESSAGE:
+           switch (ct->c_subtype) {
+               case MESSAGE_PARTIAL:
+                   return list_partial (ct, toplevel, realsize, verbose, debug);
+                   break;
+
+               case MESSAGE_EXTERNAL:
+                   return list_external (ct, toplevel, realsize, verbose, debug);
+                   break;
+
+               case MESSAGE_RFC822:
+               default:
+                   return list_content (ct, toplevel, realsize, verbose, debug);
+                   break;
+           }
+           break;
+
+       case CT_TEXT:
+       case CT_AUDIO:
+       case CT_IMAGE:
+       case CT_VIDEO:
+           return list_content (ct, toplevel, realsize, verbose, debug);
+           break;
+
+       case CT_APPLICATION:
+           return list_application (ct, toplevel, realsize, verbose, debug);
+           break;
+
+       default:
+           /* list_debug (ct); */
+           adios (NULL, "unknown content type %d", ct->c_type);
+           break;
+    }
+
+    return 0;  /* NOT REACHED */
+}
+
+
+#define empty(s) ((s) ? (s) : "")
+
+/*
+ * Method for listing information about a simple/generic content
+ */
+
+int
+list_content (CT ct, int toplevel, int realsize, int verbose, int debug)
+{
+    unsigned long size;
+    char *cp, buffer[BUFSIZ];
+    CI ci = &ct->c_ctinfo;
+
+    printf (toplevel > 0 ? LSTFMT2a : toplevel < 0 ? "part " : "     ",
+           atoi (r1bindex (empty (ct->c_file), '/')));
+    snprintf (buffer, sizeof(buffer), "%s/%s", empty (ci->ci_type),
+               empty (ci->ci_subtype));
+    printf (LSTFMT2b, empty (ct->c_partno), buffer);
+
+    if (ct->c_cesizefnx && realsize)
+       size = (*ct->c_cesizefnx) (ct);
+    else
+       size = ct->c_end - ct->c_begin;
+
+    /* find correct scale for size (Kilo/Mega/Giga/Tera) */
+    for (cp = " KMGT"; size > 9999; size >>= 10)
+       if (!*++cp)
+           break;
+
+    /* print size of this body part */
+    switch (*cp) {
+        case ' ':
+           if (size > 0 || ct->c_encoding != CE_EXTERNAL)
+               printf (LSTFMT2c1, size);
+           else
+               printf (LSTFMT2c4);
+           break;
+
+       default:
+           printf (LSTFMT2c2, size, *cp);
+           break;
+
+       case '\0':
+           printf (LSTFMT2c3);
+    }
+
+    /* print Content-Description */
+    if (ct->c_descr) {
+       char *dp;
+
+       dp = trimcpy (cp = add (ct->c_descr, NULL));
+       free (cp);
+       printf (LSTFMT2d1, dp);
+       free (dp);
+    }
+
+    printf ("\n");
+
+    /*
+     * If verbose, print any RFC-822 comments in the
+     * Content-Type line.
+     */
+    if (verbose && ci->ci_comment) {
+       char *dp;
+
+       dp = trimcpy (cp = add (ci->ci_comment, NULL));
+       free (cp);
+       snprintf (buffer, sizeof(buffer), "(%s)", dp);
+       free (dp);
+       printf (LSTFMT2d2, buffer);
+    }
+
+    if (debug)
+       list_debug (ct);
+
+    return OK;
+}
+
+
+/*
+ * Print debugging information about a content
+ */
+
+static int
+list_debug (CT ct)
+{
+    char **ap, **ep;
+    CI ci = &ct->c_ctinfo;
+
+    fflush (stdout);
+    fprintf (stderr, "  partno \"%s\"\n", empty (ct->c_partno));
+
+    /* print MIME-Version line */
+    if (ct->c_vrsn)
+       fprintf (stderr, "  %s:%s\n", VRSN_FIELD, ct->c_vrsn);
+
+    /* print Content-Type line */
+    if (ct->c_ctline)
+       fprintf (stderr, "  %s:%s\n", TYPE_FIELD, ct->c_ctline);
+
+    /* print parsed elements of content type */
+    fprintf (stderr, "    type    \"%s\"\n", empty (ci->ci_type));
+    fprintf (stderr, "    subtype \"%s\"\n", empty (ci->ci_subtype));
+    fprintf (stderr, "    comment \"%s\"\n", empty (ci->ci_comment));
+    fprintf (stderr, "    magic   \"%s\"\n", empty (ci->ci_magic));
+
+    /* print parsed parameters attached to content type */
+    fprintf (stderr, "    parameters\n");
+    for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
+       fprintf (stderr, "      %s=\"%s\"\n", *ap, *ep);
+
+    /* print internal flags for type/subtype */
+    fprintf (stderr, "    type 0x%x subtype 0x%x params 0x%x\n",
+            ct->c_type, ct->c_subtype, (unsigned int) ct->c_ctparams);
+
+    fprintf (stderr, "    showproc  \"%s\"\n", empty (ct->c_showproc));
+    fprintf (stderr, "    termproc  \"%s\"\n", empty (ct->c_termproc));
+    fprintf (stderr, "    storeproc \"%s\"\n", empty (ct->c_storeproc));
+
+    /* print transfer encoding information */
+    if (ct->c_celine)
+       fprintf (stderr, "  %s:%s", ENCODING_FIELD, ct->c_celine);
+
+    /* print internal flags for transfer encoding */
+    fprintf (stderr, "    transfer encoding 0x%x params 0x%x\n",
+            ct->c_encoding, (unsigned int) ct->c_cefile);
+
+    /* print Content-ID */
+    if (ct->c_id)
+       fprintf (stderr, "  %s:%s", ID_FIELD, ct->c_id);
+
+    /* print Content-Description */
+    if (ct->c_descr)
+       fprintf (stderr, "  %s:%s", DESCR_FIELD, ct->c_descr);
+
+    fprintf (stderr, "    read fp 0x%x file \"%s\" begin %ld end %ld\n",
+            (unsigned int) ct->c_fp, empty (ct->c_file),
+            ct->c_begin, ct->c_end);
+
+    /* print more information about transfer encoding */
+    list_encoding (ct);
+
+    return OK;
+}
+
+#undef empty
+
+
+/*
+ * list content information for type "multipart"
+ */
+
+static int
+list_multi (CT ct, int toplevel, int realsize, int verbose, int debug)
+{
+    struct multipart *m = (struct multipart *) ct->c_ctparams;
+    struct part *part;
+
+    /* list the content for toplevel of this multipart */
+    list_content (ct, toplevel, realsize, verbose, debug);
+
+    /* now list for all the subparts */
+    for (part = m->mp_parts; part; part = part->mp_next) {
+       CT p = part->mp_part;
+
+       if (part_ok (p, 1) && type_ok (p, 1))
+           list_switch (p, 0, realsize, verbose, debug);
+    }
+
+    return OK;
+}
+
+
+/*
+ * list content information for type "message/partial"
+ */
+
+static int
+list_partial (CT ct, int toplevel, int realsize, int verbose, int debug)
+{
+    struct partial *p = (struct partial *) ct->c_ctparams;
+
+    list_content (ct, toplevel, realsize, verbose, debug);
+    if (verbose) {
+       printf ("\t     [message %s, part %d", p->pm_partid, p->pm_partno);
+       if (p->pm_maxno)
+           printf (" of %d", p->pm_maxno);
+       printf ("]\n");
+    }
+
+    return OK;
+}
+
+
+/*
+ * list content information for type "message/external"
+ */
+
+static int
+list_external (CT ct, int toplevel, int realsize, int verbose, int debug)
+{
+    struct exbody *e = (struct exbody *) ct->c_ctparams;
+
+    /*
+     * First list the information for the
+     * message/external content itself.
+     */
+    list_content (ct, toplevel, realsize, verbose, debug);
+
+    if (verbose) {
+       if (e->eb_name)
+           printf ("\t     name=\"%s\"\n", e->eb_name);
+       if (e->eb_dir)
+           printf ("\t     directory=\"%s\"\n", e->eb_dir);
+       if (e->eb_site)
+           printf ("\t     site=\"%s\"\n", e->eb_site);
+       if (e->eb_server)
+           printf ("\t     server=\"%s\"\n", e->eb_server);
+       if (e->eb_subject)
+           printf ("\t     subject=\"%s\"\n", e->eb_subject);
+
+       /* This must be defined */
+       printf     ("\t     access-type=\"%s\"\n", e->eb_access);
+
+       if (e->eb_mode)
+           printf ("\t     mode=\"%s\"\n", e->eb_mode);
+       if (e->eb_permission)
+           printf ("\t     permission=\"%s\"\n", e->eb_permission);
+
+       if (e->eb_flags == NOTOK)
+           printf ("\t     [service unavailable]\n");
+    }
+
+    /*
+     * Now list the information for the external content
+     * to which this content points.
+     */
+    list_content (e->eb_content, 0, realsize, verbose, debug);
+
+    return OK;
+}
+
+
+/*
+ * list content information for type "application"
+ */
+
+static int
+list_application (CT ct, int toplevel, int realsize, int verbose, int debug)
+{
+    list_content (ct, toplevel, realsize, verbose, debug);
+    if (verbose) {
+       char **ap, **ep;
+       CI ci = &ct->c_ctinfo;
+
+       for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
+           printf ("\t     %s=\"%s\"\n", *ap, *ep);
+    }
+
+    return OK;
+}
+
+
+/*
+ * list information about the Content-Transfer-Encoding
+ * used by a content.
+ */
+
+static int
+list_encoding (CT ct)
+{
+    CE ce;
+
+    if ((ce = ct->c_cefile))
+       fprintf (stderr, "    decoded fp 0x%x file \"%s\"\n",
+                (unsigned int) ce->ce_fp, ce->ce_file ? ce->ce_file : "");
+
+    return OK;
+}
diff --git a/uip/mhlsbr.c b/uip/mhlsbr.c
new file mode 100644 (file)
index 0000000..18617d3
--- /dev/null
@@ -0,0 +1,1805 @@
+
+/*
+ * mhlsbr.c -- main routines for nmh message lister
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/signals.h>
+#include <h/addrsbr.h>
+#include <h/fmt_scan.h>
+#include <zotnet/tws/tws.h>
+#include <setjmp.h>
+#include <signal.h>
+
+/*
+ * MAJOR BUG:
+ * for a component containing addresses, ADDRFMT, if COMPRESS is also
+ * set, then addresses get split wrong (not at the spaces between commas).
+ * To fix this correctly, putstr() should know about "atomic" strings that
+ * must NOT be broken across lines.  That's too difficult for right now
+ * (it turns out that there are a number of degernate cases), so in
+ * oneline(), instead of
+ *
+ *       (*onelp == '\n' && !onelp[1])
+ *
+ * being a terminating condition,
+ *
+ *       (*onelp == '\n' && (!onelp[1] || (flags & ADDRFMT)))
+ *
+ * is used instead.  This cuts the line prematurely, and gives us a much
+ * better chance of getting things right.
+ */
+
+#define ONECOMP  0
+#define TWOCOMP  1
+#define        BODYCOMP 2
+
+#define        QUOTE   '\\'
+
+static struct swit mhlswitches[] = {
+#define        BELLSW         0
+    { "bell", 0 },
+#define        NBELLSW        1
+    { "nobell", 0 },
+#define        CLRSW          2
+    { "clear", 0 },
+#define        NCLRSW         3
+    { "noclear", 0 },
+#define        FACESW         4
+    { "faceproc program", 0 },
+#define        NFACESW        5
+    { "nofaceproc", 0 },
+#define        FOLDSW         6
+    { "folder +folder", 0 },
+#define        FORMSW         7
+    { "form formfile", 0 },
+#define        PROGSW         8
+    { "moreproc program", 0 },
+#define        NPROGSW        9
+    { "nomoreproc", 0 },
+#define        LENSW         10
+    { "length lines", 0 },
+#define        WIDTHSW       11
+    { "width columns", 0 },
+#define        SLEEPSW       12
+    { "sleep seconds",  0 },
+#define        BITSTUFFSW    13
+    { "dashstuffing", -12 },   /* interface from forw */
+#define        NBITSTUFFSW   14
+    { "nodashstuffing", -14 }, /* interface from forw */
+#define VERSIONSW     15
+    { "version", 0 },
+#define        HELPSW        16
+    { "help", 4 },
+#define        FORW1SW       17
+    { "forward", -7 },         /* interface from forw */
+#define        FORW2SW       18
+    { "forwall", -7 },         /* interface from forw */
+#define        DGSTSW        19
+    { "digest list", -6 },
+#define VOLUMSW       20
+    { "volume number", -6 },
+#define ISSUESW       21
+    { "issue number", -5 },
+#define NBODYSW       22
+    { "nobody", -6 },
+    { NULL, 0 }
+};
+
+#define NOCOMPONENT 0x000001   /* don't show component name         */
+#define UPPERCASE   0x000002   /* display in all upper case         */
+#define CENTER      0x000004   /* center line                       */
+#define CLEARTEXT   0x000008   /* cleartext                         */
+#define EXTRA       0x000010   /* an "extra" component              */
+#define HDROUTPUT   0x000020   /* already output                    */
+#define CLEARSCR    0x000040   /* clear screen                      */
+#define LEFTADJUST  0x000080   /* left justify multiple lines       */
+#define COMPRESS    0x000100   /* compress text                     */
+#define        ADDRFMT     0x000200    /* contains addresses                */
+#define        BELL        0x000400    /* sound bell at EOP                 */
+#define        DATEFMT     0x000800    /* contains dates                    */
+#define        FORMAT      0x001000    /* parse address/date/RFC-2047 field */
+#define        INIT        0x002000    /* initialize component              */
+#define        FACEFMT     0x004000    /* contains face                     */
+#define        FACEDFLT    0x008000    /* default for face                  */
+#define        SPLIT       0x010000    /* split headers (don't concatenate) */
+#define        NONEWLINE   0x020000    /* don't write trailing newline      */
+#define        LBITS   "\020\01NOCOMPONENT\02UPPERCASE\03CENTER\04CLEARTEXT\05EXTRA\06HDROUTPUT\07CLEARSCR\010LEFTADJUST\011COMPRESS\012ADDRFMT\013BELL\014DATEFMT\015FORMAT\016INIT\017FACEFMT\020FACEDFLT\021SPLIT\022NONEWLINE"
+#define        GFLAGS  (NOCOMPONENT | UPPERCASE | CENTER | LEFTADJUST | COMPRESS | SPLIT)
+
+struct mcomp {
+    char *c_name;              /* component name          */
+    char *c_text;              /* component text          */
+    char *c_ovtxt;             /* text overflow indicator */
+    char *c_nfs;               /* iff FORMAT              */
+    struct format *c_fmt;      /*   ..                    */
+    char *c_face;              /* face designator         */
+    int c_offset;              /* left margin indentation */
+    int c_ovoff;               /* overflow indentation    */
+    int c_width;               /* width of field          */
+    int c_cwidth;              /* width of component      */
+    int c_length;              /* length in lines         */
+    long c_flags;
+    struct mcomp *c_next;
+};
+
+static struct mcomp *msghd = NULL;
+static struct mcomp *msgtl = NULL;
+static struct mcomp *fmthd = NULL;
+static struct mcomp *fmttl = NULL;
+
+static struct mcomp global = {
+    NULL, NULL, "", NULL, NULL, 0, -1, 80, -1, 40, BELL, 0
+};
+
+static struct mcomp holder = {
+    NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, NOCOMPONENT, 0
+};
+
+struct pair {
+    char *p_name;
+    long p_flags;
+};
+
+static struct pair pairs[] = {
+    { "Date",            DATEFMT },
+    { "From",            ADDRFMT|FACEDFLT },
+    { "Sender",          ADDRFMT },
+    { "Reply-To",        ADDRFMT },
+    { "To",              ADDRFMT },
+    { "cc",              ADDRFMT },
+    { "Bcc",             ADDRFMT },
+    { "Resent-Date",     DATEFMT },
+    { "Resent-From",     ADDRFMT },
+    { "Resent-Sender",   ADDRFMT },
+    { "Resent-Reply-To", ADDRFMT },
+    { "Resent-To",       ADDRFMT },
+    { "Resent-cc",       ADDRFMT },
+    { "Resent-Bcc",      ADDRFMT },
+    { "Face",            FACEFMT },
+    { NULL,              0 }
+};
+
+struct triple {
+    char *t_name;
+    long t_on;
+    long t_off;
+};
+
+static struct triple triples[] = {
+    { "nocomponent",   NOCOMPONENT, 0 },
+    { "uppercase",     UPPERCASE,   0 },
+    { "nouppercase",   0,           UPPERCASE },
+    { "center",        CENTER,      0 },
+    { "nocenter",      0,           CENTER },
+    { "clearscreen",   CLEARSCR,    0 },
+    { "noclearscreen", 0,           CLEARSCR },
+    { "noclear",       0,           CLEARSCR },
+    { "leftadjust",    LEFTADJUST,  0 },
+    { "noleftadjust",  0,           LEFTADJUST },
+    { "compress",      COMPRESS,    0 },
+    { "nocompress",    0,           COMPRESS },
+    { "split",         SPLIT,       0 },
+    { "nosplit",       0,           SPLIT },
+    { "addrfield",     ADDRFMT,     DATEFMT },
+    { "bell",          BELL,        0 },
+    { "nobell",        0,           BELL },
+    { "datefield",     DATEFMT,     ADDRFMT },
+    { "newline",       0,           NONEWLINE },
+    { "nonewline",     NONEWLINE,   0 },
+    { NULL,            0,           0 }
+};
+
+
+static int bellflg   = 0;
+static int clearflg  = 0;
+static int dashstuff = 0;
+static int dobody    = 1;
+static int forwflg   = 0;
+static int forwall   = 0;
+
+static int sleepsw = NOTOK;
+
+static char *digest = NULL;
+static int volume = 0;
+static int issue = 0;
+
+static int exitstat = 0;
+static int mhldebug = 0;
+
+#define        PITTY   (-1)
+#define        NOTTY   0
+#define        ISTTY   1
+static int ontty = NOTTY;
+
+static int row;
+static int column;
+
+static int lm;
+static int llim;
+static int ovoff;
+static int term;
+static int wid;
+
+static char *ovtxt;
+
+static char *onelp;
+
+static char *parptr;
+
+static int num_ignores = 0;
+static char *ignores[MAXARGS];
+
+static  jmp_buf env;
+static  jmp_buf mhlenv;
+
+static char delim3[] =         /* from forw.c */
+    "\n----------------------------------------------------------------------\n\n";
+static char delim4[] = "\n------------------------------\n\n";
+
+static FILE *(*mhl_action) () = (FILE *(*) ()) 0;
+
+
+/*
+ * Redefine a couple of functions.
+ * These are undefined later in the code.
+ */
+#define        adios mhladios
+#define        done  mhldone
+
+/*
+ * prototypes
+ */
+static void mhl_format (char *, int, int);
+static int evalvar (struct mcomp *);
+static int ptoi (char *, int *);
+static int ptos (char *, char **);
+static char *parse (void);
+static void process (char *, char *, int, int);
+static void mhlfile (FILE *, char *, int, int);
+static int mcomp_flags (char *);
+static char *mcomp_add (long, char *, char *);
+static void mcomp_format (struct mcomp *, struct mcomp *);
+static struct mcomp *add_queue (struct mcomp **, struct mcomp **, char *, char *, int);
+static void free_queue (struct mcomp **, struct mcomp **);
+static void putcomp (struct mcomp *, struct mcomp *, int);
+static char *oneline (char *, long);
+static void putstr (char *);
+static void putch (char);
+static RETSIGTYPE intrser (int);
+static RETSIGTYPE pipeser (int);
+static RETSIGTYPE quitser (int);
+static void face_format (struct mcomp *);
+static int doface (struct mcomp *);
+static void mhladios (char *, char *, ...);
+static void mhldone (int);
+static void m_popen (char *);
+
+int mhl (int, char **);
+int mhlsbr (int, char **, FILE *(*)());
+void m_pclose (void);
+
+void clear_screen (void);             /* from termsbr.c */
+int SOprintf (char *, ...);           /* from termsbr.c */
+int sc_width (void);                  /* from termsbr.c */
+int sc_length (void);                 /* from termsbr.c */
+int sc_hardcopy (void);               /* from termsbr.c */
+struct hostent *gethostbystring ();
+
+
+int
+mhl (int argc, char **argv)
+{
+    int length = 0, nomore = 0;
+    int i, width = 0, vecp = 0;
+    char *cp, *folder = NULL, *form = NULL;
+    char buf[BUFSIZ], *files[MAXARGS];
+    char **argp, **arguments;
+
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    if ((cp = getenv ("MHLDEBUG")) && *cp)
+       mhldebug++;
+
+    if ((cp = getenv ("FACEPROC")))
+       faceproc = cp;
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, mhlswitches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, mhlswitches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown\n", cp);
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf), "%s [switches] [files ...]", invo_name);
+                   print_help (buf, mhlswitches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case BELLSW: 
+                   bellflg = 1;
+                   continue;
+               case NBELLSW: 
+                   bellflg = -1;
+                   continue;
+
+               case CLRSW: 
+                   clearflg = 1;
+                   continue;
+               case NCLRSW: 
+                   clearflg = -1;
+                   continue;
+
+               case FOLDSW: 
+                   if (!(folder = *argp++) || *folder == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+               case FORMSW: 
+                   if (!(form = *argp++) || *form == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+
+               case FACESW:
+                   if (!(faceproc = *argp++) || *faceproc == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+               case NFACESW:
+                   faceproc = NULL;
+                   continue;
+               case SLEEPSW:
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   sleepsw = atoi (cp);/* ZERO ok! */
+                   continue;
+
+               case PROGSW:
+                   if (!(moreproc = *argp++) || *moreproc == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+               case NPROGSW:
+                   nomore++;
+                   continue;
+
+               case LENSW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   if ((length = atoi (cp)) < 1)
+                       adios (NULL, "bad argument %s %s", argp[-2], cp);
+                   continue;
+               case WIDTHSW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   if ((width = atoi (cp)) < 1)
+                       adios (NULL, "bad argument %s %s", argp[-2], cp);
+                   continue;
+
+               case DGSTSW: 
+                   if (!(digest = *argp++) || *digest == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+               case ISSUESW:
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   if ((issue = atoi (cp)) < 1)
+                       adios (NULL, "bad argument %s %s", argp[-2], cp);
+                   continue;
+               case VOLUMSW:
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   if ((volume = atoi (cp)) < 1)
+                       adios (NULL, "bad argument %s %s", argp[-2], cp);
+                   continue;
+
+               case FORW2SW: 
+                   forwall++;  /* fall */
+               case FORW1SW: 
+                   forwflg++;
+                   clearflg = -1;/* XXX */
+                   continue;
+
+               case BITSTUFFSW: 
+                   dashstuff = 1;      /* trinary logic */
+                   continue;
+               case NBITSTUFFSW: 
+                   dashstuff = -1;     /* trinary logic */
+                   continue;
+
+               case NBODYSW: 
+                   dobody = 0;
+                   continue;
+           }
+       }
+       files[vecp++] = cp;
+    }
+
+    if (!folder)
+       folder = getenv ("mhfolder");
+
+    if (isatty (fileno (stdout))) {
+       if (!nomore && !sc_hardcopy() && moreproc && *moreproc != '\0') {
+           if (mhl_action) {
+               SIGNAL (SIGINT, SIG_IGN);
+               SIGNAL2 (SIGQUIT, quitser);
+           }
+           m_popen (moreproc);
+           ontty = PITTY;
+       } else {
+           SIGNAL (SIGINT, SIG_IGN);
+           SIGNAL2 (SIGQUIT, quitser);
+           ontty = ISTTY;
+       }
+    } else {
+       ontty = NOTTY;
+    }
+
+    mhl_format (form ? form : mhlformat, length, width);
+
+    if (vecp == 0) {
+       process (folder, NULL, 1, vecp = 1);
+    } else {
+       for (i = 0; i < vecp; i++)
+           process (folder, files[i], i + 1, vecp);
+    }
+
+    if (forwall) {
+       if (digest) {
+           printf ("%s", delim4);
+           if (volume == 0) {
+               snprintf (buf, sizeof(buf), "End of %s Digest\n", digest);
+           } else {
+               snprintf (buf, sizeof(buf), "End of %s Digest [Volume %d Issue %d]\n",
+                       digest, volume, issue);
+           }
+           i = strlen (buf);
+           for (cp = buf + i; i > 1; i--)
+               *cp++ = '*';
+           *cp++ = '\n';
+           *cp = 0;
+           printf ("%s", buf);
+       }
+       else
+           printf ("\n------- End of Forwarded Message%s\n\n",
+                   vecp > 1 ? "s" : "");
+    }
+
+    if (clearflg > 0 && ontty == NOTTY)
+       clear_screen ();
+
+    if (ontty == PITTY)
+       m_pclose ();
+
+    return exitstat;
+}
+
+
+static void
+mhl_format (char *file, int length, int width)
+{
+    int i;
+    char *bp, *cp, **ip;
+    char *ap, buffer[BUFSIZ], name[NAMESZ];
+    struct mcomp *c1;
+    struct stat st;
+    FILE *fp;
+    static dev_t dev = 0;
+    static ino_t ino = 0;
+    static time_t mtime = 0;
+
+    if (fmthd != NULL)
+       if (stat (etcpath (file), &st) != NOTOK
+               && mtime == st.st_mtime
+               && dev == st.st_dev
+               && ino == st.st_ino)
+           goto out;
+       else
+           free_queue (&fmthd, &fmttl);
+
+    if ((fp = fopen (etcpath (file), "r")) == NULL)
+       adios (file, "unable to open format file");
+
+    if (fstat (fileno (fp), &st) != NOTOK) {
+       mtime = st.st_mtime;
+       dev = st.st_dev;
+       ino = st.st_ino;
+    }
+
+    global.c_ovtxt = global.c_nfs = NULL;
+    global.c_fmt = NULL;
+    global.c_offset = 0;
+    global.c_ovoff = -1;
+    if ((i = sc_width ()) > 5)
+       global.c_width = i;
+    global.c_cwidth = -1;
+    if ((i = sc_length ()) > 5)
+       global.c_length = i - 1;
+    global.c_flags = BELL;             /* BELL is default */
+    *(ip = ignores) = NULL;
+
+    while (vfgets (fp, &ap) == OK) {
+       bp = ap;
+       if (*bp == ';')
+           continue;
+
+       if ((cp = strchr(bp, '\n')))
+           *cp = 0;
+
+       if (*bp == ':') {
+           c1 = add_queue (&fmthd, &fmttl, NULL, bp + 1, CLEARTEXT);
+           continue;
+       }
+
+       parptr = bp;
+       strncpy (name, parse(), sizeof(name));
+       switch (*parptr) {
+           case '\0': 
+           case ',': 
+           case '=': 
+               /*
+                * Split this list of fields to ignore, and copy
+                * it to the end of the current "ignores" list.
+                */
+               if (!strcasecmp (name, "ignores")) {
+                   char **tmparray, **p;
+                   int n = 0;
+
+                   /* split the fields */
+                   tmparray = brkstring (getcpy (++parptr), ",", NULL);
+
+                   /* count number of fields split */
+                   p = tmparray;
+                   while (*p++)
+                       n++;
+
+                   /* copy pointers to split fields to ignores array */
+                   ip = copyip (tmparray, ip, MAXARGS - num_ignores);
+                   num_ignores += n;
+                   continue;
+               }
+               parptr = bp;
+               while (*parptr) {
+                   if (evalvar (&global))
+                       adios (NULL, "format file syntax error: %s", bp);
+                   if (*parptr)
+                       parptr++;
+               }
+               continue;
+
+           case ':': 
+               c1 = add_queue (&fmthd, &fmttl, name, NULL, INIT);
+               while (*parptr == ':' || *parptr == ',') {
+                   parptr++;
+                   if (evalvar (c1))
+                       adios (NULL, "format file syntax error: %s", bp);
+               }
+               if (!c1->c_nfs && global.c_nfs)
+                   if (c1->c_flags & DATEFMT) {
+                       if (global.c_flags & DATEFMT)
+                           c1->c_nfs = getcpy (global.c_nfs);
+                   }
+                   else
+                       if (c1->c_flags & ADDRFMT) {
+                           if (global.c_flags & ADDRFMT)
+                               c1->c_nfs = getcpy (global.c_nfs);
+                       }
+               continue;
+
+           default: 
+               adios (NULL, "format file syntax error: %s", bp);
+       }
+    }
+    fclose (fp);
+
+    if (mhldebug) {
+       for (c1 = fmthd; c1; c1 = c1->c_next) {
+           fprintf (stderr, "c1: name=\"%s\" text=\"%s\" ovtxt=\"%s\"\n",
+                   c1->c_name, c1->c_text, c1->c_ovtxt);
+           fprintf (stderr, "\tnfs=0x%x fmt=0x%x\n",
+                   (unsigned int) c1->c_nfs, (unsigned int) c1->c_fmt);
+           fprintf (stderr, "\toffset=%d ovoff=%d width=%d cwidth=%d length=%d\n",
+                   c1->c_offset, c1->c_ovoff, c1->c_width,
+                   c1->c_cwidth, c1->c_length);
+           fprintf (stderr, "\tflags=%s\n",
+                   snprintb (buffer, sizeof(buffer), (unsigned) c1->c_flags, LBITS));
+       }
+    }
+
+out:
+    if (clearflg == 1) {
+       global.c_flags |= CLEARSCR;
+    } else {
+       if (clearflg == -1)
+           global.c_flags &= ~CLEARSCR;
+    }
+
+    switch (bellflg) {         /* command line may override format file */
+       case 1: 
+           global.c_flags |= BELL;
+           break;
+       case -1: 
+           global.c_flags &= ~BELL;
+           break;
+    }
+
+    if (length)
+       global.c_length = length;
+    if (width)
+       global.c_width = width;
+    if (global.c_length < 5)
+       global.c_length = 10000;
+    if (global.c_width < 5)
+       global.c_width = 10000;
+}
+
+
+static int
+evalvar (struct mcomp *c1)
+{
+    char *cp, name[NAMESZ];
+    struct triple *ap;
+
+    if (!*parptr)
+       return 0;
+    strncpy (name, parse(), sizeof(name));
+
+    if (!strcasecmp (name, "component")) {
+       if (ptos (name, &c1->c_text))
+           return 1;
+       c1->c_flags &= ~NOCOMPONENT;
+       return 0;
+    }
+
+    if (!strcasecmp (name, "overflowtext"))
+       return ptos (name, &c1->c_ovtxt);
+
+    if (!strcasecmp (name, "formatfield")) {
+       char *nfs;
+
+       if (ptos (name, &cp))
+           return 1;
+       nfs = new_fs (NULL, NULL, cp);
+       c1->c_nfs = getcpy (nfs);
+       c1->c_flags |= FORMAT;
+       return 0;
+    }
+
+    if (!strcasecmp (name, "decode")) {
+       char *nfs;
+
+       nfs = new_fs (NULL, NULL, "%(decode{text})");
+       c1->c_nfs = getcpy (nfs);
+       c1->c_flags |= FORMAT;
+       return 0;
+    }
+
+    if (!strcasecmp (name, "offset"))
+       return ptoi (name, &c1->c_offset);
+    if (!strcasecmp (name, "overflowoffset"))
+       return ptoi (name, &c1->c_ovoff);
+    if (!strcasecmp (name, "width"))
+       return ptoi (name, &c1->c_width);
+    if (!strcasecmp (name, "compwidth"))
+       return ptoi (name, &c1->c_cwidth);
+    if (!strcasecmp (name, "length"))
+       return ptoi (name, &c1->c_length);
+    if (!strcasecmp (name, "nodashstuffing"))
+       return (dashstuff = -1);
+
+    for (ap = triples; ap->t_name; ap++)
+       if (!strcasecmp (ap->t_name, name)) {
+           c1->c_flags |= ap->t_on;
+           c1->c_flags &= ~ap->t_off;
+           return 0;
+       }
+
+    return 1;
+}
+
+
+static int
+ptoi (char *name, int *i)
+{
+    char *cp;
+
+    if (*parptr++ != '=' || !*(cp = parse ())) {
+       advise (NULL, "missing argument to variable %s", name);
+       return 1;
+    }
+
+    *i = atoi (cp);
+    return 0;
+}
+
+
+static int
+ptos (char *name, char **s)
+{
+    char c, *cp;
+
+    if (*parptr++ != '=') {
+       advise (NULL, "missing argument to variable %s", name);
+       return 1;
+    }
+
+    if (*parptr != '"') {
+       for (cp = parptr;
+               *parptr && *parptr != ':' && *parptr != ',';
+               parptr++)
+           continue;
+    } else {
+       for (cp = ++parptr; *parptr && *parptr != '"'; parptr++)
+           if (*parptr == QUOTE)
+               if (!*++parptr)
+                   parptr--;
+    }
+    c = *parptr;
+    *parptr = 0;
+    *s = getcpy (cp);
+    if ((*parptr = c) == '"')
+       parptr++;
+    return 0;
+}
+
+
+static char *
+parse (void)
+{
+    int c;
+    char *cp;
+    static char result[NAMESZ];
+
+    for (cp = result; *parptr && (cp - result < NAMESZ); parptr++) {
+       c = *parptr;
+       if (isalnum (c)
+               || c == '.'
+               || c == '-'
+               || c == '_'
+               || c =='['
+               || c == ']')
+           *cp++ = c;
+       else
+           break;
+    }
+    *cp = '\0';
+
+    return result;
+}
+
+
+static void
+process (char *folder, char *fname, int ofilen, int ofilec)
+{
+    char *cp;
+    FILE *fp;
+    struct mcomp *c1;
+
+    switch (setjmp (env)) {
+       case OK: 
+           if (fname) {
+               fp = mhl_action ? (*mhl_action) (fname) : fopen (fname, "r");
+               if (fp == NULL) {
+                   advise (fname, "unable to open");
+                   exitstat++;
+                   return;
+               }
+           } else {
+               fname = "(stdin)";
+               fp = stdin;
+           }
+           cp = folder ? concat (folder, ":", fname, NULL) : getcpy (fname);
+           if (ontty != PITTY)
+               SIGNAL (SIGINT, intrser);
+           mhlfile (fp, cp, ofilen, ofilec);  /* FALL THROUGH! */
+
+       default: 
+           if (ontty != PITTY)
+               SIGNAL (SIGINT, SIG_IGN);
+           if (mhl_action == NULL && fp != stdin)
+               fclose (fp);
+           free (cp);
+           if (holder.c_text) {
+               free (holder.c_text);
+               holder.c_text = NULL;
+           }
+           free_queue (&msghd, &msgtl);
+           for (c1 = fmthd; c1; c1 = c1->c_next)
+               c1->c_flags &= ~HDROUTPUT;
+           break;
+    }
+}
+
+
+static void
+mhlfile (FILE *fp, char *mname, int ofilen, int ofilec)
+{
+    int state;
+    struct mcomp *c1, *c2, *c3;
+    char **ip, name[NAMESZ], buf[BUFSIZ];
+
+    if (forwall) {
+       if (digest)
+           printf ("%s", ofilen == 1 ? delim3 : delim4);
+       else {
+           printf ("\n-------");
+           if (ofilen == 1)
+               printf (" Forwarded Message%s", ofilec > 1 ? "s" : "");
+           else
+               printf (" Message %d", ofilen);
+           printf ("\n\n");
+       }
+    } else {
+       switch (ontty) {
+           case PITTY: 
+               if (ofilec > 1) {
+                   if (ofilen > 1) {
+                       if ((global.c_flags & CLEARSCR))
+                           clear_screen ();
+                       else
+                           printf ("\n\n\n");
+                   }
+                   printf (">>> %s\n\n", mname);
+               }
+               break;
+
+           case ISTTY: 
+               strncpy (buf, "\n", sizeof(buf));
+               if (ofilec > 1) {
+                   if (SOprintf ("Press <return> to list \"%s\"...", mname)) {
+                       if (ofilen > 1)
+                           printf ("\n\n\n");
+                       printf ("Press <return> to list \"%s\"...", mname);
+                   }
+                   fflush (stdout);
+                   buf[0] = 0;
+                   read (fileno (stdout), buf, sizeof(buf));
+               }
+               if (strchr(buf, '\n')) {
+                   if ((global.c_flags & CLEARSCR))
+                       clear_screen ();
+               }
+               else
+                   printf ("\n");
+               break;
+
+           default: 
+               if (ofilec > 1) {
+                   if (ofilen > 1) {
+                       printf ("\n\n\n");
+                       if (clearflg > 0)
+                           clear_screen ();
+                   }
+                   printf (">>> %s\n\n", mname);
+               }
+               break;
+       }
+    }
+
+    for (state = FLD;;) {
+       switch (state = m_getfld (state, name, buf, sizeof(buf), fp)) {
+           case FLD: 
+           case FLDPLUS: 
+               for (ip = ignores; *ip; ip++)
+                   if (!strcasecmp (name, *ip)) {
+                       while (state == FLDPLUS)
+                           state = m_getfld (state, name, buf, sizeof(buf), fp);
+                       break;
+                   }
+               if (*ip)
+                   continue;
+
+               for (c2 = fmthd; c2; c2 = c2->c_next)
+                   if (!strcasecmp (c2->c_name, name))
+                       break;
+               c1 = NULL;
+               if (!((c3 = c2 ? c2 : &global)->c_flags & SPLIT))
+                   for (c1 = msghd; c1; c1 = c1->c_next)
+                       if (!strcasecmp (c1->c_name, c3->c_name)) {
+                           c1->c_text =
+                               mcomp_add (c1->c_flags, buf, c1->c_text);
+                           break;
+                       }
+               if (c1 == NULL)
+                   c1 = add_queue (&msghd, &msgtl, name, buf, 0);
+               while (state == FLDPLUS) {
+                   state = m_getfld (state, name, buf, sizeof(buf), fp);
+                   c1->c_text = add (buf, c1->c_text);
+               }
+               if (c2 == NULL)
+                   c1->c_flags |= EXTRA;
+               continue;
+
+           case BODY: 
+           case FILEEOF: 
+               row = column = 0;
+               for (c1 = fmthd; c1; c1 = c1->c_next) {
+                   if (c1->c_flags & CLEARTEXT) {
+                       putcomp (c1, c1, ONECOMP);
+                       continue;
+                   }
+                   if (!strcasecmp (c1->c_name, "messagename")) {
+                       holder.c_text = concat ("(Message ", mname, ")\n",
+                                           NULL);
+                       putcomp (c1, &holder, ONECOMP);
+                       free (holder.c_text);
+                       holder.c_text = NULL;
+                       continue;
+                   }
+                   if (!strcasecmp (c1->c_name, "extras")) {
+                       for (c2 = msghd; c2; c2 = c2->c_next)
+                           if (c2->c_flags & EXTRA)
+                               putcomp (c1, c2, TWOCOMP);
+                       continue;
+                   }
+                   if (dobody && !strcasecmp (c1->c_name, "body")) {
+                       if ((holder.c_text = malloc (sizeof(buf))) == NULL)
+                           adios (NULL, "unable to allocate buffer memory");
+                       strncpy (holder.c_text, buf, sizeof(buf));
+                       while (state == BODY) {
+                           putcomp (c1, &holder, BODYCOMP);
+                           state = m_getfld (state, name, holder.c_text,
+                                       sizeof(buf), fp);
+                       }
+                       free (holder.c_text);
+                       holder.c_text = NULL;
+                       continue;
+                   }
+                   for (c2 = msghd; c2; c2 = c2->c_next)
+                       if (!strcasecmp (c2->c_name, c1->c_name)) {
+                           putcomp (c1, c2, ONECOMP);
+                           if (!(c1->c_flags & SPLIT))
+                               break;
+                       }
+                   if (faceproc && c2 == NULL && (c1->c_flags & FACEFMT))
+                       for (c2 = msghd; c2; c2 = c2->c_next)
+                           if (c2->c_flags & FACEDFLT) {
+                               if (c2->c_face == NULL)
+                                   face_format (c2);
+                               if ((holder.c_text = c2->c_face)) {
+                                   putcomp (c1, &holder, ONECOMP);
+                                   holder.c_text = NULL;
+                               }
+                               break;
+                           }
+               }
+               return;
+
+           case LENERR: 
+           case FMTERR: 
+               advise (NULL, "format error in message %s", mname);
+               exitstat++;
+               return;
+
+           default: 
+               adios (NULL, "getfld() returned %d", state);
+       }
+    }
+}
+
+
+static int
+mcomp_flags (char *name)
+{
+    struct pair *ap;
+
+    for (ap = pairs; ap->p_name; ap++)
+       if (!strcasecmp (ap->p_name, name))
+           return (ap->p_flags);
+
+    return 0;
+}
+
+
+static char *
+mcomp_add (long flags, char *s1, char *s2)
+{
+    char *dp;
+
+    if (!(flags & ADDRFMT))
+       return add (s1, s2);
+
+    if (s2 && *(dp = s2 + strlen (s2) - 1) == '\n')
+       *dp = 0;
+
+    return add (s1, add (",\n", s2));
+}
+
+
+struct pqpair {
+    char *pq_text;
+    char *pq_error;
+    struct pqpair *pq_next;
+};
+
+
+static void
+mcomp_format (struct mcomp *c1, struct mcomp *c2)
+{
+    int dat[5];
+    char *ap, *cp;
+    char buffer[BUFSIZ], error[BUFSIZ];
+    struct comp *cptr;
+    struct pqpair *p, *q;
+    struct pqpair pq;
+    struct mailname *mp;
+
+    ap = c2->c_text;
+    c2->c_text = NULL;
+    dat[0] = 0;
+    dat[1] = 0;
+    dat[2] = 0;
+    dat[3] = sizeof(buffer) - 1;
+    dat[4] = 0;
+    fmt_compile (c1->c_nfs, &c1->c_fmt);
+
+    if (!(c1->c_flags & ADDRFMT)) {
+       FINDCOMP (cptr, "text");
+       if (cptr)
+           cptr->c_text = ap;
+       if ((cp = strrchr(ap, '\n')))   /* drop ending newline */
+           if (!cp[1])
+               *cp = 0;
+
+       fmt_scan (c1->c_fmt, buffer, sizeof(buffer) - 1, dat);
+       /* Don't need to append a newline, dctime() already did */
+       c2->c_text = getcpy (buffer);
+
+       free (ap);
+       return;
+    }
+
+    (q = &pq)->pq_next = NULL;
+    while ((cp = getname (ap))) {
+       if ((p = (struct pqpair *) calloc ((size_t) 1, sizeof(*p))) == NULL)
+           adios (NULL, "unable to allocate pqpair memory");
+
+       if ((mp = getm (cp, NULL, 0, AD_NAME, error)) == NULL) {
+           p->pq_text = getcpy (cp);
+           p->pq_error = getcpy (error);
+       } else {
+           if ((c1->c_flags & FACEDFLT) && c2->c_face == NULL) {
+               char   *h, *o;
+               if ((h = mp->m_host) == NULL)
+                   h = LocalName ();
+               if ((o = OfficialName (h)))
+                   h = o;
+               c2->c_face = concat ("address ", h, " ", mp->m_mbox,
+                                   NULL);
+           }
+           p->pq_text = getcpy (mp->m_text);
+           mnfree (mp);
+       }
+       q = (q->pq_next = p);
+    }
+
+    for (p = pq.pq_next; p; p = q) {
+       FINDCOMP (cptr, "text");
+       if (cptr)
+           cptr->c_text = p->pq_text;
+       FINDCOMP (cptr, "error");
+       if (cptr)
+           cptr->c_text = p->pq_error;
+
+       fmt_scan (c1->c_fmt, buffer, sizeof(buffer) - 1, dat);
+       if (*buffer) {
+           if (c2->c_text)
+               c2->c_text = add (",\n", c2->c_text);
+           if (*(cp = buffer + strlen (buffer) - 1) == '\n')
+               *cp = 0;
+           c2->c_text = add (buffer, c2->c_text);
+       }
+
+       free (p->pq_text);
+       if (p->pq_error)
+           free (p->pq_error);
+       q = p->pq_next;
+       free ((char *) p);
+    }
+
+    c2->c_text = add ("\n", c2->c_text);
+    free (ap);
+}
+
+
+static struct mcomp *
+add_queue (struct mcomp **head, struct mcomp **tail, char *name, char *text, int flags)
+{
+    struct mcomp *c1;
+
+    if ((c1 = (struct mcomp *) calloc ((size_t) 1, sizeof(*c1))) == NULL)
+       adios (NULL, "unable to allocate comp memory");
+
+    c1->c_flags = flags & ~INIT;
+    if ((c1->c_name = name ? getcpy (name) : NULL))
+       c1->c_flags |= mcomp_flags (c1->c_name);
+    c1->c_text = text ? getcpy (text) : NULL;
+    if (flags & INIT) {
+       if (global.c_ovtxt)
+           c1->c_ovtxt = getcpy (global.c_ovtxt);
+       c1->c_offset = global.c_offset;
+       c1->c_ovoff = global. c_ovoff;
+       c1->c_width = c1->c_length = 0;
+       c1->c_cwidth = global.c_cwidth;
+       c1->c_flags |= global.c_flags & GFLAGS;
+    }
+    if (*head == NULL)
+       *head = c1;
+    if (*tail != NULL)
+       (*tail)->c_next = c1;
+    *tail = c1;
+
+    return c1;
+}
+
+
+static void
+free_queue (struct mcomp **head, struct mcomp **tail)
+{
+    struct mcomp *c1, *c2;
+
+    for (c1 = *head; c1; c1 = c2) {
+       c2 = c1->c_next;
+       if (c1->c_name)
+           free (c1->c_name);
+       if (c1->c_text)
+           free (c1->c_text);
+       if (c1->c_ovtxt)
+           free (c1->c_ovtxt);
+       if (c1->c_nfs)
+           free (c1->c_nfs);
+       if (c1->c_fmt)
+           free ((char *) c1->c_fmt);
+       if (c1->c_face)
+           free (c1->c_face);
+       free ((char *) c1);
+    }
+
+    *head = *tail = NULL;
+}
+
+
+static void
+putcomp (struct mcomp *c1, struct mcomp *c2, int flag)
+{
+    int count, cchdr;
+    char *cp;
+
+    cchdr = 0;
+    lm = 0;
+    llim = c1->c_length ? c1->c_length : -1;
+    wid = c1->c_width ? c1->c_width : global.c_width;
+    ovoff = (c1->c_ovoff >= 0 ? c1->c_ovoff : global.c_ovoff)
+       + c1->c_offset;
+    if ((ovtxt = c1->c_ovtxt ? c1->c_ovtxt : global.c_ovtxt) == NULL)
+       ovtxt = "";
+    if (wid < ovoff + strlen (ovtxt) + 5)
+       adios (NULL, "component: %s width(%d) too small for overflow(%d)",
+               c1->c_name, wid, ovoff + strlen (ovtxt) + 5);
+    onelp = NULL;
+
+    if (c1->c_flags & CLEARTEXT) {
+       putstr (c1->c_text);
+       putstr ("\n");
+       return;
+    }
+
+    if (c1->c_flags & FACEFMT)
+       switch (doface (c2)) {
+           case NOTOK:         /* error */
+           case OK:            /* async faceproc */
+               return;
+
+           default:            /* sync faceproc */
+               break;
+       }
+
+    if (c1->c_nfs && (c1->c_flags & (ADDRFMT | DATEFMT | FORMAT)))
+       mcomp_format (c1, c2);
+
+    if (c1->c_flags & CENTER) {
+       count = (c1->c_width ? c1->c_width : global.c_width)
+           - c1->c_offset - strlen (c2->c_text);
+       if (!(c1->c_flags & HDROUTPUT) && !(c1->c_flags & NOCOMPONENT))
+           count -= strlen (c1->c_text ? c1->c_text : c1->c_name) + 2;
+       lm = c1->c_offset + (count / 2);
+    } else {
+       if (c1->c_offset)
+           lm = c1->c_offset;
+    }
+
+    if (!(c1->c_flags & HDROUTPUT) && !(c1->c_flags & NOCOMPONENT)) {
+        if (c1->c_flags & UPPERCASE)           /* uppercase component also */
+           for (cp = (c1->c_text ? c1->c_text : c1->c_name); *cp; cp++)
+               if (islower (*cp))
+                   *cp = toupper (*cp);
+       putstr (c1->c_text ? c1->c_text : c1->c_name);
+       if (flag != BODYCOMP) {
+           putstr (": ");
+           if (!(c1->c_flags & SPLIT))
+               c1->c_flags |= HDROUTPUT;
+
+       cchdr++;
+       if ((count = c1->c_cwidth -
+               strlen (c1->c_text ? c1->c_text : c1->c_name) - 2) > 0)
+           while (count--)
+               putstr (" ");
+       }
+       else
+           c1->c_flags |= HDROUTPUT;           /* for BODYCOMP */
+    }
+
+    if (flag == TWOCOMP
+           && !(c2->c_flags & HDROUTPUT)
+           && !(c2->c_flags & NOCOMPONENT)) {
+        if (c1->c_flags & UPPERCASE)
+           for (cp = c2->c_name; *cp; cp++)
+               if (islower (*cp))
+                   *cp = toupper (*cp);
+       putstr (c2->c_name);
+       putstr (": ");
+       if (!(c1->c_flags & SPLIT))
+           c2->c_flags |= HDROUTPUT;
+
+       cchdr++;
+       if ((count = c1->c_cwidth - strlen (c2->c_name) - 2) > 0)
+           while (count--)
+               putstr (" ");
+    }
+    if (c1->c_flags & UPPERCASE)
+       for (cp = c2->c_text; *cp; cp++)
+           if (islower (*cp))
+               *cp = toupper (*cp);
+
+    count = 0;
+    if (cchdr)
+       if (flag == TWOCOMP)
+           count = (c1->c_cwidth >= 0) ? c1->c_cwidth
+                       : strlen (c2->c_name) + 2;
+       else
+           count = (c1->c_cwidth >= 0) ? c1->c_cwidth
+                       : strlen (c1->c_text ? c1->c_text : c1->c_name) + 2;
+    count += c1->c_offset;
+
+    if ((cp = oneline (c2->c_text, c1->c_flags)))
+       putstr(cp);
+    if (term == '\n')
+       putstr ("\n");
+    while ((cp = oneline (c2->c_text, c1->c_flags))) {
+       lm = count;
+       if (flag == BODYCOMP
+               && !(c1->c_flags & NOCOMPONENT))
+           putstr (c1->c_text ? c1->c_text : c1->c_name);
+       if (*cp)
+           putstr (cp);
+       if (term == '\n')
+           putstr ("\n");
+    }
+}
+
+
+static char *
+oneline (char *stuff, long flags)
+{
+    int spc;
+    char *cp, *ret;
+
+    if (onelp == NULL)
+       onelp = stuff;
+    if (*onelp == 0)
+       return (onelp = NULL);
+
+    ret = onelp;
+    term = 0;
+    if (flags & COMPRESS) {
+       for (spc = 1, cp = ret; *onelp; onelp++)
+           if (isspace (*onelp)) {
+               if (*onelp == '\n' && (!onelp[1] || (flags & ADDRFMT))) {
+                   term = '\n';
+                   *onelp++ = 0;
+                   break;
+               }
+               else
+                   if (!spc) {
+                       *cp++ = ' ';
+                       spc++;
+                   }
+           }
+           else {
+               *cp++ = *onelp;
+               spc = 0;
+           }
+
+       *cp = 0;
+    }
+    else {
+       while (*onelp && *onelp != '\n')
+           onelp++;
+       if (*onelp == '\n') {
+           term = '\n';
+           *onelp++ = 0;
+       }
+       if (flags & LEFTADJUST)
+           while (*ret == ' ' || *ret == '\t')
+               ret++;
+    }
+    if (*onelp == 0 && term == '\n' && (flags & NONEWLINE))
+       term = 0;
+
+    return ret;
+}
+
+
+static void
+putstr (char *string)
+{
+    if (!column && lm > 0)
+       while (lm > 0)
+           if (lm >= 8) {
+               putch ('\t');
+               lm -= 8;
+           }
+           else {
+               putch (' ');
+               lm--;
+           }
+    lm = 0;
+    while (*string)
+       putch (*string++);
+}
+
+
+static void
+putch (char ch)
+{
+    char buf[BUFSIZ];
+
+    if (llim == 0)
+       return;
+
+    switch (ch) {
+       case '\n': 
+           if (llim > 0)
+               llim--;
+           column = 0;
+           row++;
+           if (ontty != ISTTY || row != global.c_length)
+               break;
+           if (global.c_flags & BELL)
+               putchar ('\007');
+           fflush (stdout);
+           buf[0] = 0;
+           read (fileno (stdout), buf, sizeof(buf));
+           if (strchr(buf, '\n')) {
+               if (global.c_flags & CLEARSCR)
+                   clear_screen ();
+               row = 0;
+           } else {
+               putchar ('\n');
+               row = global.c_length / 3;
+           }
+           return;
+
+       case '\t': 
+           column |= 07;
+           column++;
+           break;
+
+       case '\b': 
+           column--;
+           break;
+
+       case '\r': 
+           column = 0;
+           break;
+
+       default: 
+           /*
+            * If we are forwarding this message, and the first
+            * column contains a dash, then add a dash and a space.
+            */
+           if (column == 0 && forwflg && (dashstuff >= 0) && ch == '-') {
+               putchar ('-');
+               putchar (' ');
+           }
+           if (ch >= ' ')
+               column++;
+           break;
+    }
+
+    if (column >= wid) {
+       putch ('\n');
+       if (ovoff > 0)
+           lm = ovoff;
+       putstr (ovtxt ? ovtxt : "");
+       putch (ch);
+       return;
+    }
+
+    putchar (ch);
+}
+
+
+static RETSIGTYPE
+intrser (int i)
+{
+#ifndef RELIABLE_SIGNALS
+    SIGNAL (SIGINT, intrser);
+#endif
+
+    discard (stdout);
+    putchar ('\n');
+    longjmp (env, DONE);
+}
+
+
+static RETSIGTYPE
+pipeser (int i)
+{
+#ifndef RELIABLE_SIGNALS
+    SIGNAL (SIGPIPE, pipeser);
+#endif
+
+    done (NOTOK);
+}
+
+
+static RETSIGTYPE
+quitser (int i)
+{
+#ifndef RELIABLE_SIGNALS
+    SIGNAL (SIGQUIT, quitser);
+#endif
+
+    putchar ('\n');
+    fflush (stdout);
+    done (NOTOK);
+}
+
+
+static void
+face_format (struct mcomp *c1)
+{
+    char *cp;
+    struct mailname *mp;
+
+    if ((cp = c1->c_text) == NULL)
+       return;
+
+    if ((cp = getname (cp))) {
+       if ((mp = getm (cp, NULL, 0, AD_NAME, NULL))) {
+           char *h, *o;
+           if ((h = mp->m_host) == NULL)
+               h = LocalName ();
+           if ((o = OfficialName (h)))
+               h = o;
+           c1->c_face = concat ("address ", h, " ", mp->m_mbox, NULL);
+       }
+
+       while ((cp = getname (cp)))
+           continue;
+    }
+}
+
+
+/*
+ * faceproc is two elements defining the image agent's location:
+ *     Internet host
+ *     UDP port
+ */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+
+static int
+doface (struct mcomp *c1)
+{
+    int        result, sd;
+    struct sockaddr_in in_socket;
+    struct sockaddr_in *isock = &in_socket;
+    static int inited = OK;
+    static int addrlen;
+    static struct in_addr addr;
+    static unsigned short portno;
+
+    if (inited == OK) {
+       char *cp;
+       char **ap = brkstring (cp = getcpy (faceproc), " ", "\n");
+       struct hostent *hp;
+
+       if (ap[0] == NULL || ap[1] == NULL) {
+bad_faceproc: ;
+           free (cp);
+           return (inited = NOTOK);
+       }
+
+       if (!(hp = gethostbystring (ap[0])))
+           goto bad_faceproc;
+       memcpy((char *) &addr, hp->h_addr, addrlen = hp->h_length);
+
+       portno = htons ((unsigned short) atoi (ap[1]));
+       free (cp);
+
+       inited = DONE;
+    }
+    if (inited == NOTOK)
+       return NOTOK;
+
+    isock->sin_family = AF_INET;
+    isock->sin_port = portno;
+    memcpy((char *) &isock->sin_addr, (char *) &addr, addrlen);
+
+    if ((sd = socket (AF_INET, SOCK_DGRAM, 0)) == NOTOK)
+       return NOTOK;
+
+    result = sendto (sd, c1->c_text, strlen (c1->c_text), 0,
+               (struct sockaddr *) isock, sizeof(*isock));
+
+    close (sd);
+
+    return (result != NOTOK ? OK : NOTOK);
+}
+
+/*
+ * COMMENTED OUT
+ * This version doesn't use sockets
+ */
+#if 0
+
+static int
+doface (struct mcomp *c1)
+{
+    int i, len, vecp;
+    pid_t child_id;
+    int result, pdi[2], pdo[2];
+    char *bp, *cp;
+    char buffer[BUFSIZ], *vec[10];
+
+    if (pipe (pdi) == NOTOK)
+       return NOTOK;
+    if (pipe (pdo) == NOTOK) {
+       close (pdi[0]);
+       close (pdi[1]);
+       return NOTOK;
+    }
+
+    for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
+       sleep (5);
+
+    switch (child_id) {
+       case NOTOK: 
+           /* oops... fork error */
+           return NOTOK;
+
+       case OK: 
+           /* child process */
+           SIGNAL (SIGINT, SIG_IGN);
+           SIGNAL (SIGQUIT, SIG_IGN);
+           if (pdi[0] != fileno (stdin)) {
+               dup2 (pdi[0], fileno (stdin));
+               close (pdi[0]);
+           }
+           close (pdi[1]);
+           close (pdo[0]);
+           if (pdo[1] != fileno (stdout)) {
+               dup2 (pdo[1], fileno (stdout));
+               close (pdo[1]);
+           }
+           vecp = 0;
+           vec[vecp++] = r1bindex (faceproc, '/');
+           vec[vecp++] = "-e";
+           if (sleepsw != NOTOK) {
+               vec[vecp++] = "-s";
+               snprintf (buffer, sizeof(buffer), "%d", sleepsw);
+               vec[vecp++] = buffer;
+           }
+           vec[vecp] = NULL;
+           execvp (faceproc, vec);
+           fprintf (stderr, "unable to exec ");
+           perror (faceproc);
+           _exit (-1);         /* NOTREACHED */
+
+       default: 
+           /* parent process */
+           close (pdi[0]);
+           i = strlen (c1->c_text);
+           if (write (pdi[1], c1->c_text, i) != i)
+               adios ("pipe", "error writing to");
+           free (c1->c_text), c1->c_text = NULL;
+           close (pdi[1]);
+
+           close (pdo[1]);
+           cp = NULL, len = 0;
+           result = DONE;
+           while ((i = read (pdo[0], buffer, strlen (buffer))) > 0) {
+               if (cp) {
+                   int j;
+                   char *dp;
+                   if ((dp = realloc (cp, (unsigned) (j = len + i))) == NULL)
+                       adios (NULL, "unable to allocate face storage");
+                   memcpy(dp + len, buffer, i);
+                   cp = dp, len = j;
+               }
+               else {
+                   if ((cp = malloc ((unsigned) i)) == NULL)
+                       adios (NULL, "unable to allocate face storage");
+                   memcpy(cp, buffer, i);
+                   len = i;
+               }
+               if (result == DONE)
+                   for (bp = buffer + i - 1; bp >= buffer; bp--)
+                       if (!isascii (*bp) || iscntrl (*bp)) {
+                           result = OK;
+                           break;
+                       }
+           }
+           close (pdo[0]);
+
+/* no waiting for child... */
+
+           if (result == OK) { /* binary */
+               if (write (1, cp, len) != len)
+                   adios ("writing", "error");
+               free (cp);
+           }
+           else                /* empty */
+               if ((c1->c_text = cp) == NULL)
+                   result = OK;
+           break;
+    }
+
+    return result;
+}
+#endif /* COMMENTED OUT */
+
+
+int
+mhlsbr (int argc, char **argv, FILE *(*action)())
+{
+    SIGNAL_HANDLER istat, pstat, qstat;
+    char *cp;
+    struct mcomp *c1;
+
+    switch (setjmp (mhlenv)) {
+       case OK: 
+           cp = invo_name;
+           sleepsw = 0;        /* XXX */
+           bellflg = clearflg = forwflg = forwall = exitstat = 0;
+           digest = NULL;
+           ontty = NOTTY;
+           mhl_action = action;
+
+           /*
+            * If signal is at default action, then start ignoring
+            * it, else let it set to its current action.
+            */
+           if ((istat = SIGNAL (SIGINT, SIG_IGN)) != SIG_DFL)
+               SIGNAL (SIGINT, istat);
+           if ((qstat = SIGNAL (SIGQUIT, SIG_IGN)) != SIG_DFL)
+               SIGNAL (SIGQUIT, qstat);
+           pstat = SIGNAL (SIGPIPE, pipeser);
+           mhl (argc, argv);                  /* FALL THROUGH! */
+
+       default: 
+           SIGNAL (SIGINT, istat);
+           SIGNAL (SIGQUIT, qstat);
+           SIGNAL (SIGPIPE, SIG_IGN);/* should probably change to block instead */
+           if (ontty == PITTY)
+               m_pclose ();
+           SIGNAL (SIGPIPE, pstat);
+           invo_name = cp;
+           if (holder.c_text) {
+               free (holder.c_text);
+               holder.c_text = NULL;
+           }
+           free_queue (&msghd, &msgtl);
+           for (c1 = fmthd; c1; c1 = c1->c_next)
+               c1->c_flags &= ~HDROUTPUT;
+           return exitstat;
+    }
+}
+
+#undef adios
+#undef done
+
+static void
+mhladios (char *what, char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    advertise (what, NULL, fmt, ap);
+    va_end(ap);
+    mhldone (1);
+}
+
+
+static void
+mhldone (int status)
+{
+    exitstat = status;
+    if (mhl_action)
+       longjmp (mhlenv, DONE);
+    else
+       done (exitstat);
+}
+
+
+static int m_pid = NOTOK;
+static  int sd = NOTOK;
+
+static void
+m_popen (char *name)
+{
+    int pd[2];
+
+    if (mhl_action && (sd = dup (fileno (stdout))) == NOTOK)
+       adios ("standard output", "unable to dup()");
+
+    if (pipe (pd) == NOTOK)
+       adios ("pipe", "unable to");
+
+    switch (m_pid = vfork ()) {
+       case NOTOK: 
+           adios ("fork", "unable to");
+
+       case OK: 
+           SIGNAL (SIGINT, SIG_DFL);
+           SIGNAL (SIGQUIT, SIG_DFL);
+
+           close (pd[1]);
+           if (pd[0] != fileno (stdin)) {
+               dup2 (pd[0], fileno (stdin));
+               close (pd[0]);
+           }
+           execlp (name, r1bindex (name, '/'), NULL);
+           fprintf (stderr, "unable to exec ");
+           perror (name);
+           _exit (-1);
+
+       default: 
+           close (pd[0]);
+           if (pd[1] != fileno (stdout)) {
+               dup2 (pd[1], fileno (stdout));
+               close (pd[1]);
+           }
+    }
+}
+
+
+void
+m_pclose (void)
+{
+    if (m_pid == NOTOK)
+       return;
+
+    if (sd != NOTOK) {
+       fflush (stdout);
+       if (dup2 (sd, fileno (stdout)) == NOTOK)
+           adios ("standard output", "unable to dup2()");
+
+       clearerr (stdout);
+       close (sd);
+       sd = NOTOK;
+    }
+    else
+       fclose (stdout);
+
+    pidwait (m_pid, OK);
+    m_pid = NOTOK;
+}
diff --git a/uip/mhmail.c b/uip/mhmail.c
new file mode 100644 (file)
index 0000000..f9dc33b
--- /dev/null
@@ -0,0 +1,204 @@
+
+/*
+ * mhmail.c -- simple mail program
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/signals.h>
+#include <signal.h>
+
+static struct swit switches[] = {
+#define        BODYSW             0
+    { "body text", 0 },
+#define        CCSW               1
+    { "cc addrs ...", 0 },
+#define        FROMSW             2
+    { "from addr", 0 },
+#define        SUBJSW             3
+    { "subject text", 0 },
+#define VERSIONSW          4
+    { "version", 0 },
+#define        HELPSW             5
+    { "help", 4 },
+#define        RESNDSW            6
+    { "resent", -6 },
+#define        QUEUESW            7
+    { "queued", -6 },
+    { NULL, 0 }
+};
+
+static char tmpfil[BUFSIZ];
+
+/*
+ * static prototypes
+ */
+static RETSIGTYPE intrser (int);
+
+
+int
+main (int argc, char **argv)
+{
+    pid_t child_id;
+    int status, i, iscc = 0, nvec;
+    int queued = 0, resent = 0, somebody;
+    char *cp, *tolist = NULL, *cclist = NULL, *subject = NULL;
+    char *from = NULL, *body = NULL, **argp, **arguments;
+    char *vec[5], buf[BUFSIZ];
+    FILE *out;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* foil search of user profile/context */
+    if (context_foil (NULL) == -1)
+       done (1);
+
+    /* If no arguments, just incorporate new mail */
+    if (argc == 1) {
+       execlp (incproc, r1bindex (incproc, '/'), NULL);
+       adios (incproc, "unable to exec");
+    }
+
+    arguments = getarguments (invo_name, argc, argv, 0);
+    argp = arguments;
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown", cp);
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf), "%s [addrs ... [switches]]",
+                       invo_name);
+                   print_help (buf, switches, 0);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case FROMSW: 
+                   if (!(from = *argp++) || *from == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+
+               case BODYSW: 
+                   if (!(body = *argp++) || *body == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+
+               case CCSW: 
+                   iscc++;
+                   continue;
+
+               case SUBJSW: 
+                   if (!(subject = *argp++) || *subject == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+
+               case RESNDSW: 
+                   resent++;
+                   continue;
+
+               case QUEUESW: 
+                   queued++;
+                   continue;
+           }
+       }
+       if (iscc)
+           cclist = cclist ? add (cp, add (", ", cclist)) : getcpy (cp);
+       else
+           tolist = tolist ? add (cp, add (", ", tolist)) : getcpy (cp);
+    }
+
+    if (tolist == NULL)
+       adios (NULL, "usage: %s addrs ... [switches]", invo_name);
+    strncpy (tmpfil, m_tmpfil (invo_name), sizeof(tmpfil));
+    if ((out = fopen (tmpfil, "w")) == NULL)
+       adios (tmpfil, "unable to write");
+    chmod (tmpfil, 0600);
+
+    SIGNAL2 (SIGINT, intrser);
+
+    fprintf (out, "%sTo: %s\n", resent ? "Resent-" : "", tolist);
+    if (cclist)
+       fprintf (out, "%scc: %s\n", resent ? "Resent-" : "", cclist);
+    if (subject)
+       fprintf (out, "%sSubject: %s\n", resent ? "Resent-" : "", subject);
+    if (from)
+       fprintf (out, "%sFrom: %s\n", resent ? "Resent-" : "", from);
+    if (!resent)
+       fputs ("\n", out);
+
+    if (body) {
+       fprintf (out, "%s", body);
+       if (*body && *(body + strlen (body) - 1) != '\n')
+           fputs ("\n", out);
+    } else {
+       for (somebody = 0;
+               (i = fread (buf, sizeof(*buf), sizeof(buf), stdin)) > 0;
+               somebody++)
+           if (fwrite (buf, sizeof(*buf), i, out) != i)
+               adios (tmpfil, "error writing");
+       if (!somebody) {
+           unlink (tmpfil);
+           done (1);
+       }
+    }
+    fclose (out);
+
+    nvec = 0;
+    vec[nvec++] = r1bindex (postproc, '/');
+    vec[nvec++] = tmpfil;
+    if (resent)
+       vec[nvec++] = "-dist";
+    if (queued)
+       vec[nvec++] = "-queued";
+    vec[nvec] = NULL;
+
+    for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
+       sleep (5);
+
+    if (child_id == NOTOK) {
+       /* report failure and then send it */
+       admonish (NULL, "unable to fork");
+    } else if (child_id) {
+       /* parent process */
+       if ((status = pidXwait(child_id, postproc))) {
+           fprintf (stderr, "Letter saved in dead.letter\n");
+           execl ("/bin/mv", "mv", tmpfil, "dead.letter", NULL);
+           execl ("/usr/bin/mv", "mv", tmpfil, "dead.letter", NULL);
+           perror ("mv");
+           _exit (-1);
+       }
+       unlink (tmpfil);
+       done (status ? 1 : 0);
+    } else {
+       /* child process */
+       execvp (postproc, vec);
+       fprintf (stderr, "unable to exec ");
+       perror (postproc);
+       _exit (-1);
+    }
+}
+
+
+static RETSIGTYPE
+intrser (int i)
+{
+#ifndef RELIABLE_SIGNALS
+    if (i)
+       SIGNAL (i, SIG_IGN);
+#endif
+
+    unlink (tmpfil);
+    done (i != 0 ? 1 : 0);
+}
+
diff --git a/uip/mhmisc.c b/uip/mhmisc.c
new file mode 100644 (file)
index 0000000..75ac158
--- /dev/null
@@ -0,0 +1,231 @@
+
+/*
+ * mhparse.c -- misc routines to process MIME messages
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <errno.h>
+#include <h/mime.h>
+#include <h/mhparse.h>
+
+extern int errno;
+extern int debugsw;
+
+/*
+ * limit actions to specified parts or content types
+ */
+int npart = 0;
+int ntype = 0;
+char *parts[NPARTS + 1];
+char *types[NTYPES + 1];
+
+int endian = 0;                /* little or big endian */
+int userrs = 0;
+
+static char *errs = NULL;
+
+
+/*
+ * prototypes
+ */
+int part_ok (CT, int);
+int type_ok (CT, int);
+void set_endian (void);
+int make_intermediates (char *);
+void content_error (char *, CT, char *, ...);
+void flush_errors (void);
+
+
+int
+part_ok (CT ct, int sP)
+{
+    char **ap;
+
+    if (npart == 0 || (ct->c_type == CT_MULTIPART && (sP || ct->c_subtype)))
+       return 1;
+
+    for (ap = parts; *ap; ap++)
+       if (!strcmp (*ap, ct->c_partno))
+           return 1;
+
+    return 0;
+}
+
+
+int
+type_ok (CT ct, int sP)
+{
+    char **ap;
+    char buffer[BUFSIZ];
+    CI ci = &ct->c_ctinfo;
+
+    if (ntype == 0 || (ct->c_type == CT_MULTIPART && (sP || ct->c_subtype)))
+       return 1;
+
+    snprintf (buffer, sizeof(buffer), "%s/%s", ci->ci_type, ci->ci_subtype);
+    for (ap = types; *ap; ap++)
+       if (!strcasecmp (*ap, ci->ci_type) || !strcasecmp (*ap, buffer))
+           return 1;
+
+    return 0;
+}
+
+
+void
+set_endian (void)
+{
+    union {
+       long l;
+       char c[sizeof(long)];
+    } un;
+
+    un.l = 1;
+    endian = un.c[0] ? -1 : 1;
+    if (debugsw)
+       fprintf (stderr, "%s endian architecture\n",
+               endian > 0 ? "big" : "little");
+}
+
+
+int
+make_intermediates (char *file)
+{
+    char *cp;
+
+    for (cp = file + 1; cp = strchr(cp, '/'); cp++) {
+       struct stat st;
+
+       *cp = '\0';
+       if (stat (file, &st) == NOTOK) {
+           int answer;
+           char *ep;
+           if (errno != ENOENT) {
+               advise (file, "error on directory");
+losing_directory:
+               *cp = '/';
+               return NOTOK;
+           }
+
+           ep = concat ("Create directory \"", file, "\"? ", NULL);
+           answer = getanswer (ep);
+           free (ep);
+
+           if (!answer)
+               goto losing_directory;
+           if (!makedir (file)) {
+               advise (NULL, "unable to create directory %s", file);
+               goto losing_directory;
+           }
+       }
+
+       *cp = '/';
+    }
+
+    return OK;
+}
+
+
+/*
+ * Construct error message for content
+ */
+
+void
+content_error (char *what, CT ct, char *fmt, ...)
+{
+    va_list arglist;
+    int        i, len, buflen;
+    char *bp, buffer[BUFSIZ];
+    CI ci;
+
+    bp = buffer;
+    buflen = sizeof(buffer);
+
+    if (userrs && invo_name && *invo_name) {
+       snprintf (bp, buflen, "%s: ", invo_name);
+       len = strlen (bp);
+       bp += len;
+       buflen -= len;
+    }
+
+    va_start (arglist, fmt);
+
+    vsnprintf (bp, buflen, fmt, arglist);
+    len = strlen (bp);
+    bp += len;
+    buflen -= len;
+
+    ci = &ct->c_ctinfo;
+
+    if (what) {
+       char *s;
+
+       if (*what) {
+           snprintf (bp, buflen, " %s: ", what);
+           len = strlen (bp);
+           bp += len;
+           buflen -= len;
+       }
+
+       if ((s = strerror (errno)))
+           snprintf (bp, buflen, "%s", s);
+       else
+           snprintf (bp, buflen, "Error %d", errno);
+
+       len = strlen (bp);
+       bp += len;
+       buflen -= len;
+    }
+
+    i = strlen (invo_name) + 2;
+
+    /* Now add content type and subtype */
+    snprintf (bp, buflen, "\n%*.*s(content %s/%s", i, i, "",
+       ci->ci_type, ci->ci_subtype);
+    len = strlen (bp);
+    bp += len;
+    buflen -= len;
+
+    /* Now add the message/part number */
+    if (ct->c_file) {
+       snprintf (bp, buflen, " in message %s", ct->c_file);
+       len = strlen (bp);
+       bp += len;
+       buflen -= len;
+
+       if (ct->c_partno) {
+           snprintf (bp, buflen, ", part %s", ct->c_partno);
+           len = strlen (bp);
+           bp += len;
+           buflen -= len;
+       }
+    }
+
+    snprintf (bp, buflen, ")");
+    len = strlen (bp);
+    bp += len;
+    buflen -= len;
+
+    if (userrs) {
+       *bp++ = '\n';
+       *bp = '\0';
+       buflen--;
+
+       errs = add (buffer, errs);
+    } else {
+       advise (NULL, "%s", buffer);
+    }
+}
+
+
+void
+flush_errors (void)
+{
+    if (errs) {
+       fflush (stdout);
+       fprintf (stderr, "%s", errs);
+       free (errs);
+       errs = NULL;
+    }
+}
diff --git a/uip/mhn.c b/uip/mhn.c
new file mode 100644 (file)
index 0000000..70f730b
--- /dev/null
+++ b/uip/mhn.c
@@ -0,0 +1,741 @@
+
+/*
+ * mhn.c -- display, list, cache, or store the contents of MIME messages
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+#include <h/signals.h>
+#include <h/md5.h>
+#include <errno.h>
+#include <signal.h>
+#include <zotnet/mts/mts.h>
+#include <zotnet/tws/tws.h>
+#include <h/mime.h>
+#include <h/mhparse.h>
+#include <h/mhcachesbr.h>
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+/*
+ * We allocate space for message names (msgs array)
+ * this number of elements at a time.
+ */
+#define MAXMSGS  256
+
+
+static struct swit switches[] = {
+#define        AUTOSW                  0
+    { "auto", 0 },
+#define        NAUTOSW                 1
+    { "noauto", 0 },
+#define        CACHESW                 2
+    { "cache", 0 },
+#define        NCACHESW                3
+    { "nocache", 0 },
+#define        CHECKSW                 4
+    { "check", 0 },
+#define        NCHECKSW                5
+    { "nocheck", 0 },
+#define        HEADSW                  6
+    { "headers", 0 },
+#define        NHEADSW                 7
+    { "noheaders", 0 },
+#define        LISTSW                  8
+    { "list", 0 },
+#define        NLISTSW                 9
+    { "nolist", 0 },
+#define        PAUSESW                10
+    { "pause", 0 },
+#define        NPAUSESW               11
+    { "nopause", 0 },
+#define        SIZESW                 12
+    { "realsize", 0 },
+#define        NSIZESW                13
+    { "norealsize", 0 },
+#define        SERIALSW               14
+    { "serialonly", 0 },
+#define        NSERIALSW              15
+    { "noserialonly", 0 },
+#define        SHOWSW                 16
+    { "show", 0 },
+#define        NSHOWSW                17
+    { "noshow", 0 },
+#define        STORESW                18
+    { "store", 0 },
+#define        NSTORESW               19
+    { "nostore", 0 },
+#define        VERBSW                 20
+    { "verbose", 0 },
+#define        NVERBSW                21
+    { "noverbose", 0 },
+#define        FILESW                 22       /* interface from show */
+    { "file file", 0 },
+#define        FORMSW                 23
+    { "form formfile", 0 },
+#define        PARTSW                 24
+    { "part number", 0 },
+#define        TYPESW                 25
+    { "type content", 0 },
+#define        RCACHESW               26
+    { "rcache policy", 0 },
+#define        WCACHESW               27
+    { "wcache policy", 0 },
+#define VERSIONSW              28
+    { "version", 0 },
+#define        HELPSW                 29
+    { "help", 4 },
+
+/*
+ * switches for debugging
+ */
+#define        DEBUGSW                30
+    { "debug", -5 },
+
+/*
+ * switches for moreproc/mhlproc
+ */
+#define        PROGSW                 31
+    { "moreproc program", -4 },
+#define        NPROGSW                32
+    { "nomoreproc", -3 },
+#define        LENSW                  33
+    { "length lines", -4 },
+#define        WIDTHSW                34
+    { "width columns", -4 },
+
+/*
+ * switches for mhbuild
+ */
+#define BUILDSW                35
+    { "build", -5 },
+#define NBUILDSW               36
+    { "nobuild", -7 },
+#define        EBCDICSW               37
+    { "ebcdicsafe", -10 },
+#define        NEBCDICSW              38
+    { "noebcdicsafe", -12 },
+#define        RFC934SW               39
+    { "rfc934mode", -10 },
+#define        NRFC934SW              40
+    { "norfc934mode", -12 },
+    { NULL, 0 }
+};
+
+
+extern int errno;
+
+/* mhparse.c */
+extern int checksw;
+extern char *tmp;      /* directory to place temp files */
+
+/* mhcachesbr.c */
+extern int rcachesw;
+extern int wcachesw;
+extern char *cache_public;
+extern char *cache_private;
+
+/* mhshowsbr.c */
+extern int pausesw;
+extern int serialsw;
+extern char *progsw;
+extern int nolist;
+extern int nomore;     /* flags for moreproc/header display */
+extern char *formsw;
+
+/* mhstoresbr.c */
+extern int autosw;
+extern char *cwd;      /* cache current working directory */
+
+/* mhmisc.c */
+extern int npart;
+extern int ntype;
+extern char *parts[NPARTS + 1];
+extern char *types[NTYPES + 1];
+extern int userrs;
+
+int debugsw = 0;
+int verbosw = 0;
+
+/* The list of top-level contents to display */
+CT *cts = NULL;
+
+/*
+ * variables for mhbuild (mhn -build)
+ */
+static int buildsw  = 0;
+static int ebcdicsw = 0;
+static int rfc934sw = 0;
+
+/*
+ * what action to take?
+ */
+static int cachesw = 0;
+static int listsw  = 0;
+static int showsw  = 0;
+static int storesw = 0;
+
+#define        quitser pipeser
+
+/* mhparse.c */
+CT parse_mime (char *);
+
+/* mhmisc.c */
+int part_ok (CT, int);
+int type_ok (CT, int);
+void set_endian (void);
+void flush_errors (void);
+
+/* mhshowsbr.c */
+void show_all_messages (CT *);
+
+/* mhlistsbr.c */
+void list_all_messages (CT *, int, int, int, int);
+
+/* mhstoresbr.c */
+void store_all_messages (CT *);
+
+/* mhcachesbr.c */
+void cache_all_messages (CT *);
+
+/* mhfree.c */
+void free_content (CT);
+
+/*
+ * static prototypes
+ */
+static RETSIGTYPE pipeser (int);
+
+
+int
+main (int argc, char **argv)
+{
+    int sizesw = 1, headsw = 1;
+    int nummsgs, maxmsgs, msgnum, *icachesw;
+    char *cp, *file = NULL, *folder = NULL;
+    char *maildir, buf[100], **argp;
+    char **arguments, **msgs;
+    struct msgs *mp = NULL;
+    CT ct, *ctp;
+    FILE *fp;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    /*
+     * Allocate the initial space to record message
+     * names, ranges, and sequences.
+     */
+    nummsgs = 0;
+    maxmsgs = MAXMSGS;
+    if (!(msgs = (char **) malloc ((size_t) (maxmsgs * sizeof(*msgs)))))
+       adios (NULL, "unable to allocate storage");
+
+    /*
+     * Parse arguments
+     */
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+           case AMBIGSW: 
+               ambigsw (cp, switches);
+               done (1);
+           case UNKWNSW: 
+               adios (NULL, "-%s unknown", cp);
+
+           case HELPSW: 
+               snprintf (buf, sizeof(buf), "%s [+folder] [msgs] [switches]",
+                       invo_name);
+               print_help (buf, switches, 1);
+               done (1);
+           case VERSIONSW:
+               print_version(invo_name);
+               done (1);
+
+           case AUTOSW:
+               autosw++;
+               continue;
+           case NAUTOSW:
+               autosw = 0;
+               continue;
+
+           case CACHESW:
+               cachesw++;
+               continue;
+           case NCACHESW:
+               cachesw = 0;
+               continue;
+
+           case RCACHESW:
+               icachesw = &rcachesw;
+               goto do_cache;
+           case WCACHESW:
+               icachesw = &wcachesw;
+do_cache:
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               switch (*icachesw = smatch (cp, caches)) {
+               case AMBIGSW:
+                   ambigsw (cp, caches);
+                   done (1);
+               case UNKWNSW:
+                   adios (NULL, "%s unknown", cp);
+               default:
+                   break;
+               }
+               continue;
+
+           case CHECKSW:
+               checksw++;
+               continue;
+           case NCHECKSW:
+               checksw = 0;
+               continue;
+
+           case HEADSW:
+               headsw = 1;
+               continue;
+           case NHEADSW:
+               headsw = 0;
+               continue;
+
+           case LISTSW:
+               listsw = 1;
+               continue;
+           case NLISTSW:
+               listsw = 0;
+               continue;
+
+           case PAUSESW:
+               pausesw = 1;
+               continue;
+           case NPAUSESW:
+               pausesw = 0;
+               continue;
+
+           case SERIALSW:
+               serialsw = 1;
+               continue;
+           case NSERIALSW:
+               serialsw = 0;
+               continue;
+
+           case SHOWSW:
+               showsw = 1;
+               continue;
+           case NSHOWSW:
+               showsw = 0;
+               continue;
+
+           case SIZESW:
+               sizesw = 1;
+               continue;
+           case NSIZESW:
+               sizesw = 0;
+               continue;
+
+           case STORESW:
+               storesw = 1;
+               continue;
+           case NSTORESW:
+               storesw = 0;
+               continue;
+
+           case PARTSW:
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               if (npart >= NPARTS)
+                   adios (NULL, "too many parts (starting with %s), %d max",
+                          cp, NPARTS);
+               parts[npart++] = cp;
+               continue;
+
+           case TYPESW:
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               if (ntype >= NTYPES)
+                   adios (NULL, "too many types (starting with %s), %d max",
+                          cp, NTYPES);
+               types[ntype++] = cp;
+               continue;
+
+           case FILESW:
+               if (!(cp = *argp++) || (*cp == '-' && cp[1]))
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               file = *cp == '-' ? cp : path (cp, TFILE);
+               continue;
+
+           case FORMSW:
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               if (formsw)
+                   free (formsw);
+               formsw = getcpy (etcpath (cp));
+               continue;
+
+           /*
+            * Switches for moreproc/mhlproc
+            */
+           case PROGSW:
+               if (!(progsw = *argp++) || *progsw == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               continue;
+           case NPROGSW:
+               nomore++;
+               continue;
+
+           case LENSW:
+           case WIDTHSW:
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               continue;
+
+           /*
+            * Switches for mhbuild
+            */
+           case BUILDSW:
+               buildsw = 1;
+               continue;
+           case NBUILDSW:
+               buildsw = 0;
+               continue;
+           case RFC934SW:
+               rfc934sw = 1;
+               continue;
+           case NRFC934SW:
+               rfc934sw = -1;
+               continue;
+           case EBCDICSW:
+               ebcdicsw = 1;
+               continue;
+           case NEBCDICSW:
+               ebcdicsw = -1;
+               continue;
+
+           case VERBSW: 
+               verbosw = 1;
+               continue;
+           case NVERBSW: 
+               verbosw = 0;
+               continue;
+           case DEBUGSW:
+               debugsw = 1;
+               continue;
+           }
+       }
+       if (*cp == '+' || *cp == '@') {
+           if (folder)
+               adios (NULL, "only one folder at a time!");
+           else
+               folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       } else {
+           /*
+            * Check if we need to allocate more space
+            * for message names/ranges/sequences.
+            */
+           if (nummsgs >= maxmsgs) {
+               maxmsgs += MAXMSGS;
+               if (!(msgs = (char **) realloc (msgs,
+                       (size_t) (maxmsgs * sizeof(*msgs)))))
+                   adios (NULL, "unable to reallocate msgs storage");
+           }
+           msgs[nummsgs++] = cp;
+       }
+    }
+
+    /* null terminate the list of acceptable parts/types */
+    parts[npart] = NULL;
+    types[ntype] = NULL;
+
+    set_endian ();
+
+    if ((cp = getenv ("MM_NOASK")) && !strcmp (cp, "1")) {
+       nolist  = 1;
+       listsw  = 0;
+       pausesw = 0;
+    }
+
+    /*
+     * Check if we've specified an additional profile
+     */
+    if ((cp = getenv ("MHN"))) {
+       if ((fp = fopen (cp, "r"))) {
+           readconfig ((struct node **) 0, fp, cp, 0);
+           fclose (fp);
+       } else {
+           admonish ("", "unable to read $MHN profile (%s)", cp);
+       }
+    }
+
+    /*
+     * Read the standard profile setup
+     */
+    if ((fp = fopen (cp = etcpath ("mhn.defaults"), "r"))) {
+       readconfig ((struct node **) 0, fp, cp, 0);
+       fclose (fp);
+    }
+
+    /* Check for public cache location */
+    if ((cache_public = context_find (nmhcache)) && *cache_public != '/')
+       cache_public = NULL;
+
+    /* Check for private cache location */
+    if (!(cache_private = context_find (nmhprivcache)))
+       cache_private = ".cache";
+    cache_private = getcpy (m_maildir (cache_private));
+
+    /*
+     * Cache the current directory before we do any chdirs()'s.
+     */
+    cwd = getcpy (pwd());
+
+    /*
+     * Check for storage directory.  If specified,
+     * then store temporary files there.  Else we
+     * store them in standard nmh directory.
+     */
+    if ((cp = context_find (nmhstorage)) && *cp)
+       tmp = concat (cp, "/", invo_name, NULL);
+    else
+       tmp = add (m_maildir (invo_name), NULL);
+
+    if (!context_find ("path"))
+       free (path ("./", TFOLDER));
+
+    /*
+     * Process a mhn composition file (mhn -build)
+     */
+    if (buildsw) {
+       char *vec[MAXARGS];
+       int vecp;
+
+       if (showsw || storesw || cachesw)
+           adios (NULL, "cannot use -build with -show, -store, -cache");
+       if (nummsgs < 1)
+           adios (NULL, "need to specify a %s composition file", invo_name);
+       if (nummsgs > 1)
+           adios (NULL, "only one %s composition file at a time", invo_name);
+
+       vecp = 0;
+       vec[vecp++] = "mhbuild";
+
+       if (ebcdicsw == 1)
+           vec[vecp++] = "-ebcdicsafe";
+       else if (ebcdicsw == -1)
+           vec[vecp++] = "-noebcdicsafe";
+
+       if (rfc934sw == 1)
+           vec[vecp++] = "-rfc934mode";
+       else if (rfc934sw == -1)
+           vec[vecp++] = "-norfc934mode";
+
+       vec[vecp++] = msgs[0];
+       vec[vecp] = NULL;
+
+       execvp ("mhbuild", vec);
+       fprintf (stderr, "unable to exec ");
+       _exit (-1);
+    }
+
+    /*
+     * Process a mhn composition file (old MH style)
+     */
+    if (nummsgs == 1 && !folder && !npart && !cachesw
+       && !showsw && !storesw && !ntype && !file
+       && (cp = getenv ("mhdraft"))
+       && strcmp (cp, msgs[0]) == 0) {
+
+       char *vec[MAXARGS];
+       int vecp;
+
+       vecp = 0;
+       vec[vecp++] = "mhbuild";
+
+       if (ebcdicsw == 1)
+           vec[vecp++] = "-ebcdicsafe";
+       else if (ebcdicsw == -1)
+           vec[vecp++] = "-noebcdicsafe";
+
+       if (rfc934sw == 1)
+           vec[vecp++] = "-rfc934mode";
+       else if (rfc934sw == -1)
+           vec[vecp++] = "-norfc934mode";
+
+       vec[vecp++] = cp;
+       vec[vecp] = NULL;
+
+       execvp ("mhbuild", vec);
+       fprintf (stderr, "unable to exec ");
+       _exit (-1);
+    }
+
+    if (file && nummsgs)
+       adios (NULL, "cannot specify msg and file at same time!");
+
+    /*
+     * check if message is coming from file
+     */
+    if (file) {
+       if (!(cts = (CT *) calloc ((size_t) 2, sizeof(*cts))))
+           adios (NULL, "out of memory");
+       ctp = cts;
+
+       if ((ct = parse_mime (file)));
+           *ctp++ = ct;
+    } else {
+       /*
+        * message(s) are coming from a folder
+        */
+       if (!nummsgs)
+           msgs[nummsgs++] = "cur";
+       if (!folder)
+           folder = getfolder (1);
+       maildir = m_maildir (folder);
+
+       if (chdir (maildir) == NOTOK)
+           adios (maildir, "unable to change directory to");
+
+       /* read folder and create message structure */
+       if (!(mp = folder_read (folder)))
+           adios (NULL, "unable to read folder %s", folder);
+
+       /* check for empty folder */
+       if (mp->nummsg == 0)
+           adios (NULL, "no messages in %s", folder);
+
+       /* parse all the message ranges/sequences and set SELECTED */
+       for (msgnum = 0; msgnum < nummsgs; msgnum++)
+           if (!m_convert (mp, msgs[msgnum]))
+               done (1);
+       seq_setprev (mp);       /* set the previous-sequence */
+
+       if (!(cts = (CT *) calloc ((size_t) (mp->numsel + 1), sizeof(*cts))))
+           adios (NULL, "out of memory");
+       ctp = cts;
+
+       for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
+           if (is_selected(mp, msgnum)) {
+               char *msgnam;
+
+               msgnam = m_name (msgnum);
+               if ((ct = parse_mime (msgnam)))
+                   *ctp++ = ct;
+           }
+       }
+    }
+
+    if (!*cts)
+       done (1);
+
+    /*
+     * You can't give more than one of these flags
+     * at a time.
+     */
+    if (showsw + listsw + storesw + cachesw > 1)
+       adios (NULL, "can only use one of -show, -list, -store, -cache at same time");
+
+    /* If no action is specified, assume -show */
+    if (!listsw && !showsw && !storesw && !cachesw)
+       showsw = 1;
+
+    userrs = 1;
+    SIGNAL (SIGQUIT, quitser);
+    SIGNAL (SIGPIPE, pipeser);
+
+    /*
+     * Get the associated umask for the relevant contents.
+     */
+    for (ctp = cts; *ctp; ctp++) {
+       struct stat st;
+
+       ct = *ctp;
+       if (type_ok (ct, 1) && !ct->c_umask) {
+           if (stat (ct->c_file, &st) != NOTOK)
+               ct->c_umask = ~(st.st_mode & 0777);
+           else
+               ct->c_umask = ~m_gmprot();
+       }
+    }
+
+    /*
+     * List the message content
+     */
+    if (listsw)
+       list_all_messages (cts, headsw, sizesw, verbosw, debugsw);
+
+    /*
+     * Store the message content
+     */
+    if (storesw)
+       store_all_messages (cts);
+
+    /*
+     * Cache the message content
+     */
+    if (cachesw)
+       cache_all_messages (cts);
+
+    /*
+     * Show the message content
+     */
+    if (showsw)
+       show_all_messages (cts);
+
+    /* Now free all the structures for the content */
+    for (ctp = cts; *ctp; ctp++)
+       free_content (*ctp);
+
+    free ((char *) cts);
+    cts = NULL;
+
+    /* If reading from a folder, do some updating */
+    if (mp) {
+       context_replace (pfolder, folder);/* update current folder  */
+       seq_setcur (mp, mp->hghsel);      /* update current message */
+       seq_save (mp);                    /* synchronize sequences  */
+       context_save ();                  /* save the context file  */
+    }
+
+    done (0);
+    /* NOTREACHED */
+}
+
+
+static RETSIGTYPE
+pipeser (int i)
+{
+    if (i == SIGQUIT) {
+       unlink ("core");
+       fflush (stdout);
+       fprintf (stderr, "\n");
+       fflush (stderr);
+    }
+
+    done (1);
+    /* NOTREACHED */
+}
+
+
+void
+done (int status)
+{
+    CT *ctp;
+
+    if ((ctp = cts))
+       for (; *ctp; ctp++)
+           free_content (*ctp);
+
+    exit (status);
+}
diff --git a/uip/mhoutsbr.c b/uip/mhoutsbr.c
new file mode 100644 (file)
index 0000000..6d2914a
--- /dev/null
@@ -0,0 +1,471 @@
+
+/*
+ * mhoutsbr.c -- routines to output MIME messages
+ *            -- given a Content structure
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+#include <h/signals.h>
+#include <h/md5.h>
+#include <errno.h>
+#include <signal.h>
+#include <zotnet/mts/mts.h>
+#include <zotnet/tws/tws.h>
+#include <h/mime.h>
+#include <h/mhparse.h>
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+
+extern int errno;
+extern int ebcdicsw;
+
+static char ebcdicsafe[0x100] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
+    0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static char nib2b64[0x40+1] =
+       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/*
+ * prototypes
+ */
+int output_message (CT, char *);
+int writeBase64aux (FILE *, FILE *);
+
+/*
+ * static prototypes
+ */
+static int output_content (CT, FILE *);
+static int output_headers (CT, FILE *);
+static int writeExternalBody (CT, FILE *);
+static int write8Bit (CT, FILE *);
+static int writeQuoted (CT, FILE *);
+static int writeBase64 (CT, FILE *);
+
+
+/*
+ * Main routine to output a MIME message contained
+ * in a Content structure, to a file.  Any necessary
+ * transfer encoding is added.
+ */
+
+int
+output_message (CT ct, char *file)
+{
+    FILE *fp;
+
+    if ((fp = fopen (file, "w")) == NULL) {
+       advise (file, "unable to open for writing");
+       return NOTOK;
+    }
+
+    if (output_content (ct, fp) == NOTOK)
+       return NOTOK;
+
+    if (fflush (fp)) {
+       advise (file, "error writing to");
+       return NOTOK;
+    }
+    fclose (fp);
+
+    return OK;
+}
+
+
+/*
+ * Output a Content structure to a file.
+ */
+
+static int
+output_content (CT ct, FILE *out)
+{
+    int result = 0;
+    CI ci = &ct->c_ctinfo;
+
+    /*
+     * Output all header fields for this content
+     */
+    output_headers (ct, out);
+
+    /*
+     * If this is the internal content structure for a
+     * "message/external", then we are done with the
+     * headers (since it has no body).
+     */
+    if (ct->c_ctexbody)
+       return OK;
+
+    /*
+     * Now output the content bodies.
+     */
+    switch (ct->c_type) {
+    case CT_MULTIPART:
+    {
+       struct multipart *m;
+       struct part *part;
+
+       if (ct->c_rfc934)
+           putc ('\n', out);
+
+       m = (struct multipart *) ct->c_ctparams;
+       for (part = m->mp_parts; part; part = part->mp_next) {
+           CT p = part->mp_part;
+
+           fprintf (out, "\n--%s\n", ci->ci_values[0]);
+           if (output_content (p, out) == NOTOK)
+               return NOTOK;
+       }
+       fprintf (out, "\n--%s--\n", ci->ci_values[0]);
+    }
+    break;
+
+    case CT_MESSAGE:
+       putc ('\n', out);
+       if (ct->c_subtype == MESSAGE_EXTERNAL) {
+           struct exbody *e;
+
+           e = (struct exbody *) ct->c_ctparams;
+           if (output_content (e->eb_content, out) == NOTOK)
+               return NOTOK;
+
+           /* output phantom body for access-type "mail-server" */
+           if (e->eb_body)
+               writeExternalBody (ct, out);
+       } else {
+           result = write8Bit (ct, out);
+       }
+       break;
+
+    /*
+     * Handle discrete types (text/application/audio/image/video)
+     */
+    default:
+       switch (ct->c_encoding) {
+       case CE_7BIT:
+           putc ('\n', out);
+           result = write8Bit (ct, out);
+           break;
+
+       case CE_8BIT:
+           putc ('\n', out);
+           result = write8Bit (ct, out);
+           break;
+
+       case CE_QUOTED:
+           putc ('\n', out);
+           result = writeQuoted (ct, out);
+           break;
+
+       case CE_BASE64:
+           putc ('\n', out);
+           result = writeBase64 (ct, out);
+           break;
+
+       case CE_BINARY:
+           advise (NULL, "can't handle binary transfer encoding in content");
+           result = NOTOK;
+           break;
+
+       default:
+           advise (NULL, "unknown transfer encoding in content");
+           result = NOTOK;
+           break;
+       }
+       break;
+    }
+
+    return result;
+}
+
+
+/*
+ * Output all the header fields for a content
+ */
+
+static int
+output_headers (CT ct, FILE *out)
+{
+    HF hp;
+
+    hp = ct->c_first_hf;
+    while (hp) {
+       fprintf (out, "%s:%s", hp->name, hp->value);
+       hp = hp->next;
+    }
+}
+
+
+/*
+ * Write the phantom body for access-type "mail-server".
+ */
+
+static int
+writeExternalBody (CT ct, FILE *out)
+{
+    char **ap, **ep, *cp;
+    struct exbody *e = (struct exbody *) ct->c_ctparams;
+               
+    putc ('\n', out);
+    for (cp = e->eb_body; *cp; cp++) {
+       CT ct2 = e->eb_content;
+       CI ci2 = &ct2->c_ctinfo;
+
+       if (*cp == '\\') {
+           switch (*++cp) {
+           case 'I':
+               if (ct2->c_id) {
+                   char *dp = trimcpy (ct2->c_id);
+
+                   fputs (dp, out);
+                   free (dp);
+               }
+               continue;
+
+           case 'N':
+               for (ap = ci2->ci_attrs, ep = ci2->ci_values; *ap; ap++, ep++)
+                   if (!strcasecmp (*ap, "name")) {
+                       fprintf (out, "%s", *ep);
+                       break;
+                   }
+               continue;
+
+           case 'T':
+               fprintf (out, "%s/%s", ci2->ci_type, ci2->ci_subtype);
+               for (ap = ci2->ci_attrs, ep = ci2->ci_values; *ap; ap++, ep++)
+                   fprintf (out, "; %s=\"%s\"", *ap, *ep);
+               continue;
+
+           case 'n':
+               putc ('\n', out);
+               continue;
+
+           case 't':
+               putc ('\t', out);
+               continue;
+
+           case '\0':
+               cp--;
+               break;
+
+           case '\\':
+           case '"':
+               break;
+
+           default:
+               putc ('\\', out);
+               break;
+           }
+       }
+       putc (*cp, out);
+    }
+    putc ('\n', out);
+
+    return OK;
+}
+
+
+/*
+ * Output a content without any transfer encoding
+ */
+
+static int
+write8Bit (CT ct, FILE *out)
+{
+    int fd;
+    char c, *file, buffer[BUFSIZ];
+    CE ce = ct->c_cefile;
+
+    file = NULL;
+    if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
+       return NOTOK;
+
+    c = '\n';
+    while (fgets (buffer, sizeof(buffer) - 1, ce->ce_fp)) {
+       c = buffer[strlen (buffer) - 1];
+       fputs (buffer, out);
+    }
+    if (c != '\n')
+       putc ('\n', out);
+
+    (*ct->c_ceclosefnx) (ct);
+    return OK;
+}
+
+
+/*
+ * Output a content using quoted-printable
+ */
+
+static int
+writeQuoted (CT ct, FILE *out)
+{
+    int fd;
+    char *cp, *file;
+    char c, buffer[BUFSIZ];
+    CE ce = ct->c_cefile;
+
+    file = NULL;
+    if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
+       return NOTOK;
+
+    while (fgets (buffer, sizeof(buffer) - 1, ce->ce_fp)) {
+       int n;
+
+       cp = buffer + strlen (buffer) - 1;
+       if ((c = *cp) == '\n')
+           *cp = '\0';
+
+       if (strncmp (cp = buffer, "From ", sizeof("From ") - 1) == 0) {
+           fprintf (out, "=%02X", *cp++ & 0xff);
+           n = 3;
+       } else {
+           n = 0;
+       }
+       for (; *cp; cp++) {
+           if (n > CPERLIN - 3) {
+               fputs ("=\n", out);
+               n = 0;
+           }
+
+           switch (*cp) {
+               case ' ':
+               case '\t':
+                   putc (*cp, out);
+                   n++;
+                   break;
+
+               default:
+                   if (*cp < '!' || *cp > '~'
+                           || (ebcdicsw && !ebcdicsafe[*cp & 0xff]))
+                       goto three_print;
+                   putc (*cp, out);
+                   n++;
+                   break;
+
+               case '=':
+three_print:
+                   fprintf (out, "=%02X", *cp & 0xff);
+                   n += 3;
+                   break;
+           }
+       }
+
+       if (c == '\n') {
+           if (cp > buffer && (*--cp == ' ' || *cp == '\t'))
+               fputs ("=\n", out);
+
+           putc ('\n', out);
+       } else {
+           fputs ("=\n", out);
+       }
+    }
+
+    (*ct->c_ceclosefnx) (ct);
+    return OK;
+}
+
+
+/*
+ * Output a content using base64
+ */
+
+static int
+writeBase64 (CT ct, FILE *out)
+{
+    int        fd, result;
+    char *file;
+    CE ce = ct->c_cefile;
+
+    file = NULL;
+    if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
+       return NOTOK;
+
+    result = writeBase64aux (ce->ce_fp, out);
+    (*ct->c_ceclosefnx) (ct);
+    return result;
+}
+
+
+int
+writeBase64aux (FILE *in, FILE *out)
+{
+    int        cc, n;
+    char inbuf[3];
+
+    n = BPERLIN;
+    while ((cc = fread (inbuf, sizeof(*inbuf), sizeof(inbuf), in)) > 0) {
+       unsigned long bits;
+       char *bp;
+       char outbuf[4];
+
+       if (cc < sizeof(inbuf)) {
+           inbuf[2] = 0;
+           if (cc < sizeof(inbuf) - 1)
+               inbuf[1] = 0;
+       }
+       bits = (inbuf[0] & 0xff) << 16;
+       bits |= (inbuf[1] & 0xff) << 8;
+       bits |= inbuf[2] & 0xff;
+
+       for (bp = outbuf + sizeof(outbuf); bp > outbuf; bits >>= 6)
+           *--bp = nib2b64[bits & 0x3f];
+       if (cc < sizeof(inbuf)) {
+           outbuf[3] = '=';
+           if (cc < sizeof inbuf - 1)
+               outbuf[2] = '=';
+       }
+
+       fwrite (outbuf, sizeof(*outbuf), sizeof(outbuf), out);
+
+       if (cc < sizeof(inbuf)) {
+           putc ('\n', out);
+           return OK;
+       }
+
+       if (--n <= 0) {
+           n = BPERLIN;
+           putc ('\n', out);
+       }
+    }
+    if (n != BPERLIN)
+       putc ('\n', out);
+
+    return OK;
+}
diff --git a/uip/mhparam.c b/uip/mhparam.c
new file mode 100644 (file)
index 0000000..1da25d3
--- /dev/null
@@ -0,0 +1,194 @@
+
+/*
+ * mhparam.c -- print mh_profile values
+ *
+ * Originally contributed by
+ * Jeffrey C Honig <Jeffrey_C_Honig@cornell.edu>
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+extern char *mhlibdir;
+extern char *mhetcdir;
+
+char *sbackup = BACKUP_PREFIX;
+char *slink = LINK;
+
+static struct swit switches[] = {
+#define        COMPSW    0
+    { "components", 0 },
+#define        NCOMPSW   1
+    { "nocomponents", 0 },
+#define        ALLSW     2
+    { "all", 0 },
+#define VERSIONSW 3
+    { "version", 0 },
+#define        HELPSW    4
+    { "help", 4 },
+#define DEBUGSW   5
+    { "debug", -5 },
+    { NULL, 0 }
+};
+
+struct proc {
+    char *p_name;
+    char **p_field;
+};
+
+static struct proc procs [] = {
+     { "context",       &context },
+     { "mh-sequences",  &mh_seq },
+     { "buildmimeproc", &buildmimeproc },
+     { "faceproc",      &faceproc },
+     { "fileproc",      &fileproc },
+     { "foldprot",      &foldprot },
+     { "incproc",       &incproc },
+     { "installproc",   &installproc  },
+     { "lproc",         &lproc },
+     { "mailproc",      &mailproc },
+     { "mhlproc",       &mhlproc },
+     { "moreproc",      &moreproc },
+     { "msgprot",       &msgprot },
+     { "mshproc",       &mshproc },
+     { "packproc",      &packproc },
+     { "postproc",      &postproc },
+     { "rmfproc",       &rmfproc },
+     { "rmmproc",       &rmmproc },
+     { "sendproc",      &sendproc },
+     { "showmimeproc",  &showmimeproc },
+     { "showproc",      &showproc },
+     { "version",       &version_num },
+     { "vmhproc",       &vmhproc },
+     { "whatnowproc",   &whatnowproc },
+     { "whomproc",      &whomproc },
+     { "etcdir",        &mhetcdir },
+     { "libdir",        &mhlibdir },
+     { "sbackup",       &sbackup },
+     { "link",          &slink },
+     { NULL,            NULL },
+};
+
+
+/*
+ * static prototypes
+ */
+static char *p_find(char *);
+
+
+int
+main(int argc, char **argv)
+{
+    int i, compp = 0, missed = 0;
+    int all = 0, debug = 0;
+    int components = -1;
+    char *cp, buf[BUFSIZ], **argp;
+    char **arguments, *comps[MAXARGS];
+
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown", cp);
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf), "%s [profile-components] [switches]",
+                       invo_name);
+                   print_help (buf, switches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case COMPSW:
+                   components = 1;
+                   break;
+               case NCOMPSW:
+                   components = 0;
+                   break;
+
+               case ALLSW:
+                   all = 1;
+                   break;
+
+               case DEBUGSW:
+                   debug = 1;
+                   break;
+           }
+       } else {
+           comps[compp++] = cp;
+       }
+    }
+
+    if (all) {
+        struct node *np;
+
+       if (compp)
+           advise(NULL, "profile-components ignored with -all");
+
+       if (components >= 0)
+           advise(NULL, "-%scomponents ignored with -all",
+                  components ? "" : "no");
+
+       /* print all entries in context/profile list */
+       for (np = m_defs; np; np = np->n_next)
+           printf("%s: %s\n", np->n_name, np->n_field);
+
+    } if (debug) {
+       struct proc *ps;
+
+       /*
+        * Print the current value of everything in
+        * procs array.  This will show their current
+        * value (as determined after context is read).
+         */
+       for (ps = procs; ps->p_name; ps++)
+           printf ("%s: %s\n", ps->p_name, *ps->p_field ? *ps->p_field : "");
+
+    } else {
+        if (components < 0)
+           components = compp > 1;
+
+       for (i = 0; i < compp; i++)  {
+           register char *value;
+
+           value = context_find (comps[i]);
+           if (!value)
+               value = p_find (comps[i]);
+           if (value) {
+               if (components)
+                   printf("%s: ", comps[i]);
+
+               printf("%s\n", value);
+           } else
+               missed++;
+       }
+    }
+    
+    done (missed);
+}
+
+
+static char *
+p_find(char *str)
+{
+    struct proc *ps;
+
+    for (ps = procs; ps->p_name; ps++)
+       if (!strcasecmp (ps->p_name, str))
+           return (*ps->p_field);
+
+    return NULL;
+}
diff --git a/uip/mhparse.c b/uip/mhparse.c
new file mode 100644 (file)
index 0000000..1839a15
--- /dev/null
@@ -0,0 +1,2640 @@
+
+/*
+ * mhparse.c -- routines to parse the contents of MIME messages
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+#include <h/signals.h>
+#include <h/md5.h>
+#include <errno.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <zotnet/mts/mts.h>
+#include <zotnet/tws/tws.h>
+#include <h/mime.h>
+#include <h/mhparse.h>
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+
+extern int errno;
+extern int debugsw;
+
+extern int endian;     /* mhmisc.c     */
+
+extern pid_t xpid;     /* mhshowsbr.c  */
+
+/* cache policies */
+extern int rcachesw;   /* mhcachesbr.c */
+extern int wcachesw;   /* mhcachesbr.c */
+
+int checksw = 0;       /* check Content-MD5 field */
+
+/*
+ * Directory to place temp files.  This must
+ * be set before these routines are called.
+ */
+char *tmp;
+
+/*
+ * Structure for mapping types to their internal flags
+ */
+struct k2v {
+    char *kv_key;
+    int          kv_value;
+};
+
+/*
+ * Structures for TEXT messages
+ */
+static struct k2v SubText[] = {
+    { "plain",    TEXT_PLAIN },
+    { "richtext", TEXT_RICHTEXT },  /* defined in RFC-1341    */
+    { "enriched", TEXT_ENRICHED },  /* defined in RFC-1896    */
+    { NULL,       TEXT_UNKNOWN }    /* this one must be last! */
+};
+
+static struct k2v Charset[] = {
+    { "us-ascii",   CHARSET_USASCII },
+    { "iso-8859-1", CHARSET_LATIN },
+    { NULL,         CHARSET_UNKNOWN }  /* this one must be last! */
+};
+
+/*
+ * Structures for MULTIPART messages
+ */
+static struct k2v SubMultiPart[] = {
+    { "mixed",       MULTI_MIXED },
+    { "alternative", MULTI_ALTERNATE },
+    { "digest",      MULTI_DIGEST },
+    { "parallel",    MULTI_PARALLEL },
+    { NULL,          MULTI_UNKNOWN }    /* this one must be last! */
+};
+
+/*
+ * Structures for MESSAGE messages
+ */
+static struct k2v SubMessage[] = {
+    { "rfc822",        MESSAGE_RFC822 },
+    { "partial",       MESSAGE_PARTIAL },
+    { "external-body", MESSAGE_EXTERNAL },
+    { NULL,            MESSAGE_UNKNOWN }       /* this one must be last! */
+};
+
+/*
+ * Structure for APPLICATION messages
+ */
+static struct k2v SubApplication[] = {
+    { "octet-stream", APPLICATION_OCTETS },
+    { "postscript",   APPLICATION_POSTSCRIPT },
+    { NULL,           APPLICATION_UNKNOWN }    /* this one must be last! */
+};
+
+
+/* ftpsbr.c */
+int ftp_get (char *, char *, char *, char *, char *, char *, int, int);
+
+/* mhcachesbr.c */
+int find_cache (CT, int, int *, char *, char *, int);
+
+/* mhmisc.c */
+int part_ok (CT, int);
+int type_ok (CT, int);
+int make_intermediates (char *);
+void content_error (char *, CT, char *, ...);
+
+/* mhfree.c */
+void free_content (CT);
+void free_encoding (CT, int);
+
+/*
+ * prototypes
+ */
+int pidcheck (int);
+CT parse_mime (char *);
+
+/*
+ * static prototypes
+ */
+static CT get_content (FILE *, char *, int);
+static int add_header (CT, char *, char *);
+static int get_ctinfo (char *, CT);
+static int get_comment (CT, char **, int);
+static int InitGeneric (CT);
+static int InitText (CT);
+static int InitMultiPart (CT);
+static void reverse_parts (CT);
+static int InitMessage (CT);
+static int params_external (CT, int);
+static int InitApplication (CT);
+static int init_encoding (CT, OpenCEFunc);
+static void close_encoding (CT);
+static unsigned long size_encoding (CT);
+static int InitBase64 (CT);
+static int openBase64 (CT, char **);
+static int InitQuoted (CT);
+static int openQuoted (CT, char **);
+static int Init7Bit (CT);
+static int open7Bit (CT, char **);
+static int openExternal (CT, CT, CE, char **, int *);
+static int InitFile (CT);
+static int openFile (CT, char **);
+static int InitFTP (CT);
+static int openFTP (CT, char **);
+static int InitMail (CT);
+static int openMail (CT, char **);
+static int readDigest (CT, char *);
+
+/*
+ * Structures for mapping (content) types to
+ * the functions to handle them.
+ */
+struct str2init {
+    char *si_key;
+    int          si_val;
+    InitFunc si_init;
+};
+
+static struct str2init str2cts[] = {
+    { "application", CT_APPLICATION, InitApplication },
+    { "audio",      CT_AUDIO,       InitGeneric },
+    { "image",      CT_IMAGE,       InitGeneric },
+    { "message",     CT_MESSAGE,     InitMessage },
+    { "multipart",   CT_MULTIPART,   InitMultiPart },
+    { "text",       CT_TEXT,        InitText },
+    { "video",      CT_VIDEO,       InitGeneric },
+    { NULL,         CT_EXTENSION,   NULL },  /* these two must be last! */
+    { NULL,         CT_UNKNOWN,     NULL },
+};
+
+static struct str2init str2ces[] = {
+    { "base64",                  CE_BASE64,    InitBase64 },
+    { "quoted-printable", CE_QUOTED,   InitQuoted },
+    { "8bit",            CE_8BIT,      Init7Bit },
+    { "7bit",            CE_7BIT,      Init7Bit },
+    { "binary",                  CE_BINARY,    NULL },
+    { NULL,              CE_EXTENSION, NULL },  /* these two must be last! */
+    { NULL,              CE_UNKNOWN,   NULL },
+};
+
+/*
+ * NOTE WELL: si_key MUST NOT have value of NOTOK
+ *
+ * si_key is 1 if access method is anonymous.
+ */
+static struct str2init str2methods[] = {
+    { "afs",         1,        InitFile },
+    { "anon-ftp",    1,        InitFTP },
+    { "ftp",         0,        InitFTP },
+    { "local-file",  0,        InitFile },
+    { "mail-server", 0,        InitMail },
+    { NULL,          0, NULL }
+};
+
+
+int
+pidcheck (int status)
+{
+    if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
+       return status;
+
+    fflush (stdout);
+    fflush (stderr);
+    done (1);
+    /* NOTREACHED */
+}
+
+
+/*
+ * Main entry point for parsing a MIME message or file.
+ * It returns the Content structure for the top level
+ * entity in the file.
+ */
+
+CT
+parse_mime (char *file)
+{
+    int is_stdin;
+    char buffer[BUFSIZ];
+    FILE *fp;
+    CT ct;
+
+    /*
+     * Check if file is actually standard input
+     */
+    if ((is_stdin = !(strcmp (file, "-")))) {
+       file = add (m_tmpfil (invo_name), NULL);
+       if ((fp = fopen (file, "w+")) == NULL) {
+           advise (file, "unable to fopen for writing and reading");
+           return NULL;
+       }
+       chmod (file, 0600);
+       while (fgets (buffer, sizeof(buffer), stdin))
+           fputs (buffer, fp);
+       fflush (fp);
+
+       if (ferror (stdin)) {
+           unlink (file);
+           advise ("stdin", "error reading");
+           return NULL;
+       }
+       if (ferror (fp)) {
+           unlink (file);
+           advise (file, "error writing");
+           return NULL;
+       }
+       fseek (fp, 0L, SEEK_SET);
+    } else if ((fp = fopen (file, "r")) == NULL) {
+       advise (file, "unable to read");
+       return NULL;
+    }
+
+    if (!(ct = get_content (fp, file, 1))) {
+       if (is_stdin)
+           unlink (file);
+       fclose (fp);
+       advise (NULL, "unable to decode %s", file);
+       return NULL;
+    }
+
+    if (is_stdin)
+       ct->c_unlink = 1;       /* temp file to remove */
+
+    ct->c_fp = NULL;
+
+    if (ct->c_end == 0L) {
+       fseek (fp, 0L, SEEK_END);
+       ct->c_end = ftell (fp);
+    }
+
+    if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
+       fclose (fp);
+       free_content (ct);
+       return NULL;
+    }
+
+    fclose (fp);
+    return ct;
+}
+
+
+/*
+ * Main routine for reading/parsing the headers
+ * of a message content.
+ *
+ * toplevel =  1   # we are at the top level of the message
+ * toplevel =  0   # we are inside message type or multipart type
+ *                 # other than multipart/digest
+ * toplevel = -1   # we are inside multipart/digest
+ */
+
+static CT
+get_content (FILE *in, char *file, int toplevel)
+{
+    int compnum, state;
+    char buf[BUFSIZ], name[NAMESZ];
+    char *np, *vp;
+    CT ct;
+    HF hp;
+
+    /* allocate the content structure */
+    if (!(ct = (CT) calloc (1, sizeof(*ct))))
+       adios (NULL, "out of memory");
+
+    ct->c_fp = in;
+    ct->c_file = add (file, NULL);
+    ct->c_begin = ftell (ct->c_fp) + 1;
+
+    /*
+     * Parse the header fields for this
+     * content into a linked list.
+     */
+    for (compnum = 1, state = FLD;;) {
+       switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
+       case FLD:
+       case FLDPLUS:
+       case FLDEOF:
+           compnum++;
+
+           /* get copies of the buffers */
+           np = add (name, NULL);
+           vp = add (buf, NULL);
+
+           /* if necessary, get rest of field */
+           while (state == FLDPLUS) {
+               state = m_getfld (state, name, buf, sizeof(buf), in);
+               vp = add (buf, vp);     /* add to previous value */
+           }
+
+           /* Now add the header data to the list */
+           add_header (ct, np, vp);
+
+           /* continue, if this isn't the last header field */
+           if (state != FLDEOF) {
+               ct->c_begin = ftell (in) + 1;
+               continue;
+           }
+           /* else fall... */
+
+       case BODY:
+       case BODYEOF:
+           ct->c_begin = ftell (in) - strlen (buf);
+           break;
+
+       case FILEEOF:
+           ct->c_begin = ftell (in);
+           break;
+
+       case LENERR:
+       case FMTERR:
+           adios (NULL, "message format error in component #%d", compnum);
+
+       default:
+           adios (NULL, "getfld() returned %d", state);
+       }
+
+       /* break out of the loop */
+       break;
+    }
+
+    /*
+     * Read the content headers.  We will parse the
+     * MIME related header fields into their various
+     * structures and set internal flags related to
+     * content type/subtype, etc.
+     */
+
+    hp = ct->c_first_hf;       /* start at first header field */
+    while (hp) {
+       /* Get MIME-Version field */
+       if (!strcasecmp (hp->name, VRSN_FIELD)) {
+           int ucmp;
+           char c, *cp, *dp;
+
+           if (ct->c_vrsn) {
+               advise (NULL, "message %s has multiple %s: fields",
+                       ct->c_file, VRSN_FIELD);
+               goto next_header;
+           }
+           ct->c_vrsn = add (hp->value, NULL);
+
+           /* Now, cleanup this field */
+           cp = ct->c_vrsn;
+
+           while (isspace (*cp))
+               cp++;
+           for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
+               *dp++ = ' ';
+           for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
+               if (!isspace (*dp))
+                   break;
+           *++dp = '\0';
+           if (debugsw)
+               fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
+
+           if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
+               goto out;
+
+           for (dp = cp; istoken (*dp); dp++)
+               continue;
+           c = *dp;
+           *dp = '\0';
+           ucmp = !strcasecmp (cp, VRSN_VALUE);
+           *dp = c;
+           if (!ucmp) {
+               admonish (NULL, "message %s has unknown value for %s: field (%s)",
+               ct->c_file, VRSN_FIELD, cp);
+           }
+       }
+       else if (!strcasecmp (hp->name, TYPE_FIELD)) {
+       /* Get Content-Type field */
+           struct str2init *s2i;
+           CI ci = &ct->c_ctinfo;
+
+           /* Check if we've already seen a Content-Type header */
+           if (ct->c_ctline) {
+               advise (NULL, "message %s has multiple %s: fields",
+                       ct->c_file, TYPE_FIELD);
+               goto next_header;
+           }
+
+           /* Parse the Content-Type field */
+           if (get_ctinfo (hp->value, ct) == NOTOK)
+               goto out;
+
+           /*
+            * Set the Init function and the internal
+            * flag for this content type.
+            */
+           for (s2i = str2cts; s2i->si_key; s2i++)
+               if (!strcasecmp (ci->ci_type, s2i->si_key))
+                   break;
+           if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
+               s2i++;
+           ct->c_type = s2i->si_val;
+           ct->c_ctinitfnx = s2i->si_init;
+       }
+       else if (!strcasecmp (hp->name, ENCODING_FIELD)) {
+       /* Get Content-Transfer-Encoding field */
+           char c, *cp, *dp;
+           struct str2init *s2i;
+
+           /*
+            * Check if we've already seen the
+            * Content-Transfer-Encoding field
+            */
+           if (ct->c_celine) {
+               advise (NULL, "message %s has multiple %s: fields",
+                       ct->c_file, ENCODING_FIELD);
+               goto next_header;
+           }
+
+           /* get copy of this field */
+           ct->c_celine = cp = add (hp->value, NULL);
+
+           while (isspace (*cp))
+               cp++;
+           for (dp = cp; istoken (*dp); dp++)
+               continue;
+           c = *dp;
+           *dp = '\0';
+
+           /*
+            * Find the internal flag and Init function
+            * for this transfer encoding.
+            */
+           for (s2i = str2ces; s2i->si_key; s2i++)
+               if (!strcasecmp (cp, s2i->si_key))
+                   break;
+           if (!s2i->si_key && !uprf (cp, "X-"))
+               s2i++;
+           *dp = c;
+           ct->c_encoding = s2i->si_val;
+
+           /* Call the Init function for this encoding */
+           if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
+               goto out;
+       }
+       else if (!strcasecmp (hp->name, MD5_FIELD)) {
+       /* Get Content-MD5 field */
+           char *cp, *dp, *ep;
+
+           if (!checksw)
+               goto next_header;
+
+           if (ct->c_digested) {
+               advise (NULL, "message %s has multiple %s: fields",
+                       ct->c_file, MD5_FIELD);
+               goto next_header;
+           }
+
+           ep = cp = add (hp->value, NULL);    /* get a copy */
+
+           while (isspace (*cp))
+               cp++;
+           for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
+               *dp++ = ' ';
+           for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
+               if (!isspace (*dp))
+                   break;
+           *++dp = '\0';
+           if (debugsw)
+               fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
+
+           if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
+               free (ep);
+               goto out;
+           }
+
+           for (dp = cp; *dp && !isspace (*dp); dp++)
+               continue;
+           *dp = '\0';
+
+           readDigest (ct, cp);
+           free (ep);
+           ct->c_digested++;
+       }
+       else if (!strcasecmp (hp->name, ID_FIELD)) {
+       /* Get Content-ID field */
+           ct->c_id = add (hp->value, ct->c_id);
+       }
+       else if (!strcasecmp (hp->name, DESCR_FIELD)) {
+       /* Get Content-Description field */
+           ct->c_descr = add (hp->value, ct->c_descr);
+       }
+
+next_header:
+       hp = hp->next;  /* next header field */
+    }
+
+    /*
+     * Check if we saw a Content-Type field.
+     * If not, then assign a default value for
+     * it, and the Init function.
+     */
+    if (!ct->c_ctline) {
+       /*
+        * If we are inside a multipart/digest message,
+        * so default type is message/rfc822
+        */
+       if (toplevel < 0) {
+           if (get_ctinfo ("message/rfc822", ct) == NOTOK)
+               goto out;
+           ct->c_type = CT_MESSAGE;
+           ct->c_ctinitfnx = InitMessage;
+       } else {
+           /*
+            * Else default type is text/plain
+            */
+           if (get_ctinfo ("text/plain", ct) == NOTOK)
+               goto out;
+           ct->c_type = CT_TEXT;
+           ct->c_ctinitfnx = InitText;
+       }
+    }
+
+    /* Use default Transfer-Encoding, if necessary */
+    if (!ct->c_celine) {
+       ct->c_encoding = CE_7BIT;
+       Init7Bit (ct);
+    }
+
+    return ct;
+
+out:
+    free_content (ct);
+    return NULL;
+}
+
+
+/*
+ * small routine to add header field to list
+ */
+
+static int
+add_header (CT ct, char *name, char *value)
+{
+    HF hp;
+
+    /* allocate header field structure */
+    if (!(hp = malloc (sizeof(*hp))))
+       adios (NULL, "out of memory");
+
+    /* link data into header structure */
+    hp->name = name;
+    hp->value = value;
+    hp->next = NULL;
+
+    /* link header structure into the list */
+    if (ct->c_first_hf == NULL) {
+       ct->c_first_hf = hp;            /* this is the first */
+       ct->c_last_hf = hp;
+    } else {
+       ct->c_last_hf->next = hp;       /* add it to the end */
+       ct->c_last_hf = hp;
+    }
+
+    return 0;
+}
+
+
+/*
+ * Parse Content-Type line and fill in the
+ * information of the CTinfo structure.
+ */
+
+static int
+get_ctinfo (char *cp, CT ct)
+{
+    int        i;
+    char *dp, **ap, **ep;
+    char c;
+    CI ci;
+
+    ci = &ct->c_ctinfo;
+    i = strlen (invo_name) + 2;
+
+    /* store copy of Content-Type line */
+    cp = ct->c_ctline = add (cp, NULL);
+
+    while (isspace (*cp))      /* trim leading spaces */
+       cp++;
+
+    /* change newlines to spaces */
+    for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
+       *dp++ = ' ';
+
+    /* trim trailing spaces */
+    for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
+       if (!isspace (*dp))
+           break;
+    *++dp = '\0';
+
+    if (debugsw)
+       fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
+
+    if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
+       return NOTOK;
+
+    for (dp = cp; istoken (*dp); dp++)
+       continue;
+    c = *dp, *dp = '\0';
+    ci->ci_type = add (cp, NULL);      /* store content type */
+    *dp = c, cp = dp;
+
+    if (!*ci->ci_type) {
+       advise (NULL, "invalid %s: field in message %s (empty type)", 
+               TYPE_FIELD, ct->c_file);
+       return NOTOK;
+    }
+
+    /* down case the content type string */
+    for (dp = ci->ci_type; *dp; dp++)
+       if (isalpha(*dp) && isupper (*dp))
+           *dp = tolower (*dp);
+
+    while (isspace (*cp))
+       cp++;
+
+    if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
+       return NOTOK;
+
+    if (*cp != '/') {
+       ci->ci_subtype = add ("", NULL);
+       goto magic_skip;
+    }
+
+    cp++;
+    while (isspace (*cp))
+       cp++;
+
+    if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
+       return NOTOK;
+
+    for (dp = cp; istoken (*dp); dp++)
+       continue;
+    c = *dp, *dp = '\0';
+    ci->ci_subtype = add (cp, NULL);   /* store the content subtype */
+    *dp = c, cp = dp;
+
+    if (!*ci->ci_subtype) {
+       advise (NULL,
+               "invalid %s: field in message %s (empty subtype for \"%s\")",
+               TYPE_FIELD, ct->c_file, ci->ci_type);
+       return NOTOK;
+    }
+
+    /* down case the content subtype string */
+    for (dp = ci->ci_subtype; *dp; dp++)
+       if (isalpha(*dp) && isupper (*dp))
+           *dp = tolower (*dp);
+
+magic_skip:
+    while (isspace (*cp))
+       cp++;
+
+    if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
+       return NOTOK;
+
+    /*
+     * Parse attribute/value pairs given with Content-Type
+     */
+    ep = (ap = ci->ci_attrs) + NPARMS;
+    while (*cp == ';') {
+       char *vp, *up;
+
+       if (ap >= ep) {
+           advise (NULL,
+                   "too many parameters in message %s's %s: field (%d max)",
+                   ct->c_file, TYPE_FIELD, NPARMS);
+           return NOTOK;
+       }
+
+       cp++;
+       while (isspace (*cp))
+           cp++;
+
+       if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
+           return NOTOK;
+
+       if (*cp == 0) {
+           advise (NULL,
+                   "extraneous trailing ';' in message %s's %s: parameter list",
+                   ct->c_file, TYPE_FIELD);
+           return OK;
+       }
+
+       /* down case the attribute name */
+       for (dp = cp; istoken (*dp); dp++)
+           if (isalpha(*dp) && isupper (*dp))
+               *dp = tolower (*dp);
+
+       for (up = dp; isspace (*dp);)
+           dp++;
+       if (dp == cp || *dp != '=') {
+           advise (NULL,
+                   "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
+                   ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
+           return NOTOK;
+       }
+
+       vp = (*ap = add (cp, NULL)) + (up - cp);
+       *vp = '\0';
+       for (dp++; isspace (*dp);)
+           dp++;
+
+       /* now add the attribute value */
+       ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
+
+       if (*dp == '"') {
+           for (cp = ++dp, dp = vp;;) {
+               switch (c = *cp++) {
+                   case '\0':
+bad_quote:
+                       advise (NULL,
+                               "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
+                               ct->c_file, TYPE_FIELD, i, i, "", *ap);
+                       return NOTOK;
+
+                   case '\\':
+                       *dp++ = c;
+                       if ((c = *cp++) == '\0')
+                           goto bad_quote;
+                       /* else fall... */
+
+                   default:
+                       *dp++ = c;
+                       continue;
+
+                   case '"':
+                       *dp = '\0';
+                       break;
+               }
+               break;
+           }
+       } else {
+           for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
+               continue;
+           *dp = '\0';
+       }
+       if (!*vp) {
+           advise (NULL,
+                   "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
+                   ct->c_file, TYPE_FIELD, i, i, "", *ap);
+           return NOTOK;
+       }
+       ap++;
+
+       while (isspace (*cp))
+           cp++;
+
+       if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
+           return NOTOK;
+    }
+
+    /*
+     * Check if anything is left over
+     */
+    if (*cp) {
+       advise (NULL, "extraneous information in message %s's %s: field\n%*.*s(%s)",
+           ct->c_file, TYPE_FIELD, i, i, "", cp);
+    }
+
+    return OK;
+}
+
+
+static int
+get_comment (CT ct, char **ap, int istype)
+{
+    int i;
+    char *bp, *cp;
+    char c, buffer[BUFSIZ], *dp;
+    CI ci;
+
+    ci = &ct->c_ctinfo;
+    cp = *ap;
+    bp = buffer;
+    cp++;
+
+    for (i = 0;;) {
+       switch (c = *cp++) {
+       case '\0':
+invalid:
+       advise (NULL, "invalid comment in message %s's %s: field",
+               ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
+       return NOTOK;
+
+       case '\\':
+           *bp++ = c;
+           if ((c = *cp++) == '\0')
+               goto invalid;
+           *bp++ = c;
+           continue;
+
+       case '(':
+           i++;
+           /* and fall... */
+       default:
+           *bp++ = c;
+           continue;
+
+       case ')':
+           if (--i < 0)
+               break;
+           *bp++ = c;
+           continue;
+       }
+       break;
+    }
+    *bp = '\0';
+
+    if (istype) {
+       if ((dp = ci->ci_comment)) {
+           ci->ci_comment = concat (dp, " ", buffer, NULL);
+           free (dp);
+       } else {
+           ci->ci_comment = add (buffer, NULL);
+       }
+    }
+
+    while (isspace (*cp))
+       cp++;
+
+    *ap = cp;
+    return OK;
+}
+
+
+/*
+ * CONTENTS
+ *
+ * Handles content types audio, image, and video.
+ * There's not much to do right here.
+ */
+
+static int
+InitGeneric (CT ct)
+{
+    return OK;         /* not much to do here */
+}
+
+
+/*
+ * TEXT
+ */
+
+static int
+InitText (CT ct)
+{
+    char buffer[BUFSIZ];
+    char *chset;
+    char **ap, **ep, *cp;
+    struct k2v *kv;
+    struct text *t;
+    CI ci = &ct->c_ctinfo;
+
+    /* check for missing subtype */
+    if (!*ci->ci_subtype)
+       ci->ci_subtype = add ("plain", ci->ci_subtype);
+
+    /* match subtype */
+    for (kv = SubText; kv->kv_key; kv++)
+       if (!strcasecmp (ci->ci_subtype, kv->kv_key))
+           break;
+    ct->c_subtype = kv->kv_value;
+
+    /* allocate text structure */
+    if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
+       adios (NULL, "out of memory");
+    ct->c_ctparams = (void *) t;
+
+    /* scan for charset parameter */
+    for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
+       if (!strcasecmp (*ap, "charset"))
+           break;
+
+    if (*ap)
+       chset = *ep;
+    else
+       chset = "US-ASCII";     /* default for text */
+
+    /* match character set, or set to unknown */
+    for (kv = Charset; kv->kv_key; kv++)
+       if (!strcasecmp (chset, kv->kv_key))
+           break;
+    t->tx_charset = kv->kv_value;
+
+    /*
+     * If we can not handle character set natively,
+     * then check profile for string to modify the
+     * terminal or display method.
+     */
+    if (!check_charset (chset, strlen (chset))) {
+       snprintf (buffer, sizeof(buffer), "%s-charset-%s", invo_name, chset);
+       if ((cp = context_find (buffer)))
+           ct->c_termproc = getcpy (cp);
+    }
+
+    return OK;
+}
+
+
+/*
+ * MULTIPART
+ */
+
+static int
+InitMultiPart (CT ct)
+{
+    int        inout;
+    long last, pos;
+    char *cp, *dp, **ap, **ep;
+    char *bp, buffer[BUFSIZ];
+    struct multipart *m;
+    struct k2v *kv;
+    struct part *part, **next;
+    CI ci = &ct->c_ctinfo;
+    CT p;
+    FILE *fp;
+
+    /*
+     * The encoding for multipart messages must be either
+     * 7bit, 8bit, or binary (per RFC2045).
+     */
+    if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
+       && ct->c_encoding != CE_BINARY) {
+       admonish (NULL,
+                 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
+                 ci->ci_type, ci->ci_subtype, ct->c_file);
+       return NOTOK;
+    }
+
+    /* match subtype */
+    for (kv = SubMultiPart; kv->kv_key; kv++)
+       if (!strcasecmp (ci->ci_subtype, kv->kv_key))
+           break;
+    ct->c_subtype = kv->kv_value;
+
+    /*
+     * Check for "boundary" parameter, which is
+     * required for multipart messages.
+     */
+    for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
+       if (!strcasecmp (*ap, "boundary")) {
+           bp = *ep;
+           break;
+       }
+    }
+
+    /* complain if boundary parameter is missing */
+    if (!*ap) {
+       advise (NULL,
+               "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
+               ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
+       return NOTOK;
+    }
+
+    /* allocate primary structure for multipart info */
+    if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
+       adios (NULL, "out of memory");
+    ct->c_ctparams = (void *) m;
+
+    /* check if boundary parameter contains only whitespace characters */
+    for (cp = bp; isspace (*cp); cp++)
+       continue;
+    if (!*cp) {
+       advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
+               ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
+       return NOTOK;
+    }
+
+    /* remove trailing whitespace from boundary parameter */
+    for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
+       if (!isspace (*dp))
+           break;
+    *++dp = '\0';
+
+    /* record boundary separators */
+    m->mp_start = concat (bp, "\n", NULL);
+    m->mp_stop = concat (bp, "--\n", NULL);
+
+    if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
+       advise (ct->c_file, "unable to open for reading");
+       return NOTOK;
+    }
+
+    fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
+    last = ct->c_end;
+    next = &m->mp_parts;
+    part = NULL;
+    inout = 1;
+
+    while (fgets (buffer, sizeof(buffer) - 1, fp)) {
+       if (pos > last)
+           break;
+
+       pos += strlen (buffer);
+       if (buffer[0] != '-' || buffer[1] != '-')
+           continue;
+       if (inout) {
+           if (strcmp (buffer + 2, m->mp_start))
+               continue;
+next_part:
+           if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
+               adios (NULL, "out of memory");
+           *next = part;
+           next = &part->mp_next;
+
+           if (!(p = get_content (fp, ct->c_file,
+                       ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
+               fclose (ct->c_fp);
+               ct->c_fp = NULL;
+               return NOTOK;
+           }
+           p->c_fp = NULL;
+           part->mp_part = p;
+           pos = p->c_begin;
+           fseek (fp, pos, SEEK_SET);
+           inout = 0;
+       } else {
+           if (strcmp (buffer + 2, m->mp_start) == 0) {
+               inout = 1;
+end_part:
+               p = part->mp_part;
+               p->c_end = ftell(fp) - (strlen(buffer) + 1);
+               if (p->c_end < p->c_begin)
+                   p->c_begin = p->c_end;
+               if (inout)
+                   goto next_part;
+               goto last_part;
+           } else {
+               if (strcmp (buffer + 2, m->mp_stop) == 0)
+                   goto end_part;
+           }
+       }
+    }
+
+    advise (NULL, "bogus multipart content in message %s", ct->c_file);
+    if (!inout && part) {
+       p = part->mp_part;
+       p->c_end = ct->c_end;
+
+       if (p->c_begin >= p->c_end) {
+           for (next = &m->mp_parts; *next != part;
+                    next = &((*next)->mp_next))
+               continue;
+           *next = NULL;
+           free_content (p);
+           free ((char *) part);
+       }
+    }
+
+last_part:
+    /* reverse the order of the parts for multipart/alternative */
+    if (ct->c_subtype == MULTI_ALTERNATE)
+       reverse_parts (ct);
+
+    /*
+     * label all subparts with part number, and
+     * then initialize the content of the subpart.
+     */
+    {
+       int partnum;
+       char *pp;
+       char partnam[BUFSIZ];
+
+       if (ct->c_partno) {
+           snprintf (partnam, sizeof(partnum), "%s.", ct->c_partno);
+           pp = partnam + strlen (partnam);
+       } else {
+           pp = partnam;
+       }
+
+       for (part = m->mp_parts, partnum = 1; part;
+                part = part->mp_next, partnum++) {
+           p = part->mp_part;
+
+           sprintf (pp, "%d", partnum);
+           p->c_partno = add (partnam, NULL);
+
+           /* initialize the content of the subparts */
+           if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
+               fclose (ct->c_fp);
+               ct->c_fp = NULL;
+               return NOTOK;
+           }
+       }
+    }
+
+    fclose (ct->c_fp);
+    ct->c_fp = NULL;
+    return OK;
+}
+
+
+/*
+ * reverse the order of the parts of a multipart
+ */
+
+static void
+reverse_parts (CT ct)
+{
+    int i;
+    struct multipart *m;
+    struct part **base, **bmp, **next, *part;
+
+    m = (struct multipart *) ct->c_ctparams;
+
+    /* if only one part, just return */
+    if (!m->mp_parts || !m->mp_parts->mp_next)
+       return;
+
+    /* count number of parts */
+    i = 0;
+    for (part = m->mp_parts; part; part = part->mp_next)
+       i++;
+
+    /* allocate array of pointers to the parts */
+    if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
+       adios (NULL, "out of memory");
+    bmp = base;
+
+    /* point at all the parts */
+    for (part = m->mp_parts; part; part = part->mp_next)
+       *bmp++ = part;
+    *bmp = NULL;
+
+    /* reverse the order of the parts */
+    next = &m->mp_parts;
+    for (bmp--; bmp >= base; bmp--) {
+       part = *bmp;
+       *next = part;
+       next = &part->mp_next;
+    }
+    *next = NULL;
+
+    /* free array of pointers */
+    free ((char *) base);
+}
+
+
+/*
+ * MESSAGE
+ */
+
+static int
+InitMessage (CT ct)
+{
+    struct k2v *kv;
+    CI ci = &ct->c_ctinfo;
+
+    if (ct->c_encoding != CE_7BIT) {
+       admonish (NULL,
+                 "\"%s/%s\" type in message %s should be encoded in 7bit",
+                 ci->ci_type, ci->ci_subtype, ct->c_file);
+       return NOTOK;
+    }
+
+    /* check for missing subtype */
+    if (!*ci->ci_subtype)
+       ci->ci_subtype = add ("rfc822", ci->ci_subtype);
+
+    /* match subtype */
+    for (kv = SubMessage; kv->kv_key; kv++)
+       if (!strcasecmp (ci->ci_subtype, kv->kv_key))
+           break;
+    ct->c_subtype = kv->kv_value;
+
+    switch (ct->c_subtype) {
+       case MESSAGE_RFC822:
+           break;
+
+       case MESSAGE_PARTIAL:
+           {
+               char **ap, **ep;
+               struct partial *p;
+
+               if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
+                   adios (NULL, "out of memory");
+               ct->c_ctparams = (void *) p;
+
+               /* scan for parameters "id", "number", and "total" */
+               for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
+                   if (!strcasecmp (*ap, "id")) {
+                       p->pm_partid = add (*ep, NULL);
+                       continue;
+                   }
+                   if (!strcasecmp (*ap, "number")) {
+                       if (sscanf (*ep, "%d", &p->pm_partno) != 1
+                               || p->pm_partno < 1) {
+invalid_param:
+                           advise (NULL,
+                                   "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
+                                   *ap, ci->ci_type, ci->ci_subtype,
+                                   ct->c_file, TYPE_FIELD);
+                           return NOTOK;
+                       }
+                       continue;
+                   }
+                   if (!strcasecmp (*ap, "total")) {
+                       if (sscanf (*ep, "%d", &p->pm_maxno) != 1
+                               || p->pm_maxno < 1)
+                           goto invalid_param;
+                       continue;
+                   }
+               }
+
+               if (!p->pm_partid
+                       || !p->pm_partno
+                       || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
+                   advise (NULL,
+                           "invalid parameters for \"%s/%s\" type in message %s's %s field",
+                           ci->ci_type, ci->ci_subtype,
+                           ct->c_file, TYPE_FIELD);
+                   return NOTOK;
+               }
+           }
+           break;
+
+       case MESSAGE_EXTERNAL:
+           {
+               int exresult;
+               struct exbody *e;
+               CT p;
+               FILE *fp;
+
+               if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
+                   adios (NULL, "out of memory");
+               ct->c_ctparams = (void *) e;
+
+               if (!ct->c_fp
+                       && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
+                   advise (ct->c_file, "unable to open for reading");
+                   return NOTOK;
+               }
+
+               fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
+
+               if (!(p = get_content (fp, ct->c_file, 0))) {
+                   fclose (ct->c_fp);
+                   ct->c_fp = NULL;
+                   return NOTOK;
+               }
+
+               e->eb_parent = ct;
+               e->eb_content = p;
+               p->c_ctexbody = e;
+               if ((exresult = params_external (ct, 0)) != NOTOK
+                       && p->c_ceopenfnx == openMail) {
+                   int cc, size;
+                   char *bp;
+                   
+                   if ((size = ct->c_end - p->c_begin) <= 0) {
+                       if (!e->eb_subject)
+                           content_error (NULL, ct,
+                                          "empty body for access-type=mail-server");
+                       goto no_body;
+                   }
+                   
+                   if ((e->eb_body = bp = malloc ((unsigned) size)) == NULL)
+                       adios (NULL, "out of memory");
+                   fseek (p->c_fp, p->c_begin, SEEK_SET);
+                   while (size > 0)
+                       switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
+                           case NOTOK:
+                               adios ("failed", "fread");
+
+                           case OK:
+                               adios (NULL, "unexpected EOF from fread");
+
+                           default:
+                               bp += cc, size -= cc;
+                               break;
+                       }
+                   *bp = 0;
+               }
+no_body:
+               p->c_fp = NULL;
+               p->c_end = p->c_begin;
+
+               fclose (ct->c_fp);
+               ct->c_fp = NULL;
+
+               if (exresult == NOTOK)
+                   return NOTOK;
+               if (e->eb_flags == NOTOK)
+                   return OK;
+
+               switch (p->c_type) {
+                   case CT_MULTIPART:
+                       break;
+
+                   case CT_MESSAGE:
+                       if (p->c_subtype != MESSAGE_RFC822)
+                           break;
+                       /* else fall... */
+                   default:
+                       e->eb_partno = ct->c_partno;
+                       if (p->c_ctinitfnx)
+                           (*p->c_ctinitfnx) (p);
+                       break;
+               }
+           }
+           break;
+
+       default:
+           break;
+    }
+
+    return OK;
+}
+
+
+static int
+params_external (CT ct, int composing)
+{
+    char **ap, **ep;
+    struct exbody *e = (struct exbody *) ct->c_ctparams;
+    CI ci = &ct->c_ctinfo;
+
+    for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
+       if (!strcasecmp (*ap, "access-type")) {
+           struct str2init *s2i;
+           CT p = e->eb_content;
+
+           for (s2i = str2methods; s2i->si_key; s2i++)
+               if (!strcasecmp (*ep, s2i->si_key))
+                   break;
+           if (!s2i->si_key) {
+               e->eb_access = *ep;
+               e->eb_flags = NOTOK;
+               p->c_encoding = CE_EXTERNAL;
+               continue;
+           }
+           e->eb_access = s2i->si_key;
+           e->eb_flags = s2i->si_val;
+           p->c_encoding = CE_EXTERNAL;
+
+           /* Call the Init function for this external type */
+           if ((*s2i->si_init)(p) == NOTOK)
+               return NOTOK;
+           continue;
+       }
+       if (!strcasecmp (*ap, "name")) {
+           e->eb_name = *ep;
+           continue;
+       }
+       if (!strcasecmp (*ap, "permission")) {
+           e->eb_permission = *ep;
+           continue;
+       }
+       if (!strcasecmp (*ap, "site")) {
+           e->eb_site = *ep;
+           continue;
+       }
+       if (!strcasecmp (*ap, "directory")) {
+           e->eb_dir = *ep;
+           continue;
+       }
+       if (!strcasecmp (*ap, "mode")) {
+           e->eb_mode = *ep;
+           continue;
+       }
+       if (!strcasecmp (*ap, "size")) {
+           sscanf (*ep, "%lu", &e->eb_size);
+           continue;
+       }
+       if (!strcasecmp (*ap, "server")) {
+           e->eb_server = *ep;
+           continue;
+       }
+       if (!strcasecmp (*ap, "subject")) {
+           e->eb_subject = *ep;
+           continue;
+       }
+       if (composing && !strcasecmp (*ap, "body")) {
+           e->eb_body = getcpy (*ep);
+           continue;
+       }
+    }
+
+    if (!e->eb_access) {
+       advise (NULL,
+               "invalid parameters for \"%s/%s\" type in message %s's %s field",
+               ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
+       return NOTOK;
+    }
+
+    return OK;
+}
+
+
+/*
+ * APPLICATION
+ */
+
+static int
+InitApplication (CT ct)
+{
+    struct k2v *kv;
+    CI ci = &ct->c_ctinfo;
+
+    /* match subtype */
+    for (kv = SubApplication; kv->kv_key; kv++)
+       if (!strcasecmp (ci->ci_subtype, kv->kv_key))
+           break;
+    ct->c_subtype = kv->kv_value;
+
+    return OK;
+}
+
+
+/*
+ * TRANSFER ENCODINGS
+ */
+
+static int
+init_encoding (CT ct, OpenCEFunc openfnx)
+{
+    CE ce;
+
+    if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
+       adios (NULL, "out of memory");
+
+    ct->c_cefile     = ce;
+    ct->c_ceopenfnx  = openfnx;
+    ct->c_ceclosefnx = close_encoding;
+    ct->c_cesizefnx  = size_encoding;
+
+    return OK;
+}
+
+
+static void
+close_encoding (CT ct)
+{
+    CE ce;
+
+    if (!(ce = ct->c_cefile))
+       return;
+
+    if (ce->ce_fp) {
+       fclose (ce->ce_fp);
+       ce->ce_fp = NULL;
+    }
+}
+
+
+static unsigned long
+size_encoding (CT ct)
+{
+    int        fd;
+    unsigned long size;
+    char *file;
+    CE ce;
+    struct stat st;
+
+    if (!(ce = ct->c_cefile))
+       return (ct->c_end - ct->c_begin);
+
+    if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
+       return (long) st.st_size;
+
+    if (ce->ce_file) {
+       if (stat (ce->ce_file, &st) != NOTOK)
+           return (long) st.st_size;
+       else
+           return 0L;
+    }
+
+    if (ct->c_encoding == CE_EXTERNAL)
+       return (ct->c_end - ct->c_begin);       
+
+    file = NULL;
+    if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
+       return (ct->c_end - ct->c_begin);
+
+    if (fstat (fd, &st) != NOTOK)
+       size = (long) st.st_size;
+    else
+       size = 0L;
+
+    (*ct->c_ceclosefnx) (ct);
+    return size;
+}
+
+
+/*
+ * BASE64
+ */
+
+static unsigned char b642nib[0x80] = {
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
+    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
+    0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 
+    0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+    0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+    0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 
+    0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+    0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+    0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+
+static int
+InitBase64 (CT ct)
+{
+    return init_encoding (ct, openBase64);
+}
+
+
+static int
+openBase64 (CT ct, char **file)
+{
+    int        bitno, cc, digested;
+    int fd, len, skip;
+    unsigned long bits;
+    unsigned char value, *b, *b1, *b2, *b3;
+    char *cp, *ep, buffer[BUFSIZ];
+    CE ce;
+    MD5_CTX mdContext;
+
+    b  = (unsigned char *) &bits;
+    b1 = &b[endian > 0 ? 1 : 2];
+    b2 = &b[endian > 0 ? 2 : 1];
+    b3 = &b[endian > 0 ? 3 : 0];
+
+    ce = ct->c_cefile;
+    if (ce->ce_fp) {
+       fseek (ce->ce_fp, 0L, SEEK_SET);
+       goto ready_to_go;
+    }
+
+    if (ce->ce_file) {
+       if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
+           content_error (ce->ce_file, ct, "unable to fopen for reading");
+           return NOTOK;
+       }
+       goto ready_to_go;
+    }
+
+    if (*file == NULL) {
+       ce->ce_file = add (m_scratch ("", tmp), NULL);
+       ce->ce_unlink = 1;
+    } else {
+       ce->ce_file = add (*file, NULL);
+       ce->ce_unlink = 0;
+    }
+
+    if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
+       content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
+       return NOTOK;
+    }
+
+    if ((len = ct->c_end - ct->c_begin) < 0)
+       adios (NULL, "internal error(1)");
+
+    if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
+       content_error (ct->c_file, ct, "unable to open for reading");
+       return NOTOK;
+    }
+    
+    if ((digested = ct->c_digested))
+       MD5Init (&mdContext);
+
+    bitno = 18;
+    bits = 0L;
+    skip = 0;
+
+    lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
+    while (len > 0) {
+       switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
+       case NOTOK:
+           content_error (ct->c_file, ct, "error reading from");
+           goto clean_up;
+
+       case OK:
+           content_error (NULL, ct, "premature eof");
+           goto clean_up;
+
+       default:
+           if (cc > len)
+               cc = len;
+           len -= cc;
+
+           for (ep = (cp = buffer) + cc; cp < ep; cp++) {
+               switch (*cp) {
+               default:
+                   if (isspace (*cp))
+                       break;
+                   if (skip || (*cp & 0x80)
+                       || (value = b642nib[*cp & 0x7f]) > 0x3f) {
+                       if (debugsw) {
+                           fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
+                               *cp,
+                               (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
+                               skip);
+                       }
+                       content_error (NULL, ct,
+                                      "invalid BASE64 encoding -- continuing");
+                       continue;
+                   }
+
+                   bits |= value << bitno;
+test_end:
+                   if ((bitno -= 6) < 0) {
+                       putc ((char) *b1, ce->ce_fp);
+                       if (digested)
+                           MD5Update (&mdContext, b1, 1);
+                       if (skip < 2) {
+                           putc ((char) *b2, ce->ce_fp);
+                           if (digested)
+                               MD5Update (&mdContext, b2, 1);
+                           if (skip < 1) {
+                               putc ((char) *b3, ce->ce_fp);
+                               if (digested)
+                                   MD5Update (&mdContext, b3, 1);
+                           }
+                       }
+
+                       if (ferror (ce->ce_fp)) {
+                           content_error (ce->ce_file, ct,
+                                          "error writing to");
+                           goto clean_up;
+                       }
+                       bitno = 18, bits = 0L, skip = 0;
+                   }
+                   break;
+
+               case '=':
+                   if (++skip > 3)
+                       goto self_delimiting;
+                   goto test_end;
+               }
+           }
+       }
+    }
+
+    if (bitno != 18) {
+       if (debugsw)
+           fprintf (stderr, "premature ending (bitno %d)\n", bitno);
+
+       content_error (NULL, ct, "invalid BASE64 encoding");
+       goto clean_up;
+    }
+
+self_delimiting:
+    fseek (ct->c_fp, 0L, SEEK_SET);
+
+    if (fflush (ce->ce_fp)) {
+       content_error (ce->ce_file, ct, "error writing to");
+       goto clean_up;
+    }
+
+    if (digested) {
+       unsigned char digest[16];
+
+       MD5Final (digest, &mdContext);
+       if (memcmp((char *) digest, (char *) ct->c_digest,
+                  sizeof(digest) / sizeof(digest[0])))
+           content_error (NULL, ct,
+                          "content integrity suspect (digest mismatch) -- continuing");
+       else
+           if (debugsw)
+               fprintf (stderr, "content integrity confirmed\n");
+    }
+
+    fseek (ce->ce_fp, 0L, SEEK_SET);
+
+ready_to_go:
+    *file = ce->ce_file;
+    return fileno (ce->ce_fp);
+
+clean_up:
+    free_encoding (ct, 0);
+    return NOTOK;
+}
+
+
+/*
+ * QUOTED PRINTABLE
+ */
+
+static char hex2nib[0x80] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, 
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+
+static int 
+InitQuoted (CT ct)
+{
+    return init_encoding (ct, openQuoted);
+}
+
+
+static int
+openQuoted (CT ct, char **file)
+{
+    int        cc, digested, len, quoted;
+    char *cp, *ep;
+    char buffer[BUFSIZ];
+    unsigned char mask;
+    CE ce;
+    MD5_CTX mdContext;
+
+    ce = ct->c_cefile;
+    if (ce->ce_fp) {
+       fseek (ce->ce_fp, 0L, SEEK_SET);
+       goto ready_to_go;
+    }
+
+    if (ce->ce_file) {
+       if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
+           content_error (ce->ce_file, ct, "unable to fopen for reading");
+           return NOTOK;
+       }
+       goto ready_to_go;
+    }
+
+    if (*file == NULL) {
+       ce->ce_file = add (m_scratch ("", tmp), NULL);
+       ce->ce_unlink = 1;
+    } else {
+       ce->ce_file = add (*file, NULL);
+       ce->ce_unlink = 0;
+    }
+
+    if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
+       content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
+       return NOTOK;
+    }
+
+    if ((len = ct->c_end - ct->c_begin) < 0)
+       adios (NULL, "internal error(2)");
+
+    if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
+       content_error (ct->c_file, ct, "unable to open for reading");
+       return NOTOK;
+    }
+
+    if ((digested = ct->c_digested))
+       MD5Init (&mdContext);
+
+    quoted = 0;
+#ifdef lint
+    mask = 0;
+#endif
+
+    fseek (ct->c_fp, ct->c_begin, SEEK_SET);
+    while (len > 0) {
+       char *dp;
+
+       if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
+           content_error (NULL, ct, "premature eof");
+           goto clean_up;
+       }
+
+       if ((cc = strlen (buffer)) > len)
+           cc = len;
+       len -= cc;
+
+       for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
+           if (!isspace (*ep))
+               break;
+       *++ep = '\n', ep++;
+
+       for (; cp < ep; cp++) {
+           if (quoted) {
+               if (quoted > 1) {
+                   if (!isxdigit (*cp)) {
+invalid_hex:
+                       dp = "expecting hexidecimal-digit";
+                       goto invalid_encoding;
+                   }
+                   mask <<= 4;
+                   mask |= hex2nib[*cp & 0x7f];
+                   putc (mask, ce->ce_fp);
+                   if (digested)
+                       MD5Update (&mdContext, &mask, 1);
+               } else {
+                   switch (*cp) {
+                   case ':':
+                       putc (*cp, ce->ce_fp);
+                       if (digested)
+                           MD5Update (&mdContext, (unsigned char *) ":", 1);
+                       break;
+
+                   default:
+                       if (!isxdigit (*cp))
+                           goto invalid_hex;
+                       mask = hex2nib[*cp & 0x7f];
+                       quoted = 2;
+                       continue;
+                   }
+               }
+
+               if (ferror (ce->ce_fp)) {
+                   content_error (ce->ce_file, ct, "error writing to");
+                   goto clean_up;
+               }
+               quoted = 0;
+               continue;
+           }
+
+           switch (*cp) {
+           default:
+               if (*cp < '!' || *cp > '~') {
+                   int i;
+                   dp = "expecting character in range [!..~]";
+
+invalid_encoding:
+                   i = strlen (invo_name) + 2;
+                   content_error (NULL, ct,
+                                  "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x",
+                                  dp, i, i, "", *cp);
+                   goto clean_up;
+               }
+               /* and fall...*/
+           case ' ':
+           case '\t':
+           case '\n':
+               putc (*cp, ce->ce_fp);
+               if (digested) {
+                   if (*cp == '\n')
+                       MD5Update (&mdContext, (unsigned char *) "\r\n",2);
+                   else
+                       MD5Update (&mdContext, (unsigned char *) cp, 1);
+               }
+               if (ferror (ce->ce_fp)) {
+                   content_error (ce->ce_file, ct, "error writing to");
+                   goto clean_up;
+               }
+               break;
+
+           case '=':
+               if (*++cp != '\n') {
+                   quoted = 1;
+                   cp--;
+               }
+               break;
+           }
+       }
+    }
+    if (quoted) {
+       content_error (NULL, ct,
+                      "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
+       goto clean_up;
+    }
+
+    fseek (ct->c_fp, 0L, SEEK_SET);
+
+    if (fflush (ce->ce_fp)) {
+       content_error (ce->ce_file, ct, "error writing to");
+       goto clean_up;
+    }
+
+    if (digested) {
+       unsigned char digest[16];
+
+       MD5Final (digest, &mdContext);
+       if (memcmp((char *) digest, (char *) ct->c_digest,
+                  sizeof(digest) / sizeof(digest[0])))
+           content_error (NULL, ct,
+                          "content integrity suspect (digest mismatch) -- continuing");
+       else
+           if (debugsw)
+               fprintf (stderr, "content integrity confirmed\n");
+    }
+
+    fseek (ce->ce_fp, 0L, SEEK_SET);
+
+ready_to_go:
+    *file = ce->ce_file;
+    return fileno (ce->ce_fp);
+
+clean_up:
+    free_encoding (ct, 0);
+    return NOTOK;
+}
+
+
+/*
+ * 7BIT
+ */
+
+static int
+Init7Bit (CT ct)
+{
+    if (init_encoding (ct, open7Bit) == NOTOK)
+       return NOTOK;
+
+    ct->c_cesizefnx = NULL;    /* no need to decode for real size */
+    return OK;
+}
+
+
+static int
+open7Bit (CT ct, char **file)
+{
+    int        cc, fd, len;
+    char buffer[BUFSIZ];
+    CE ce;
+
+    ce = ct->c_cefile;
+    if (ce->ce_fp) {
+       fseek (ce->ce_fp, 0L, SEEK_SET);
+       goto ready_to_go;
+    }
+
+    if (ce->ce_file) {
+       if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
+           content_error (ce->ce_file, ct, "unable to fopen for reading");
+           return NOTOK;
+       }
+       goto ready_to_go;
+    }
+
+    if (*file == NULL) {
+       ce->ce_file = add (m_scratch ("", tmp), NULL);
+       ce->ce_unlink = 1;
+    } else {
+       ce->ce_file = add (*file, NULL);
+       ce->ce_unlink = 0;
+    }
+
+    if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
+       content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
+       return NOTOK;
+    }
+
+    if (ct->c_type == CT_MULTIPART) {
+       char **ap, **ep;
+       CI ci = &ct->c_ctinfo;
+
+       len = 0;
+       fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
+       len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
+           + 1 + strlen (ci->ci_subtype);
+       for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
+           putc (';', ce->ce_fp);
+           len++;
+
+           snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
+
+           if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
+               fputs ("\n\t", ce->ce_fp);
+               len = 8;
+           } else {
+               putc (' ', ce->ce_fp);
+               len++;
+           }
+           fprintf (ce->ce_fp, "%s", buffer);
+           len += cc;
+       }
+
+       if (ci->ci_comment) {
+           if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
+               fputs ("\n\t", ce->ce_fp);
+               len = 8;
+           }
+           else {
+               putc (' ', ce->ce_fp);
+               len++;
+           }
+           fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
+           len += cc;
+       }
+       fprintf (ce->ce_fp, "\n");
+       if (ct->c_id)
+           fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
+       if (ct->c_descr)
+           fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
+       fprintf (ce->ce_fp, "\n");
+    }
+
+    if ((len = ct->c_end - ct->c_begin) < 0)
+       adios (NULL, "internal error(3)");
+
+    if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
+       content_error (ct->c_file, ct, "unable to open for reading");
+       return NOTOK;
+    }
+
+    lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
+    while (len > 0)
+       switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
+       case NOTOK:
+           content_error (ct->c_file, ct, "error reading from");
+           goto clean_up;
+
+       case OK:
+           content_error (NULL, ct, "premature eof");
+           goto clean_up;
+
+       default:
+           if (cc > len)
+               cc = len;
+           len -= cc;
+
+           fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
+           if (ferror (ce->ce_fp)) {
+               content_error (ce->ce_file, ct, "error writing to");
+               goto clean_up;
+           }
+       }
+
+    fseek (ct->c_fp, 0L, SEEK_SET);
+
+    if (fflush (ce->ce_fp)) {
+       content_error (ce->ce_file, ct, "error writing to");
+       goto clean_up;
+    }
+
+    fseek (ce->ce_fp, 0L, SEEK_SET);
+
+ready_to_go:
+    *file = ce->ce_file;
+    return fileno (ce->ce_fp);
+
+clean_up:
+    free_encoding (ct, 0);
+    return NOTOK;
+}
+
+
+/*
+ * External
+ */
+
+static int
+openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
+{
+    char cachefile[BUFSIZ];
+
+    if (ce->ce_fp) {
+       fseek (ce->ce_fp, 0L, SEEK_SET);
+       goto ready_already;
+    }
+
+    if (ce->ce_file) {
+       if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
+           content_error (ce->ce_file, ct, "unable to fopen for reading");
+           return NOTOK;
+       }
+       goto ready_already;
+    }
+
+    if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
+               cachefile, sizeof(cachefile)) != NOTOK) {
+       if ((ce->ce_fp = fopen (cachefile, "r"))) {
+           ce->ce_file = getcpy (cachefile);
+           ce->ce_unlink = 0;
+           goto ready_already;
+       } else {
+           admonish (cachefile, "unable to fopen for reading");
+       }
+    }
+
+    return OK;
+
+ready_already:
+    *file = ce->ce_file;
+    *fd = fileno (ce->ce_fp);
+    return DONE;
+}
+
+/*
+ * File
+ */
+
+static int
+InitFile (CT ct)
+{
+    return init_encoding (ct, openFile);
+}
+
+
+static int
+openFile (CT ct, char **file)
+{
+    int        fd, cachetype;
+    char cachefile[BUFSIZ];
+    struct exbody *e = ct->c_ctexbody;
+    CE ce = ct->c_cefile;
+
+    switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
+       case NOTOK:
+           return NOTOK;
+
+       case OK:
+           break;
+
+       case DONE:
+           return fd;
+    }
+
+    if (!e->eb_name) {
+       content_error (NULL, ct, "missing name parameter");
+       return NOTOK;
+    }
+
+    ce->ce_file = getcpy (e->eb_name);
+    ce->ce_unlink = 0;
+
+    if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
+       content_error (ce->ce_file, ct, "unable to fopen for reading");
+       return NOTOK;
+    }
+
+    if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
+           && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
+               cachefile, sizeof(cachefile)) != NOTOK) {
+       int mask;
+       FILE *fp;
+
+       mask = umask (cachetype ? ~m_gmprot () : 0222);
+       if ((fp = fopen (cachefile, "w"))) {
+           int cc;
+           char buffer[BUFSIZ];
+           FILE *gp = ce->ce_fp;
+
+           fseek (gp, 0L, SEEK_SET);
+
+           while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
+                      > 0)
+               fwrite (buffer, sizeof(*buffer), cc, fp);
+           fflush (fp);
+
+           if (ferror (gp)) {
+               admonish (ce->ce_file, "error reading");
+               unlink (cachefile);
+           }
+           else
+               if (ferror (fp)) {
+                   admonish (cachefile, "error writing");
+                   unlink (cachefile);
+               }
+           fclose (fp);
+       }
+       umask (mask);
+    }
+
+    fseek (ce->ce_fp, 0L, SEEK_SET);
+    *file = ce->ce_file;
+    return fileno (ce->ce_fp);
+}
+
+/*
+ * FTP
+ */
+
+static int
+InitFTP (CT ct)
+{
+    return init_encoding (ct, openFTP);
+}
+
+
+static int
+openFTP (CT ct, char **file)
+{
+    int        cachetype, caching, fd;
+    int len, buflen;
+    char *bp, *ftp, *user, *pass;
+    char buffer[BUFSIZ], cachefile[BUFSIZ];
+    struct exbody *e;
+    CE ce;
+    static char *username = NULL;
+    static char *password = NULL;
+
+    e  = ct->c_ctexbody;
+    ce = ct->c_cefile;
+
+    if ((ftp = context_find (nmhaccessftp)) && !*ftp)
+       ftp = NULL;
+
+#ifndef BUILTIN_FTP
+    if (!ftp)
+       return NOTOK;
+#endif
+
+    switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
+       case NOTOK:
+           return NOTOK;
+
+       case OK:
+           break;
+
+       case DONE:
+           return fd;
+    }
+
+    if (!e->eb_name || !e->eb_site) {
+       content_error (NULL, ct, "missing %s parameter",
+                      e->eb_name ? "site": "name");
+       return NOTOK;
+    }
+
+    if (xpid) {
+       if (xpid < 0)
+           xpid = -xpid;
+       pidcheck (pidwait (xpid, NOTOK));
+       xpid = 0;
+    }
+
+    /* Get the buffer ready to go */
+    bp = buffer;
+    buflen = sizeof(buffer);
+
+    /*
+     * Construct the query message for user
+     */
+    snprintf (bp, buflen, "Retrieve %s", e->eb_name);
+    len = strlen (bp);
+    bp += len;
+    buflen -= len;
+
+    if (e->eb_partno) {
+       snprintf (bp, buflen, " (content %s)", e->eb_partno);
+       len = strlen (bp);
+       bp += len;
+       buflen -= len;
+    }
+
+    snprintf (bp, buflen, "\n    using %sFTP from site %s",
+                   e->eb_flags ? "anonymous " : "", e->eb_site);
+    len = strlen (bp);
+    bp += len;
+    buflen -= len;
+
+    if (e->eb_size > 0) {
+       snprintf (bp, buflen, " (%lu octets)", e->eb_size);
+       len = strlen (bp);
+       bp += len;
+       buflen -= len;
+    }
+    snprintf (bp, buflen, "? ");
+
+    /*
+     * Now, check the answer
+     */
+    if (!getanswer (buffer))
+       return NOTOK;
+
+    if (e->eb_flags) {
+       user = "anonymous";
+       snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
+       pass = buffer;
+    } else {
+       ruserpass (e->eb_site, &username, &password);
+       user = username;
+       pass = password;
+    }
+
+    ce->ce_unlink = (*file == NULL);
+    caching = 0;
+    cachefile[0] = '\0';
+    if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
+           && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
+               cachefile, sizeof(cachefile)) != NOTOK) {
+       if (*file == NULL) {
+           ce->ce_unlink = 0;
+           caching = 1;
+       }
+    }
+
+    if (*file)
+       ce->ce_file = add (*file, NULL);
+    else if (caching)
+       ce->ce_file = add (cachefile, NULL);
+    else
+       ce->ce_file = add (m_scratch ("", tmp), NULL);
+
+    if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
+       content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
+       return NOTOK;
+    }
+
+#ifdef BUILTIN_FTP
+    if (ftp)
+#endif
+    {
+       int child_id, i, vecp;
+       char *vec[9];
+
+       vecp = 0;
+       vec[vecp++] = r1bindex (ftp, '/');
+       vec[vecp++] = e->eb_site;
+       vec[vecp++] = user;
+       vec[vecp++] = pass;
+       vec[vecp++] = e->eb_dir;
+       vec[vecp++] = e->eb_name;
+       vec[vecp++] = ce->ce_file,
+       vec[vecp++] = e->eb_mode && !strcasecmp (e->eb_mode, "ascii")
+                       ? "ascii" : "binary";
+       vec[vecp] = NULL;
+
+       fflush (stdout);
+
+       for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
+           sleep (5);
+       switch (child_id) {
+           case NOTOK:
+               adios ("fork", "unable to");
+               /* NOTREACHED */
+
+           case OK:
+               close (fileno (ce->ce_fp));
+               execvp (ftp, vec);
+               fprintf (stderr, "unable to exec ");
+               perror (ftp);
+               _exit (-1);
+               /* NOTREACHED */
+
+           default:
+               if (pidXwait (child_id, NULL)) {
+#ifdef BUILTIN_FTP
+losing_ftp:
+#endif
+                   username = password = NULL;
+                   ce->ce_unlink = 1;
+                   return NOTOK;
+               }
+               break;
+       }
+    }
+#ifdef BUILTIN_FTP
+    else
+       if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
+                    ce->ce_file,
+                    e->eb_mode && !strcasecmp (e->eb_mode, "ascii"), 0)
+               == NOTOK)
+           goto losing_ftp;
+#endif
+
+    if (cachefile[0])
+       if (caching)
+           chmod (cachefile, cachetype ? m_gmprot () : 0444);
+       else {
+           int mask;
+           FILE *fp;
+
+           mask = umask (cachetype ? ~m_gmprot () : 0222);
+           if ((fp = fopen (cachefile, "w"))) {
+               int cc;
+               FILE *gp = ce->ce_fp;
+
+               fseek (gp, 0L, SEEK_SET);
+
+               while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
+                          > 0)
+                   fwrite (buffer, sizeof(*buffer), cc, fp);
+               fflush (fp);
+
+               if (ferror (gp)) {
+                   admonish (ce->ce_file, "error reading");
+                   unlink (cachefile);
+               }
+               else
+                   if (ferror (fp)) {
+                       admonish (cachefile, "error writing");
+                       unlink (cachefile);
+                   }
+               fclose (fp);
+           }
+           umask (mask);
+       }
+
+    fseek (ce->ce_fp, 0L, SEEK_SET);
+    *file = ce->ce_file;
+    return fileno (ce->ce_fp);
+}
+
+
+/*
+ * Mail
+ */
+
+static int
+InitMail (CT ct)
+{
+    return init_encoding (ct, openMail);
+}
+
+
+static int
+openMail (CT ct, char **file)
+{
+    int        child_id, fd, i, vecp;
+    int len, buflen;
+    char *bp, buffer[BUFSIZ], *vec[7];
+    struct exbody *e = ct->c_ctexbody;
+    CE ce = ct->c_cefile;
+
+    switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
+       case NOTOK:
+           return NOTOK;
+
+       case OK:
+           break;
+
+       case DONE:
+           return fd;
+    }
+
+    if (!e->eb_server) {
+       content_error (NULL, ct, "missing server parameter");
+       return NOTOK;
+    }
+
+    if (xpid) {
+       if (xpid < 0)
+           xpid = -xpid;
+       pidcheck (pidwait (xpid, NOTOK));
+       xpid = 0;
+    }
+
+    /* Get buffer ready to go */
+    bp = buffer;
+    buflen = sizeof(buffer);
+
+    /* Now, construct query message */
+    snprintf (bp, buflen, "Retrieve content");
+    len = strlen (bp);
+    bp += len;
+    buflen -= len;
+
+    if (e->eb_partno) {
+       snprintf (bp, buflen, " %s", e->eb_partno);
+       len = strlen (bp);
+       bp += len;
+       buflen -= len;
+    }
+
+    snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
+                   e->eb_server,
+                   e->eb_subject ? e->eb_subject : e->eb_body);
+
+    /* Now, check answer */
+    if (!getanswer (buffer))
+       return NOTOK;
+
+    vecp = 0;
+    vec[vecp++] = r1bindex (mailproc, '/');
+    vec[vecp++] = e->eb_server;
+    vec[vecp++] = "-subject";
+    vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
+    vec[vecp++] = "-body";
+    vec[vecp++] = e->eb_body;
+    vec[vecp] = NULL;
+
+    for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
+       sleep (5);
+    switch (child_id) {
+       case NOTOK:
+           advise ("fork", "unable to");
+           return NOTOK;
+
+       case OK:
+           execvp (mailproc, vec);
+           fprintf (stderr, "unable to exec ");
+           perror (mailproc);
+           _exit (-1);
+           /* NOTREACHED */
+
+       default:
+           if (pidXwait (child_id, NULL) == OK)
+               advise (NULL, "request sent");
+           break;
+    }
+
+    if (*file == NULL) {
+       ce->ce_file = add (m_scratch ("", tmp), NULL);
+       ce->ce_unlink = 1;
+    } else {
+       ce->ce_file = add (*file, NULL);
+       ce->ce_unlink = 0;
+    }
+
+    if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
+       content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
+       return NOTOK;
+    }
+
+    if (ct->c_showproc)
+       free (ct->c_showproc);
+    ct->c_showproc = add ("true", NULL);
+
+    fseek (ce->ce_fp, 0L, SEEK_SET);
+    *file = ce->ce_file;
+    return fileno (ce->ce_fp);
+}
+
+
+static int
+readDigest (CT ct, char *cp)
+{
+    int        bitno, skip;
+    unsigned long bits;
+    char *bp = cp;
+    unsigned char *dp, value, *ep;
+    unsigned char *b, *b1, *b2, *b3;
+
+    b  = (unsigned char *) &bits,
+    b1 = &b[endian > 0 ? 1 : 2],
+    b2 = &b[endian > 0 ? 2 : 1],
+    b3 = &b[endian > 0 ? 3 : 0];
+    bitno = 18;
+    bits = 0L;
+    skip = 0;
+
+    for (ep = (dp = ct->c_digest)
+                + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
+       switch (*cp) {
+           default:
+               if (skip
+                       || (*cp & 0x80)
+                       || (value = b642nib[*cp & 0x7f]) > 0x3f) {
+                   if (debugsw)
+                       fprintf (stderr, "invalid BASE64 encoding\n");
+                   return NOTOK;
+               }
+
+               bits |= value << bitno;
+test_end:
+               if ((bitno -= 6) < 0) {
+                   if (dp + (3 - skip) > ep)
+                       goto invalid_digest;
+                   *dp++ = *b1;
+                   if (skip < 2) {
+                       *dp++ = *b2;
+                       if (skip < 1)
+                           *dp++ = *b3;
+                   }
+                   bitno = 18;
+                   bits = 0L;
+                   skip = 0;
+               }
+               break;
+
+           case '=':
+               if (++skip > 3)
+                   goto self_delimiting;
+               goto test_end;
+       }
+    if (bitno != 18) {
+       if (debugsw)
+           fprintf (stderr, "premature ending (bitno %d)\n", bitno);
+
+       return NOTOK;
+    }
+self_delimiting:
+    if (dp != ep) {
+invalid_digest:
+       if (debugsw) {
+           while (*cp)
+               cp++;
+           fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
+                    cp - bp);
+       }
+
+       return NOTOK;
+    }
+
+    if (debugsw) {
+       fprintf (stderr, "MD5 digest=");
+       for (dp = ct->c_digest; dp < ep; dp++)
+           fprintf (stderr, "%02x", *dp & 0xff);
+       fprintf (stderr, "\n");
+    }
+
+    return OK;
+}
diff --git a/uip/mhpath.c b/uip/mhpath.c
new file mode 100644 (file)
index 0000000..ac1854e
--- /dev/null
@@ -0,0 +1,148 @@
+
+/*
+ * mhpath.c -- print full pathnames of nmh messages and folders
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+static struct swit switches[] = {
+#define VERSIONSW 0
+    { "version", 0 },
+#define        HELPSW  1
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+/*
+ * Add space for message/sequence names
+ * by MAXMSGS at a time.
+ */
+#define MAXMSGS 256
+
+
+int
+main(int argc, char **argv)
+{
+    int i, maxmsgs, nummsgs;
+    char *cp, *maildir, *folder = NULL;
+    char **argp, **msgs;
+    char **arguments, buf[BUFSIZ];
+    struct msgs *mp;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    /*
+     * Allocate initial space to record message
+     * and sequence names
+     */
+    nummsgs = 0;
+    maxmsgs = MAXMSGS;
+    if (!(msgs = (char **) malloc ((size_t) (maxmsgs * sizeof(*msgs)))))
+       adios (NULL, "unable to allocate storage");
+
+    /*
+     * Parse arguments
+     */
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown", cp);
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf), "%s [+folder] [msgs] [switches]",
+                       invo_name);
+                   print_help (buf, switches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+           }
+       }
+       if (*cp == '+' || *cp == '@') {
+           if (folder)
+               adios (NULL, "only one folder at a time!");
+           else
+               folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       } else {
+           /*
+            * if necessary, reallocate space for
+            * message/sequence names
+            */
+           if (nummsgs >= maxmsgs) {
+               maxmsgs += MAXMSGS;
+               if (!(msgs = (char **) realloc (msgs,
+                       (size_t) (maxmsgs * sizeof(*msgs)))))
+                   adios (NULL, "unable to reallocate msgs storage ");
+           }
+           msgs[nummsgs++] = cp;
+       }
+    }
+
+    if (!context_find ("path"))
+       free (path ("./", TFOLDER));
+
+    if (!folder)
+       folder = getfolder (1);
+    maildir = m_maildir (folder);
+
+    /* If no messages are given, print folder pathname */
+    if (!nummsgs) {
+       printf ("%s\n", maildir);
+       done (0);
+    }
+
+    if (chdir (maildir) == NOTOK)
+       adios (maildir, "unable to change directory to");
+
+    /* read folder and create message structure */
+    if (!(mp = folder_read (folder)))
+       adios (NULL, "unable to read folder %s", folder);
+
+    /*
+     * We need to make sure there is message status space
+     * for all the message numbers from 1 to "new" since
+     * mhpath can select empty slots.  If we are adding
+     * space at the end, we go ahead and add 10 slots.
+     */
+    if (mp->hghmsg >= mp->hghoff) {
+       if (!(mp = folder_realloc (mp, 1, mp->hghmsg + 10)))
+           adios (NULL, "unable to allocate folder storage");
+    } else if (mp->lowoff > 1) {
+       if (!(mp = folder_realloc (mp, 1, mp->hghoff)))
+           adios (NULL, "unable to allocate folder storage");
+    }
+
+    mp->msgflags |= ALLOW_NEW; /* allow the "new" sequence */
+
+    /* parse all the message ranges/sequences and set SELECTED */
+    for (i = 0; i < nummsgs; i++)
+       if (!m_convert (mp, msgs[i]))
+           done (1);
+
+    seq_setprev (mp);  /* set the previous-sequence */
+
+    /* print the path of all selected messages */
+    for (i = mp->lowsel; i <= mp->hghsel; i++)
+       if (is_selected (mp, i))
+           printf ("%s/%s\n", mp->foldpath, m_name (i));
+
+    seq_save (mp);     /* synchronize message sequences */
+    context_save ();   /* save the context file         */
+    folder_free (mp);  /* free folder/message structure */
+    done (0);
+}
diff --git a/uip/mhshow.c b/uip/mhshow.c
new file mode 100644 (file)
index 0000000..ff2bee8
--- /dev/null
@@ -0,0 +1,508 @@
+
+/*
+ * mhshow.c -- display the contents of MIME messages
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+#include <h/signals.h>
+#include <h/md5.h>
+#include <errno.h>
+#include <signal.h>
+#include <zotnet/mts/mts.h>
+#include <zotnet/tws/tws.h>
+#include <h/mime.h>
+#include <h/mhparse.h>
+#include <h/mhcachesbr.h>
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+/*
+ * We allocate space for message names (msgs array)
+ * this number of elements at a time.
+ */
+#define MAXMSGS  256
+
+
+static struct swit switches[] = {
+#define        CHECKSW                 0
+    { "check", 0 },
+#define        NCHECKSW                1
+    { "nocheck", 0 },
+#define        PAUSESW                 2
+    { "pause", 0 },
+#define        NPAUSESW                3
+    { "nopause", 0 },
+#define        SERIALSW                4
+    { "serialonly", 0 },
+#define        NSERIALSW               5
+    { "noserialonly", 0 },
+#define        VERBSW                  6
+    { "verbose", 0 },
+#define        NVERBSW                 7
+    { "noverbose", 0 },
+#define        FILESW                  8       /* interface from show */
+    { "file file", 0 },
+#define        FORMSW                  9
+    { "form formfile", 0 },
+#define        PARTSW                 10
+    { "part number", 0 },
+#define        TYPESW                 11
+    { "type content", 0 },
+#define        RCACHESW               12
+    { "rcache policy", 0 },
+#define        WCACHESW               13
+    { "wcache policy", 0 },
+#define VERSIONSW              14
+    { "version", 0 },
+#define        HELPSW                 15
+    { "help", 4 },
+
+/*
+ * switches for moreproc/mhlproc
+ */
+#define        PROGSW                 16
+    { "moreproc program", -4 },
+#define        NPROGSW                17
+    { "nomoreproc", -3 },
+#define        LENSW                  18
+    { "length lines", -4 },
+#define        WIDTHSW                19
+    { "width columns", -4 },
+
+/*
+ * switches for debugging
+ */
+#define        DEBUGSW                20
+    { "debug", -5 },
+    { NULL, 0 }
+};
+
+
+extern int errno;
+
+/* mhparse.c */
+extern int checksw;
+extern char *tmp;      /* directory to place temp files */
+
+/* mhcachesbr.c */
+extern int rcachesw;
+extern int wcachesw;
+extern char *cache_public;
+extern char *cache_private;
+
+/* mhshowsbr.c */
+extern int pausesw;
+extern int serialsw;
+extern char *progsw;
+extern int nolist;
+extern int nomore;     /* flags for moreproc/header display */
+extern char *formsw;
+
+/* mhmisc.c */
+extern int npart;
+extern int ntype;
+extern char *parts[NPARTS + 1];
+extern char *types[NTYPES + 1];
+extern int userrs;
+
+int debugsw = 0;
+int verbosw = 0;
+
+/* The list of top-level contents to display */
+CT *cts = NULL;
+
+#define        quitser pipeser
+
+/* mhparse.c */
+CT parse_mime (char *);
+
+/* mhmisc.c */
+int part_ok (CT, int);
+int type_ok (CT, int);
+void set_endian (void);
+void flush_errors (void);
+
+/* mhshowsbr.c */
+void show_all_messages (CT *);
+
+/* mhfree.c */
+void free_content (CT);
+
+/*
+ * static prototypes
+ */
+static RETSIGTYPE pipeser (int);
+
+
+int
+main (int argc, char **argv)
+{
+    int nummsgs, maxmsgs, msgnum, *icachesw;
+    char *cp, *file = NULL, *folder = NULL;
+    char *maildir, buf[100], **argp;
+    char **arguments, **msgs;
+    struct msgs *mp = NULL;
+    CT ct, *ctp;
+    FILE *fp;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    /*
+     * Allocate the initial space to record message
+     * names, ranges, and sequences.
+     */
+    nummsgs = 0;
+    maxmsgs = MAXMSGS;
+    if (!(msgs = (char **) malloc ((size_t) (maxmsgs * sizeof(*msgs)))))
+       adios (NULL, "unable to allocate storage");
+
+    /*
+     * Parse arguments
+     */
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+           case AMBIGSW: 
+               ambigsw (cp, switches);
+               done (1);
+           case UNKWNSW: 
+               adios (NULL, "-%s unknown", cp);
+
+           case HELPSW: 
+               snprintf (buf, sizeof(buf), "%s [+folder] [msgs] [switches]",
+                       invo_name);
+               print_help (buf, switches, 1);
+               done (1);
+           case VERSIONSW:
+               print_version(invo_name);
+               done (1);
+
+           case RCACHESW:
+               icachesw = &rcachesw;
+               goto do_cache;
+           case WCACHESW:
+               icachesw = &wcachesw;
+do_cache:
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               switch (*icachesw = smatch (cp, caches)) {
+               case AMBIGSW:
+                   ambigsw (cp, caches);
+                   done (1);
+               case UNKWNSW:
+                   adios (NULL, "%s unknown", cp);
+               default:
+                   break;
+               }
+               continue;
+
+           case CHECKSW:
+               checksw++;
+               continue;
+           case NCHECKSW:
+               checksw = 0;
+               continue;
+
+           case PAUSESW:
+               pausesw = 1;
+               continue;
+           case NPAUSESW:
+               pausesw = 0;
+               continue;
+
+           case SERIALSW:
+               serialsw = 1;
+               continue;
+           case NSERIALSW:
+               serialsw = 0;
+               continue;
+
+           case PARTSW:
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               if (npart >= NPARTS)
+                   adios (NULL, "too many parts (starting with %s), %d max",
+                          cp, NPARTS);
+               parts[npart++] = cp;
+               continue;
+
+           case TYPESW:
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               if (ntype >= NTYPES)
+                   adios (NULL, "too many types (starting with %s), %d max",
+                          cp, NTYPES);
+               types[ntype++] = cp;
+               continue;
+
+           case FILESW:
+               if (!(cp = *argp++) || (*cp == '-' && cp[1]))
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               file = *cp == '-' ? cp : path (cp, TFILE);
+               continue;
+
+           case FORMSW:
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               if (formsw)
+                   free (formsw);
+               formsw = getcpy (etcpath (cp));
+               continue;
+
+           /*
+            * Switches for moreproc/mhlproc
+            */
+           case PROGSW:
+               if (!(progsw = *argp++) || *progsw == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               continue;
+           case NPROGSW:
+               nomore++;
+               continue;
+
+           case LENSW:
+           case WIDTHSW:
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               continue;
+
+           case VERBSW: 
+               verbosw = 1;
+               continue;
+           case NVERBSW: 
+               verbosw = 0;
+               continue;
+           case DEBUGSW:
+               debugsw = 1;
+               continue;
+           }
+       }
+       if (*cp == '+' || *cp == '@') {
+           if (folder)
+               adios (NULL, "only one folder at a time!");
+           else
+               folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       } else {
+           /*
+            * Check if we need to allocate more space
+            * for message names/ranges/sequences.
+            */
+           if (nummsgs >= maxmsgs) {
+               maxmsgs += MAXMSGS;
+               if (!(msgs = (char **) realloc (msgs,
+                       (size_t) (maxmsgs * sizeof(*msgs)))))
+                   adios (NULL, "unable to reallocate msgs storage");
+           }
+           msgs[nummsgs++] = cp;
+       }
+    }
+
+    /* null terminate the list of acceptable parts/types */
+    parts[npart] = NULL;
+    types[ntype] = NULL;
+
+    set_endian ();
+
+    if ((cp = getenv ("MM_NOASK")) && !strcmp (cp, "1")) {
+       nolist  = 1;
+       pausesw = 0;
+    }
+
+    /*
+     * Check if we've specified an additional profile
+     */
+    if ((cp = getenv ("MHSHOW"))) {
+       if ((fp = fopen (cp, "r"))) {
+           readconfig ((struct node **) 0, fp, cp, 0);
+           fclose (fp);
+       } else {
+           admonish ("", "unable to read $MHSHOW profile (%s)", cp);
+       }
+    }
+
+    /*
+     * Read the standard profile setup
+     */
+    if ((fp = fopen (cp = etcpath ("mhn.defaults"), "r"))) {
+       readconfig ((struct node **) 0, fp, cp, 0);
+       fclose (fp);
+    }
+
+    /* Check for public cache location */
+    if ((cache_public = context_find (nmhcache)) && *cache_public != '/')
+       cache_public = NULL;
+
+    /* Check for private cache location */
+    if (!(cache_private = context_find (nmhprivcache)))
+       cache_private = ".cache";
+    cache_private = getcpy (m_maildir (cache_private));
+
+    /*
+     * Check for storage directory.  If specified,
+     * then store temporary files there.  Else we
+     * store them in standard nmh directory.
+     */
+    if ((cp = context_find (nmhstorage)) && *cp)
+       tmp = concat (cp, "/", invo_name, NULL);
+    else
+       tmp = add (m_maildir (invo_name), NULL);
+
+    if (!context_find ("path"))
+       free (path ("./", TFOLDER));
+
+    if (file && nummsgs)
+       adios (NULL, "cannot specify msg and file at same time!");
+
+    /*
+     * check if message is coming from file
+     */
+    if (file) {
+       if (!(cts = (CT *) calloc ((size_t) 2, sizeof(*cts))))
+           adios (NULL, "out of memory");
+       ctp = cts;
+
+       if ((ct = parse_mime (file)));
+           *ctp++ = ct;
+    } else {
+       /*
+        * message(s) are coming from a folder
+        */
+       if (!nummsgs)
+           msgs[nummsgs++] = "cur";
+       if (!folder)
+           folder = getfolder (1);
+       maildir = m_maildir (folder);
+
+       if (chdir (maildir) == NOTOK)
+           adios (maildir, "unable to change directory to");
+
+       /* read folder and create message structure */
+       if (!(mp = folder_read (folder)))
+           adios (NULL, "unable to read folder %s", folder);
+
+       /* check for empty folder */
+       if (mp->nummsg == 0)
+           adios (NULL, "no messages in %s", folder);
+
+       /* parse all the message ranges/sequences and set SELECTED */
+       for (msgnum = 0; msgnum < nummsgs; msgnum++)
+           if (!m_convert (mp, msgs[msgnum]))
+               done (1);
+
+       /*
+        * Set the SELECT_UNSEEN bit for all the SELECTED messages,
+        * since we will use that as a tag to know which messages
+        * to remove from the "unseen" sequence.
+        */
+       for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
+           if (is_selected(mp, msgnum))
+               set_unseen (mp, msgnum);
+
+       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))))
+           adios (NULL, "out of memory");
+       ctp = cts;
+
+       /*
+        * Parse all the SELECTED messages.
+        */
+       for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
+           if (is_selected(mp, msgnum)) {
+               char *msgnam;
+
+               msgnam = m_name (msgnum);
+               if ((ct = parse_mime (msgnam)))
+                   *ctp++ = ct;
+           }
+       }
+    }
+
+    if (!*cts)
+       done (1);
+
+    userrs = 1;
+    SIGNAL (SIGQUIT, quitser);
+    SIGNAL (SIGPIPE, pipeser);
+
+    /*
+     * Get the associated umask for the relevant contents.
+     */
+    for (ctp = cts; *ctp; ctp++) {
+       struct stat st;
+
+       ct = *ctp;
+       if (type_ok (ct, 1) && !ct->c_umask) {
+           if (stat (ct->c_file, &st) != NOTOK)
+               ct->c_umask = ~(st.st_mode & 0777);
+           else
+               ct->c_umask = ~m_gmprot();
+       }
+    }
+
+    /*
+     * Show the message content
+     */
+    show_all_messages (cts);
+
+    /* Now free all the structures for the content */
+    for (ctp = cts; *ctp; ctp++)
+       free_content (*ctp);
+
+    free ((char *) cts);
+    cts = NULL;
+
+    /* If reading from a folder, do some updating */
+    if (mp) {
+       context_replace (pfolder, folder);/* update current folder  */
+       seq_setcur (mp, mp->hghsel);      /* update current message */
+       seq_save (mp);                    /* synchronize sequences  */
+       context_save ();                  /* save the context file  */
+    }
+
+    done (0);
+    /* NOTREACHED */
+}
+
+
+static RETSIGTYPE
+pipeser (int i)
+{
+    if (i == SIGQUIT) {
+       unlink ("core");
+       fflush (stdout);
+       fprintf (stderr, "\n");
+       fflush (stderr);
+    }
+
+    done (1);
+    /* NOTREACHED */
+}
+
+
+void
+done (int status)
+{
+    CT *ctp;
+
+    if ((ctp = cts))
+       for (; *ctp; ctp++)
+           free_content (*ctp);
+
+    exit (status);
+}
diff --git a/uip/mhshowsbr.c b/uip/mhshowsbr.c
new file mode 100644 (file)
index 0000000..a95ff7d
--- /dev/null
@@ -0,0 +1,1014 @@
+
+/*
+ * mhshowsbr.c -- routines to display the contents of MIME messages
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+#include <h/signals.h>
+#include <h/md5.h>
+#include <errno.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <zotnet/mts/mts.h>
+#include <zotnet/tws/tws.h>
+#include <h/mime.h>
+#include <h/mhparse.h>
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+/*
+ * Just use sigjmp/longjmp on older machines that
+ * don't have sigsetjmp/siglongjmp.
+ */
+#ifndef HAVE_SIGSETJMP
+# define sigjmp_buf jmp_buf
+# define sigsetjmp(env,mask) setjmp(env)
+# define siglongjmp(env,val) longjmp(env,val)
+#endif
+
+extern int errno;
+extern int debugsw;
+
+int pausesw  = 1;
+int serialsw = 0;
+int nolist   = 0;
+
+char *progsw = NULL;
+
+/* flags for moreproc/header display */
+int nomore   = 0;
+char *formsw = NULL;
+
+pid_t xpid = 0;
+
+static sigjmp_buf intrenv;
+
+
+/* termsbr.c */
+int SOprintf (char *, ...);
+
+/* mhparse.c */
+int pidcheck (int);
+
+/* mhmisc.c */
+int part_ok (CT, int);
+int type_ok (CT, int);
+void content_error (char *, CT, char *, ...);
+void flush_errors (void);
+
+/* mhlistsbr.c */
+int list_switch (CT, int, int, int, int);
+int list_content (CT, int, int, int, int);
+
+/*
+ * prototypes
+ */
+void show_all_messages (CT *);
+int show_content_aux (CT, int, int, char *, char *);
+
+/*
+ * static prototypes
+ */
+static void show_single_message (CT, char *);
+static void DisplayMsgHeader (CT, char *);
+static int show_switch (CT, int, int);
+static int show_content (CT, int, int);
+static int show_content_aux2 (CT, int, int, char *, char *, int, int, int, int, int);
+static int show_text (CT, int, int);
+static int show_multi (CT, int, int);
+static int show_multi_internal (CT, int, int);
+static int show_multi_aux (CT, int, int, char *);
+static int show_message_rfc822 (CT, int, int);
+static int show_partial (CT, int, int);
+static int show_external (CT, int, int);
+static RETSIGTYPE intrser (int);
+
+
+/*
+ * Top level entry point to show/display a group of messages
+ */
+
+void
+show_all_messages (CT *cts)
+{
+    CT ct, *ctp;
+
+    /*
+     * If form is not specified, then get default form
+     * for showing headers of MIME messages.
+     */
+    if (!formsw)
+       formsw = getcpy (etcpath ("mhl.headers"));
+
+    /*
+     * If form is "mhl.null", suppress display of header.
+     */
+    if (!strcmp (formsw, "mhl.null"))
+       formsw = NULL;
+
+    for (ctp = cts; *ctp; ctp++) {
+       ct = *ctp;
+
+       /* if top-level type is ok, then display message */
+       if (type_ok (ct, 0))
+           show_single_message (ct, formsw);
+    }
+}
+
+
+/*
+ * Entry point to show/display a single message
+ */
+
+static void
+show_single_message (CT ct, char *form)
+{
+    sigset_t set, oset;
+
+#ifdef WAITINT
+    int status;
+#else
+    union wait status;
+#endif
+
+    umask (ct->c_umask);
+
+    /*
+     * If you have a format file, then display
+     * the message headers.
+     */
+    if (form)
+       DisplayMsgHeader(ct, form);
+    else
+       xpid = 0;
+
+    /* Show the body of the message */
+    show_switch (ct, 1, 0);
+
+    if (ct->c_fp) {
+       fclose (ct->c_fp);
+       ct->c_fp = NULL;
+    }
+    if (ct->c_ceclosefnx)
+       (*ct->c_ceclosefnx) (ct);
+
+    /* block a few signals */
+    sigemptyset (&set);
+    sigaddset (&set, SIGHUP);
+    sigaddset (&set, SIGINT);
+    sigaddset (&set, SIGQUIT);
+    sigaddset (&set, SIGTERM);
+    SIGPROCMASK (SIG_BLOCK, &set, &oset);
+
+    while (wait (&status) != NOTOK) {
+#ifdef WAITINT
+       pidcheck (status);
+#else
+       pidcheck (status.w_status);
+#endif
+       continue;
+    }
+
+    /* reset the signal mask */
+    SIGPROCMASK (SIG_SETMASK, &oset, &set);
+
+    xpid = 0;
+    flush_errors ();
+}
+
+
+/*
+ * Use the mhlproc to show the header fields
+ */
+
+static void
+DisplayMsgHeader (CT ct, char *form)
+{
+    pid_t child_id;
+    int i, vecp;
+    char *vec[8];
+
+    vecp = 0;
+    vec[vecp++] = r1bindex (mhlproc, '/');
+    vec[vecp++] = "-form";
+    vec[vecp++] = form;
+    vec[vecp++] = "-nobody";
+    vec[vecp++] = ct->c_file;
+
+    /*
+     * If we've specified -(no)moreproc,
+     * then just pass that along.
+     */
+    if (nomore) {
+       vec[vecp++] = "-nomoreproc";
+    } else if (progsw) {
+       vec[vecp++] = "-moreproc";
+       vec[vecp++] = progsw;
+    }
+    vec[vecp] = NULL;
+
+    fflush (stdout);
+
+    for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
+       sleep (5);
+
+    switch (child_id) {
+    case NOTOK:
+       adios ("fork", "unable to");
+       /* NOTREACHED */
+
+    case OK:
+       execvp (mhlproc, vec);
+       fprintf (stderr, "unable to exec ");
+       perror (mhlproc);
+       _exit (-1);
+       /* NOTREACHED */
+
+    default:
+       xpid = -child_id;
+       break;
+    }
+}
+
+
+/*
+ * Switching routine.  Call the correct routine
+ * based on content type.
+ */
+
+static int
+show_switch (CT ct, int serial, int alternate)
+{
+    switch (ct->c_type) {
+       case CT_MULTIPART:
+           return show_multi (ct, serial, alternate);
+           break;
+
+       case CT_MESSAGE:
+           switch (ct->c_subtype) {
+               case MESSAGE_PARTIAL:
+                   return show_partial (ct, serial, alternate);
+                   break;
+
+               case MESSAGE_EXTERNAL:
+                   return show_external (ct, serial, alternate);
+                   break;
+
+               case MESSAGE_RFC822:
+               default:
+                   return show_message_rfc822 (ct, serial, alternate);
+                   break;
+           }
+           break;
+
+       case CT_TEXT:
+           return show_text (ct, serial, alternate);
+           break;
+
+       case CT_AUDIO:
+       case CT_IMAGE:
+       case CT_VIDEO:
+       case CT_APPLICATION:
+           return show_content (ct, serial, alternate);
+           break;
+
+       default:
+           adios (NULL, "unknown content type %d", ct->c_type);
+           break;
+    }
+
+    return 0;  /* NOT REACHED */
+}
+
+
+/*
+ * Generic method for displaying content
+ */
+
+static int
+show_content (CT ct, int serial, int alternate)
+{
+    char *cp, buffer[BUFSIZ];
+    CI ci = &ct->c_ctinfo;
+
+    /* Check for mhn-show-type/subtype */
+    snprintf (buffer, sizeof(buffer), "%s-show-%s/%s",
+               invo_name, ci->ci_type, ci->ci_subtype);
+    if ((cp = context_find (buffer)) && *cp != '\0')
+       return show_content_aux (ct, serial, alternate, cp, NULL);
+
+    /* Check for mhn-show-type */
+    snprintf (buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
+    if ((cp = context_find (buffer)) && *cp != '\0')
+       return show_content_aux (ct, serial, alternate, cp, NULL);
+
+    if ((cp = ct->c_showproc))
+       return show_content_aux (ct, serial, alternate, cp, NULL);
+
+    /* complain if we are not a part of a multipart/alternative */
+    if (!alternate)
+       content_error (NULL, ct, "don't know how to display content");
+
+    return NOTOK;
+}
+
+
+/*
+ * Parse the display string for displaying generic content
+ */
+
+int
+show_content_aux (CT ct, int serial, int alternate, char *cp, char *cracked)
+{
+    int fd, len, buflen;
+    int        xstdin, xlist, xpause, xtty;
+    char *bp, *file, buffer[BUFSIZ];
+    CI ci = &ct->c_ctinfo;
+
+    if (!ct->c_ceopenfnx) {
+       if (!alternate)
+           content_error (NULL, ct, "don't know how to decode content");
+
+       return NOTOK;
+    }
+
+    file = NULL;
+    if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
+       return NOTOK;
+    if (ct->c_showproc && !strcmp (ct->c_showproc, "true"))
+       return (alternate ? DONE : OK);
+    
+    xlist  = 0;
+    xpause = 0;
+    xstdin = 0;
+    xtty   = 0;
+
+    if (cracked) {
+       strncpy (buffer, cp, sizeof(buffer));
+       goto got_command;
+    }
+
+    /* get buffer ready to go */
+    bp = buffer;
+    bp[0] = '\0';
+    buflen = sizeof(buffer);
+
+    /* Now parse display string */
+    for ( ; *cp; cp++) {
+       if (*cp == '%') {
+           switch (*++cp) {
+           case 'a':
+               /* insert parameters from Content-Type field */
+           {
+               char **ap, **ep;
+               char *s = "";
+
+               for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
+                   snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
+                   len = strlen (bp);
+                   bp += len;
+                   buflen -= len;
+                   s = " ";
+               }
+           }
+           break;
+
+           case 'd':
+               /* insert content description */
+               if (ct->c_descr) {
+                   char *s;
+
+                   s = trimcpy (ct->c_descr);
+                   strncpy (bp, s, buflen);
+                   free (s);
+               }
+               break;
+
+           case 'e':
+               /* exclusive execution */
+               xtty = 1;
+               break;
+
+           case 'F':
+               /* %e, %f, and stdin is terminal not content */
+               xstdin = 1;
+               xtty = 1;
+               /* and fall... */
+
+           case 'f':
+               /* insert filename containing content */
+               snprintf (bp, buflen, "%s", file);
+               break;
+
+           case 'p':
+               /* %l, and pause prior to displaying content */
+               xpause = pausesw;
+               /* and fall... */
+
+           case 'l':
+               /* display listing prior to displaying content */
+               xlist = !nolist;
+               break;
+
+           case 's':
+               /* insert subtype of content */
+               strncpy (bp, ci->ci_subtype, buflen);
+               break;
+
+           case '%':
+               /* insert character % */
+               goto raw;
+
+           default:
+               *bp++ = *--cp;
+               *bp = '\0';
+               buflen--;
+               continue;
+           }
+           len = strlen (bp);
+           bp += len;
+           buflen -= len;
+       } else {
+raw:
+       *bp++ = *cp;
+       *bp = '\0';
+       buflen--;
+       }
+    }
+
+    /* use charset string to modify display method */
+    if (ct->c_termproc) {
+       char term[BUFSIZ];
+
+       strncpy (term, buffer, sizeof(term));
+       snprintf (buffer, sizeof(buffer), ct->c_termproc, term);
+    }
+
+got_command:
+    return show_content_aux2 (ct, serial, alternate, cracked, buffer,
+                             fd, xlist, xpause, xstdin, xtty);
+}
+
+
+/*
+ * Routine to actually display the content
+ */
+
+static int
+show_content_aux2 (CT ct, int serial, int alternate, char *cracked, char *buffer,
+                   int fd, int xlist, int xpause, int xstdin, int xtty)
+{
+    pid_t child_id;
+    int i;
+    char *vec[4], exec[BUFSIZ + sizeof "exec "];
+    
+    if (debugsw || cracked) {
+       fflush (stdout);
+
+       fprintf (stderr, "%s msg %s", cracked ? "storing" : "show",
+                ct->c_file);
+       if (ct->c_partno)
+           fprintf (stderr, " part %s", ct->c_partno);
+       if (cracked)
+           fprintf (stderr, " using command (cd %s; %s)\n", cracked, buffer);
+       else
+           fprintf (stderr, " using command %s\n", buffer);
+    }
+
+    if (xpid < 0 || (xtty && xpid)) {
+       if (xpid < 0)
+           xpid = -xpid;
+       pidcheck(pidwait (xpid, NOTOK));
+       xpid = 0;
+    }
+
+    if (xlist) {
+       char prompt[BUFSIZ];
+
+       if (ct->c_type == CT_MULTIPART)
+           list_content (ct, -1, 1, 0, 0);
+       else
+           list_switch (ct, -1, 1, 0, 0);
+
+       if (xpause && SOprintf ("Press <return> to show content..."))
+           printf ("Press <return> to show content...");
+
+       if (xpause) {
+           int intr;
+           SIGNAL_HANDLER istat;
+
+           istat = SIGNAL (SIGINT, intrser);
+           if ((intr = sigsetjmp (intrenv, 1)) == OK) {
+               fflush (stdout);
+               prompt[0] = 0;
+               read (fileno (stdout), prompt, sizeof(prompt));
+           }
+           SIGNAL (SIGINT, istat);
+           if (intr != OK) {
+               (*ct->c_ceclosefnx) (ct);
+               return (alternate ? DONE : NOTOK);
+           }
+       }
+    }
+
+    snprintf (exec, sizeof(exec), "exec %s", buffer);
+
+    vec[0] = "/bin/sh";
+    vec[1] = "-c";
+    vec[2] = exec;
+    vec[3] = NULL;
+
+    fflush (stdout);
+
+    for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
+       sleep (5);
+    switch (child_id) {
+       case NOTOK:
+           advise ("fork", "unable to");
+           (*ct->c_ceclosefnx) (ct);
+           return NOTOK;
+
+       case OK:
+           if (cracked)
+               chdir (cracked);
+           if (!xstdin)
+               dup2 (fd, 0);
+           close (fd);
+           execvp ("/bin/sh", vec);
+           fprintf (stderr, "unable to exec ");
+           perror ("/bin/sh");
+           _exit (-1);
+           /* NOTREACHED */
+
+       default:
+           if (!serial) {
+               ct->c_pid = child_id;
+               if (xtty)
+                   xpid = child_id;
+           } else {
+               pidcheck (pidXwait (child_id, NULL));
+           }
+
+           if (fd != NOTOK)
+               (*ct->c_ceclosefnx) (ct);
+           return (alternate ? DONE : OK);
+    }
+}
+
+
+/*
+ * show content of type "text"
+ */
+
+static int
+show_text (CT ct, int serial, int alternate)
+{
+    char *cp, buffer[BUFSIZ];
+    CI ci = &ct->c_ctinfo;
+
+    /* Check for mhn-show-type/subtype */
+    snprintf (buffer, sizeof(buffer), "%s-show-%s/%s",
+               invo_name, ci->ci_type, ci->ci_subtype);
+    if ((cp = context_find (buffer)) && *cp != '\0')
+       return show_content_aux (ct, serial, alternate, cp, NULL);
+
+    /* Check for mhn-show-type */
+    snprintf (buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
+    if ((cp = context_find (buffer)) && *cp != '\0')
+       return show_content_aux (ct, serial, alternate, cp, NULL);
+
+    /*
+     * Use default method if content is text/plain, or if
+     * if it is not a text part of a multipart/alternative
+     */
+    if (!alternate || ct->c_subtype == TEXT_PLAIN) {
+       snprintf (buffer, sizeof(buffer), "%%p%s '%%F'", progsw ? progsw :
+               moreproc && *moreproc ? moreproc : "more");
+       cp = (ct->c_showproc = add (buffer, NULL));
+       return show_content_aux (ct, serial, alternate, cp, NULL);
+    }
+
+    return NOTOK;
+}
+
+
+/*
+ * show message body of type "multipart"
+ */
+
+static int
+show_multi (CT ct, int serial, int alternate)
+{
+    char *cp, buffer[BUFSIZ];
+    CI ci = &ct->c_ctinfo;
+
+    /* Check for mhn-show-type/subtype */
+    snprintf (buffer, sizeof(buffer), "%s-show-%s/%s",
+               invo_name, ci->ci_type, ci->ci_subtype);
+    if ((cp = context_find (buffer)) && *cp != '\0')
+       return show_multi_aux (ct, serial, alternate, cp);
+
+    /* Check for mhn-show-type */
+    snprintf (buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
+    if ((cp = context_find (buffer)) && *cp != '\0')
+       return show_multi_aux (ct, serial, alternate, cp);
+
+    if ((cp = ct->c_showproc))
+       return show_multi_aux (ct, serial, alternate, cp);
+
+    /*
+     * Use default method to display this multipart content
+     * if it is not a (nested) part of a multipart/alternative,
+     * or if it is one of the known subtypes of multipart.
+     */
+    if (!alternate || ct->c_subtype != MULTI_UNKNOWN)
+       return show_multi_internal (ct, serial, alternate);
+
+    return NOTOK;
+}
+
+
+/*
+ * show message body of subtypes of multipart that
+ * we understand directly (mixed, alternate, etc...)
+ */
+
+static int
+show_multi_internal (CT ct, int serial, int alternate)
+{
+    int        alternating, nowalternate, nowserial, result;
+    struct multipart *m = (struct multipart *) ct->c_ctparams;
+    struct part *part;
+    CT p;
+    sigset_t set, oset;
+
+    alternating = 0;
+    nowalternate = alternate;
+
+    if (ct->c_subtype == MULTI_PARALLEL) {
+       nowserial = serialsw;
+    } else if (ct->c_subtype == MULTI_ALTERNATE) {
+       nowalternate = 1;
+       alternating  = 1;
+       nowserial = serial;
+    } else {
+       /*
+        * multipart/mixed
+        * mutlipart/digest
+        * unknown subtypes of multipart (treat as mixed per rfc2046)
+        */
+       nowserial = serial;
+    }
+
+    /* block a few signals */
+    if (!nowserial) {
+       sigemptyset (&set);
+       sigaddset (&set, SIGHUP);
+       sigaddset (&set, SIGINT);
+       sigaddset (&set, SIGQUIT);
+       sigaddset (&set, SIGTERM);
+       SIGPROCMASK (SIG_BLOCK, &set, &oset);
+    }
+
+/*
+ * alternate   -> we are a part inside an multipart/alternative
+ * alternating -> we are a multipart/alternative 
+ */
+
+    result = alternate ? NOTOK : OK;
+
+    for (part = m->mp_parts; part; part = part->mp_next) {
+       p = part->mp_part;
+
+       if (part_ok (p, 0) && type_ok (p, 0)) {
+           int inneresult;
+
+           inneresult = show_switch (p, nowserial, nowalternate);
+           switch (inneresult) {
+               case NOTOK:
+                   if (alternate && !alternating) {
+                       result = NOTOK;
+                       goto out;
+                   }
+                   continue;
+
+               case OK:
+               case DONE:
+                   if (alternating) {
+                       result = DONE;
+                       break;
+                   }
+                   if (alternate) {
+                       alternate = nowalternate = 0;
+                       if (result == NOTOK)
+                           result = inneresult;
+                   }
+                   continue;
+           }
+           break;
+       }
+    }
+
+    if (alternating && !part) {
+       if (!alternate)
+           content_error (NULL, ct, "don't know how to display any of the contents");
+       result = NOTOK;
+       goto out;
+    }
+
+    if (serial && !nowserial) {
+       pid_t pid;
+       int kids;
+#ifdef WAITINT
+       int status;
+#else
+       union wait status;
+#endif
+
+       kids = 0;
+       for (part = m->mp_parts; part; part = part->mp_next) {
+           p = part->mp_part;
+
+           if (p->c_pid > OK)
+               if (kill (p->c_pid, 0) == NOTOK)
+                   p->c_pid = 0;
+               else
+                   kids++;
+       }
+
+       while (kids > 0 && (pid = wait (&status)) != NOTOK) {
+#ifdef WAITINT
+           pidcheck (status);
+#else
+           pidcheck (status.w_status);
+#endif
+
+           for (part = m->mp_parts; part; part = part->mp_next) {
+               p = part->mp_part;
+
+               if (xpid == pid)
+                   xpid = 0;
+               if (p->c_pid == pid) {
+                   p->c_pid = 0;
+                   kids--;
+                   break;
+               }
+           }
+       }
+    }
+
+out:
+    if (!nowserial) {
+       /* reset the signal mask */
+       SIGPROCMASK (SIG_SETMASK, &oset, &set);
+    }
+
+    return result;
+}
+
+
+/*
+ * Parse display string for multipart content
+ * and use external program to display it.
+ */
+
+static int
+show_multi_aux (CT ct, int serial, int alternate, char *cp)
+{
+    int len, buflen;
+    int xlist, xpause, xtty;
+    char *bp, *file, buffer[BUFSIZ];
+    struct multipart *m = (struct multipart *) ct->c_ctparams;
+    struct part *part;
+    CI ci = &ct->c_ctinfo;
+    CT p;
+
+    for (part = m->mp_parts; part; part = part->mp_next) {
+       p = part->mp_part;
+
+       if (!p->c_ceopenfnx) {
+           if (!alternate)
+               content_error (NULL, p, "don't know how to decode content");
+           return NOTOK;
+       }
+
+       if (p->c_storage == NULL) {
+           file = NULL;
+           if ((*p->c_ceopenfnx) (p, &file) == NOTOK)
+               return NOTOK;
+
+           /* I'm not sure if this is necessary? */
+           p->c_storage = add (file, NULL);
+
+           if (p->c_showproc && !strcmp (p->c_showproc, "true"))
+               return (alternate ? DONE : OK);
+           (*p->c_ceclosefnx) (p);
+       }
+    }
+
+    xlist     = 0;
+    xpause    = 0;
+    xtty      = 0;
+
+    /* get buffer ready to go */
+    bp = buffer;
+    bp[0] = '\0';
+    buflen = sizeof(buffer);
+
+    /* Now parse display string */
+    for ( ; *cp; cp++) {
+       if (*cp == '%') {
+           switch (*++cp) {
+           case 'a':
+               /* insert parameters from Content-Type field */
+           {
+               char **ap, **ep;
+               char *s = "";
+
+               for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
+                   snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
+                   len = strlen (bp);
+                   bp += len;
+                   buflen -= len;
+                   s = " ";
+               }
+           }
+           break;
+
+           case 'd':
+               /* insert content description */
+               if (ct->c_descr) {
+                   char *s;
+
+                   s = trimcpy (ct->c_descr);
+                   strncpy (bp, s, buflen);
+                   free (s);
+               }
+               break;
+
+           case 'e':
+               /* exclusive execution */
+               xtty = 1;
+               break;
+
+           case 'F':
+               /* %e and %f */
+               xtty = 1;
+               /* and fall... */
+
+           case 'f':
+               /* insert filename(s) containing content */
+           {
+               char *s = "";
+                       
+               for (part = m->mp_parts; part; part = part->mp_next) {
+                   p = part->mp_part;
+
+                   snprintf (bp, buflen, "%s'%s'", s, p->c_storage);
+                   len = strlen (bp);
+                   bp += len;
+                   buflen -= len;
+                   s = " ";
+               }
+           }
+           break;
+
+           case 'p':
+               /* %l, and pause prior to displaying content */
+               xpause = pausesw;
+               /* and fall... */
+
+           case 'l':
+               /* display listing prior to displaying content */
+               xlist = !nolist;
+               break;
+
+           case 's':
+               /* insert subtype of content */
+               strncpy (bp, ci->ci_subtype, buflen);
+               break;
+
+           case '%':
+               /* insert character % */
+               goto raw;
+
+           default:
+               *bp++ = *--cp;
+               *bp = '\0';
+               buflen--;
+               continue;
+           }
+           len = strlen (bp);
+           bp += len;
+           buflen -= len;
+       } else {
+raw:
+       *bp++ = *cp;
+       *bp = '\0';
+       buflen--;
+       }
+    }
+
+    /* use charset string to modify display method */
+    if (ct->c_termproc) {
+       char term[BUFSIZ];
+
+       strncpy (term, buffer, sizeof(term));
+       snprintf (buffer, sizeof(buffer), ct->c_termproc, term);
+    }
+
+    return show_content_aux2 (ct, serial, alternate, NULL, buffer,
+                             NOTOK, xlist, xpause, 0, xtty);
+}
+
+
+/*
+ * show content of type "message/rfc822"
+ */
+
+static int
+show_message_rfc822 (CT ct, int serial, int alternate)
+{
+    char *cp, buffer[BUFSIZ];
+    CI ci = &ct->c_ctinfo;
+
+    /* Check for mhn-show-type/subtype */
+    snprintf (buffer, sizeof(buffer), "%s-show-%s/%s",
+               invo_name, ci->ci_type, ci->ci_subtype);
+    if ((cp = context_find (buffer)) && *cp != '\0')
+       return show_content_aux (ct, serial, alternate, cp, NULL);
+
+    /* Check for mhn-show-type */
+    snprintf (buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
+    if ((cp = context_find (buffer)) && *cp != '\0')
+       return show_content_aux (ct, serial, alternate, cp, NULL);
+
+    if ((cp = ct->c_showproc))
+       return show_content_aux (ct, serial, alternate, cp, NULL);
+
+    /* default method for message/rfc822 */
+    if (ct->c_subtype == MESSAGE_RFC822) {
+       cp = (ct->c_showproc = add ("%pshow -file '%F'", NULL));
+       return show_content_aux (ct, serial, alternate, cp, NULL);
+    }
+
+    /* complain if we are not a part of a multipart/alternative */
+    if (!alternate)
+       content_error (NULL, ct, "don't know how to display content");
+
+    return NOTOK;
+}
+
+
+/*
+ * Show content of type "message/partial".
+ */
+
+static int
+show_partial (CT ct, int serial, int alternate)
+{
+    content_error (NULL, ct,
+       "in order to display this message, you must reassemble it");
+    return NOTOK;
+}
+
+
+/*
+ * Show content of type "message/external".
+ *
+ * THE ERROR CHECKING IN THIS ONE IS NOT DONE YET.
+ */
+
+static int
+show_external (CT ct, int serial, int alternate)
+{
+    struct exbody *e = (struct exbody *) ct->c_ctparams;
+    CT p = e->eb_content;
+
+    if (!type_ok (p, 0))
+       return OK;
+
+    return show_switch (p, serial, alternate);
+
+#if 0
+    content_error (NULL, p, "don't know how to display content");
+    return NOTOK;
+#endif
+}
+
+
+static RETSIGTYPE
+intrser (int i)
+{
+#ifndef RELIABLE_SIGNALS
+    SIGNAL (SIGINT, intrser);
+#endif
+
+    putchar ('\n');
+    siglongjmp (intrenv, DONE);
+}
diff --git a/uip/mhstore.c b/uip/mhstore.c
new file mode 100644 (file)
index 0000000..a52e00b
--- /dev/null
@@ -0,0 +1,440 @@
+
+/*
+ * mhstore.c -- store the contents of MIME messages
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+#include <h/signals.h>
+#include <h/md5.h>
+#include <errno.h>
+#include <signal.h>
+#include <zotnet/mts/mts.h>
+#include <zotnet/tws/tws.h>
+#include <h/mime.h>
+#include <h/mhparse.h>
+#include <h/mhcachesbr.h>
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+/*
+ * We allocate space for message names (msgs array)
+ * this number of elements at a time.
+ */
+#define MAXMSGS  256
+
+
+static struct swit switches[] = {
+#define        AUTOSW                  0
+    { "auto", 0 },
+#define        NAUTOSW                 1
+    { "noauto", 0 },
+#define        CHECKSW                 2
+    { "check", 0 },
+#define        NCHECKSW                3
+    { "nocheck", 0 },
+#define        VERBSW                  4
+    { "verbose", 0 },
+#define        NVERBSW                 5
+    { "noverbose", 0 },
+#define        FILESW                  6       /* interface from show */
+    { "file file", 0 },
+#define        PARTSW                  7
+    { "part number", 0 },
+#define        TYPESW                  8
+    { "type content", 0 },
+#define        RCACHESW                9
+    { "rcache policy", 0 },
+#define        WCACHESW               10
+    { "wcache policy", 0 },
+#define VERSIONSW              11
+    { "version", 0 },
+#define        HELPSW                 12
+    { "help", 4 },
+
+/*
+ * switches for debugging
+ */
+#define        DEBUGSW                13
+    { "debug", -5 },
+    { NULL, 0 }
+};
+
+
+extern int errno;
+
+/* mhparse.c */
+extern int checksw;
+extern char *tmp;      /* directory to place temp files */
+
+/* mhcachesbr.c */
+extern int rcachesw;
+extern int wcachesw;
+extern char *cache_public;
+extern char *cache_private;
+
+/* mhstoresbr.c */
+extern int autosw;
+extern char *cwd;      /* cache current working directory */
+
+/* mhmisc.c */
+extern int npart;
+extern int ntype;
+extern char *parts[NPARTS + 1];
+extern char *types[NTYPES + 1];
+extern int userrs;
+
+int debugsw = 0;
+int verbosw = 0;
+
+/* The list of top-level contents to display */
+CT *cts = NULL;
+
+#define        quitser pipeser
+
+/* mhparse.c */
+CT parse_mime (char *);
+
+/* mhmisc.c */
+int part_ok (CT, int);
+int type_ok (CT, int);
+void set_endian (void);
+void flush_errors (void);
+
+/* mhstoresbr.c */
+void store_all_messages (CT *);
+
+/* mhfree.c */
+void free_content (CT);
+
+/*
+ * static prototypes
+ */
+static RETSIGTYPE pipeser (int);
+
+
+int
+main (int argc, char **argv)
+{
+    int nummsgs, maxmsgs, msgnum, *icachesw;
+    char *cp, *file = NULL, *folder = NULL;
+    char *maildir, buf[100], **argp;
+    char **arguments, **msgs;
+    struct msgs *mp = NULL;
+    CT ct, *ctp;
+    FILE *fp;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    /*
+     * Allocate the initial space to record message
+     * names, ranges, and sequences.
+     */
+    nummsgs = 0;
+    maxmsgs = MAXMSGS;
+    if (!(msgs = (char **) malloc ((size_t) (maxmsgs * sizeof(*msgs)))))
+       adios (NULL, "unable to allocate storage");
+
+    /*
+     * Parse arguments
+     */
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+           case AMBIGSW: 
+               ambigsw (cp, switches);
+               done (1);
+           case UNKWNSW: 
+               adios (NULL, "-%s unknown", cp);
+
+           case HELPSW: 
+               snprintf (buf, sizeof(buf), "%s [+folder] [msgs] [switches]",
+                       invo_name);
+               print_help (buf, switches, 1);
+               done (1);
+           case VERSIONSW:
+               print_version(invo_name);
+               done (1);
+
+           case AUTOSW:
+               autosw++;
+               continue;
+           case NAUTOSW:
+               autosw = 0;
+               continue;
+
+           case RCACHESW:
+               icachesw = &rcachesw;
+               goto do_cache;
+           case WCACHESW:
+               icachesw = &wcachesw;
+do_cache:
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               switch (*icachesw = smatch (cp, caches)) {
+               case AMBIGSW:
+                   ambigsw (cp, caches);
+                   done (1);
+               case UNKWNSW:
+                   adios (NULL, "%s unknown", cp);
+               default:
+                   break;
+               }
+               continue;
+
+           case CHECKSW:
+               checksw++;
+               continue;
+           case NCHECKSW:
+               checksw = 0;
+               continue;
+
+           case PARTSW:
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               if (npart >= NPARTS)
+                   adios (NULL, "too many parts (starting with %s), %d max",
+                          cp, NPARTS);
+               parts[npart++] = cp;
+               continue;
+
+           case TYPESW:
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               if (ntype >= NTYPES)
+                   adios (NULL, "too many types (starting with %s), %d max",
+                          cp, NTYPES);
+               types[ntype++] = cp;
+               continue;
+
+           case FILESW:
+               if (!(cp = *argp++) || (*cp == '-' && cp[1]))
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               file = *cp == '-' ? cp : path (cp, TFILE);
+               continue;
+
+           case VERBSW: 
+               verbosw = 1;
+               continue;
+           case NVERBSW: 
+               verbosw = 0;
+               continue;
+           case DEBUGSW:
+               debugsw = 1;
+               continue;
+           }
+       }
+       if (*cp == '+' || *cp == '@') {
+           if (folder)
+               adios (NULL, "only one folder at a time!");
+           else
+               folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       } else {
+           /*
+            * Check if we need to allocate more space
+            * for message names/ranges/sequences.
+            */
+           if (nummsgs >= maxmsgs) {
+               maxmsgs += MAXMSGS;
+               if (!(msgs = (char **) realloc (msgs,
+                       (size_t) (maxmsgs * sizeof(*msgs)))))
+                   adios (NULL, "unable to reallocate msgs storage");
+           }
+           msgs[nummsgs++] = cp;
+       }
+    }
+
+    /* null terminate the list of acceptable parts/types */
+    parts[npart] = NULL;
+    types[ntype] = NULL;
+
+    set_endian ();
+
+    /*
+     * Check if we've specified an additional profile
+     */
+    if ((cp = getenv ("MHSTORE"))) {
+       if ((fp = fopen (cp, "r"))) {
+           readconfig ((struct node **) 0, fp, cp, 0);
+           fclose (fp);
+       } else {
+           admonish ("", "unable to read $MHSTORE profile (%s)", cp);
+       }
+    }
+
+    /*
+     * Read the standard profile setup
+     */
+    if ((fp = fopen (cp = etcpath ("mhn.defaults"), "r"))) {
+       readconfig ((struct node **) 0, fp, cp, 0);
+       fclose (fp);
+    }
+
+    /* Check for public cache location */
+    if ((cache_public = context_find (nmhcache)) && *cache_public != '/')
+       cache_public = NULL;
+
+    /* Check for private cache location */
+    if (!(cache_private = context_find (nmhprivcache)))
+       cache_private = ".cache";
+    cache_private = getcpy (m_maildir (cache_private));
+
+    /*
+     * Cache the current directory before we do any chdirs()'s.
+     */
+    cwd = getcpy (pwd());
+
+    /*
+     * Check for storage directory.  If specified,
+     * then store temporary files there.  Else we
+     * store them in standard nmh directory.
+     */
+    if ((cp = context_find (nmhstorage)) && *cp)
+       tmp = concat (cp, "/", invo_name, NULL);
+    else
+       tmp = add (m_maildir (invo_name), NULL);
+
+    if (!context_find ("path"))
+       free (path ("./", TFOLDER));
+
+    if (file && nummsgs)
+       adios (NULL, "cannot specify msg and file at same time!");
+
+    /*
+     * check if message is coming from file
+     */
+    if (file) {
+       if (!(cts = (CT *) calloc ((size_t) 2, sizeof(*cts))))
+           adios (NULL, "out of memory");
+       ctp = cts;
+
+       if ((ct = parse_mime (file)));
+           *ctp++ = ct;
+    } else {
+       /*
+        * message(s) are coming from a folder
+        */
+       if (!nummsgs)
+           msgs[nummsgs++] = "cur";
+       if (!folder)
+           folder = getfolder (1);
+       maildir = m_maildir (folder);
+
+       if (chdir (maildir) == NOTOK)
+           adios (maildir, "unable to change directory to");
+
+       /* read folder and create message structure */
+       if (!(mp = folder_read (folder)))
+           adios (NULL, "unable to read folder %s", folder);
+
+       /* check for empty folder */
+       if (mp->nummsg == 0)
+           adios (NULL, "no messages in %s", folder);
+
+       /* parse all the message ranges/sequences and set SELECTED */
+       for (msgnum = 0; msgnum < nummsgs; msgnum++)
+           if (!m_convert (mp, msgs[msgnum]))
+               done (1);
+       seq_setprev (mp);       /* set the previous-sequence */
+
+       if (!(cts = (CT *) calloc ((size_t) (mp->numsel + 1), sizeof(*cts))))
+           adios (NULL, "out of memory");
+       ctp = cts;
+
+       for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
+           if (is_selected(mp, msgnum)) {
+               char *msgnam;
+
+               msgnam = m_name (msgnum);
+               if ((ct = parse_mime (msgnam)))
+                   *ctp++ = ct;
+           }
+       }
+    }
+
+    if (!*cts)
+       done (1);
+
+    userrs = 1;
+    SIGNAL (SIGQUIT, quitser);
+    SIGNAL (SIGPIPE, pipeser);
+
+    /*
+     * Get the associated umask for the relevant contents.
+     */
+    for (ctp = cts; *ctp; ctp++) {
+       struct stat st;
+
+       ct = *ctp;
+       if (type_ok (ct, 1) && !ct->c_umask) {
+           if (stat (ct->c_file, &st) != NOTOK)
+               ct->c_umask = ~(st.st_mode & 0777);
+           else
+               ct->c_umask = ~m_gmprot();
+       }
+    }
+
+    /*
+     * Store the message content
+     */
+    store_all_messages (cts);
+
+    /* Now free all the structures for the content */
+    for (ctp = cts; *ctp; ctp++)
+       free_content (*ctp);
+
+    free ((char *) cts);
+    cts = NULL;
+
+    /* If reading from a folder, do some updating */
+    if (mp) {
+       context_replace (pfolder, folder);/* update current folder  */
+       seq_setcur (mp, mp->hghsel);      /* update current message */
+       seq_save (mp);                    /* synchronize sequences  */
+       context_save ();                  /* save the context file  */
+    }
+
+    done (0);
+    /* NOTREACHED */
+}
+
+
+static RETSIGTYPE
+pipeser (int i)
+{
+    if (i == SIGQUIT) {
+       unlink ("core");
+       fflush (stdout);
+       fprintf (stderr, "\n");
+       fflush (stderr);
+    }
+
+    done (1);
+    /* NOTREACHED */
+}
+
+
+void
+done (int status)
+{
+    CT *ctp;
+
+    if ((ctp = cts))
+       for (; *ctp; ctp++)
+           free_content (*ctp);
+
+    exit (status);
+}
diff --git a/uip/mhstoresbr.c b/uip/mhstoresbr.c
new file mode 100644 (file)
index 0000000..d849609
--- /dev/null
@@ -0,0 +1,1112 @@
+
+/*
+ * mhstoresbr.c -- routines to save/store the contents of MIME messages
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+#include <h/signals.h>
+#include <h/md5.h>
+#include <errno.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <zotnet/mts/mts.h>
+#include <zotnet/tws/tws.h>
+#include <h/mime.h>
+#include <h/mhparse.h>
+
+extern int errno;
+
+/*
+ * The list of top-level contents to display
+ */
+extern CT *cts;
+
+int autosw = 0;
+
+/*
+ * Cache of current directory.  This must be
+ * set before these routines are called.
+ */
+char *cwd;
+
+/*
+ * The directory in which to store the contents.
+ */
+static char *dir;
+
+/*
+ * Type for a compare function for qsort.  This keeps
+ * the compiler happy.
+ */
+typedef int (*qsort_comp) (const void *, const void *);
+
+
+/* mhmisc.c */
+int part_ok (CT, int);
+int type_ok (CT, int);
+int make_intermediates (char *);
+void flush_errors (void);
+
+/* mhshowsbr.c */
+int show_content_aux (CT, int, int, char *, char *);
+
+/*
+ * prototypes
+ */
+void store_all_messages (CT *);
+
+/*
+ * static prototypes
+ */
+static void store_single_message (CT);
+static int store_switch (CT);
+static int store_generic (CT);
+static int store_application (CT);
+static int store_multi (CT);
+static int store_partial (CT);
+static int store_external (CT);
+static int ct_compar (CT *, CT *);
+static int store_content (CT, CT);
+static int output_content_file (CT, int);
+static int check_folder (char *);
+static int output_content_folder (char *, char *);
+static int parse_format_string (CT, char *, char *, int, char *);
+static void get_storeproc (CT);
+static int copy_some_headers (FILE *, CT);
+
+
+/*
+ * Main entry point to store content
+ * from a collection of messages.
+ */
+
+void
+store_all_messages (CT *cts)
+{
+    CT ct, *ctp;
+    char *cp;
+
+    /*
+     * Check for the directory in which to
+     * store any contents.
+     */
+    if (autosw)
+       dir = getcpy (cwd);
+    else if ((cp = context_find (nmhstorage)) && *cp)
+       dir = getcpy (cp);
+    else
+       dir = getcpy (cwd);
+
+    for (ctp = cts; *ctp; ctp++) {
+       ct = *ctp;
+       store_single_message (ct);
+    }
+
+    flush_errors ();
+}
+
+
+/*
+ * Entry point to store the content
+ * in a (single) message
+ */
+
+static void
+store_single_message (CT ct)
+{
+    if (type_ok (ct, 1)) {
+       umask (ct->c_umask);
+       store_switch (ct);
+       if (ct->c_fp) {
+           fclose (ct->c_fp);
+           ct->c_fp = NULL;
+       }
+       if (ct->c_ceclosefnx)
+           (*ct->c_ceclosefnx) (ct);
+    }
+}
+
+
+/*
+ * Switching routine to store different content types
+ */
+
+static int
+store_switch (CT ct)
+{
+    switch (ct->c_type) {
+       case CT_MULTIPART:
+           return store_multi (ct);
+           break;
+
+       case CT_MESSAGE:
+           switch (ct->c_subtype) {
+               case MESSAGE_PARTIAL:
+                   return store_partial (ct);
+                   break;
+
+               case MESSAGE_EXTERNAL:
+                   return store_external (ct);
+
+               case MESSAGE_RFC822:
+               default:
+                   return store_generic (ct);
+                   break;
+           }
+           break;
+
+       case CT_APPLICATION:
+           return store_application (ct);
+           break;
+
+       case CT_TEXT:
+       case CT_AUDIO:
+       case CT_IMAGE:
+       case CT_VIDEO:
+           return store_generic (ct);
+           break;
+
+       default:
+           adios (NULL, "unknown content type %d", ct->c_type);
+           break;
+    }
+
+    return OK; /* NOT REACHED */
+}
+
+
+/*
+ * Generic routine to store a MIME content.
+ * (audio, video, image, text, message/rfc922)
+ */
+
+static int
+store_generic (CT ct)
+{
+    /*
+     * Check if the content specifies a filename.
+     * Don't bother with this for type "message"
+     * (only "message/rfc822" will use store_generic).
+     */
+    if (autosw && ct->c_type != CT_MESSAGE)
+       get_storeproc (ct);
+
+    return store_content (ct, NULL);
+}
+
+
+/*
+ * Store content of type "application"
+ */
+
+static int
+store_application (CT ct)
+{
+    char **ap, **ep;
+    CI ci = &ct->c_ctinfo;
+
+    /* Check if the content specifies a filename */
+    if (autosw)
+       get_storeproc (ct);
+
+    /*
+     * If storeproc is not defined, and the content is type
+     * "application/octet-stream", we also check for various
+     * attribute/value pairs which specify if this a tar file.
+     */
+    if (!ct->c_storeproc && ct->c_subtype == APPLICATION_OCTETS) {
+       int tarP = 0, zP = 0;
+
+       for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
+           /* check for "type=tar" attribute */
+           if (!strcasecmp (*ap, "type")) {
+               if (strcasecmp (*ep, "tar"))
+                   break;
+
+               tarP = 1;
+               continue;
+           }
+
+           /* check for "conversions=compress" attribute */
+           if ((!strcasecmp (*ap, "conversions") || !strcasecmp (*ap, "x-conversions"))
+               && (!strcasecmp (*ep, "compress") || !strcasecmp (*ep, "x-compress"))) {
+               zP = 1;
+               continue;
+           }
+       }
+
+       if (tarP) {
+           ct->c_showproc = add (zP ? "%euncompress | tar tvf -"
+                                 : "%etar tvf -", NULL);
+           if (!ct->c_storeproc)
+               if (autosw) {
+                   ct->c_storeproc = add (zP ? "| uncompress | tar xvpf -"
+                                          : "| tar xvpf -", NULL);
+                   ct->c_umask = 0022;
+               } else {
+                   ct->c_storeproc = add (zP ? "%m%P.tar.Z" : "%m%P.tar", NULL);
+               }
+       }
+    }
+
+    return store_content (ct, NULL);
+}
+
+
+/*
+ * Store the content of a multipart message
+ */
+
+static int
+store_multi (CT ct)
+{
+    int        result;
+    struct multipart *m = (struct multipart *) ct->c_ctparams;
+    struct part *part;
+
+    result = NOTOK;
+    for (part = m->mp_parts; part; part = part->mp_next) {
+       CT  p = part->mp_part;
+
+       if (part_ok (p, 1) && type_ok (p, 1)) {
+           result = store_switch (p);
+           if (result == OK && ct->c_subtype == MULTI_ALTERNATE)
+               break;
+       }
+    }
+
+    return result;
+}
+
+
+/*
+ * Reassemble and store the contents of a collection
+ * of messages of type "message/partial".
+ */
+
+static int
+store_partial (CT ct)
+{
+    int        cur, hi, i;
+    CT p, *ctp, *ctq;
+    CT *base;
+    struct partial *pm, *qm;
+
+    qm = (struct partial *) ct->c_ctparams;
+    if (qm->pm_stored)
+       return OK;
+
+    hi = i = 0;
+    for (ctp = cts; *ctp; ctp++) {
+       p = *ctp;
+       if (p->c_type == CT_MESSAGE && p->c_subtype == ct->c_subtype) {
+           pm = (struct partial *) p->c_ctparams;
+           if (!pm->pm_stored
+                   && strcmp (qm->pm_partid, pm->pm_partid) == 0) {
+               pm->pm_marked = pm->pm_partno;
+               if (pm->pm_maxno)
+                   hi = pm->pm_maxno;
+               pm->pm_stored = 1;
+               i++;
+           }
+           else
+               pm->pm_marked = 0;
+       }
+    }
+
+    if (hi == 0) {
+       advise (NULL, "missing (at least) last part of multipart message");
+       return NOTOK;
+    }
+
+    if ((base = (CT *) calloc ((size_t) (i + 1), sizeof(*base))) == NULL)
+       adios (NULL, "out of memory");
+
+    ctq = base;
+    for (ctp = cts; *ctp; ctp++) {
+       p = *ctp;
+       if (p->c_type == CT_MESSAGE && p->c_subtype == ct->c_subtype) {
+           pm = (struct partial *) p->c_ctparams;
+           if (pm->pm_marked)
+               *ctq++ = p;
+       }
+    }
+    *ctq = NULL;
+
+    if (i > 1)
+       qsort ((char *) base, i, sizeof(*base), (qsort_comp) ct_compar);
+
+    cur = 1;
+    for (ctq = base; *ctq; ctq++) {
+       p = *ctq;
+       pm = (struct partial *) p->c_ctparams;
+       if (pm->pm_marked != cur) {
+           if (pm->pm_marked == cur - 1) {
+               admonish (NULL,
+                         "duplicate part %d of %d part multipart message",
+                         pm->pm_marked, hi);
+               continue;
+           }
+
+missing_part:
+           advise (NULL,
+                   "missing %spart %d of %d part multipart message",
+                   cur != hi ? "(at least) " : "", cur, hi);
+           goto losing;
+       }
+        else
+           cur++;
+    }
+    if (hi != --cur) {
+       cur = hi;
+       goto missing_part;
+    }
+
+    /*
+     * Now cycle through the sorted list of messages of type
+     * "message/partial" and save/append them to a file.
+     */
+
+    ctq = base;
+    ct = *ctq++;
+    if (store_content (ct, NULL) == NOTOK) {
+losing:
+       free ((char *) base);
+       return NOTOK;
+    }
+
+    for (; *ctq; ctq++) {
+       p = *ctq;
+       if (store_content (p, ct) == NOTOK)
+           goto losing;
+    }
+
+    free ((char *) base);
+    return OK;
+}
+
+
+/*
+ * Store content from a message of type "message/external".
+ */
+
+static int
+store_external (CT ct)
+{
+    int        result = NOTOK;
+    struct exbody *e = (struct exbody *) ct->c_ctparams;
+    CT p = e->eb_content;
+
+    if (!type_ok (p, 1))
+       return OK;
+
+    /*
+     * Check if the parameters for the external body
+     * specified a filename.
+     */
+    if (autosw) {
+       char *cp;
+
+       if ((cp = e->eb_name)
+           && *cp != '/'
+           && *cp != '.'
+           && *cp != '|'
+           && *cp != '!'
+           && !strchr (cp, '%')) {
+           if (!ct->c_storeproc)
+               ct->c_storeproc = add (cp, NULL);
+           if (!p->c_storeproc)
+               p->c_storeproc = add (cp, NULL);
+       }
+    }
+
+    /*
+     * Since we will let the Content structure for the
+     * external body substitute for the current content,
+     * we temporarily change its partno (number inside
+     * multipart), so everything looks right.
+     */
+    p->c_partno = ct->c_partno;
+
+    /* we probably need to check if content is really there */
+    result = store_switch (p);
+
+    p->c_partno = NULL;
+    return result;
+}
+
+
+/*
+ * Compare the numbering from two different
+ * message/partials (needed for sorting).
+ */
+
+static int
+ct_compar (CT *a, CT *b)
+{
+    struct partial *am = (struct partial *) ((*a)->c_ctparams);
+    struct partial *bm = (struct partial *) ((*b)->c_ctparams);
+
+    return (am->pm_marked - bm->pm_marked);
+}
+
+
+/*
+ * Store contents of a message or message part to
+ * a folder, a file, the standard output, or pass
+ * the contents to a command.
+ *
+ * If the current content to be saved is a followup part
+ * to a collection of messages of type "message/partial",
+ * then field "p" is a pointer to the Content structure
+ * to the first message/partial in the group.
+ */
+
+static int
+store_content (CT ct, CT p)
+{
+    int appending = 0, msgnum;
+    int is_partial = 0, first_partial = 0;
+    int last_partial = 0;
+    char *cp, buffer[BUFSIZ];
+
+    /*
+     * Do special processing for messages of
+     * type "message/partial".
+     *
+     * We first check if this content is of type
+     * "message/partial".  If it is, then we need to check
+     * whether it is the first and/or last in the group.
+     *
+     * Then if "p" is a valid pointer, it points to the Content
+     * structure of the first partial in the group.  So we copy
+     * the file name and/or folder name from that message.  In
+     * this case, we also note that we will be appending.
+     */
+    if (ct->c_type == CT_MESSAGE && ct->c_subtype == MESSAGE_PARTIAL) {
+       struct partial *pm = (struct partial *) ct->c_ctparams;
+
+       /* Yep, it's a message/partial */
+       is_partial = 1;
+
+       /* But is it the first and/or last in the collection? */
+       if (pm->pm_partno == 1)
+           first_partial = 1;
+       if (pm->pm_maxno && pm->pm_partno == pm->pm_maxno)
+           last_partial = 1;
+
+       /*
+        * If "p" is a valid pointer, then it points to the
+        * Content structure for the first message in the group.
+        * So we just copy the filename or foldername information
+        * from the previous iteration of this function.
+        */
+       if (p) {
+           appending = 1;
+           ct->c_storage = add (p->c_storage, NULL);
+
+           /* record the folder name */
+           if (p->c_folder) {
+               ct->c_folder = add (p->c_folder, NULL);
+           }
+           goto got_filename;
+       }
+    }
+
+    /*
+     * Get storage formatting string.
+     *
+     * 1) If we have storeproc defined, then use that
+     * 2) Else check for a mhn-store-<type>/<subtype> entry
+     * 3) Else check for a mhn-store-<type> entry
+     * 4) Else if content is "message", use "+" (current folder)
+     * 5) Else use string "%m%P.%s".
+     */
+    if ((cp = ct->c_storeproc) == NULL || *cp == '\0') {
+       CI ci = &ct->c_ctinfo;
+
+       snprintf (buffer, sizeof(buffer), "%s-store-%s/%s",
+               invo_name, ci->ci_type, ci->ci_subtype);
+       if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
+           snprintf (buffer, sizeof(buffer), "%s-store-%s", invo_name, ci->ci_type);
+           if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
+               cp = ct->c_type == CT_MESSAGE ? "+" : "%m%P.%s";
+           }
+       }
+    }
+
+    /*
+     * Check the beginning of storage formatting string
+     * to see if we are saving content to a folder.
+     */
+    if (*cp == '+' || *cp == '@') {
+       char *tmpfilenam, *folder;
+
+       /* Store content in temporary file for now */
+       tmpfilenam = m_scratch ("", invo_name);
+       ct->c_storage = add (tmpfilenam, NULL);
+
+       /* Get the folder name */
+       if (cp[1])
+           folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       else
+           folder = getfolder (1);
+
+       /* Check if folder exists */
+       if (check_folder (folder) == NOTOK)
+           return NOTOK;
+
+       /* Record the folder name */
+       ct->c_folder = add (folder, NULL);
+
+       if (cp[1])
+           free (folder);
+
+       goto got_filename;
+    }
+
+    /*
+     * Parse and expand the storage formatting string
+     * in `cp' into `buffer'.
+     */
+    parse_format_string (ct, cp, buffer, sizeof(buffer), dir);
+
+    /*
+     * If formatting begins with '|' or '!', then pass
+     * content to standard input of a command and return.
+     */
+    if (buffer[0] == '|' || buffer[0] == '!')
+       return show_content_aux (ct, 1, 0, buffer + 1, dir);
+
+    /* record the filename */
+    ct->c_storage = add (buffer, NULL);
+
+got_filename:
+    /* flush the output stream */
+    fflush (stdout);
+
+    /* Now save or append the content to a file */
+    if (output_content_file (ct, appending) == NOTOK)
+       return NOTOK;
+
+    /*
+     * If necessary, link the file into a folder and remove
+     * the temporary file.  If this message is a partial,
+     * then only do this if it is the last one in the group.
+     */
+    if (ct->c_folder && (!is_partial || last_partial)) {
+       msgnum = output_content_folder (ct->c_folder, ct->c_storage);
+       unlink (ct->c_storage);
+       if (msgnum == NOTOK)
+           return NOTOK;
+    }
+
+    /*
+     * Now print out the name/number of the message
+     * that we are storing.
+     */
+    if (is_partial) {
+       if (first_partial)
+           fprintf (stderr, "reassembling partials ");
+       if (last_partial)
+           fprintf (stderr, "%s", ct->c_file);
+       else
+           fprintf (stderr, "%s,", ct->c_file);
+    } else {
+       fprintf (stderr, "storing message %s", ct->c_file);
+       if (ct->c_partno)
+           fprintf (stderr, " part %s", ct->c_partno);
+    }
+
+    /*
+     * Unless we are in the "middle" of group of message/partials,
+     * we now print the name of the file, folder, and/or message
+     * to which we are storing the content.
+     */
+    if (!is_partial || last_partial) {
+       if (ct->c_folder) {
+           fprintf (stderr, " to folder %s as message %d\n", ct->c_folder, msgnum);
+       } else if (!strcmp(ct->c_storage, "-")) {
+           fprintf (stderr, " to stdout\n");
+       } else {
+           int cwdlen;
+
+           cwdlen = strlen (cwd);
+           fprintf (stderr, " as file %s\n",
+               strncmp (ct->c_storage, cwd, cwdlen)
+               || ct->c_storage[cwdlen] != '/'
+               ? ct->c_storage : ct->c_storage + cwdlen + 1);
+       }
+    }
+
+    return OK;
+}
+
+
+/*
+ * Output content to a file
+ */
+
+static int
+output_content_file (CT ct, int appending)
+{
+    int filterstate;
+    char *file, buffer[BUFSIZ];
+    long pos, last;
+    FILE *fp;
+
+    /*
+     * If the pathname is absolute, make sure
+     * all the relevant directories exist.
+     */
+    if (strchr(ct->c_storage, '/')
+           && make_intermediates (ct->c_storage) == NOTOK)
+       return NOTOK;
+
+    if (ct->c_encoding != CE_7BIT) {
+       int cc, fd;
+
+       if (!ct->c_ceopenfnx) {
+           advise (NULL, "don't know how to decode part %s of message %s",
+                   ct->c_partno, ct->c_file);
+           return NOTOK;
+       }
+
+       file = appending || !strcmp (ct->c_storage, "-") ? NULL
+                                                          : ct->c_storage;
+       if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
+           return NOTOK;
+       if (!strcmp (file, ct->c_storage)) {
+           (*ct->c_ceclosefnx) (ct);
+           return OK;
+       }
+
+       /*
+        * Send to standard output
+        */
+       if (!strcmp (ct->c_storage, "-")) {
+           int gd;
+
+           if ((gd = dup (fileno (stdout))) == NOTOK) {
+               advise ("stdout", "unable to dup");
+losing:
+               (*ct->c_ceclosefnx) (ct);
+               return NOTOK;
+           }
+           if ((fp = fdopen (gd, appending ? "a" : "w")) == NULL) {
+               advise ("stdout", "unable to fdopen (%d, \"%s\") from", gd,
+                       appending ? "a" : "w");
+               close (gd);
+               goto losing;
+           }
+       } else {
+           /*
+            * Open output file
+            */
+           if ((fp = fopen (ct->c_storage, appending ? "a" : "w")) == NULL) {
+               advise (ct->c_storage, "unable to fopen for %s",
+                       appending ? "appending" : "writing");
+               goto losing;
+           }
+       }
+
+       /*
+        * Filter the header fields of the initial enclosing
+        * message/partial into the file.
+        */
+       if (ct->c_type == CT_MESSAGE && ct->c_subtype == MESSAGE_PARTIAL) {
+           struct partial *pm = (struct partial *) ct->c_ctparams;
+
+           if (pm->pm_partno == 1)
+               copy_some_headers (fp, ct);
+       }
+
+       for (;;) {
+           switch (cc = read (fd, buffer, sizeof(buffer))) {
+               case NOTOK:
+                   advise (file, "error reading content from");
+                   break;
+
+               case OK:
+                   break;
+
+               default:
+                   fwrite (buffer, sizeof(*buffer), cc, fp);
+                   continue;
+           }
+           break;
+       }
+
+       (*ct->c_ceclosefnx) (ct);
+
+       if (cc != NOTOK && fflush (fp))
+           advise (ct->c_storage, "error writing to");
+
+       fclose (fp);
+
+       return (cc != NOTOK ? OK : NOTOK);
+    }
+
+    if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
+       advise (ct->c_file, "unable to open for reading");
+       return NOTOK;
+    }
+
+    pos = ct->c_begin;
+    last = ct->c_end;
+    fseek (ct->c_fp, pos, SEEK_SET);
+
+    if (!strcmp (ct->c_storage, "-")) {
+       int gd;
+
+       if ((gd = dup (fileno (stdout))) == NOTOK) {
+           advise ("stdout", "unable to dup");
+           return NOTOK;
+       }
+       if ((fp = fdopen (gd, appending ? "a" : "w")) == NULL) {
+           advise ("stdout", "unable to fdopen (%d, \"%s\") from", gd,
+                   appending ? "a" : "w");
+           close (gd);
+           return NOTOK;
+       }
+    } else {
+       if ((fp = fopen (ct->c_storage, appending ? "a" : "w")) == NULL) {
+           advise (ct->c_storage, "unable to fopen for %s",
+                   appending ? "appending" : "writing");
+           return NOTOK;
+       }
+    }
+
+    /*
+     * Copy a few of the header fields of the initial
+     * enclosing message/partial into the file.
+     */
+    filterstate = 0;
+    if (ct->c_type == CT_MESSAGE && ct->c_subtype == MESSAGE_PARTIAL) {
+       struct partial *pm = (struct partial *) ct->c_ctparams;
+
+       if (pm->pm_partno == 1) {
+           copy_some_headers (fp, ct);
+           filterstate = 1;
+       }
+    }
+
+    while (fgets (buffer, sizeof(buffer) - 1, ct->c_fp)) {
+       if ((pos += strlen (buffer)) > last) {
+           int diff;
+
+           diff = strlen (buffer) - (pos - last);
+           if (diff >= 0)
+               buffer[diff] = '\0';
+       }
+       /*
+        * If this is the first content of a group of
+        * message/partial contents, then we only copy a few
+        * of the header fields of the enclosed message.
+        */
+       if (filterstate) {
+           switch (buffer[0]) {
+               case ' ':
+               case '\t':
+                   if (filterstate < 0)
+                       buffer[0] = 0;
+                   break;
+
+               case '\n':
+                   filterstate = 0;
+                   break;
+
+               default:
+                   if (!uprf (buffer, XXX_FIELD_PRF)
+                           && !uprf (buffer, VRSN_FIELD)
+                           && !uprf (buffer, "Subject:")
+                           && !uprf (buffer, "Encrypted:")
+                           && !uprf (buffer, "Message-ID:")) {
+                       filterstate = -1;
+                       buffer[0] = 0;
+                       break;
+                   }
+                   filterstate = 1;
+                   break;
+           }
+       }
+       fputs (buffer, fp);
+       if (pos >= last)
+           break;
+    }
+
+    if (fflush (fp))
+       advise (ct->c_storage, "error writing to");
+
+    fclose (fp);
+    fclose (ct->c_fp);
+    ct->c_fp = NULL;
+    return OK;
+}
+
+
+/*
+ * Check if folder exists, and create
+ * if necessary.
+ */
+
+static int
+check_folder (char *folder)
+{
+    char *folderdir;
+    struct stat st;
+
+    /* expand path to the folder */
+    folderdir = m_mailpath (folder);
+
+    /* Check if folder exists */
+    if (stat (folderdir, &st) == NOTOK) {
+       int answer;
+       char *ep;
+
+       if (errno != ENOENT) {
+           advise (folderdir, "error on folder");
+           return NOTOK;
+       }
+
+       ep = concat ("Create folder \"", folderdir, "\"? ", NULL);
+       answer = getanswer (ep);
+       free (ep);
+
+       if (!answer)
+           return NOTOK;
+
+       if (!makedir (folderdir)) {
+           advise (NULL, "unable to create folder %s", folderdir);
+           return NOTOK;
+       }
+    }
+
+    return OK;
+}
+
+
+/*
+ * Add a file to a folder.
+ *
+ * Return the new message number of the file
+ * when added to the folder.  Return -1, if
+ * there is an error.
+ */
+
+static int
+output_content_folder (char *folder, char *filename)
+{
+    int msgnum;
+    struct msgs *mp;
+
+    /* Read the folder. */
+    if ((mp = folder_read (folder))) {
+       /* Link file into folder */
+       msgnum = folder_addmsg (&mp, filename, 0, 0, 0);
+    } else {
+       advise (NULL, "unable to read folder %s", folder);
+       return NOTOK;
+    }
+
+    /* free folder structure */
+    folder_free (mp);
+
+    /*
+     * Return msgnum.  We are relying on the fact that
+     * msgnum will be -1, if folder_addmsg() had an error.
+     */
+    return msgnum;
+}
+
+
+/*
+ * Parse and expand the storage formatting string
+ * pointed to by "cp" into "buffer".
+ */
+
+static int
+parse_format_string (CT ct, char *cp, char *buffer, int buflen, char *dir)
+{
+    int len;
+    char *bp;
+    CI ci = &ct->c_ctinfo;
+
+    /*
+     * If storage string is "-", just copy it, and
+     * return (send content to standard output).
+     */
+    if (cp[0] == '-' && cp[1] == '\0') {
+       strncpy (buffer, cp, buflen);
+       return 0;
+    }
+
+    bp = buffer;
+    bp[0] = '\0';
+
+    /*
+     * If formatting string is a pathname that doesn't
+     * begin with '/', then preface the path with the
+     * appropriate directory.
+     */
+    if (*cp != '/' && *cp != '|' && *cp != '!') {
+       snprintf (bp, buflen, "%s/", dir[1] ? dir : "");
+       len = strlen (bp);
+       bp += len;
+       buflen -= len;
+    }
+
+    for (; *cp; cp++) {
+
+       /* We are processing a storage escape */
+       if (*cp == '%') {
+           switch (*++cp) {
+               case 'a':
+                   /*
+                    * Insert parameters from Content-Type.
+                    * This is only valid for '|' commands.
+                    */
+                   if (buffer[0] != '|' && buffer[0] != '!') {
+                       *bp++ = *--cp;
+                       *bp = '\0';
+                       buflen--;
+                       continue;
+                   } else {
+                       char **ap, **ep;
+                       char *s = "";
+
+                       for (ap = ci->ci_attrs, ep = ci->ci_values;
+                                *ap; ap++, ep++) {
+                           snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
+                           len = strlen (bp);
+                           bp += len;
+                           buflen -= len;
+                           s = " ";
+                       }
+                   }
+                   break;
+
+               case 'm':
+                   /* insert message number */
+                   snprintf (bp, buflen, "%s", r1bindex (ct->c_file, '/'));
+                   break;
+
+               case 'P':
+                   /* insert part number with leading dot */
+                   if (ct->c_partno)
+                       snprintf (bp, buflen, ".%s", ct->c_partno);
+                   break;
+
+               case 'p':
+                   /* insert part number withouth leading dot */
+                   if (ct->c_partno)
+                       strncpy (bp, ct->c_partno, buflen);
+                   break;
+
+               case 't':
+                   /* insert content type */
+                   strncpy (bp, ci->ci_type, buflen);
+                   break;
+
+               case 's':
+                   /* insert content subtype */
+                   strncpy (bp, ci->ci_subtype, buflen);
+                   break;
+
+               case '%':
+                   /* insert the character % */
+                   goto raw;
+
+               default:
+                   *bp++ = *--cp;
+                   *bp = '\0';
+                   buflen--;
+                   continue;
+           }
+
+           /* Advance bp and decrement buflen */
+           len = strlen (bp);
+           bp += len;
+           buflen -= len;
+
+       } else {
+raw:
+           *bp++ = *cp;
+           *bp = '\0';
+           buflen--;
+       }
+    }
+
+    return 0;
+}
+
+
+/*
+ * Check if the content specifies a filename
+ * in its MIME parameters.
+ */
+
+static void
+get_storeproc (CT ct)
+{
+    char **ap, **ep, *cp;
+    CI ci = &ct->c_ctinfo;
+
+    /*
+     * If the storeproc has already been defined,
+     * we just return (for instance, if this content
+     * is part of a "message/external".
+     */
+    if (ct->c_storeproc)
+       return;
+
+    /*
+     * Check the attribute/value pairs, for the attribute "name".
+     * If found, do a few sanity checks and copy the value into
+     * the storeproc.
+     */
+    for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
+       if (!strcasecmp (*ap, "name")
+           && *(cp = *ep) != '/'
+           && *cp != '.'
+           && *cp != '|'
+           && *cp != '!'
+           && !strchr (cp, '%')) {
+           ct->c_storeproc = add (cp, NULL);
+           return;
+       }
+    }
+}
+
+
+/*
+ * Copy some of the header fields of the initial message/partial
+ * message into the header of the reassembled message.
+ */
+
+static int
+copy_some_headers (FILE *out, CT ct)
+{
+    HF hp;
+
+    hp = ct->c_first_hf;       /* start at first header field */
+
+    while (hp) {
+       /*
+        * A few of the header fields of the enclosing
+        * messages are not copied.
+        */
+       if (!uprf (hp->name, XXX_FIELD_PRF)
+               && strcasecmp (hp->name, VRSN_FIELD)
+               && strcasecmp (hp->name, "Subject")
+               && strcasecmp (hp->name, "Encrypted")
+               && strcasecmp (hp->name, "Message-ID"))
+           fprintf (out, "%s:%s", hp->name, hp->value);
+       hp = hp->next;  /* next header field */
+    }
+
+    return OK;
+}
diff --git a/uip/mhtest.c b/uip/mhtest.c
new file mode 100644 (file)
index 0000000..80a0c7b
--- /dev/null
@@ -0,0 +1,434 @@
+
+/*
+ * mhtest.c -- test harness for MIME routines
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+#include <h/signals.h>
+#include <h/md5.h>
+#include <errno.h>
+#include <signal.h>
+#include <zotnet/mts/mts.h>
+#include <zotnet/tws/tws.h>
+#include <h/mime.h>
+#include <h/mhparse.h>
+#include <h/mhcachesbr.h>
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+/*
+ * We allocate space for message names (msgs array)
+ * this number of elements at a time.
+ */
+#define MAXMSGS  256
+
+
+static struct swit switches[] = {
+#define        CHECKSW                 0
+    { "check", 0 },
+#define        NCHECKSW                1
+    { "nocheck", 0 },
+#define        VERBSW                  2
+    { "verbose", 0 },
+#define        NVERBSW                 3
+    { "noverbose", 0 },
+#define        FILESW                  4
+    { "file file", 0 },
+#define OUTFILESW               5
+    { "outfile file", 0 },
+#define        PARTSW                  6
+    { "part number", 0 },
+#define        TYPESW                  7
+    { "type content", 0 },
+#define        RCACHESW                8
+    { "rcache policy", 0 },
+#define        WCACHESW                9
+    { "wcache policy", 0 },
+#define VERSIONSW              10
+    { "version", 0 },
+#define        HELPSW                 11
+    { "help", 4 },
+
+/*
+ * switches for debugging
+ */
+#define        DEBUGSW                12
+    { "debug", -5 },
+    { NULL, 0 }
+};
+
+
+extern int errno;
+
+int ebcdicsw = 0;      /* hack for linking purposes */
+
+/* mhparse.c */
+extern int checksw;
+extern char *tmp;      /* directory to place temp files */
+
+/* mhcachesbr.c */
+extern int rcachesw;
+extern int wcachesw;
+extern char *cache_public;
+extern char *cache_private;
+
+/* mhmisc.c */
+extern int npart;
+extern int ntype;
+extern char *parts[NPARTS + 1];
+extern char *types[NTYPES + 1];
+extern int userrs;
+
+/*
+ * This is currently needed to keep mhparse happy.
+ * This needs to be changed.
+ */
+pid_t xpid  = 0;
+
+int debugsw = 0;
+int verbosw = 0;
+
+/* The list of top-level contents to display */
+CT *cts = NULL;
+
+#define        quitser pipeser
+
+/* mhparse.c */
+CT parse_mime (char *);
+
+/* mhoutsbr.c */
+int output_message (CT, char *);
+
+/* mhmisc.c */
+int part_ok (CT, int);
+int type_ok (CT, int);
+void set_endian (void);
+void flush_errors (void);
+
+/* mhfree.c */
+void free_content (CT);
+
+/*
+ * static prototypes
+ */
+static int write_content (CT *, char *);
+static RETSIGTYPE pipeser (int);
+
+
+int
+main (int argc, char **argv)
+{
+    int nummsgs, maxmsgs, msgnum, *icachesw;
+    char *cp, *file = NULL, *folder = NULL;
+    char *maildir, buf[100], *outfile = NULL;
+    char **argp, **arguments, **msgs;
+    struct msgs *mp = NULL;
+    CT ct, *ctp;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    /*
+     * Allocate the initial space to record message
+     * names, ranges, and sequences.
+     */
+    nummsgs = 0;
+    maxmsgs = MAXMSGS;
+    if (!(msgs = (char **) malloc ((size_t) (maxmsgs * sizeof(*msgs)))))
+       adios (NULL, "unable to allocate storage");
+
+    /*
+     * Parse arguments
+     */
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+           case AMBIGSW: 
+               ambigsw (cp, switches);
+               done (1);
+           case UNKWNSW: 
+               adios (NULL, "-%s unknown", cp);
+
+           case HELPSW: 
+               snprintf (buf, sizeof(buf), "%s [+folder] [msgs] [switches]",
+                       invo_name);
+               print_help (buf, switches, 1);
+               done (1);
+           case VERSIONSW:
+               print_version(invo_name);
+               done (1);
+
+           case RCACHESW:
+               icachesw = &rcachesw;
+               goto do_cache;
+           case WCACHESW:
+               icachesw = &wcachesw;
+do_cache:
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               switch (*icachesw = smatch (cp, caches)) {
+               case AMBIGSW:
+                   ambigsw (cp, caches);
+                   done (1);
+               case UNKWNSW:
+                   adios (NULL, "%s unknown", cp);
+               default:
+                   break;
+               }
+               continue;
+
+           case CHECKSW:
+               checksw++;
+               continue;
+           case NCHECKSW:
+               checksw = 0;
+               continue;
+
+           case PARTSW:
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               if (npart >= NPARTS)
+                   adios (NULL, "too many parts (starting with %s), %d max",
+                          cp, NPARTS);
+               parts[npart++] = cp;
+               continue;
+
+           case TYPESW:
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               if (ntype >= NTYPES)
+                   adios (NULL, "too many types (starting with %s), %d max",
+                          cp, NTYPES);
+               types[ntype++] = cp;
+               continue;
+
+           case FILESW:
+               if (!(cp = *argp++) || (*cp == '-' && cp[1]))
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               file = *cp == '-' ? cp : path (cp, TFILE);
+               continue;
+
+           case OUTFILESW:
+               if (!(cp = *argp++) || (*cp == '-' && cp[1]))
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               outfile = *cp == '-' ? cp : path (cp, TFILE);
+               continue;
+
+           case VERBSW: 
+               verbosw = 1;
+               continue;
+           case NVERBSW: 
+               verbosw = 0;
+               continue;
+           case DEBUGSW:
+               debugsw = 1;
+               continue;
+           }
+       }
+       if (*cp == '+' || *cp == '@') {
+           if (folder)
+               adios (NULL, "only one folder at a time!");
+           else
+               folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       } else {
+           /*
+            * Check if we need to allocate more space
+            * for message names/ranges/sequences.
+            */
+           if (nummsgs >= maxmsgs) {
+               maxmsgs += MAXMSGS;
+               if (!(msgs = (char **) realloc (msgs,
+                       (size_t) (maxmsgs * sizeof(*msgs)))))
+                   adios (NULL, "unable to reallocate msgs storage");
+           }
+           msgs[nummsgs++] = cp;
+       }
+    }
+
+    /* null terminate the list of acceptable parts/types */
+    parts[npart] = NULL;
+    types[ntype] = NULL;
+
+    set_endian ();
+
+    if (outfile == NULL)
+       adios (NULL, "must specify output file");
+
+    /* Check for public cache location */
+    if ((cache_public = context_find (nmhcache)) && *cache_public != '/')
+       cache_public = NULL;
+
+    /* Check for private cache location */
+    if (!(cache_private = context_find (nmhprivcache)))
+       cache_private = ".cache";
+    cache_private = getcpy (m_maildir (cache_private));
+
+    /*
+     * Check for storage directory.  If specified,
+     * then store temporary files there.  Else we
+     * store them in standard nmh directory.
+     */
+    if ((cp = context_find (nmhstorage)) && *cp)
+       tmp = concat (cp, "/", invo_name, NULL);
+    else
+       tmp = add (m_maildir (invo_name), NULL);
+
+    if (!context_find ("path"))
+       free (path ("./", TFOLDER));
+
+    if (file && nummsgs)
+       adios (NULL, "cannot specify msg and file at same time!");
+
+    /*
+     * check if message is coming from file
+     */
+    if (file) {
+       if (!(cts = (CT *) calloc ((size_t) 2, sizeof(*cts))))
+           adios (NULL, "out of memory");
+       ctp = cts;
+
+       if ((ct = parse_mime (file)));
+           *ctp++ = ct;
+    } else {
+       /*
+        * message(s) are coming from a folder
+        */
+       if (!nummsgs)
+           msgs[nummsgs++] = "cur";
+       if (!folder)
+           folder = getfolder (1);
+       maildir = m_maildir (folder);
+
+       if (chdir (maildir) == NOTOK)
+           adios (maildir, "unable to change directory to");
+
+       /* read folder and create message structure */
+       if (!(mp = folder_read (folder)))
+           adios (NULL, "unable to read folder %s", folder);
+
+       /* check for empty folder */
+       if (mp->nummsg == 0)
+           adios (NULL, "no messages in %s", folder);
+
+       /* parse all the message ranges/sequences and set SELECTED */
+       for (msgnum = 0; msgnum < nummsgs; msgnum++)
+           if (!m_convert (mp, msgs[msgnum]))
+               done (1);
+       seq_setprev (mp);       /* set the previous-sequence */
+
+       if (!(cts = (CT *) calloc ((size_t) (mp->numsel + 1), sizeof(*cts))))
+           adios (NULL, "out of memory");
+       ctp = cts;
+
+       for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
+           if (is_selected(mp, msgnum)) {
+               char *msgnam;
+
+               msgnam = m_name (msgnum);
+               if ((ct = parse_mime (msgnam)))
+                   *ctp++ = ct;
+           }
+       }
+    }
+
+    if (!*cts)
+       done (1);
+
+    userrs = 1;
+    SIGNAL (SIGQUIT, quitser);
+    SIGNAL (SIGPIPE, pipeser);
+
+    /*
+     * Get the associated umask for the relevant contents.
+     */
+    for (ctp = cts; *ctp; ctp++) {
+       struct stat st;
+
+       ct = *ctp;
+       if (type_ok (ct, 1) && !ct->c_umask) {
+           if (stat (ct->c_file, &st) != NOTOK)
+               ct->c_umask = ~(st.st_mode & 0777);
+           else
+               ct->c_umask = ~m_gmprot();
+       }
+    }
+
+    /*
+     * Write the content to a file
+     */
+    write_content (cts, outfile);
+
+    /* Now free all the structures for the content */
+    for (ctp = cts; *ctp; ctp++)
+       free_content (*ctp);
+
+    free ((char *) cts);
+    cts = NULL;
+
+    /* If reading from a folder, do some updating */
+    if (mp) {
+       context_replace (pfolder, folder);/* update current folder  */
+       seq_setcur (mp, mp->hghsel);      /* update current message */
+       seq_save (mp);                    /* synchronize sequences  */
+       context_save ();                  /* save the context file  */
+    }
+
+    done (0);
+    /* NOTREACHED */
+}
+
+
+static int
+write_content (CT *cts, char *outfile)
+{
+    CT ct, *ctp;
+
+    for (ctp = cts; *ctp; ctp++) {
+       ct = *ctp;
+       output_message (ct, outfile);
+    }
+
+    flush_errors ();
+    return OK;
+}
+
+
+static RETSIGTYPE
+pipeser (int i)
+{
+    if (i == SIGQUIT) {
+       unlink ("core");
+       fflush (stdout);
+       fprintf (stderr, "\n");
+       fflush (stderr);
+    }
+
+    done (1);
+    /* NOTREACHED */
+}
+
+
+void
+done (int status)
+{
+    CT *ctp;
+
+    if ((ctp = cts))
+       for (; *ctp; ctp++)
+           free_content (*ctp);
+
+    exit (status);
+}
diff --git a/uip/msgchk.c b/uip/msgchk.c
new file mode 100644 (file)
index 0000000..65fed7e
--- /dev/null
@@ -0,0 +1,419 @@
+
+/*
+ * msgchk.c -- check for mail
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <zotnet/mts/mts.h>
+#include <zotnet/tws/tws.h>
+#include <pwd.h>
+
+#ifdef POP
+# include <h/popsbr.h>
+#endif
+
+#ifdef HESIOD
+# include <hesiod.h>
+#endif
+
+#ifndef        POP
+# define POPminc(a) (a)
+#else
+# define POPminc(a)  0
+#endif
+
+#ifndef        RPOP
+# define RPOPminc(a) (a)
+#else
+# define RPOPminc(a)  0
+#endif
+
+#ifndef        APOP
+# define APOPminc(a) (a)
+#else
+# define APOPminc(a)  0
+#endif
+
+static struct swit switches[] = {
+#define        DATESW                   0
+    { "date", 0 },
+#define        NDATESW                  1
+    { "nodate", 0 },
+#define        NOTESW                   2
+    { "notify type", 0 },
+#define        NNOTESW                  3
+    { "nonotify type", 0 },
+#define        HOSTSW                   4
+    { "host hostname", POPminc (-4) },
+#define        USERSW                   5
+    { "user username", POPminc (-4) },
+#define        APOPSW                   6
+    { "apop", APOPminc (-4) },
+#define        NAPOPSW                  7
+    { "noapop", APOPminc (-6) },
+#define        RPOPSW                   8
+    { "rpop", RPOPminc (-4) },
+#define        NRPOPSW                  9
+    { "norpop", RPOPminc (-6) },
+#define VERSIONSW               10
+    { "version", 0 },
+#define        HELPSW                  11
+    { "help", 4 },
+#define SNOOPSW                 12
+    { "snoop", -5 },
+    { NULL, 0 }
+};
+
+/*
+ * Maximum numbers of users we can check (plus
+ * one for the NULL vector at the end).
+ */
+#define MAXVEC  51
+
+#define        NT_NONE 0x0
+#define        NT_MAIL 0x1
+#define        NT_NMAI 0x2
+#define        NT_ALL  (NT_MAIL | NT_NMAI)
+
+#define        NONEOK  0x0
+#define        UUCPOLD 0x1
+#define        UUCPNEW 0x2
+#define        UUCPOK  (UUCPOLD | UUCPNEW)
+#define        MMDFOLD 0x4
+#define        MMDFNEW 0x8
+#define        MMDFOK  (MMDFOLD | MMDFNEW)
+
+
+/*
+ * static prototypes
+ */
+static int donote (char *, int);
+static int checkmail (char *, char *, int, int, int);
+
+#ifdef POP
+static int remotemail (char *, char *, int, int, int, int);
+#endif
+
+
+int
+main (int argc, char **argv)
+{
+    int datesw = 1, notifysw = NT_ALL;
+    int rpop, status = 0;
+    int snoop = 0, vecp = 0;
+    uid_t uid;
+    char *cp, *host = NULL, *user, buf[BUFSIZ];
+    char **argp, **arguments, *vec[MAXVEC];
+    struct passwd *pw;
+
+#ifdef HESIOD
+    struct hes_postoffice *po;
+    char *tmphost;
+#endif
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    mts_init (invo_name);
+    uid = getuid ();
+    user = getusername();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+#ifdef POP
+    if ((cp = getenv ("MHPOPDEBUG")) && *cp)
+       snoop++;
+#endif
+
+#ifdef KPOP
+    rpop = 1;
+#else
+    rpop = 0;
+#endif
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown", cp);
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf), "%s [switches] [users ...]",
+                       invo_name);
+                   print_help (buf, switches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case DATESW:
+                   datesw++;
+                   continue;
+               case NDATESW:
+                   datesw = 0;
+                   continue;
+
+               case NOTESW:
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   notifysw |= donote (cp, 1);
+                   continue;
+               case NNOTESW:
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   notifysw &= ~donote (cp, 0);
+                   continue;
+
+               case HOSTSW: 
+                   if (!(host = *argp++) || *host == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+               case USERSW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   if (vecp >= MAXVEC-1)
+                       adios (NULL, "you can only check %d users at a time", MAXVEC-1);
+                   else
+                       vec[vecp++] = cp;
+                   continue;
+
+               case APOPSW: 
+                   rpop = -1;
+                   continue;
+               case NAPOPSW:
+                   rpop = 0;
+                   continue;
+
+               case RPOPSW: 
+                   rpop = 1;
+                   continue;
+               case NRPOPSW: 
+                   rpop = 0;
+                   continue;
+
+               case SNOOPSW:
+                   snoop++;
+                   continue;
+           }
+       }
+       if (vecp >= MAXVEC-1)
+           adios (NULL, "you can only check %d users at a time", MAXVEC-1);
+       else
+           vec[vecp++] = cp;
+    }
+
+#ifdef POP
+    /*
+     * If -host is not specified by user
+     */
+    if (!host || !*host) {
+# ifdef HESIOD
+       /*
+        * Scheme is:
+        *        use MAILHOST environment variable if present,
+        *  else try Hesiod.
+        *  If that fails, use the default (if any)
+        *  provided by mts.conf in mts_init()
+        */
+       if ((tmphost = getenv("MAILHOST")) != NULL)
+           pophost = tmphost;
+       else if ((po = hes_getmailhost(vecp ? vec[0] : user)) != NULL &&
+               strcmp(po->po_type, "POP") == 0)
+           pophost = po->po_host;
+# endif /* HESIOD */
+       /*
+        * If "pophost" is specified in mts.conf,
+        * use it as default value.
+        */
+       if (pophost && *pophost)
+           host = pophost;
+    }
+    if (!host || !*host)
+       host = NULL;
+    if (!host || rpop <= 0)
+       setuid (uid);
+#endif /* POP */
+
+    if (vecp != 0)
+       vec[vecp] = NULL;
+
+#ifdef POP
+    if (host) {
+       if (vecp == 0) {
+           status = remotemail (host, user, rpop, notifysw, 1, snoop);
+       } else {
+           for (vecp = 0; vec[vecp]; vecp++)
+               status += remotemail (host, vec[vecp], rpop, notifysw, 0, snoop);
+       }
+    } else {
+#endif /* POP */
+
+    if (vecp == 0) {
+       char *home;
+
+       home = (uid = geteuid()) ? home = getenv ("HOME") : NULL;
+       if (home == NULL) {
+           pw = getpwnam (user);
+           if (pw == NULL)
+               adios (NULL, "unable to get information about user");
+           if (home == NULL)
+               home = pw->pw_dir;
+       }
+       status = checkmail (user, home, datesw, notifysw, 1);
+    } else {
+       for (vecp = 0; vec[vecp]; vecp++) {
+           if ((pw = getpwnam (vec[vecp])))
+               status += checkmail (pw->pw_name, pw->pw_dir, datesw, notifysw, 0);
+           else
+               advise (NULL, "no such user as %s", vec[vecp]);
+       }
+    }
+#ifdef POP
+    }          /* host == NULL */
+#endif
+
+    done (status);
+}
+
+
+static struct swit ntswitches[] = {
+#define        NALLSW     0
+    { "all", 0 },
+#define        NMAISW     1
+    { "mail", 0 },
+#define        NNMAISW    2
+    { "nomail", 0 },
+    { NULL, 0 }
+};
+
+
+static int
+donote (char *cp, int ntflag)
+{
+    switch (smatch (cp, ntswitches)) {
+       case AMBIGSW: 
+           ambigsw (cp, ntswitches);
+           done (1);
+       case UNKWNSW: 
+           adios (NULL, "-%snotify %s unknown", ntflag ? "" : "no", cp);
+
+       case NALLSW: 
+           return NT_ALL;
+       case NMAISW: 
+           return NT_MAIL;
+       case NNMAISW: 
+           return NT_NMAI;
+    }
+}
+
+
+static int
+checkmail (char *user, char *home, int datesw, int notifysw, int personal)
+{
+    int mf, status;
+    char buffer[BUFSIZ];
+    struct stat st;
+
+    snprintf (buffer, sizeof(buffer), "%s/%s", mmdfldir[0] ? mmdfldir : home, mmdflfil[0] ? mmdflfil : user);
+    if (datesw) {
+       st.st_size = 0;
+       st.st_atime = st.st_mtime = 0;
+    }
+    mf = (stat (buffer, &st) == NOTOK || st.st_size == 0) ? NONEOK
+       : st.st_atime <= st.st_mtime ? MMDFNEW : MMDFOLD;
+
+    if ((mf & UUCPOK) || (mf & MMDFOK)) {
+       if (notifysw & NT_MAIL) {
+           printf (personal ? "You have " : "%s has ", user);
+           if (mf & UUCPOK)
+               printf ("%s old-style bell", mf & UUCPOLD ? "old" : "new");
+           if ((mf & UUCPOK) && (mf & MMDFOK))
+               printf (" and ");
+           if (mf & MMDFOK)
+               printf ("%s%s", mf & MMDFOLD ? "old" : "new",
+                       mf & UUCPOK ? " Internet" : "");
+           printf (" mail waiting");
+       } else {
+           notifysw = 0;
+       }
+       status = 0;
+    }
+    else {
+       if (notifysw & NT_NMAI)
+           printf (personal ? "You don't %s%s" : "%s doesn't %s",
+                   personal ? "" : user, "have any mail waiting");
+       else
+           notifysw = 0;
+
+       status = 1;
+    }
+
+    if (notifysw)
+       if (datesw && st.st_atime)
+           printf ("; last read on %s", dtime (&st.st_atime, 1));
+    if (notifysw)
+       printf ("\n");
+
+    return status;
+}
+
+
+#ifdef POP
+extern char response[];
+
+static int
+remotemail (char *host, char *user, int rpop, int notifysw, int personal, int snoop)
+{
+    int nmsgs, nbytes, status;
+    char *pass = NULL;
+
+    if (user == NULL)
+       user = getusername ();
+    if (rpop > 0)
+       pass = getusername ();
+    else
+       ruserpass (host, &user, &pass);
+
+    /* open the POP connection */
+    if (pop_init (host, user, pass, snoop, rpop) == NOTOK
+           || pop_stat (&nmsgs, &nbytes) == NOTOK      /* check for messages  */
+           || pop_quit () == NOTOK) {                  /* quit POP connection */
+       advise (NULL, "%s", response);
+       return 1;
+    }
+
+    if (nmsgs) {
+       if (notifysw & NT_MAIL) {
+           printf (personal ? "You have " : "%s has ", user);
+           printf ("%d message%s (%d bytes)",
+                   nmsgs, nmsgs != 1 ? "s" : "", nbytes);
+       }
+       else
+           notifysw = 0;
+
+       status = 0;
+    } else {
+       if (notifysw & NT_NMAI)
+           printf (personal ? "You don't %s%s" : "%s doesn't %s",
+                   personal ? "" : user, "have any mail waiting");
+       else
+           notifysw = 0;
+       status = 1;
+    }
+    if (notifysw)
+       printf (" on %s\n", host);
+
+    return status;
+}
+#endif /* POP */
diff --git a/uip/msh.c b/uip/msh.c
new file mode 100644 (file)
index 0000000..6755a8c
--- /dev/null
+++ b/uip/msh.c
@@ -0,0 +1,2588 @@
+
+/*
+ * msh.c -- The nmh shell
+ *
+ * $Id$
+ */
+
+/*
+ * TODO:
+ *    Keep more status information in maildrop map
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+#include <h/signals.h>
+#include <h/dropsbr.h>
+#include <h/fmt_scan.h>
+#include <h/scansbr.h>
+#include <zotnet/tws/tws.h>
+#include <zotnet/mts/mts.h>
+
+#ifdef HAVE_TERMIOS_H
+# include <termios.h>
+#else
+# ifdef HAVE_TERMIO_H
+#  include <termio.h>
+# else
+#  include <sgtty.h>
+# endif
+#endif
+
+#include <pwd.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <h/msh.h>
+#include <h/vmhsbr.h>
+
+#define        QUOTE   '\\'            /* sigh */
+
+static struct swit switches[] = {
+#define        IDSW                  0
+    { "idstart number", -7 },          /* interface from bbc */
+#define        FDSW                  1
+    { "idstop number", -6 },           /*  .. */
+#define        QDSW                  2
+    { "idquit number", -6 },           /*  .. */
+#define        NMSW                  3
+    { "idname BBoard", -6 },           /*  .. */
+#define        PRMPTSW               4
+    { "prompt string", 0 },
+#define        SCANSW                5
+    { "scan", 0 },
+#define        NSCANSW               6
+    { "noscan", 0 },
+#define        READSW                7
+    { "vmhread fd", -7 },
+#define        WRITESW               8
+    { "vmhwrite fd", -8 },     
+#define        PREADSW               9
+    { "popread fd", -7 },
+#define        PWRITSW              10
+    { "popwrite fd", -8 },
+#define        TCURSW               11
+    { "topcur", 0 },
+#define        NTCURSW              12
+    { "notopcur", 0 },
+#define VERSIONSW            13
+    { "version", 0 },
+#define        HELPSW               14
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+static int mbx_style = MMDF_FORMAT;
+
+/*
+ * FOLDER
+ */
+char*fmsh = NULL;                      /* folder instead of file              */
+int modified;                          /* command modified folder             */
+struct msgs *mp;                       /* used a lot                          */
+static int nMsgs = 0;
+struct Msg *Msgs = NULL;               /* Msgs[0] not used                    */
+static FILE *fp;                       /* input file                          */
+static FILE *yp = NULL;                        /* temporary file                      */
+static int mode;                       /* mode of file                        */
+static int numfds = 0;                 /* number of files cached              */
+static int maxfds = 0;                 /* number of files cached to be cached */
+static time_t mtime = (time_t) 0;      /* mtime of file                       */
+
+/*
+ * VMH
+ */
+#define        ALARM   ((unsigned int) 10)
+#define        ttyN(c) ttyNaux ((c), NULL)
+
+static int vmh = 0;
+
+static int vmhpid = OK;
+static int vmhfd0;
+static int vmhfd1;
+static int vmhfd2;
+
+static int vmhtty = NOTOK;
+
+#define        SCAN    1
+#define        STATUS  2
+#define        DISPLAY 3
+#define        NWIN    DISPLAY
+
+static int topcur = 0;
+
+static int numwins = 0;
+static int windows[NWIN + 1];
+
+static jmp_buf peerenv;
+
+#ifdef BPOP
+int pmsh = 0;                  /* BPOP enabled */
+extern char response[];
+#endif /* BPOP */
+
+/*
+ * PARENT
+ */
+static int pfd = NOTOK;                /* fd parent is reading from */
+static int ppid = 0;           /* pid of parent             */
+
+/*
+ * COMMAND
+ */
+int interactive;               /* running from a /dev/tty */
+int redirected;                        /* re-directing output     */
+FILE *sp = NULL;               /* original stdout         */
+
+char *cmd_name;                        /* command being run   */
+char myfilter[BUFSIZ];         /* path to mhl.forward */
+
+static char *myprompt = "(%s) ";/* prompting string */
+
+/*
+ * BBOARDS
+ */
+static int gap;                        /* gap in BBoard-ID:s */
+static char *myname = NULL;    /* BBoard name        */
+char *BBoard_ID = "BBoard-ID"; /* BBoard-ID constant */
+
+/*
+ * SIGNALS
+ */
+SIGNAL_HANDLER istat;          /* original SIGINT  */
+static SIGNAL_HANDLER pstat;   /* current SIGPIPE  */
+SIGNAL_HANDLER qstat;          /* original SIGQUIT */
+
+#ifdef SIGTSTP
+SIGNAL_HANDLER tstat;          /* original SIGTSTP */
+#endif
+
+int interrupted;               /* SIGINT detected  */
+int broken_pipe;               /* SIGPIPE detected */
+int told_to_quit;              /* SIGQUIT detected */
+
+#ifdef BSD42
+int should_intr;               /* signal handler should interrupt call */
+jmp_buf sigenv;                        /* the environment pointer              */
+#endif
+
+/*
+ * prototypes
+ */
+int SOprintf (char *, ...);  /* from termsbr.c */
+int sc_width (void);         /* from termsbr.c */
+void fsetup (char *);
+void setup (char *);
+FILE *msh_ready (int, int);
+void readids (int);
+int readid (int);
+void display_info (int);
+int expand (char *);
+void m_reset (void);
+void seq_setcur (struct msgs *, int);
+void padios (char *, char *, ...);
+void padvise (char *, char *, ...);
+
+
+/*
+ * static prototypes
+ */
+static void msh (int);
+static int read_map (char *, long);
+static int read_file (long, int);
+
+#ifdef BPOP
+# ifdef NNTP
+static int pop_statmsg (char *);
+# endif /* NNTP */
+static int read_pop (void);
+static int pop_action (char *);
+#endif /* BPOP */
+
+static void m_gMsgs (int);
+FILE *msh_ready (int, int);
+static int check_folder (int);
+static void scanrange (int, int);
+static void scanstring (char *);
+static void write_ids (void);
+static void quit (void);
+static int getargs (char *, struct swit *, struct Cmd *);
+static int getcmds (struct swit *, struct Cmd *, int);
+static int parse (char *, struct Cmd *);
+static int init_io (struct Cmd *, int);
+static int initaux_io (struct Cmd *);
+static void fin_io (struct Cmd *, int);
+static void finaux_io (struct Cmd *);
+static void m_init (void);
+static RETSIGTYPE intrser (int);
+static RETSIGTYPE pipeser (int);
+static RETSIGTYPE quitser (int);
+static RETSIGTYPE alrmser (int);
+static int pINI (void);
+static int pQRY (char *, int);
+static int pQRY1 (int);
+static int pQRY2 (void);
+static int pCMD (char *, struct swit *, struct Cmd *);
+static int pFIN (void);
+static int peerwait (void);
+static int ttyNaux (struct Cmd *, char *);
+static int ttyR (struct Cmd *);
+static int winN (struct Cmd *, int, int);
+static int winR (struct Cmd *);
+static int winX (int);
+
+
+int
+main (int argc, char **argv)
+{
+    int        id = 0, scansw = 0, vmh1 = 0, vmh2 = 0;
+    char *cp, *file = NULL, *folder = NULL;
+    char **argp, **arguments, buf[BUFSIZ];
+#ifdef BPOP
+    int        pmsh1 = 0, pmsh2 = 0;
+#endif
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    mts_init (invo_name);
+    arguments = getarguments (invo_name, argc,argv, 1);
+    argp = arguments;
+
+    while ((cp = *argp++)) {
+       if (*cp == '-')
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown", cp);
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf), "%s [switches] file", invo_name);
+                   print_help (buf, switches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case IDSW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   if ((id = atoi (cp)) < 1)
+                       adios (NULL, "bad argument %s %s", argp[-2], cp);
+                   continue;
+               case FDSW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   if ((pfd = atoi (cp)) <= 1)
+                       adios (NULL, "bad argument %s %s", argp[-2], cp);
+                   continue;
+               case QDSW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   if ((ppid = atoi (cp)) <= 1)
+                       adios (NULL, "bad argument %s %s", argp[-2], cp);
+                   continue;
+               case NMSW:
+                   if (!(myname = *argp++) || *myname == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+
+               case SCANSW: 
+                   scansw++;
+                   continue;
+               case NSCANSW: 
+                   scansw = 0;
+                   continue;
+
+               case PRMPTSW:
+                   if (!(myprompt = *argp++) || *myprompt == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+
+               case READSW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   if ((vmh1 = atoi (cp)) < 1)
+                       adios (NULL, "bad argument %s %s", argp[-2], cp);
+                   continue;
+               case WRITESW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   if ((vmh2 = atoi (cp)) < 1)
+                       adios (NULL, "bad argument %s %s", argp[-2], cp);
+                   continue;
+
+               case PREADSW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+#ifdef BPOP
+                   if ((pmsh1 = atoi (cp)) < 1)
+                       adios (NULL, "bad argument %s %s", argp[-2], cp);
+#endif /* BPOP */
+                   continue;
+               case PWRITSW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+#ifdef BPOP
+                   if ((pmsh2 = atoi (cp)) < 1)
+                       adios (NULL, "bad argument %s %s", argp[-2], cp);
+#endif /* BPOP */
+                   continue;
+
+               case TCURSW:
+                   topcur++;
+                   continue;
+               case NTCURSW:
+                   topcur = 0;
+                   continue;
+           }
+       if (*cp == '+' || *cp == '@') {
+           if (folder)
+               adios (NULL, "only one folder at a time!");
+           else
+               folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       }
+       else
+           if (file)
+               adios (NULL, "only one file at a time!");
+           else
+               file = cp;
+    }
+
+    if (!file && !folder)
+       file = "./msgbox";
+    if (file && folder)
+       adios (NULL, "use a file or a folder, not both");
+    strncpy (myfilter, etcpath (mhlforward), sizeof(myfilter));
+#ifdef FIOCLEX
+    if (pfd > 1)
+       ioctl (pfd, FIOCLEX, NULL);
+#endif /* FIOCLEX */
+
+#ifdef BSD42
+    should_intr = 0;
+#endif /* BSD42 */
+    istat = SIGNAL2 (SIGINT, intrser);
+    qstat = SIGNAL2 (SIGQUIT, quitser);
+
+    sc_width ();               /* MAGIC... */
+
+    if ((vmh = vmh1 && vmh2)) {
+       rcinit (vmh1, vmh2);
+       pINI ();
+       SIGNAL (SIGINT, SIG_IGN);
+       SIGNAL (SIGQUIT, SIG_IGN);
+#ifdef SIGTSTP
+       tstat = SIGNAL (SIGTSTP, SIG_IGN);
+#endif /* SIGTSTP */
+    }
+
+#ifdef BPOP
+    if (pmsh = pmsh1 && pmsh2) {
+       cp = getenv ("MHPOPDEBUG");
+#ifdef NNTP
+       if (pop_set (pmsh1, pmsh2, cp && *cp, myname) == NOTOK)
+#else /* NNTP */
+       if (pop_set (pmsh1, pmsh2, cp && *cp) == NOTOK)
+#endif /* NNTP */
+           padios (NULL, "%s", response);
+       if (folder)
+           file = folder, folder = NULL;
+    }
+#endif /* BPOP */
+
+    if (folder)
+       fsetup (folder);
+    else
+       setup (file);
+    readids (id);
+    display_info (id > 0 ? scansw : 0);
+
+    msh (id > 0 ? scansw : 0);
+
+    m_reset ();
+    
+    done (0);
+}
+
+
+static struct swit mshcmds[] = {
+#define        ADVCMD  0
+    { "advance", -7 },
+#define        ALICMD  1
+    { "ali", 0 },
+#define        EXPLCMD 2
+    { "burst", 0 },
+#define        COMPCMD 3
+    { "comp", 0 },
+#define        DISTCMD 4
+    { "dist", 0 },
+#define        EXITCMD 5
+    { "exit", 0 },
+#define        FOLDCMD 6
+    { "folder", 0 },
+#define        FORWCMD 7
+    { "forw", 0 },
+#define        HELPCMD 8
+    { "help", 0 },
+#define        INCMD   9
+    { "inc", 0 },
+#define        MARKCMD 10
+    { "mark", 0 },
+#define        MAILCMD 11
+    { "mhmail", 0 },
+#define        MHNCMD  12
+    { "mhn", 0 },
+#define        MSGKCMD 13
+    { "msgchk", 0 },
+#define        NEXTCMD 14
+    { "next", 0 },
+#define        PACKCMD 15
+    { "packf", 0 },
+#define        PICKCMD 16
+    { "pick", 0 },
+#define        PREVCMD 17
+    { "prev", 0 },
+#define        QUITCMD 18
+    { "quit", 0 },
+#define        FILECMD 19
+    { "refile", 0 },
+#define        REPLCMD 20
+    { "repl", 0 },
+#define        RMMCMD  21
+    { "rmm", 0 },
+#define        SCANCMD 22
+    { "scan", 0 },
+#define        SENDCMD 23
+    { "send", 0 },
+#define        SHOWCMD 24
+    { "show", 0 },
+#define        SORTCMD 25
+    { "sortm", 0 },
+#define        WHATCMD 26
+    { "whatnow", 0 },
+#define        WHOMCMD 27
+    { "whom", 0 },
+    { NULL, 0 }
+};
+
+
+static void
+msh (int scansw)
+{
+    int i;
+    register char *cp, **ap;
+    char prompt[BUFSIZ], *vec[MAXARGS];
+    struct Cmd typein;
+    register struct Cmd *cmdp;
+    static int once_only = ADVCMD;
+
+    snprintf (prompt, sizeof(prompt), myprompt, invo_name);
+    cmdp = &typein;
+
+    for (;;) {
+       if (yp) {
+           fclose (yp);
+           yp = NULL;
+       }
+       if (vmh) {
+           if ((i = getcmds (mshcmds, cmdp, scansw)) == EOF) {
+               rcdone ();
+               return;
+           }
+       } else {
+           check_folder (scansw);
+           if ((i = getargs (prompt, mshcmds, cmdp)) == EOF) {
+               putchar ('\n');
+               return;
+           }
+       }
+       cmd_name = mshcmds[i].sw;
+
+       switch (i) {
+           case QUITCMD: 
+               quit ();
+               return;
+
+           case ADVCMD:
+               if (once_only == ADVCMD)
+                   once_only = i = SHOWCMD;
+               else
+                   i = mp->curmsg != mp->hghmsg ? NEXTCMD : EXITCMD;
+               cmd_name = mshcmds[i].sw;
+               /* and fall... */
+
+           case EXITCMD:
+           case EXPLCMD: 
+           case FOLDCMD: 
+           case FORWCMD:       /* sigh */
+           case MARKCMD: 
+           case NEXTCMD: 
+           case PACKCMD: 
+           case PICKCMD: 
+           case PREVCMD: 
+           case RMMCMD: 
+           case SHOWCMD: 
+           case SCANCMD: 
+           case SORTCMD: 
+               if ((cp = context_find (cmd_name))) {
+                   cp = getcpy (cp);
+                   ap = brkstring (cp, " ", "\n");
+                   ap = copyip (ap, vec, MAXARGS);
+               } else {
+                   ap = vec;
+               }
+               break;
+
+           default: 
+               cp = NULL;
+               ap = vec;
+               break;
+       }
+       copyip (cmdp->args + 1, ap, MAXARGS);
+
+       m_init ();
+
+       if (!vmh && init_io (cmdp, vmh) == NOTOK) {
+           if (cp != NULL)
+               free (cp);
+           continue;
+       }
+       modified = 0;
+       redirected = vmh || cmdp->direction != STDIO;
+
+       switch (i) {
+           case ALICMD: 
+           case COMPCMD: 
+           case INCMD: 
+           case MAILCMD: 
+           case MSGKCMD: 
+           case SENDCMD: 
+           case WHATCMD: 
+           case WHOMCMD: 
+               if (!vmh || ttyN (cmdp) != NOTOK)
+                   forkcmd (vec, cmd_name);
+               break;
+
+           case DISTCMD: 
+               if (!vmh || ttyN (cmdp) != NOTOK)
+                   distcmd (vec);
+               break;
+
+           case EXPLCMD: 
+               if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK)
+                   explcmd (vec);
+               break;
+
+           case FILECMD: 
+               if (!vmh 
+                       || (filehak (vec) == OK ? ttyN (cmdp)
+                                       : winN (cmdp, DISPLAY, 1)) != NOTOK)
+                   filecmd (vec);
+               break;
+
+           case FOLDCMD: 
+               if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK)
+                   foldcmd (vec);
+               break;
+
+           case FORWCMD: 
+               if (!vmh || ttyN (cmdp) != NOTOK)
+                   forwcmd (vec);
+               break;
+
+           case HELPCMD: 
+               if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK)
+                   helpcmd (vec);
+               break;
+
+           case EXITCMD:
+           case MARKCMD: 
+               if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK)
+                   markcmd (vec);
+               break;
+
+           case MHNCMD:
+               if (!vmh || ttyN (cmdp) != NOTOK)
+                   mhncmd (vec);
+               break;
+
+           case NEXTCMD: 
+           case PREVCMD: 
+           case SHOWCMD: 
+               if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK)
+                   showcmd (vec);
+               break;
+
+           case PACKCMD: 
+               if (!vmh 
+                       || (packhak (vec) == OK ? ttyN (cmdp)
+                                       : winN (cmdp, DISPLAY, 1)) != NOTOK)
+                   packcmd (vec);
+               break;
+
+           case PICKCMD: 
+               if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK)
+                   pickcmd (vec);
+               break;
+
+           case REPLCMD: 
+               if (!vmh || ttyN (cmdp) != NOTOK)
+                   replcmd (vec);
+               break;
+
+           case RMMCMD: 
+               if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK)
+                   rmmcmd (vec);
+               break;
+
+           case SCANCMD: 
+               if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK)
+                   scancmd (vec);
+               break;
+
+           case SORTCMD: 
+               if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK)
+                   sortcmd (vec);
+               break;
+
+           default: 
+               padios (NULL, "no dispatch for %s", cmd_name);
+       }
+
+       if (vmh) {
+           if (vmhtty != NOTOK)
+               ttyR (cmdp);
+           if (vmhpid > OK)
+               winR (cmdp);
+       }
+       else
+           fin_io (cmdp, vmh);
+       if (cp != NULL)
+           free (cp);
+       if (i == EXITCMD) {
+           quit ();
+           return;
+       }
+    }
+}
+
+
+void
+fsetup (char *folder)
+{
+    register int msgnum;
+    char *maildir;
+    struct stat st;
+
+    maildir = m_maildir (folder);
+    if (chdir (maildir) == NOTOK)
+       padios (maildir, "unable to change directory to");
+
+    /* read folder and create message structure */
+    if (!(mp = folder_read (folder)))
+       padios (NULL, "unable to read folder %s", folder);
+
+    /* check for empty folder */
+    if (mp->nummsg == 0)
+       padios (NULL, "no messages in %s", folder);
+
+    mode = m_gmprot ();
+    mtime = stat (mp->foldpath, &st) != NOTOK ? st.st_mtime : 0;
+
+    m_gMsgs (mp->hghmsg);
+
+    for (msgnum = mp->lowmsg; msgnum <= mp->hghmsg; msgnum++) {
+       Msgs[msgnum].m_bboard_id = 0;
+       Msgs[msgnum].m_top = NOTOK;
+       Msgs[msgnum].m_start = Msgs[msgnum].m_stop = 0L;
+       Msgs[msgnum].m_scanl = NULL;
+    }
+
+    m_init ();
+
+    fmsh = getcpy (folder);
+
+    maxfds = OPEN_MAX / 2;
+
+    if ((maxfds -= 2) < 1)
+       maxfds = 1;
+}
+
+
+void
+setup (char *file)
+{
+    int i, msgp;
+    struct stat st;
+#ifdef BPOP
+    char tmpfil[BUFSIZ];
+#endif
+
+#ifdef BPOP
+    if (pmsh) {
+       strncpy (tmpfil, m_tmpfil (invo_name), sizeof(tmpfil));
+       if ((fp = fopen (tmpfil, "w+")) == NULL)
+           padios (tmpfil, "unable to create");
+       unlink (tmpfil);
+    }
+    else
+#endif /* BPOP */
+    if ((fp = fopen (file, "r")) == NULL)
+       padios (file, "unable to read");
+#ifdef FIOCLEX
+    ioctl (fileno (fp), FIOCLEX, NULL);
+#endif /* FIOCLEX */
+    if (fstat (fileno (fp), &st) != NOTOK) {
+       mode = (int) (st.st_mode & 0777), mtime = st.st_mtime;
+       msgp = read_map (file, (long) st.st_size);
+    }
+    else {
+       mode = m_gmprot (), mtime = 0;
+       msgp = 0;
+    }
+
+    if ((msgp = read_file (msgp ? Msgs[msgp].m_stop : 0L, msgp + 1)) < 1)
+       padios (NULL, "no messages in %s", myname ? myname : file);
+
+    if (!(mp = (struct msgs  *) calloc ((size_t) 1, sizeof(*mp))))
+       padios (NULL, "unable to allocate folder storage");
+
+    if (!(mp->msgstats = calloc ((size_t) 1, msgp + 3)))
+       padios (NULL, "unable to allocate message status storage");
+
+    mp->hghmsg = msgp;
+    mp->nummsg = msgp;
+    mp->lowmsg = 1;
+    mp->curmsg = 0;
+    mp->foldpath = getcpy (myname ? myname : file);
+    clear_folder_flags (mp);
+
+#ifdef BPOP
+    if (pmsh)
+       set_readonly (mp);
+    else {
+#endif /* BPOP */
+       stat (file, &st);
+       if (st.st_uid != getuid () || access (file, W_OK) == NOTOK)
+           set_readonly (mp);
+#ifdef BPOP
+    }
+#endif /* BPOP */
+
+    mp->lowoff = 1;
+    mp->hghoff = mp->hghmsg + 1;
+
+#ifdef BPOP
+    if (pmsh) {
+#ifndef        NNTP
+       for (i = mp->lowmsg; i <= mp->hghmsg; i++) {
+           Msgs[i].m_top = i;
+           clear_msg_flags (mp, i);
+           set_exists (mp, i);
+           set_virtual (mp, i);
+       }
+#else /* NNTP */
+       for (i = mp->lowmsg; i <= mp->hghmsg; i++) {
+           if (Msgs[i].m_top)                  /* set in read_pop() */
+               clear_msg_flags (mp, i);
+               set_exists (mp, i);
+               set_virtual (mp, i);
+       }
+#endif /* NNTP */
+    }
+    else
+#endif /* BPOP */
+    for (i = mp->lowmsg; i <= mp->hghmsg; i++) {
+       clear_msg_flags (mp, i);
+       set_exists (mp, i);
+    }
+    m_init ();
+
+    mp->msgattrs[0] = getcpy ("unseen");
+    mp->msgattrs[1] = NULL;
+
+    m_unknown (fp);            /* the MAGIC invocation */    
+    if (fmsh) {
+       free (fmsh);
+       fmsh = NULL;
+    }
+}
+
+
+static int
+read_map (char *file, long size)
+{
+    register int i, msgp;
+    register struct drop *dp, *mp;
+    struct drop *rp;
+
+#ifdef BPOP
+    if (pmsh)
+       return read_pop ();
+#endif /* BPOP */
+
+    if ((i = map_read (file, size, &rp, 1)) == 0)
+       return 0;
+
+    m_gMsgs (i);
+
+    msgp = 1;
+    for (dp = rp + 1; i-- > 0; msgp++, dp++) {
+       mp = &Msgs[msgp].m_drop;
+       mp->d_id = dp->d_id;
+       mp->d_size = dp->d_size;
+       mp->d_start = dp->d_start;
+       mp->d_stop = dp->d_stop;
+       Msgs[msgp].m_scanl = NULL;
+    }
+    free ((char *) rp);
+
+    return (msgp - 1);
+}
+
+
+static int
+read_file (long pos, int msgp)
+{
+    register int i;
+    register struct drop *dp, *mp;
+    struct drop *rp;
+
+#ifdef BPOP
+    if (pmsh)
+       return (msgp - 1);
+#endif /* BPOP */
+
+    if ((i = mbx_read (fp, pos, &rp, 1)) <= 0)
+       return (msgp - 1);
+
+    m_gMsgs ((msgp - 1) + i);
+
+    for (dp = rp; i-- > 0; msgp++, dp++) {
+       mp = &Msgs[msgp].m_drop;
+       mp->d_id = 0;
+       mp->d_size = dp->d_size;
+       mp->d_start = dp->d_start;
+       mp->d_stop = dp->d_stop;
+       Msgs[msgp].m_scanl = NULL;
+    }
+    free ((char *) rp);
+
+    return (msgp - 1);
+}
+
+
+#ifdef BPOP
+#ifdef NNTP
+static int pop_base = 0;
+
+static int
+pop_statmsg (char *s)
+{
+    register int i, n;
+
+    n = (i = atoi (s)) - pop_base;      /* s="nnn header-line..." */
+    Msgs[n].m_top = Msgs[n].m_bboard_id = i;
+}
+
+#endif /* NNTP */
+
+static int
+read_pop (void)
+{
+    int        nmsgs, nbytes;
+
+    if (pop_stat (&nmsgs, &nbytes) == NOTOK)
+       padios (NULL, "%s", response);
+
+    m_gMsgs (nmsgs);
+
+#ifdef NNTP    /* this makes read_pop() do some real work... */
+    pop_base = nbytes - 1;     /* nmsgs=last-first+1, nbytes=first */
+    pop_exists (pop_statmsg);
+#endif /* NNTP */
+    return nmsgs;
+}
+
+
+static int
+pop_action (char *s)
+{
+    fprintf (yp, "%s\n", s);
+}
+#endif /* BPOP */
+
+
+static void
+m_gMsgs (int n)
+{
+    int        nmsgs;
+
+    if (Msgs == NULL) {
+       nMsgs = n + MAXFOLDER / 2;
+       Msgs = (struct Msg *) calloc ((size_t) (nMsgs + 2), sizeof *Msgs);
+       if (Msgs == NULL)
+           padios (NULL, "unable to allocate Msgs structure");
+       return;
+    }
+
+    if (nMsgs >= n)
+       return;
+
+    nmsgs = nMsgs + n + MAXFOLDER / 2;
+    Msgs = (struct Msg *) realloc ((char *) Msgs, (size_t) (nmsgs + 2) * sizeof *Msgs);
+    if (Msgs == NULL)
+       padios (NULL, "unable to reallocate Msgs structure");
+    memset((char *) (Msgs + nMsgs + 2), 0, (size_t) ((nmsgs - nMsgs) * sizeof *Msgs));
+
+    nMsgs = nmsgs;
+}
+
+
+FILE *
+msh_ready (int msgnum, int full)
+{
+    register int msgp;
+    int fd;
+    char *cp;
+#ifdef BPOP
+    char tmpfil[BUFSIZ];
+    long pos1, pos2;
+#endif
+
+    if (yp) {
+       fclose (yp);
+       yp = NULL;
+    }
+
+    if (fmsh) {
+       if ((fd = Msgs[msgnum].m_top) == NOTOK) {
+           if (numfds >= maxfds)
+               for (msgp = mp->lowmsg; msgp <= mp->hghmsg; msgp++)
+                   if (Msgs[msgp].m_top != NOTOK) {
+                       close (Msgs[msgp].m_top);
+                       Msgs[msgp].m_top = NOTOK;
+                       numfds--;
+                       break;
+                   }
+
+           if ((fd = open (cp = m_name (msgnum), O_RDONLY)) == NOTOK)
+               padios (cp, "unable to open message");
+           Msgs[msgnum].m_top = fd;
+           numfds++;
+       }
+
+       if ((fd = dup (fd)) == NOTOK)
+           padios ("cached message", "unable to dup");
+       if ((yp = fdopen (fd, "r")) == NULL)
+           padios (NULL, "unable to fdopen cached message");
+       fseek (yp, 0L, SEEK_SET);
+       return yp;
+    }
+
+#ifdef BPOP
+    if (pmsh && is_virtual (mp, msgnum)) {
+       if (Msgs[msgnum].m_top == 0)
+           padios (NULL, "msh_ready (%d, %d) botch", msgnum, full);
+       if (!full) {
+           strncpy (tmpfil, m_tmpfil (invo_name), sizeof(tmpfil));
+           if ((yp = fopen (tmpfil, "w+")) == NULL)
+               padios (tmpfil, "unable to create");
+           unlink (tmpfil);
+
+           if (pop_top (Msgs[msgnum].m_top, 4, pop_action) == NOTOK)
+               padios (NULL, "%s", response);
+
+           m_eomsbr ((int (*)()) 0);   /* XXX */
+           msg_style = MS_DEFAULT;     /*  .. */
+           fseek (yp, 0L, SEEK_SET);
+           return yp;
+       }
+
+       fseek (fp, 0L, SEEK_END);
+       fwrite (mmdlm1, 1, strlen (mmdlm1), fp);
+       if (fflush (fp))
+           padios ("temporary file", "write error on");
+       fseek (fp, 0L, SEEK_END);
+       pos1 = ftell (fp);
+
+       yp = fp;
+       if (pop_retr (Msgs[msgnum].m_top, pop_action) == NOTOK)
+           padios (NULL, "%s", response);
+       yp = NULL;
+
+       fseek (fp, 0L, SEEK_END);
+       pos2 = ftell (fp);
+       fwrite (mmdlm2, 1, strlen (mmdlm2), fp);
+       if (fflush (fp))
+           padios ("temporary file", "write error on");
+
+       Msgs[msgnum].m_start = pos1;
+       Msgs[msgnum].m_stop = pos2;
+
+       unset_virtual (mp, msgnum);
+    }
+#endif /* BPOP */
+
+    m_eomsbr ((int (*)()) 0);  /* XXX */
+    fseek (fp, Msgs[msgnum].m_start, SEEK_SET);
+    return fp;
+}
+
+
+static int
+check_folder (int scansw)
+{
+    int seqnum, i, low, hgh, msgp;
+    struct stat st;
+
+#ifdef BPOP
+    if (pmsh)
+       return 0;
+#endif /* BPOP */
+
+    if (fmsh) {
+       if (stat (mp->foldpath, &st) == NOTOK)
+           padios (mp->foldpath, "unable to stat");
+       if (mtime == st.st_mtime)
+           return 0;
+       mtime = st.st_mtime;
+
+       low = mp->hghmsg + 1;
+       folder_free (mp);               /* free folder/message structure */
+
+       if (!(mp = folder_read (fmsh)))
+           padios (NULL, "unable to re-read folder %s", fmsh);
+
+       hgh = mp->hghmsg;
+
+       for (msgp = mp->lowmsg; msgp <= mp->hghmsg; msgp++) {
+           if (Msgs[msgp].m_top != NOTOK) {
+               close (Msgs[msgp].m_top);
+               Msgs[msgp].m_top = NOTOK;
+               numfds--;
+           }
+           if (Msgs[msgp].m_scanl) {
+               free (Msgs[msgp].m_scanl);
+               Msgs[msgp].m_scanl = NULL;
+           }
+       }
+
+       m_init ();
+
+       if (modified || low > hgh)
+           return 1;
+       goto check_vmh;
+    }
+    if (fstat (fileno (fp), &st) == NOTOK)
+       padios (mp->foldpath, "unable to fstat");
+    if (mtime == st.st_mtime)
+       return 0;
+    mode = (int) (st.st_mode & 0777);
+    mtime = st.st_mtime;
+
+    if ((msgp = read_file (Msgs[mp->hghmsg].m_stop, mp->hghmsg + 1)) < 1)
+       padios (NULL, "no messages in %s", mp->foldpath);       /* XXX */
+    if (msgp >= MAXFOLDER)
+       padios (NULL, "more than %d messages in %s", MAXFOLDER,
+               mp->foldpath);
+    if (msgp <= mp->hghmsg)
+       return 0;               /* XXX */
+
+    if (!(mp = folder_realloc (mp, mp->lowoff, msgp)))
+       padios (NULL, "unable to allocate folder storage");
+
+    low = mp->hghmsg + 1, hgh = msgp;
+    seqnum = scansw ? seq_getnum (mp, "unseen") : -1;
+    for (i = mp->hghmsg + 1; i <= msgp; i++) {
+       set_exists(mp, i);
+       if (seqnum != -1)
+           add_sequence(mp, seqnum, i);
+       mp->nummsg++;
+    }
+    mp->hghmsg = msgp;
+    m_init ();
+
+check_vmh: ;
+    if (vmh)
+       return 1;
+
+    advise (NULL, "new messages have arrived!\007");
+    if (scansw)
+       scanrange (low, hgh);
+
+    return 1;
+}
+
+
+static void
+scanrange (int low, int hgh)
+{
+    char buffer[BUFSIZ];
+
+    snprintf (buffer, sizeof(buffer), "%d-%d", low, hgh);
+    scanstring (buffer);
+}
+
+
+static void
+scanstring (char *arg)
+{
+    char *cp, **ap, *vec[MAXARGS];
+
+    /*
+     * This should be replace with a call to getarguments()
+     */
+    if ((cp = context_find (cmd_name = "scan"))) {
+       cp = getcpy (cp);
+       ap = brkstring (cp, " ", "\n");
+       ap = copyip (ap, vec, MAXARGS);
+    } else {
+       ap = vec;
+    }
+    *ap++ = arg;
+    *ap = NULL;
+    m_init ();
+    scancmd (vec);
+    if (cp != NULL)
+       free (cp);
+}
+
+
+void
+readids (int id)
+{
+    register int cur, seqnum, i, msgnum;
+
+    if (mp->curmsg == 0)
+       seq_setcur (mp, mp->lowmsg);
+    if (id <= 0 || (seqnum = seq_getnum (mp, "unseen")) == -1)
+       return;
+
+    for (msgnum = mp->hghmsg; msgnum >= mp->lowmsg; msgnum--)
+       add_sequence(mp, seqnum, msgnum);
+
+    if (id != 1) {
+       cur = mp->curmsg;
+
+       for (msgnum = mp->hghmsg; msgnum >= mp->lowmsg; msgnum--)
+         if (does_exist(mp, msgnum))           /* FIX */
+           if ((i = readid (msgnum)) > 0 && i < id) {
+               cur = msgnum + 1;
+               clear_sequence(mp, seqnum, msgnum);
+               break;
+           }
+       for (i = mp->lowmsg; i < msgnum; i++)
+           clear_sequence(mp, seqnum, i);
+
+       if (cur > mp->hghmsg)
+           cur = mp->hghmsg;
+
+       seq_setcur (mp, cur);
+    }
+
+    if ((gap = 1 < id && id < (i = readid (mp->lowmsg)) ? id : 0) && !vmh)
+       advise (NULL, "gap in ID:s, last seen %d, lowest present %d\n",
+               id - 1, i);
+}
+
+
+int
+readid (int msgnum)
+{
+    int i, state;
+    char *bp, buf[BUFSIZ], name[NAMESZ];
+    register FILE *zp;
+#ifdef BPOP
+    int        arg1, arg2, arg3;
+#endif
+
+    if (Msgs[msgnum].m_bboard_id)
+       return Msgs[msgnum].m_bboard_id;
+#ifdef BPOP
+    if (pmsh) {
+       if (Msgs[msgnum].m_top == 0)
+           padios (NULL, "readid (%d) botch", msgnum);
+       if (pop_list (Msgs[msgnum].m_top, (int *) 0, &arg1, &arg2, &arg3) == OK
+               && arg3 > 0)
+           return (Msgs[msgnum].m_bboard_id = arg3);
+    }
+#endif /* BPOP */
+
+    zp = msh_ready (msgnum, 0);
+    for (state = FLD;;)
+       switch (state = m_getfld (state, name, buf, sizeof(buf), zp)) {
+           case FLD: 
+           case FLDEOF: 
+           case FLDPLUS: 
+               if (!strcasecmp (name, BBoard_ID)) {
+                   bp = getcpy (buf);
+                   while (state == FLDPLUS) {
+                       state = m_getfld (state, name, buf, sizeof(buf), zp);
+                       bp = add (buf, bp);
+                   }
+                   i = atoi (bp);
+                   free (bp);
+                   if (i > 0)
+                       return (Msgs[msgnum].m_bboard_id = i);
+                   else
+                       continue;
+               }
+               while (state == FLDPLUS)
+                   state = m_getfld (state, name, buf, sizeof(buf), zp);
+               if (state != FLDEOF)
+                   continue;
+
+           default: 
+               return 0;
+       }
+}
+
+
+void
+display_info (int scansw)
+{
+    int seqnum, sd;
+
+    interactive = isatty (fileno (stdout));
+    if (sp == NULL) {
+       if ((sd = dup (fileno (stdout))) == NOTOK)
+           padios ("standard output", "unable to dup");
+#ifndef BSD42                  /* XXX */
+#ifdef FIOCLEX
+       ioctl (sd, FIOCLEX, NULL);
+#endif /* FIOCLEX */
+#endif /* not BSD42 */
+       if ((sp = fdopen (sd, "w")) == NULL)
+           padios ("standard output", "unable to fdopen");
+    }
+
+    m_putenv ("mhfolder", mp->foldpath);
+    if (vmh)
+       return;
+
+    if (myname) {
+       printf ("Reading ");
+       if (SOprintf ("%s", myname))
+           printf ("%s", myname);
+       printf (", currently at message %d of %d\n",
+               mp->curmsg, mp->hghmsg);
+    }
+    else {
+       printf ("Reading ");
+       if (fmsh)
+           printf ("+%s", fmsh);
+       else
+           printf ("%s", mp->foldpath);
+       printf (", currently at message %d of %d\n",
+               mp->curmsg, mp->hghmsg);
+    }
+
+    if (((seqnum = seq_getnum (mp, "unseen")) != -1)
+           && scansw
+           && in_sequence(mp, seqnum, mp->hghmsg))
+       scanstring ("unseen");
+}
+
+
+static void
+write_ids (void)
+{
+    int i = 0, seqnum, msgnum;
+    char buffer[80];
+
+    if (pfd <= 1)
+       return;
+
+    if ((seqnum = seq_getnum (mp, "unseen")) != -1)
+       for (msgnum = mp->hghmsg; msgnum >= mp->lowmsg; msgnum--)
+           if (!in_sequence(mp, seqnum, msgnum)) {
+               if (Msgs[msgnum].m_bboard_id == 0)
+                   readid (msgnum);
+               if ((i = Msgs[msgnum].m_bboard_id) > 0)
+                   break;
+           }
+
+    snprintf (buffer, sizeof(buffer), "%d %d\n", i, Msgs[mp->hghmsg].m_bboard_id);
+    write (pfd, buffer, sizeof(buffer));
+    close (pfd);
+    pfd = NOTOK;
+}
+
+
+static void
+quit (void)
+{
+    int i, md, msgnum;
+    char *cp, tmpfil[BUFSIZ];
+    char map1[BUFSIZ], map2[BUFSIZ];
+    struct stat st;
+    FILE *dp;
+
+    if (!(mp->msgflags & MODIFIED) || is_readonly(mp) || fmsh) {
+           if (vmh)
+               rc2peer (RC_FIN, 0, NULL);
+       return;
+    }
+
+    if (vmh) 
+       ttyNaux (NULLCMD, "FAST");
+    cp = NULL;
+    if ((dp = lkfopen (mp->foldpath, "r")) == NULL) {
+       advise (mp->foldpath, "unable to lock");
+       if (vmh) {
+           ttyR (NULLCMD);
+           pFIN ();
+       }       
+       return;
+    }
+    if (fstat (fileno (dp), &st) == NOTOK) {
+       advise (mp->foldpath, "unable to stat");
+       goto release;
+    }
+    if (mtime != st.st_mtime) {
+       advise (NULL, "new messages have arrived, no update");
+       goto release;
+    }
+    mode = (int) (st.st_mode & 0777);
+
+    if (mp->nummsg == 0) {
+       cp = concat ("Zero file \"", mp->foldpath, "\"? ", NULL);
+       if (getanswer (cp)) {
+           if ((i = creat (mp->foldpath, mode)) != NOTOK)
+               close (i);
+           else
+               advise (mp->foldpath, "error zero'ing");
+           unlink (map_name (mp->foldpath));/* XXX */
+       }
+       goto release;
+    }
+
+    cp = concat ("Update file \"", mp->foldpath, "\"? ", NULL);
+    if (!getanswer (cp))
+       goto release;
+    strncpy (tmpfil, m_backup (mp->foldpath), sizeof(tmpfil));
+    if ((md = mbx_open (tmpfil, mbx_style, st.st_uid, st.st_gid, mode)) == NOTOK) {
+       advise (tmpfil, "unable to open");
+       goto release;
+    }
+
+    for (msgnum = mp->lowmsg; msgnum <= mp->hghmsg; msgnum++)
+       if (does_exist(mp, msgnum) && pack (tmpfil, md, msgnum) == NOTOK) {
+           mbx_close (tmpfil, md);
+           unlink (tmpfil);
+           unlink (map_name (tmpfil));
+           goto release;
+       }
+    mbx_close (tmpfil, md);
+
+    if (rename (tmpfil, mp->foldpath) == NOTOK)
+       admonish (mp->foldpath, "unable to rename %s to", tmpfil);
+    else {
+       strncpy (map1, map_name (tmpfil), sizeof(map1));
+       strncpy (map2, map_name (mp->foldpath), sizeof(map2));
+
+       if (rename (map1, map2) == NOTOK) {
+           admonish (map2, "unable to rename %s to", map1);
+           unlink (map1);
+           unlink (map2);
+       }
+    }
+
+release: ;
+    if (cp)
+       free (cp);
+    lkfclose (dp, mp->foldpath);
+    if (vmh) {
+       ttyR (NULLCMD);
+       pFIN ();
+    }
+}
+
+
+static int
+getargs (char *prompt, struct swit *sw, struct Cmd *cmdp)
+{
+    int i;
+    char *cp;
+    static char buffer[BUFSIZ];
+
+    told_to_quit = 0;
+    for (;;) {
+       interrupted = 0;
+#ifdef BSD42
+       switch (setjmp (sigenv)) {
+           case OK:
+               should_intr = 1;
+               break;
+
+           default:
+               should_intr = 0;
+               if (interrupted && !told_to_quit) {
+                   putchar ('\n');
+                   continue;
+               }
+               if (ppid > 0)
+#ifdef SIGEMT
+                   kill (ppid, SIGEMT);
+#else
+                   kill (ppid, SIGTERM);
+#endif
+               return EOF;
+       }
+#endif /* BSD42 */
+       if (interactive) {
+           printf ("%s", prompt);
+           fflush (stdout);
+       }
+       for (cp = buffer; (i = getchar ()) != '\n';) {
+#ifndef BSD42
+           if (interrupted && !told_to_quit) {
+               buffer[0] = '\0';
+               putchar ('\n');
+               break;
+           }
+           if (told_to_quit || i == EOF) {
+               if (ppid > 0)
+#ifdef SIGEMT
+                   kill (ppid, SIGEMT);
+#else
+                   kill (ppid, SIGTERM);
+#endif
+               return EOF;
+           }
+#else /* BSD42 */
+           if (i == EOF)
+               longjmp (sigenv, DONE);
+#endif /* BSD42 */
+           if (cp < &buffer[sizeof buffer - 2])
+               *cp++ = i;
+       }
+       *cp = 0;
+
+       if (buffer[0] == 0)
+           continue;
+       if (buffer[0] == '?') {
+           printf ("commands:\n");
+           print_sw (ALL, sw, "");
+           printf ("type CTRL-D or use ``quit'' to leave %s\n",
+                   invo_name);
+           continue;
+       }
+
+       if (parse (buffer, cmdp) == NOTOK)
+           continue;
+
+       switch (i = smatch (cmdp->args[0], sw)) {
+           case AMBIGSW: 
+               ambigsw (cmdp->args[0], sw);
+               continue;
+           case UNKWNSW: 
+               printf ("say what: ``%s'' -- type ? (or help) for help\n",
+                       cmdp->args[0]);
+               continue;
+           default: 
+#ifdef BSD42
+               should_intr = 0;
+#endif /* BSD42 */
+               return i;
+       }
+    }
+}
+
+
+static int
+getcmds (struct swit *sw, struct Cmd *cmdp, int scansw)
+{
+    int i;
+    struct record rcs, *rc;
+
+    rc = &rcs;
+    initrc (rc);
+
+    for (;;)
+       switch (peer2rc (rc)) {
+           case RC_QRY: 
+               pQRY (rc->rc_data, scansw);
+               break;
+
+           case RC_CMD: 
+               if ((i = pCMD (rc->rc_data, sw, cmdp)) != NOTOK)
+                   return i;
+               break;
+
+           case RC_FIN: 
+               if (ppid > 0)
+#ifdef SIGEMT
+                   kill (ppid, SIGEMT);
+#else
+                   kill (ppid, SIGTERM);
+#endif
+               return EOF;
+
+           case RC_XXX: 
+               padios (NULL, "%s", rc->rc_data);
+
+           default: 
+               fmt2peer (RC_ERR, "pLOOP protocol screw-up");
+               done (1);
+       }
+}
+
+
+static int
+parse (char *buffer, struct Cmd *cmdp)
+{
+    int argp = 0;
+    char c, *cp, *pp;
+
+    cmdp->line[0] = 0;
+    pp = cmdp->args[argp++] = cmdp->line;
+    cmdp->redirect = NULL;
+    cmdp->direction = STDIO;
+    cmdp->stream = NULL;
+
+    for (cp = buffer; c = *cp; cp++) {
+       if (!isspace (c))
+           break;
+    }
+    if (c == '\0') {
+       if (vmh)
+           fmt2peer (RC_EOF, "null command");
+       return NOTOK;
+    }
+
+    while ((c = *cp++)) {
+       if (isspace (c)) {
+           while (isspace (c))
+               c = *cp++;
+           if (c == 0)
+               break;
+           *pp++ = 0;
+           cmdp->args[argp++] = pp;
+           *pp = 0;
+       }
+
+       switch (c) {
+           case '"': 
+               for (;;) {
+                   switch (c = *cp++) {
+                       case 0: 
+                           padvise (NULL, "unmatched \"");
+                           return NOTOK;
+                       case '"': 
+                           break;
+                       case QUOTE: 
+                           if ((c = *cp++) == 0)
+                               goto no_quoting;
+                       default: 
+                           *pp++ = c;
+                           continue;
+                   }
+                   break;
+               }
+               continue;
+
+           case QUOTE: 
+               if ((c = *cp++) == 0) {
+           no_quoting: ;
+                   padvise (NULL, "the newline character can not be quoted");
+                   return NOTOK;
+               }
+
+           default: ;
+               *pp++ = c;
+               continue;
+
+           case '>': 
+           case '|': 
+               if (pp == cmdp->line) {
+                   padvise (NULL, "invalid null command");
+                   return NOTOK;
+               }
+               if (*cmdp->args[argp - 1] == 0)
+                   argp--;
+               cmdp->direction = c == '>' ? CRTIO : PIPIO;
+               if (cmdp->direction == CRTIO && (c = *cp) == '>') {
+                   cmdp->direction = APPIO;
+                   cp++;
+               }
+               cmdp->redirect = pp + 1;/* sigh */
+               for (; c = *cp; cp++)
+                   if (!isspace (c))
+                       break;
+               if (c == 0) {
+                   padvise (NULL, cmdp->direction != PIPIO
+                           ? "missing name for redirect"
+                           : "invalid null command");
+                   return NOTOK;
+               }
+               strcpy (cmdp->redirect, cp);
+               if (cmdp->direction != PIPIO) {
+                   for (; *cp; cp++)
+                       if (isspace (*cp)) {
+                           padvise (NULL, "bad name for redirect");
+                           return NOTOK;
+                       }
+                   if (expand (cmdp->redirect) == NOTOK)
+                       return NOTOK;
+               }
+               break;
+       }
+       break;
+    }
+
+    *pp++ = 0;
+    cmdp->args[argp] = NULL;
+
+    return OK;
+}
+
+
+int
+expand (char *redirect)
+{
+    char *cp, *pp;
+    char path[BUFSIZ];
+    struct passwd  *pw;
+
+    if (*redirect != '~')
+       return OK;
+
+    if ((cp = strchr(pp = redirect + 1, '/')))
+       *cp++ = 0;
+    if (*pp == 0)
+       pp = mypath;
+    else
+       if ((pw = getpwnam (pp)))
+           pp = pw->pw_dir;
+       else {
+           padvise (NULL, "unknown user: %s", pp);
+           return NOTOK;
+       }
+
+    snprintf (path, sizeof(path), "%s/%s", pp, cp ? cp : "");
+    strcpy (redirect, path);
+    return OK;
+}
+
+
+static int
+init_io (struct Cmd *cmdp, int vio)
+{
+    int io, result;
+
+    io = vmh;
+
+    vmh = vio;
+    result = initaux_io (cmdp);
+    vmh = io;
+
+    return result;
+}
+
+
+static int
+initaux_io (struct Cmd *cmdp)
+{
+    char *mode;
+
+    switch (cmdp->direction) {
+       case STDIO: 
+           return OK;
+
+       case CRTIO: 
+       case APPIO: 
+           mode = cmdp->direction == CRTIO ? "write" : "append";
+           if ((cmdp->stream = fopen (cmdp->redirect, mode)) == NULL) {
+               padvise (cmdp->redirect, "unable to %s ", mode);
+               cmdp->direction = STDIO;
+               return NOTOK;
+           }
+           break;
+
+       case PIPIO: 
+           if ((cmdp->stream = popen (cmdp->redirect, "w")) == NULL) {
+               padvise (cmdp->redirect, "unable to pipe");
+               cmdp->direction = STDIO;
+               return NOTOK;
+           }
+           SIGNAL (SIGPIPE, pipeser);
+           broken_pipe = 0;
+           break;
+
+       default: 
+           padios (NULL, "unknown redirection for command");
+    }
+
+    fflush (stdout);
+    if (dup2 (fileno (cmdp->stream), fileno (stdout)) == NOTOK)
+       padios ("standard output", "unable to dup2");
+    clearerr (stdout);
+
+    return OK;
+}
+
+
+static void
+fin_io (struct Cmd *cmdp, int vio)
+{
+    int io;
+
+    io = vmh;
+    vmh = vio;
+    finaux_io (cmdp);
+    vmh = io;
+}
+
+
+static void
+finaux_io (struct Cmd *cmdp)
+{
+    switch (cmdp->direction) {
+       case STDIO: 
+           return;
+
+       case CRTIO: 
+       case APPIO: 
+           fflush (stdout);
+           close (fileno (stdout));
+           if (ferror (stdout))
+               padvise (NULL, "problems writing %s", cmdp->redirect);
+           fclose (cmdp->stream);
+           break;
+
+       case PIPIO: 
+           fflush (stdout);
+           close (fileno (stdout));
+           pclose (cmdp->stream);
+           SIGNAL (SIGPIPE, SIG_DFL);
+           break;
+
+       default: 
+           padios (NULL, "unknown redirection for command");
+    }
+
+    if (dup2 (fileno (sp), fileno (stdout)) == NOTOK)
+       padios ("standard output", "unable to dup2");
+    clearerr (stdout);
+
+    cmdp->direction = STDIO;
+}
+
+
+static void
+m_init (void)
+{
+    int msgnum;
+
+    for (msgnum = mp->lowmsg; msgnum <= mp->hghmsg; msgnum++)
+       unset_selected (mp, msgnum);
+    mp->lowsel = mp->hghsel = mp->numsel = 0;
+}
+
+
+void
+m_reset (void)
+{
+    write_ids ();
+    folder_free (mp);  /* free folder/message structure */
+    myname = NULL;
+#ifdef BPOP
+    if (pmsh) {
+       pop_done ();
+       pmsh = 0;
+    }
+#endif /* BPOP */
+}
+
+
+void
+seq_setcur (struct msgs *mp, int msgnum)
+{
+    if (mp->curmsg == msgnum)
+       return;
+
+    if (mp->curmsg && Msgs[mp->curmsg].m_scanl) {
+       free (Msgs[mp->curmsg].m_scanl);
+       Msgs[mp->curmsg].m_scanl = NULL;
+    }
+    if (Msgs[msgnum].m_scanl) {
+       free (Msgs[msgnum].m_scanl);
+       Msgs[msgnum].m_scanl = NULL;
+    }
+
+    mp->curmsg = msgnum;
+}
+
+
+
+static RETSIGTYPE
+intrser (int i)
+{
+#ifndef RELIABLE_SIGNALS
+    SIGNAL (SIGINT, intrser);
+#endif
+
+    discard (stdout);
+    interrupted++;
+
+#ifdef BSD42
+    if (should_intr)
+       longjmp (sigenv, NOTOK);
+#endif
+}
+
+
+static RETSIGTYPE
+pipeser (int i)
+{
+#ifndef RELIABLE_SIGNALS
+    SIGNAL (SIGPIPE, pipeser);
+#endif
+
+    if (broken_pipe++ == 0)
+       fprintf (stderr, "broken pipe\n");
+    told_to_quit++;
+    interrupted++;
+
+#ifdef BSD42
+    if (should_intr)
+       longjmp (sigenv, NOTOK);
+#endif
+}
+
+
+static RETSIGTYPE
+quitser (int i)
+{
+#ifndef RELIABLE_SIGNALS
+    SIGNAL (SIGQUIT, quitser);
+#endif
+
+    told_to_quit++;
+    interrupted++;
+
+#ifdef BSD42
+    if (should_intr)
+       longjmp (sigenv, NOTOK);
+#endif
+}
+
+
+static RETSIGTYPE
+alrmser (int i)
+{
+    longjmp (peerenv, DONE);
+}
+
+
+static int
+pINI (void)
+{
+    int i, vrsn;
+    char *bp;
+    struct record rcs, *rc;
+
+    rc = &rcs;
+    initrc (rc);
+
+    switch (peer2rc (rc)) {
+       case RC_INI: 
+           bp = rc->rc_data;
+           while (isspace (*bp))
+               bp++;
+           if (sscanf (bp, "%d", &vrsn) != 1) {
+       bad_init: ;
+               fmt2peer (RC_ERR, "bad init \"%s\"", rc->rc_data);
+               done (1);
+           }
+           if (vrsn != RC_VRSN) {
+               fmt2peer (RC_ERR, "version %d unsupported", vrsn);
+               done (1);
+           }
+
+           while (*bp && !isspace (*bp))
+               bp++;
+           while (isspace (*bp))
+               bp++;
+           if (sscanf (bp, "%d", &numwins) != 1 || numwins <= 0)
+               goto bad_init;
+           if (numwins > NWIN)
+               numwins = NWIN;
+
+           for (i = 1; i <= numwins; i++) {
+               while (*bp && !isspace (*bp))
+                   bp++;
+               while (isspace (*bp))
+                   bp++;
+               if (sscanf (bp, "%d", &windows[i]) != 1 || windows[i] <= 0)
+                   goto bad_init;
+           }
+           rc2peer (RC_ACK, 0, NULL);
+           return OK;
+
+       case RC_XXX: 
+           padios (NULL, "%s", rc->rc_data);
+
+       default: 
+           fmt2peer (RC_ERR, "pINI protocol screw-up");
+           done (1);           /* NOTREACHED */
+    }
+}
+
+
+static int
+pQRY (char *str, int scansw)
+{
+    if (pQRY1 (scansw) == NOTOK || pQRY2 () == NOTOK)
+       return NOTOK;
+
+    rc2peer (RC_EOF, 0, NULL);
+    return OK;
+}
+       
+
+static int
+pQRY1 (int scansw)
+{
+    int oldhgh;
+    static int lastlow = 0,
+               lastcur = 0,
+               lasthgh = 0,
+               lastnum = 0;
+
+    oldhgh = mp->hghmsg;
+    if (check_folder (scansw) && oldhgh < mp->hghmsg) {
+       switch (winX (STATUS)) {
+           case NOTOK: 
+               return NOTOK;
+
+           case OK: 
+               printf ("new messages have arrived!");
+               fflush (stdout);
+               fflush (stderr);
+               _exit (0);      /* NOTREACHED */
+
+           default: 
+               lastlow = lastcur = lasthgh = lastnum = 0;
+               break;
+       }
+
+       switch (winX (DISPLAY)) {
+           case NOTOK: 
+               return NOTOK;
+
+           case OK: 
+               scanrange (oldhgh + 1, mp->hghmsg);
+               fflush (stdout);
+               fflush (stderr);
+               _exit (0);      /* NOTREACHED */
+
+           default: 
+               break;
+       }
+       return OK;
+    }
+
+    if (gap)
+       switch (winX (STATUS)) {
+           case NOTOK:
+               return NOTOK;
+
+           case OK:
+               printf ("%s: gap in ID:s, last seen %d, lowest present %d\n",
+                   myname ? myname : fmsh ? fmsh : mp->foldpath, gap - 1,
+                   readid (mp->lowmsg));
+               fflush (stdout);
+               fflush (stderr);
+               _exit (0);      /* NOTREACHED */
+
+           default:
+               gap = 0;
+               return OK;
+       }
+
+    if (mp->lowmsg != lastlow
+           || mp->curmsg != lastcur
+           || mp->hghmsg != lasthgh
+           || mp->nummsg != lastnum)
+       switch (winX (STATUS)) {
+           case NOTOK: 
+               return NOTOK;
+
+           case OK: 
+               foldcmd (NULL);
+               fflush (stdout);
+               fflush (stderr);
+               _exit (0);      /* NOTREACHED */
+
+           default: 
+               lastlow = mp->lowmsg;
+               lastcur = mp->curmsg;
+               lasthgh = mp->hghmsg;
+               lastnum = mp->nummsg;
+               return OK;
+       }
+
+    return OK;
+}
+
+
+static int
+pQRY2 (void)
+{
+    int i, j, k, msgnum, n;
+    static int cur = 0,
+               num = 0,
+               lo = 0,
+               hi = 0;
+
+    if (mp->nummsg == 0 && mp->nummsg != num)
+       switch (winX (SCAN)) {
+           case NOTOK: 
+               return NOTOK;
+
+           case OK: 
+               printf ("empty!");
+               fflush (stdout);
+               fflush (stderr);
+               _exit (0);      /* NOTREACHED */
+
+           default: 
+               num = mp->nummsg;
+               return OK;
+       }
+    num = mp->nummsg;
+
+    i = 0;
+    j = (k = windows[SCAN]) / 2;
+    for (msgnum = mp->curmsg; msgnum <= mp->hghmsg; msgnum++)
+       if (does_exist (mp, msgnum))
+           i++;
+    if (i-- > 0)
+       if (topcur)
+           k = i >= k ? 1 : k - i;
+       else
+           k -= i > j ? j : i;
+
+    i = j = 0;
+    n = 1;
+    for (msgnum = mp->curmsg; msgnum >= mp->lowmsg; msgnum--)
+       if (does_exist (mp, msgnum)) {
+           i = msgnum;
+           if (j == 0)
+               j = msgnum;
+           if (n++ >= k)
+               break;
+       }
+    for (msgnum = mp->curmsg + 1; msgnum <= mp->hghmsg; msgnum++)
+       if (does_exist (mp, msgnum)) {
+           if (i == 0)
+               i = msgnum;
+           j = msgnum;
+           if (n++ >= windows[SCAN])
+               break;
+       }
+    if (!topcur
+           && lo > 0
+           && hi > 0
+           && does_exist (mp, lo)
+           && does_exist (mp, hi)
+           && (lo < mp->curmsg
+                   || (lo == mp->curmsg && lo == mp->lowmsg))
+           && (mp->curmsg < hi
+                   || (hi == mp->curmsg && hi == mp->hghmsg))
+           && hi - lo == j - i)
+       i = lo, j = hi;
+
+    if (mp->curmsg != cur || modified)
+       switch (winN (NULLCMD, SCAN, 0)) {
+           case NOTOK: 
+               return NOTOK;
+
+           case OK:
+               return OK;
+
+           default: 
+               scanrange (lo = i, hi = j);
+               cur = mp->curmsg;
+               winR (NULLCMD);
+               return OK;
+       }
+
+    return OK;
+}
+
+
+static int
+pCMD (char *str, struct swit *sw, struct Cmd *cmdp)
+{
+    int i;
+
+    if (*str == '?')
+       switch (winX (DISPLAY)) {
+           case NOTOK: 
+               return NOTOK;
+
+           case OK: 
+               printf ("commands:\n");
+               print_sw (ALL, sw, "");
+               printf ("type ``quit'' to leave %s\n", invo_name);
+               fflush (stdout);
+               fflush (stderr);
+               _exit (0);      /* NOTREACHED */
+
+           default: 
+               rc2peer (RC_EOF, 0, NULL);
+               return NOTOK;
+       }
+
+    if (parse (str, cmdp) == NOTOK)
+       return NOTOK;
+
+    switch (i = smatch (cmdp->args[0], sw)) {
+       case AMBIGSW: 
+           switch (winX (DISPLAY)) {
+               case NOTOK: 
+                   return NOTOK;
+
+               case OK: 
+                   ambigsw (cmdp->args[0], sw);
+                   fflush (stdout);
+                   fflush (stderr);
+                   _exit (0);  /* NOTREACHED */
+
+               default: 
+                   rc2peer (RC_EOF, 0, NULL);
+                   return NOTOK;
+           }
+
+       case UNKWNSW: 
+           fmt2peer (RC_ERR,
+                   "say what: ``%s'' -- type ? (or help) for help",
+                   cmdp->args[0]);
+           return NOTOK;
+
+       default: 
+           return i;
+    }
+}
+
+
+static int
+pFIN (void)
+{
+    int status;
+
+    switch (setjmp (peerenv)) {
+       case OK: 
+           SIGNAL (SIGALRM, alrmser);
+           alarm (ALARM);
+
+           status = peerwait ();
+
+           alarm (0);
+           return status;
+
+       default: 
+           return NOTOK;
+    }
+}
+
+
+static int
+peerwait (void)
+{
+    struct record rcs, *rc;
+
+    rc = &rcs;
+    initrc (rc);
+
+    switch (peer2rc (rc)) {
+       case RC_QRY: 
+       case RC_CMD: 
+           rc2peer (RC_FIN, 0, NULL);
+           return OK;
+
+       case RC_XXX: 
+           advise (NULL, "%s", rc->rc_data);
+           return NOTOK;
+
+       default: 
+           fmt2peer (RC_FIN, "pLOOP protocol screw-up");
+           return NOTOK;
+    }
+}
+
+
+static int
+ttyNaux (struct Cmd *cmdp, char *s)
+{
+    struct record rcs, *rc;
+
+    rc = &rcs;
+    initrc (rc);
+
+    if (cmdp && init_io (cmdp, vmh) == NOTOK)
+       return NOTOK;
+
+    /* XXX: fseek() too tricky for our own good */
+    if (!fmsh)
+       fseek (fp, 0L, SEEK_SET);
+
+    vmhtty = NOTOK;
+    switch (rc2rc (RC_TTY, s ? strlen (s) : 0, s, rc)) {
+       case RC_ACK: 
+           vmhtty = OK;        /* fall */
+       case RC_ERR: 
+           break;
+
+       case RC_XXX: 
+           padios (NULL, "%s", rc->rc_data);/* NOTREACHED */
+
+       default: 
+           fmt2peer (RC_ERR, "pTTY protocol screw-up");
+           done (1);           /* NOTREACHED */
+    }
+
+#ifdef SIGTSTP
+    SIGNAL (SIGTSTP, tstat);
+#endif
+    return vmhtty;
+}
+
+
+static int
+ttyR (struct Cmd *cmdp)
+{
+    struct record rcs, *rc;
+
+    rc = &rcs;
+
+#ifdef SIGTSTP
+    SIGNAL (SIGTSTP, SIG_IGN);
+#endif
+
+    if (vmhtty != OK)
+       return NOTOK;
+
+    initrc (rc);
+
+    if (cmdp)
+       fin_io (cmdp, 0);
+
+    vmhtty = NOTOK;
+    switch (rc2rc (RC_EOF, 0, NULL, rc)) {
+       case RC_ACK: 
+           rc2peer (RC_EOF, 0, NULL);
+           return OK;
+
+       case RC_XXX: 
+           padios (NULL, "%s", rc->rc_data);/* NOTREACHED */
+
+       default: 
+           fmt2peer (RC_ERR, "pTTY protocol screw-up");
+           done (1);           /* NOTREACHED */
+    }
+}
+
+
+static int
+winN (struct Cmd *cmdp, int n, int eof)
+{
+    int i, pd[2];
+    char buffer[BUFSIZ];
+    struct record rcs, *rc;
+
+    rc = &rcs;
+    if (vmhpid == NOTOK)
+       return OK;
+
+    initrc (rc);
+
+    /* XXX: fseek() too tricky for our own good */
+    if (!fmsh)
+       fseek (fp, 0L, SEEK_SET);
+
+    vmhpid = OK;
+
+    snprintf (buffer, sizeof(buffer), "%d", n);
+    switch (str2rc (RC_WIN, buffer, rc)) {
+       case RC_ACK: 
+           break;
+
+       case RC_ERR: 
+           return NOTOK;
+
+       case RC_XXX: 
+           padios (NULL, "%s", rc->rc_data);
+
+       default: 
+           fmt2peer (RC_ERR, "pWIN protocol screw-up");
+           done (1);
+    }
+
+    if (pipe (pd) == NOTOK) {
+       err2peer (RC_ERR, "pipe", "unable to");
+       return NOTOK;
+    }
+
+    switch (vmhpid = fork()) {
+       case NOTOK: 
+           err2peer (RC_ERR, "fork", "unable to");
+           close (pd[0]);
+           close (pd[1]);
+           return NOTOK;
+
+       case OK: 
+           close (pd[1]);
+           SIGNAL (SIGPIPE, SIG_IGN);
+           while ((i = read (pd[0], buffer, sizeof buffer)) > 0)
+               switch (rc2rc (RC_DATA, i, buffer, rc)) {
+                   case RC_ACK: 
+                       break;
+
+                   case RC_ERR: 
+                       _exit (1);
+
+                   case RC_XXX: 
+                       advise (NULL, "%s", rc->rc_data);
+                       _exit (2);
+
+                   default: 
+                       fmt2peer (RC_ERR, "pWIN protocol screw-up");
+                       _exit (2);
+               }
+           if (i == OK)
+               switch (rc2rc (RC_EOF, 0, NULL, rc)) {
+                   case RC_ACK: 
+                       if (eof)
+                           rc2peer (RC_EOF, 0, NULL);
+                       i = 0;
+                       break;
+
+                   case RC_XXX: 
+                       advise (NULL, "%s", rc->rc_data);
+                       i = 2;
+                       break;
+
+                   default: 
+                       fmt2peer (RC_ERR, "pWIN protocol screw-up");
+                       i = 2;
+                       break;
+               }
+           if (i == NOTOK)
+               err2peer (RC_ERR, "pipe", "error reading from");
+           close (pd[0]);
+           _exit (i != NOTOK ? i : 1);
+
+       default: 
+           if ((vmhfd0 = dup (fileno (stdin))) == NOTOK)
+               padios ("standard input", "unable to dup");
+           if ((vmhfd1 = dup (fileno (stdout))) == NOTOK)
+               padios ("standard output", "unable to dup");
+           if ((vmhfd2 = dup (fileno (stderr))) == NOTOK)
+               padios ("diagnostic output", "unable to dup");
+
+           close (0);
+           if ((i = open ("/dev/null", O_RDONLY)) != NOTOK && i != fileno (stdin)) {
+               dup2 (i, fileno (stdin));
+               close (i);
+           }
+
+           fflush (stdout);
+           if (dup2 (pd[1], fileno (stdout)) == NOTOK)
+               padios ("standard output", "unable to dup2");
+           clearerr (stdout);
+
+           fflush (stderr);
+           if (dup2 (pd[1], fileno (stderr)) == NOTOK)
+               padios ("diagnostic output", "unable to dup2");
+           clearerr (stderr);
+
+           if (cmdp && init_io (cmdp, 0) == NOTOK)
+               return NOTOK;
+           pstat = SIGNAL (SIGPIPE, pipeser);
+           broken_pipe = 1;
+
+           close (pd[0]);
+           close (pd[1]);
+
+           return vmhpid;
+    }
+}
+
+
+static int
+winR (struct Cmd *cmdp)
+{
+    int status;
+
+    if (vmhpid <= OK)
+       return NOTOK;
+
+    if (cmdp)
+       fin_io (cmdp, 0);
+
+    if (dup2 (vmhfd0, fileno (stdin)) == NOTOK)
+       padios ("standard input", "unable to dup2");
+    clearerr (stdin);
+    close (vmhfd0);
+
+    fflush (stdout);
+    if (dup2 (vmhfd1, fileno (stdout)) == NOTOK)
+       padios ("standard output", "unable to dup2");
+    clearerr (stdout);
+    close (vmhfd1);
+
+    fflush (stderr);
+    if (dup2 (vmhfd2, fileno (stderr)) == NOTOK)
+       padios ("diagnostic output", "unable to dup2");
+    clearerr (stderr);
+    close (vmhfd2);
+
+    SIGNAL (SIGPIPE, pstat);
+
+    if ((status = pidwait (vmhpid, OK)) == 2)
+       done (1);
+
+    vmhpid = OK;
+    return (status == 0 ? OK : NOTOK);
+}
+
+
+static int
+winX (int n)
+{
+    int i, pid, pd[2];
+    char buffer[BUFSIZ];
+    struct record rcs, *rc;
+
+    rc = &rcs;
+    initrc (rc);
+
+    /* XXX: fseek() too tricky for our own good */
+    if (!fmsh)
+       fseek (fp, 0L, SEEK_SET);
+
+    snprintf (buffer, sizeof(buffer), "%d", n);
+    switch (str2rc (RC_WIN, buffer, rc)) {
+       case RC_ACK: 
+           break;
+
+       case RC_ERR: 
+           return NOTOK;
+
+       case RC_XXX: 
+           padios (NULL, "%s", rc->rc_data);
+
+       default: 
+           fmt2peer (RC_ERR, "pWIN protocol screw-up");
+           done (1);
+    }
+
+    if (pipe (pd) == NOTOK) {
+       err2peer (RC_ERR, "pipe", "unable to");
+       return NOTOK;
+    }
+
+    switch (pid = fork ()) {
+       case NOTOK: 
+           err2peer (RC_ERR, "fork", "unable to");
+           close (pd[0]);
+           close (pd[1]);
+           return NOTOK;
+
+       case OK: 
+           close (fileno (stdin));
+           if ((i = open ("/dev/null", O_RDONLY)) != NOTOK && i != fileno (stdin)) {
+               dup2 (i, fileno (stdin));
+               close (i);
+           }
+           dup2 (pd[1], fileno (stdout));
+           dup2 (pd[1], fileno (stderr));
+           close (pd[0]);
+           close (pd[1]);
+           vmhpid = NOTOK;
+           return OK;
+
+       default: 
+           close (pd[1]);
+           while ((i = read (pd[0], buffer, sizeof buffer)) > 0)
+               switch (rc2rc (RC_DATA, i, buffer, rc)) {
+                   case RC_ACK: 
+                       break;
+
+                   case RC_ERR: 
+                       close (pd[0]);
+                       pidwait (pid, OK);
+                       return NOTOK;
+
+                   case RC_XXX: 
+                       padios (NULL, "%s", rc->rc_data);
+
+                   default: 
+                       fmt2peer (RC_ERR, "pWIN protocol screw-up");
+                       done (1);
+               }
+           if (i == OK)
+               switch (rc2rc (RC_EOF, 0, NULL, rc)) {
+                   case RC_ACK: 
+                       break;
+
+                   case RC_XXX: 
+                       padios (NULL, "%s", rc->rc_data);
+
+                   default: 
+                       fmt2peer (RC_ERR, "pWIN protocol screw-up");
+                       done (1);
+               }
+           if (i == NOTOK)
+               err2peer (RC_ERR, "pipe", "error reading from");
+
+           close (pd[0]);
+           pidwait (pid, OK);
+           return (i != NOTOK ? pid : NOTOK);
+    }
+}
+
+
+void
+padios (char *what, char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    if (vmh) {
+       verr2peer (RC_FIN, what, fmt, ap);
+       rcdone ();
+    } else {
+       advertise (what, NULL, fmt, ap);
+    }
+    va_end(ap);
+
+    done (1);
+}
+
+
+void
+padvise (char *what, char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    if (vmh) {
+       verr2peer (RC_ERR, what, fmt, ap);
+    } else {
+       advertise (what, NULL, fmt, ap);
+    }
+    va_end(ap);
+}
diff --git a/uip/mshcmds.c b/uip/mshcmds.c
new file mode 100644 (file)
index 0000000..fcdd5e1
--- /dev/null
@@ -0,0 +1,3095 @@
+
+/*
+ * mshcmds.c -- command handlers in msh
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/signals.h>
+#include <h/dropsbr.h>
+#include <h/fmt_scan.h>
+#include <h/scansbr.h>
+#include <zotnet/tws/tws.h>
+#include <zotnet/mts/mts.h>
+#include <errno.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <h/msh.h>
+#include <h/picksbr.h>
+
+extern int errno;
+
+static char delim3[] = "-------";      /* from burst.c */
+
+static int mhlnum;
+static FILE *mhlfp;
+
+#if defined(NNTP) && defined(MPOP)
+# undef        MPOP
+#endif
+
+#ifdef MPOP
+# ifdef BPOP
+extern int pmsh;
+extern char response[];
+# endif
+#endif /* MPOP */
+
+/*
+ * Type for a compare function for qsort.  This keeps
+ * the compiler happy.
+ */
+typedef int (*qsort_comp) (const void *, const void *);
+
+/*
+ * prototypes
+ */
+void clear_screen (void);   /* from termsbr.c */
+int SOprintf (char *, ...); /* from termsbr.c */
+int sc_width (void);        /* from termsbr.c */
+
+/*
+ * static prototypes
+ */
+static int burst (struct Msg *, int, int, int, int);
+static void forw (char *, char *, int, char **);
+static void rmm (void);
+static void show (int);
+static int eom_action (int);
+static FILE *mhl_action (char *);
+static int ask (int);
+static int is_nontext (int);
+static int get_fields (char *, char *, int, struct Msg *);
+static int msgsort (struct Msg *, struct Msg *);
+static int subsort (struct Msg *, struct Msg *);
+static char *sosmash (char *, char *);
+static int process (int, char *, int, char **);
+static void copy_message (int, FILE *);
+static void copy_digest (int, FILE *);
+
+
+void
+forkcmd (char **args, char *pgm)
+{
+    int child_id;
+    char *vec[MAXARGS];
+
+    vec[0] = r1bindex (pgm, '/');
+    copyip (args, vec + 1, MAXARGS - 1);
+
+    if (fmsh) {
+       context_del (pfolder);
+       context_replace (pfolder, fmsh);/* update current folder   */
+       seq_save (mp);
+       context_save ();                /* save the context file   */
+    }
+    fflush (stdout);
+    switch (child_id = fork ()) {
+       case NOTOK: 
+           advise ("fork", "unable to");
+           return;
+
+       case OK: 
+           closefds (3);
+           SIGNAL (SIGINT, istat);
+           SIGNAL (SIGQUIT, qstat);
+
+           execvp (pgm, vec);
+           fprintf (stderr, "unable to exec ");
+           perror (cmd_name);
+           _exit (1);
+
+       default: 
+           pidXwait (child_id, NULL);
+           break;
+    }
+    if (fmsh) {                        /* assume the worst case */
+       mp->msgflags |= MODIFIED;
+       modified++;
+    }
+}
+
+
+static struct swit distswit[] = {
+#define        DIANSW                    0
+    { "annotate", 0 },
+#define        DINANSW                   1
+    { "noannotate", 0 },
+#define        DIDFSW                    2
+    { "draftfolder +folder", 0 },
+#define        DIDMSW                    3
+    { "draftmessage msg", 0 },
+#define        DINDFSW                   4
+    { "nodraftfolder", 0 },
+#define        DIEDTSW                   5
+    { "editor editor", 0 },
+#define        DINEDSW                   6
+    { "noedit", 0 },
+#define        DIFRMSW                   7
+    { "form formfile", 0 },
+#define        DIINSW                    8
+    { "inplace", 0 },
+#define        DININSW                   9
+    { "noinplace", 0 },
+#define        DIWHTSW                  10
+    { "whatnowproc program", 0 },
+#define        DINWTSW                  11
+    { "nowhatnowproc", 0 },
+#define        DIHELP                   12
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+
+void
+distcmd (char **args)
+{
+    int vecp = 1;
+    char *cp, *msg = NULL;
+    char buf[BUFSIZ], *vec[MAXARGS];
+
+    if (fmsh) {
+       forkcmd (args, cmd_name);
+       return;
+    }
+
+    while ((cp = *args++)) {
+       if (*cp == '-')
+           switch (smatch (++cp, distswit)) {
+               case AMBIGSW: 
+                   ambigsw (cp, distswit);
+                   return;
+               case UNKWNSW: 
+                   fprintf (stderr, "-%s unknown\n", cp);
+                   return;
+               case DIHELP: 
+                   snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
+                   print_help (buf, distswit, 1);
+                   return;
+
+               case DIANSW:    /* not implemented */
+               case DINANSW: 
+               case DIINSW: 
+               case DININSW: 
+                   continue;
+
+               case DINDFSW:
+               case DINEDSW:
+               case DINWTSW:
+                   vec[vecp++] = --cp;
+                   continue;
+
+               case DIEDTSW: 
+               case DIFRMSW: 
+               case DIDFSW:
+               case DIDMSW:
+               case DIWHTSW:
+                   vec[vecp++] = --cp;
+                   if (!(cp = *args++) || *cp == '-') {
+                       advise (NULL, "missing argument to %s", args[-2]);
+                       return;
+                   }
+                   vec[vecp++] = cp;
+                   continue;
+           }
+       if (*cp == '+' || *cp == '@') {
+           advise (NULL, "sorry, no folders allowed!");
+           return;
+       }
+       else
+           if (msg) {
+               advise (NULL, "only one message at a time!");
+               return;
+           }
+           else
+               msg = cp;
+    }
+
+    vec[0] = cmd_name;
+    vec[vecp++] = "-file";
+    vec[vecp] = NULL;
+    if (!msg)
+       msg = "cur";
+    if (!m_convert (mp, msg))
+       return;
+    seq_setprev (mp);
+
+    if (mp->numsel > 1) {
+       advise (NULL, "only one message at a time!");
+       return;
+    }
+    process (mp->hghsel, cmd_name, vecp, vec);
+    seq_setcur (mp, mp->hghsel);
+}
+
+
+static struct swit explswit[] = {
+#define        EXINSW         0
+    { "inplace", 0 },
+#define        EXNINSW        1
+    { "noinplace", 0 },
+#define        EXQISW         2
+    { "quiet", 0 },
+#define        EXNQISW        3
+    { "noquiet", 0 },
+#define        EXVBSW         4
+    { "verbose", 0 },
+#define        EXNVBSW        5
+    { "noverbose", 0 },
+#define        EXHELP         6
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+
+void
+explcmd (char **args)
+{
+    int inplace = 0, quietsw = 0, verbosw = 0;
+    int msgp = 0, hi, msgnum;
+    char *cp, buf[BUFSIZ], *msgs[MAXARGS];
+    struct Msg *smsgs;
+
+    if (fmsh) {
+       forkcmd (args, cmd_name);
+       return;
+    }
+
+    while ((cp = *args++)) {
+       if (*cp == '-')
+           switch (smatch (++cp, explswit)) {
+               case AMBIGSW: 
+                   ambigsw (cp, explswit);
+                   return;
+               case UNKWNSW: 
+                   fprintf (stderr, "-%s unknown\n", cp);
+                   return;
+               case EXHELP: 
+                   snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
+                   print_help (buf, explswit, 1);
+                   return;
+
+               case EXINSW: 
+                   inplace++;
+                   continue;
+               case EXNINSW: 
+                   inplace = 0;
+                   continue;
+               case EXQISW: 
+                   quietsw++;
+                   continue;
+               case EXNQISW: 
+                   quietsw = 0;
+                   continue;
+               case EXVBSW: 
+                   verbosw++;
+                   continue;
+               case EXNVBSW: 
+                   verbosw = 0;
+                   continue;
+           }
+       if (*cp == '+' || *cp == '@') {
+           advise (NULL, "sorry, no folders allowed!");
+           return;
+       }
+       else
+           msgs[msgp++] = cp;
+    }
+
+    if (!msgp)
+       msgs[msgp++] = "cur";
+    for (msgnum = 0; msgnum < msgp; msgnum++)
+       if (!m_convert (mp, msgs[msgnum]))
+           return;
+    seq_setprev (mp);
+
+    smsgs = (struct Msg *)
+               calloc ((size_t) (MAXFOLDER + 2), sizeof *smsgs);
+    if (smsgs == NULL)
+       adios (NULL, "unable to allocate folder storage");
+
+    hi = mp->hghmsg + 1;
+    interrupted = 0;
+    for (msgnum = mp->lowsel;
+           msgnum <= mp->hghsel && !interrupted;
+           msgnum++)
+       if (is_selected (mp, msgnum))
+           if (burst (smsgs, msgnum, inplace, quietsw, verbosw) != OK)
+               break;
+
+    free ((char *) smsgs);
+
+    if (inplace)
+       seq_setcur (mp, mp->lowsel);
+    else
+       if (hi <= mp->hghmsg)
+           seq_setcur (mp, hi);
+
+    mp->msgflags |= MODIFIED;
+    modified++;
+}
+
+
+static int
+burst (struct Msg *smsgs, int msgnum, int inplace, int quietsw, int verbosw)
+{
+    int i, j, ld3, wasdlm, msgp;
+    long pos;
+    char c, buffer[BUFSIZ];
+    register FILE *zp;
+
+    ld3 = strlen (delim3);
+
+    if (Msgs[msgnum].m_scanl) {
+       free (Msgs[msgnum].m_scanl);
+       Msgs[msgnum].m_scanl = NULL;
+    }
+
+    pos = ftell (zp = msh_ready (msgnum, 1));
+    for (msgp = 0; msgp <= MAXFOLDER;) {
+       while (fgets (buffer, sizeof buffer, zp) != NULL
+               && buffer[0] == '\n'
+               && pos < Msgs[msgnum].m_stop)
+           pos += (long) strlen (buffer);
+       if (feof (zp) || pos >= Msgs[msgnum].m_stop)
+           break;
+       fseek (zp, pos, SEEK_SET);
+       smsgs[msgp].m_start = pos;
+
+       for (c = 0;
+               pos < Msgs[msgnum].m_stop
+               && fgets (buffer, sizeof buffer, zp) != NULL;
+               c = buffer[0])
+           if (strncmp (buffer, delim3, ld3) == 0
+                   && (msgp == 1 || c == '\n')
+                   && peekc (zp) == '\n')
+               break;
+           else
+               pos += (long) strlen (buffer);
+
+       wasdlm = strncmp (buffer, delim3, ld3) == 0;
+       if (smsgs[msgp].m_start != pos)
+           smsgs[msgp++].m_stop = (c == '\n' && wasdlm) ? pos - 1 : pos;
+       if (feof (zp) || pos >= Msgs[msgnum].m_stop) {
+           if (wasdlm)
+               smsgs[msgp - 1].m_stop -= ((long) strlen (buffer) + 1);
+           break;
+       }
+       pos += (long) strlen (buffer);
+    }
+
+    switch (msgp--) {          /* toss "End of XXX Digest" */
+       case 0: 
+           adios (NULL, "burst() botch -- you lose big");
+
+       case 1: 
+           if (!quietsw)
+               printf ("message %d not in digest format\n", msgnum);
+           return OK;
+
+       default: 
+           if (verbosw)
+               printf ("%d message%s exploded from digest %d\n",
+                       msgp, msgp != 1 ? "s" : "", msgnum);
+           break;
+    }
+
+    if ((i = msgp + mp->hghmsg) > MAXFOLDER) {
+       advise (NULL, "more than %d messages", MAXFOLDER);
+       return NOTOK;
+    }
+    if (!(mp = folder_realloc (mp, mp->lowoff, i)))
+       adios (NULL, "unable to allocate folder storage");
+
+    j = mp->hghmsg;
+    mp->hghmsg += msgp;
+    mp->nummsg += msgp;
+    if (mp->hghsel > msgnum)
+       mp->hghsel += msgp;
+
+    if (inplace)
+       for (i = mp->hghmsg; j > msgnum; i--, j--) {
+           if (verbosw)
+               printf ("message %d becomes message %d\n", j, i);
+
+           Msgs[i].m_bboard_id = Msgs[j].m_bboard_id;
+           Msgs[i].m_top = Msgs[j].m_top;
+           Msgs[i].m_start = Msgs[j].m_start;
+           Msgs[i].m_stop = Msgs[j].m_stop;
+           Msgs[i].m_scanl = NULL;
+           if (Msgs[j].m_scanl) {
+               free (Msgs[j].m_scanl);
+               Msgs[j].m_scanl = NULL;
+           }
+           copy_msg_flags (mp, i, j);
+       }
+
+    if (Msgs[msgnum].m_bboard_id == 0)
+       readid (msgnum);
+
+    unset_selected (mp, msgnum);
+    i = inplace ? msgnum + msgp : mp->hghmsg;
+    for (j = msgp; j >= (inplace ? 0 : 1); i--, j--) {
+       if (verbosw && i != msgnum)
+           printf ("message %d of digest %d becomes message %d\n",
+                   j, msgnum, i);
+
+       Msgs[i].m_bboard_id = Msgs[msgnum].m_bboard_id;
+       Msgs[i].m_top = Msgs[j].m_top;
+       Msgs[i].m_start = smsgs[j].m_start;
+       Msgs[i].m_stop = smsgs[j].m_stop;
+       Msgs[i].m_scanl = NULL;
+       copy_msg_flags (mp, i, msgnum);
+    }
+
+    return OK;
+}
+
+
+static struct swit fileswit[] = {
+#define        FIDRFT               0
+    { "draft", 0 },
+#define        FILINK               1
+    { "link", 0 },
+#define        FINLINK              2
+    { "nolink", 0 },
+#define        FIPRES               3
+    { "preserve", 0 },
+#define FINPRES              4
+    { "nopreserve", 0 },
+#define        FISRC                5
+    { "src +folder", 0 },
+#define        FIFILE               6
+    { "file file", 0 },
+#define        FIPROC               7
+    { "rmmproc program", 0 },
+#define        FINPRC               8
+    { "normmproc", 0 },
+#define        FIHELP               9
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+
+void
+filecmd (char **args)
+{
+    int        linksw = 0, msgp = 0;
+    int vecp = 1, i, msgnum;
+    char *cp, buf[BUFSIZ];
+    char *msgs[MAXARGS], *vec[MAXARGS];
+
+    if (fmsh) {
+       forkcmd (args, cmd_name);
+       return;
+    }
+
+    while ((cp = *args++)) {
+       if (*cp == '-')
+           switch (i = smatch (++cp, fileswit)) {
+               case AMBIGSW: 
+                   ambigsw (cp, fileswit);
+                   return;
+               case UNKWNSW: 
+                   fprintf (stderr, "-%s unknown\n", cp);
+                   return;
+               case FIHELP: 
+                   snprintf (buf, sizeof(buf), "%s +folder... [msgs] [switches]", cmd_name);
+                   print_help (buf, fileswit, 1);
+                   return;
+
+               case FILINK:
+                   linksw++;
+                   continue;
+               case FINLINK: 
+                   linksw = 0;
+                   continue;
+
+               case FIPRES: 
+               case FINPRES: 
+                   continue;
+
+               case FISRC: 
+               case FIDRFT:
+               case FIFILE: 
+               case FIPROC:
+               case FINPRC:
+                   advise (NULL, "sorry, -%s not allowed!", fileswit[i].sw);
+                   return;
+           }
+       if (*cp == '+' || *cp == '@')
+           vec[vecp++] = cp;
+       else
+           msgs[msgp++] = cp;
+    }
+
+    vec[0] = cmd_name;
+    vec[vecp++] = "-file";
+    vec[vecp] = NULL;
+    if (!msgp)
+       msgs[msgp++] = "cur";
+    for (msgnum = 0; msgnum < msgp; msgnum++)
+       if (!m_convert (mp, msgs[msgnum]))
+           return;
+    seq_setprev (mp);
+
+    interrupted = 0;
+    for (msgnum = mp->lowsel;
+           msgnum <= mp->hghsel && !interrupted;
+           msgnum++)
+       if (is_selected (mp, msgnum))
+           if (process (msgnum, fileproc, vecp, vec)) {
+               unset_selected (mp, msgnum);
+               mp->numsel--;
+           }
+
+    if (mp->numsel != mp->nummsg || linksw)
+       seq_setcur (mp, mp->hghsel);
+    if (!linksw)
+       rmm ();
+}
+
+
+int
+filehak (char **args)
+{
+    int        result, vecp = 0;
+    char *cp, *cwd, *vec[MAXARGS];
+
+    while ((cp = *args++)) {
+       if (*cp == '-')
+           switch (smatch (++cp, fileswit)) {
+               case AMBIGSW: 
+               case UNKWNSW: 
+               case FIHELP: 
+                   return NOTOK;
+
+               case FILINK:
+               case FINLINK: 
+               case FIPRES: 
+               case FINPRES: 
+                   continue;
+
+               case FISRC: 
+               case FIDRFT:
+               case FIFILE: 
+                   return NOTOK;
+           }
+       if (*cp == '+' || *cp == '@')
+           vec[vecp++] = cp;
+    }
+    vec[vecp] = NULL;
+
+    result = NOTOK;
+    cwd = NULL;
+    for (vecp = 0; (cp = vec[vecp]) && result == NOTOK; vecp++) {
+       if (cwd == NULL)
+           cwd = getcpy (pwd ());
+       chdir (m_maildir (""));
+       cp = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       if (access (m_maildir (cp), F_OK) == NOTOK)
+           result = OK;
+       free (cp);
+    }
+    if (cwd)
+       chdir (cwd);
+
+    return result;
+}
+
+
+static struct swit foldswit[] = {
+#define        FLALSW         0
+    { "all", 0 },
+#define        FLFASW         1
+    { "fast", 0 },
+#define        FLNFASW        2
+    { "nofast", 0 },
+#define        FLHDSW         3
+    { "header", 0 },
+#define        FLNHDSW        4
+    { "noheader", 0 },
+#define        FLPKSW         5
+    { "pack", 0 },
+#define        FLNPKSW        6
+    { "nopack", 0 },
+#define        FLRCSW         7
+    { "recurse", 0 },
+#define        FLNRCSW        8
+    { "norecurse", 0 },
+#define        FLTLSW         9
+    { "total", 0 },
+#define        FLNTLSW       10
+    { "nototal", 0 },
+#define        FLPRSW        11
+    { "print", 0 },
+#define        FLPUSW        12
+    { "push", 0 },
+#define        FLPOSW        13
+    { "pop", 0 },
+#define        FLLISW        14
+    { "list", 0 },
+#define        FLHELP        15
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+
+void
+foldcmd (char **args)
+{
+    int fastsw = 0, headersw = 0, packsw = 0;
+    int hole, msgnum;
+    char *cp, *folder = NULL, *msg = NULL;
+    char buf[BUFSIZ], **vec = args;
+
+    if (args == NULL)
+       goto fast;
+
+    while ((cp = *args++)) {
+       if (*cp == '-')
+           switch (smatch (++cp, foldswit)) {
+               case AMBIGSW: 
+                   ambigsw (cp, foldswit);
+                   return;
+               case UNKWNSW: 
+                   fprintf (stderr, "-%s unknown\n", cp);
+                   return;
+               case FLHELP: 
+                   snprintf (buf, sizeof(buf), "%s [+folder] [msg] [switches]", cmd_name);
+                   print_help (buf, foldswit, 1);
+                   return;
+
+               case FLALSW:    /* not implemented */
+               case FLRCSW: 
+               case FLNRCSW: 
+               case FLTLSW: 
+               case FLNTLSW: 
+               case FLPRSW:
+               case FLPUSW:
+               case FLPOSW:
+               case FLLISW:
+                   continue;
+
+               case FLFASW: 
+                   fastsw++;
+                   continue;
+               case FLNFASW: 
+                   fastsw = 0;
+                   continue;
+               case FLHDSW: 
+                   headersw++;
+                   continue;
+               case FLNHDSW: 
+                   headersw = 0;
+                   continue;
+               case FLPKSW: 
+                   packsw++;
+                   continue;
+               case FLNPKSW: 
+                   packsw = 0;
+                   continue;
+           }
+       if (*cp == '+' || *cp == '@')
+           if (folder) {
+               advise (NULL, "only one folder at a time!\n");
+               return;
+           }
+           else
+               folder = fmsh ? path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF)
+                           : cp + 1;
+       else
+           if (msg) {
+               advise (NULL, "only one message at a time!\n");
+               return;
+           }
+           else
+               msg = cp;
+    }
+
+    if (folder) {
+       if (*folder == 0) {
+           advise (NULL, "null folder names are not permitted");
+           return;
+       }
+       if (fmsh) {
+           if (access (m_maildir (folder), R_OK) == NOTOK) {
+               advise (folder, "unable to read");
+               return;
+           }
+       }
+       else {
+           strncpy (buf, folder, sizeof(buf));
+           if (expand (buf) == NOTOK)
+               return;
+           folder = buf;
+           if (access (folder, R_OK) == NOTOK) {
+               advise (folder, "unable to read");
+               return;
+           }
+       }
+       m_reset ();
+
+       if (fmsh)
+           fsetup (folder);
+       else
+           setup (folder);
+       readids (0);
+       display_info (0);
+    }
+
+    if (msg) {
+       if (!m_convert (mp, msg))
+           return;
+       seq_setprev (mp);
+
+       if (mp->numsel > 1) {
+           advise (NULL, "only one message at a time!");
+           return;
+       }
+       seq_setcur (mp, mp->hghsel);
+    }
+
+    if (packsw) {
+       if (fmsh) {
+           forkcmd (vec, cmd_name);
+           return;
+       }
+
+       if (mp->lowoff > 1 && !(mp = folder_realloc (mp, 1, mp->hghmsg)))
+           adios (NULL, "unable to allocate folder storage");
+
+       for (msgnum = mp->lowmsg, hole = 1; msgnum <= mp->hghmsg; msgnum++)
+           if (does_exist (mp, msgnum)) {
+               if (msgnum != hole) {
+                   Msgs[hole].m_bboard_id = Msgs[msgnum].m_bboard_id;
+                   Msgs[hole].m_top = Msgs[msgnum].m_top;
+                   Msgs[hole].m_start = Msgs[msgnum].m_start;
+                   Msgs[hole].m_stop = Msgs[msgnum].m_stop;
+                   Msgs[hole].m_scanl = NULL;
+                   if (Msgs[msgnum].m_scanl) {
+                       free (Msgs[msgnum].m_scanl);
+                       Msgs[msgnum].m_scanl = NULL;
+                   }
+                   copy_msg_flags (mp, hole, msgnum);
+                   if (mp->curmsg == msgnum)
+                       seq_setcur (mp, hole);
+               }
+               hole++;
+           }
+       if (mp->nummsg > 0) {
+           mp->lowmsg = 1;
+           mp->hghmsg = hole - 1;
+       }
+       mp->msgflags |= MODIFIED;
+       modified++;
+    }
+
+fast: ;
+    if (fastsw)
+       printf ("%s\n", fmsh ? fmsh : mp->foldpath);
+    else {
+       if (headersw)
+           printf ("\t\tFolder  %*s# of messages (%*srange%*s); cur%*smsg\n",
+               DMAXFOLDER, "", DMAXFOLDER - 2, "", DMAXFOLDER - 2, "",
+               DMAXFOLDER - 2, "");
+       printf (args ? "%22s  " : "%s ", fmsh ? fmsh : mp->foldpath);
+
+       /* check for empty folder */
+       if (mp->nummsg == 0) {
+           printf ("has   no messages%*s",
+                   mp->msgflags & OTHERS ? DMAXFOLDER * 2 + 4 : 0, "");
+       } else {
+           printf ("has %*d message%s (%*d-%*d)",
+                   DMAXFOLDER, mp->nummsg, mp->nummsg != 1 ? "s" : "",
+                   DMAXFOLDER, mp->lowmsg, DMAXFOLDER, mp->hghmsg);
+           if (mp->curmsg >= mp->lowmsg
+                   && mp->curmsg <= mp->hghmsg)
+               printf ("; cur=%*d", DMAXFOLDER, mp->curmsg);
+       }
+       printf (".\n");
+    }
+}
+
+
+static struct swit forwswit[] = {
+#define        FOANSW                   0
+    { "annotate", 0 },
+#define        FONANSW                  1
+    { "noannotate", 0 },
+#define        FODFSW                   2
+    { "draftfolder +folder", 0 },
+#define        FODMSW                   3
+    { "draftmessage msg", 0 },
+#define        FONDFSW                  4
+    { "nodraftfolder", 0 },
+#define        FOEDTSW                  5
+    { "editor editor", 0 },
+#define        FONEDSW                  6
+    { "noedit", 0 },
+#define        FOFTRSW                  7
+    { "filter filterfile", 0 },
+#define        FOFRMSW                  8
+    { "form formfile", 0 },
+#define        FOFTSW                   9
+    { "format", 5 },
+#define        FONFTSW                 10
+    { "noformat", 7 },
+#define        FOINSW                  11
+    { "inplace", 0 },
+#define        FONINSW                 12
+    { "noinplace", 0 },
+#define        FOMISW                  13
+    { "mime", 0 },
+#define        FONMISW                 14
+    { "nomime", 0 },
+#define        FOWHTSW                 15
+    { "whatnowproc program", 0 },
+#define        FONWTSW                 16
+    { "nowhatnow", 0 },
+#define        FOHELP                  17
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+
+void
+forwcmd (char **args)
+{
+    int        msgp = 0, vecp = 1, msgnum;
+    char *cp, *filter = NULL, buf[BUFSIZ];
+    char *msgs[MAXARGS], *vec[MAXARGS];
+
+    if (fmsh) {
+       forkcmd (args, cmd_name);
+       return;
+    }
+
+    while ((cp = *args++)) {
+       if (*cp == '-')
+           switch (smatch (++cp, forwswit)) {
+               case AMBIGSW: 
+                   ambigsw (cp, forwswit);
+                   return;
+               case UNKWNSW: 
+                   fprintf (stderr, "-%s unknown\n", cp);
+                   return;
+               case FOHELP: 
+                   snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
+                   print_help (buf, forwswit, 1);
+                   return;
+
+               case FOANSW:    /* not implemented */
+               case FONANSW: 
+               case FOINSW: 
+               case FONINSW: 
+               case FOMISW: 
+               case FONMISW: 
+                   continue;
+
+               case FONDFSW:
+               case FONEDSW:
+               case FONWTSW:
+                   vec[vecp++] = --cp;
+                   continue;
+
+               case FOEDTSW: 
+               case FOFRMSW: 
+               case FODFSW:
+               case FODMSW:
+               case FOWHTSW:
+                   vec[vecp++] = --cp;
+                   if (!(cp = *args++) || *cp == '-') {
+                       advise (NULL, "missing argument to %s", args[-2]);
+                       return;
+                   }
+                   vec[vecp++] = cp;
+                   continue;
+               case FOFTRSW: 
+                   if (!(filter = *args++) || *filter == '-') {
+                       advise (NULL, "missing argument to %s", args[-2]);
+                       return;
+                   }
+                   continue;
+               case FOFTSW: 
+                   if (access (filter = myfilter, R_OK) == NOTOK) {
+                       advise (filter, "unable to read default filter file");
+                       return;
+                   }
+                   continue;
+               case FONFTSW: 
+                   filter = NULL;
+                   continue;
+           }
+       if (*cp == '+' || *cp == '@') {
+           advise (NULL, "sorry, no folders allowed!");
+           return;
+       }
+       else
+           msgs[msgp++] = cp;
+    }
+
+                                       /* foil search of .mh_profile */
+    snprintf (buf, sizeof(buf), "%sXXXXXX", invo_name);
+    vec[0] = (char *)mktemp (buf);
+    vec[vecp++] = "-file";
+    vec[vecp] = NULL;
+    if (!msgp)
+       msgs[msgp++] = "cur";
+    for (msgnum = 0; msgnum < msgp; msgnum++)
+       if (!m_convert (mp, msgs[msgnum]))
+           return;
+    seq_setprev (mp);
+
+    if (filter) {
+       strncpy (buf, filter, sizeof(buf));
+       if (expand (buf) == NOTOK)
+           return;
+       if (access (filter = getcpy (etcpath (buf)), R_OK) == NOTOK) {
+           advise (filter, "unable to read");
+           free (filter);
+           return;
+       }
+    }
+    forw (cmd_name, filter, vecp, vec);
+    seq_setcur (mp, mp->hghsel);
+    if (filter)
+       free (filter);
+}
+
+
+static void
+forw (char *proc, char *filter, int vecp, char **vec)
+{
+    int i, child_id, msgnum, msgcnt;
+    char tmpfil[80], *args[MAXARGS];
+    FILE *out;
+
+    strncpy (tmpfil, m_tmpfil (invo_name), sizeof(tmpfil));
+    interrupted = 0;
+    if (filter)
+       switch (child_id = fork ()) {
+           case NOTOK: 
+               advise ("fork", "unable to");
+               return;
+
+           case OK:            /* "trust me" */
+               if (freopen (tmpfil, "w", stdout) == NULL) {
+                   fprintf (stderr, "unable to create ");
+                   perror (tmpfil);
+                   _exit (1);
+               }
+               args[0] = r1bindex (mhlproc, '/');
+               i = 1;
+               args[i++] = "-forwall";
+               args[i++] = "-form";
+               args[i++] = filter;
+               for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
+                   if (is_selected (mp, msgnum))
+                       args[i++] = getcpy (m_name (msgnum));
+               args[i] = NULL;
+               mhlsbr (i, args, mhl_action);
+               m_eomsbr ((int (*) ()) 0);
+               fclose (stdout);
+               _exit (0);
+
+           default: 
+               if (pidXwait (child_id, NULL))
+                   interrupted++;
+               break;
+       }
+    else {
+       if ((out = fopen (tmpfil, "w")) == NULL) {
+           advise (tmpfil, "unable to create temporary file");
+           return;
+       }
+
+       msgcnt = 1;
+       for (msgnum = mp->lowsel;
+               msgnum <= mp->hghsel && !interrupted;
+               msgnum++)
+           if (is_selected (mp, msgnum)) {
+               fprintf (out, "\n\n-------");
+               if (msgnum == mp->lowsel)
+                   fprintf (out, " Forwarded Message%s",
+                           mp->numsel > 1 ? "s" : "");
+               else
+                   fprintf (out, " Message %d", msgcnt);
+               fprintf (out, "\n\n");
+               copy_digest (msgnum, out);
+               msgcnt++;
+           }
+
+       fprintf (out, "\n\n------- End of Forwarded Message%s\n",
+               mp->numsel > 1 ? "s" : "");
+       fclose (out);
+    }
+
+    fflush (stdout);
+    if (!interrupted)
+       switch (child_id = fork ()) {
+           case NOTOK: 
+               advise ("fork", "unable to");
+               break;
+
+           case OK: 
+               closefds (3);
+               SIGNAL (SIGINT, istat);
+               SIGNAL (SIGQUIT, qstat);
+
+               vec[vecp++] = tmpfil;
+               vec[vecp] = NULL;
+
+               execvp (proc, vec);
+               fprintf (stderr, "unable to exec ");
+               perror (proc);
+               _exit (1);
+
+           default: 
+               pidXwait (child_id, NULL);
+               break;
+       }
+
+    unlink (tmpfil);
+}
+
+
+static char *hlpmsg[] = {
+    "The %s program emulates many of the commands found in the nmh",
+    "system.  Instead of operating on nmh folders, commands to %s concern",
+    "a single file.",
+    "",
+    "To see the list of commands available, just type a ``?'' followed by",
+    "the RETURN key.  To find out what switches each command takes, type",
+    "the name of the command followed by ``-help''.  To leave %s, use the",
+    "``quit'' command.",
+    "",
+    "Although a lot of nmh commands are found in %s, not all are fully",
+    "implemented.  %s will always recognize all legal switches for a",
+    "given command though, and will let you know when you ask for an",
+    "option that it is unable to perform.",
+    "",
+    "Running %s is fun, but using nmh from your shell is far superior.",
+    "After you have familiarized yourself with the nmh style by using %s,",
+    "you should try using nmh from the shell.  You can still use %s for",
+    "message files that aren't in nmh format, such as BBoard files.",
+    NULL
+};
+
+
+void
+helpcmd (char **args)
+{
+    int i;
+
+    for (i = 0; hlpmsg[i]; i++) {
+       printf (hlpmsg[i], invo_name);
+       putchar ('\n');
+    }
+}
+
+
+static struct swit markswit[] = {
+#define        MADDSW             0
+    { "add", 0 },
+#define        MDELSW             1
+    { "delete", 0 },
+#define        MLSTSW             2
+    { "list", 0 },
+#define        MSEQSW             3
+    { "sequence name", 0 },
+#define        MPUBSW             4
+    { "public", 0 },
+#define        MNPUBSW            5
+    { "nopublic", 0 },
+#define        MZERSW             6
+    { "zero", 0 },
+#define        MNZERSW            7
+    { "nozero", 0 },
+#define        MHELP              8
+    { "help", 4 },
+#define        MDBUGSW            9
+    { "debug", -5 },
+    { NULL, 0 }
+};
+
+
+void
+markcmd (char **args)
+{
+    int addsw = 0, deletesw = 0, debugsw = 0;
+    int listsw = 0, zerosw = 0, seqp = 0;
+    int msgp = 0, msgnum;
+    char *cp, buf[BUFSIZ];
+    char *seqs[NUMATTRS + 1], *msgs[MAXARGS];
+
+    while ((cp = *args++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, markswit)) {
+               case AMBIGSW: 
+                   ambigsw (cp, markswit);
+                   return;
+               case UNKWNSW: 
+                   fprintf (stderr, "-%s unknown\n", cp);
+                   return;
+               case MHELP: 
+                   snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
+                   print_help (buf, markswit, 1);
+                   return;
+
+               case MADDSW: 
+                   addsw++;
+                   deletesw = listsw = 0;
+                   continue;
+               case MDELSW: 
+                   deletesw++;
+                   addsw = listsw = 0;
+                   continue;
+               case MLSTSW: 
+                   listsw++;
+                   addsw = deletesw = 0;
+                   continue;
+
+               case MSEQSW: 
+                   if (!(cp = *args++) || *cp == '-') {
+                       advise (NULL, "missing argument to %s", args[-2]);
+                       return;
+                   }
+                   if (seqp < NUMATTRS)
+                       seqs[seqp++] = cp;
+                   else {
+                       advise (NULL, "only %d sequences allowed!", NUMATTRS);
+                       return;
+                   }
+                   continue;
+
+               case MPUBSW:    /* not implemented */
+               case MNPUBSW: 
+                   continue;
+
+               case MDBUGSW: 
+                   debugsw++;
+                   continue;
+
+               case MZERSW: 
+                   zerosw++;
+                   continue;
+               case MNZERSW: 
+                   zerosw = 0;
+                   continue;
+           }
+       }
+       if (*cp == '+' || *cp == '@') {
+           advise (NULL, "sorry, no folders allowed!");
+           return;
+       } else {
+           msgs[msgp++] = cp;
+       }
+    }
+
+    if (!addsw && !deletesw && !listsw)
+       if (seqp)
+           addsw++;
+       else
+           if (debugsw)
+               listsw++;
+           else {
+               seqs[seqp++] = "unseen";
+               deletesw++;
+               zerosw = 0;
+               if (!msgp)
+                   msgs[msgp++] = "all";
+           }
+
+    if (!msgp)
+       msgs[msgp++] = listsw ? "all" :"cur";
+    for (msgnum = 0; msgnum < msgp; msgnum++)
+       if (!m_convert (mp, msgs[msgnum]))
+           return;
+
+    if (debugsw) {
+       printf ("invo_name=%s mypath=%s defpath=%s\n",
+               invo_name, mypath, defpath);
+       printf ("ctxpath=%s context flags=%s\n",
+               ctxpath, snprintb (buf, sizeof(buf), (unsigned) ctxflags, DBITS));
+       printf ("foldpath=%s flags=%s\n",
+               mp->foldpath,
+               snprintb (buf, sizeof(buf), (unsigned) mp->msgflags, FBITS));
+       printf ("hghmsg=%d lowmsg=%d nummsg=%d curmsg=%d\n",
+               mp->hghmsg, mp->lowmsg, mp->nummsg, mp->curmsg);
+       printf ("lowsel=%d hghsel=%d numsel=%d\n",
+               mp->lowsel, mp->hghsel, mp->numsel);
+       printf ("lowoff=%d hghoff=%d\n", mp->lowoff, mp->hghoff);
+    }
+
+    if (seqp == 0 && (addsw || deletesw)) {
+       advise (NULL, "-%s requires at least one -sequence argument",
+               addsw ? "add" : "delete");
+       return;
+    }
+    seqs[seqp] = NULL;
+
+    if (addsw) {
+       for (seqp = 0; seqs[seqp]; seqp++)
+           if (!seq_addsel (mp, seqs[seqp], 0, zerosw))
+               return;
+    }
+
+    if (deletesw) {
+       for (seqp = 0; seqs[seqp]; seqp++)
+           if (!seq_delsel (mp, seqs[seqp], 0, zerosw))
+               return;
+    }
+
+    /* Listing messages in sequences */
+    if (listsw) {
+       if (seqp) {
+           /* list the given sequences */
+           for (seqp = 0; seqs[seqp]; seqp++)
+               seq_print (mp, seqs[seqp]);
+       } else {
+           /* else list them all */
+           seq_printall (mp);
+       }
+
+       interrupted = 0;
+       if (debugsw)
+           for (msgnum = mp->lowsel;
+                   msgnum <= mp->hghsel && !interrupted;
+                   msgnum++)
+               if (is_selected (mp, msgnum)) {
+                   printf ("%*d: id=%d top=%d start=%ld stop=%ld %s\n",
+                       DMAXFOLDER,
+                       msgnum,
+                       Msgs[msgnum].m_bboard_id,
+                       Msgs[msgnum].m_top,
+                       (long) Msgs[msgnum].m_start,
+                       (long) Msgs[msgnum].m_stop,
+                       snprintb (buf, sizeof(buf),
+                               (unsigned) mp->msgstats[msgnum - mp->lowoff],
+                               seq_bits (mp)));
+                   if (Msgs[msgnum].m_scanl)
+                       printf ("%s", Msgs[msgnum].m_scanl);
+               }                           
+    }
+}
+
+
+static struct swit mhnswit[] = {
+#define        MHNAUTOSW           0
+    { "auto", 0 },
+#define        MHNNAUTOSW          1
+    { "noauto", 0 },
+#define        MHNDEBUGSW          2
+    { "debug", -5 },
+#define        MHNEBCDICSW         3
+    { "ebcdicsafe", 0 },
+#define        MHNNEBCDICSW        4
+    { "noebcdicsafe", 0 },
+#define        MHNFORMSW           5
+    { "form formfile", 4 },
+#define        MHNHEADSW           6
+    { "headers", 0 },
+#define        MHNNHEADSW          7
+    { "noheaders", 0 },
+#define        MHNLISTSW           8
+    { "list", 0 },
+#define        MHNNLISTSW          9
+    { "nolist", 0 },
+#define        MHNPARTSW          10
+    { "part number", 0 },
+#define        MHNSIZESW          11
+    { "realsize", 0 },
+#define        MHNNSIZESW         12
+    { "norealsize", 0 },
+#define        MHNRFC934SW        13
+    { "rfc934mode", 0 },
+#define        MHNNRFC934SW       14
+    { "norfc934mode", 0 },
+#define        MHNSERIALSW        15
+    { "serialonly", 0 },
+#define        MHNNSERIALSW       16
+    { "noserialonly", 0 },
+#define        MHNSHOWSW          17
+    { "show", 0 },
+#define        MHNNSHOWSW         18
+    { "noshow", 0 },
+#define        MHNSTORESW         19
+    { "store", 0 },
+#define        MHNNSTORESW        20
+    { "nostore", 0 },
+#define        MHNTYPESW          21
+    { "type content", 0 },
+#define        MHNVERBSW          22
+    { "verbose", 0 },
+#define        MHNNVERBSW         23
+    { "noverbose", 0 },
+#define        MHNHELPSW          24
+    { "help", 4 },
+#define        MHNPROGSW          25
+    { "moreproc program", -4 },
+#define        MHNNPROGSW         26
+    { "nomoreproc", -3 },
+#define        MHNLENSW           27
+    { "length lines", -4 },
+#define        MHNWIDSW           28
+    { "width columns", -4 },
+    { NULL, 0 }
+};
+
+
+void
+mhncmd (char **args)
+{
+    int msgp = 0, vecp = 1;
+    int msgnum;
+    char *cp, buf[BUFSIZ];
+    char *msgs[MAXARGS], *vec[MAXARGS];
+
+    if (fmsh) {
+       forkcmd (args, cmd_name);
+       return;
+    }
+    while ((cp = *args++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, mhnswit)) {
+               case AMBIGSW: 
+                   ambigsw (cp, mhnswit);
+                   return;
+               case UNKWNSW: 
+                   fprintf (stderr, "-%s unknown\n", cp);
+                   return;
+               case MHNHELPSW:
+                   snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
+                   print_help (buf, mhnswit, 1);
+                   return;
+
+               case MHNAUTOSW:
+               case MHNNAUTOSW:
+               case MHNDEBUGSW:
+               case MHNEBCDICSW:
+               case MHNNEBCDICSW:
+               case MHNHEADSW:
+               case MHNNHEADSW:
+               case MHNLISTSW:
+               case MHNNLISTSW:
+               case MHNSIZESW:
+               case MHNNSIZESW:
+               case MHNRFC934SW:
+               case MHNNRFC934SW:
+               case MHNSERIALSW:
+               case MHNNSERIALSW:
+               case MHNSHOWSW:
+               case MHNNSHOWSW:
+               case MHNSTORESW:
+               case MHNNSTORESW:
+               case MHNVERBSW:
+               case MHNNVERBSW:
+               case MHNNPROGSW:
+                   vec[vecp++] = --cp;
+                   continue;
+
+               case MHNFORMSW:
+               case MHNPARTSW:
+               case MHNTYPESW:
+               case MHNPROGSW:
+               case MHNLENSW:
+               case MHNWIDSW:
+                   vec[vecp++] = --cp;
+                   if (!(cp = *args++) || *cp == '-') {
+                       advise (NULL, "missing argument to %s", args[-2]);
+                       return;
+                   }
+                   vec[vecp++] = cp;
+                   continue;
+           }
+       }
+       if (*cp == '+' || *cp == '@') {
+           advise (NULL, "sorry, no folders allowed!");
+           return;
+       } else {
+           msgs[msgp++] = cp;
+       }
+    }
+
+    vec[0] = cmd_name;
+    vec[vecp++] = "-file";
+    vec[vecp] = NULL;
+    if (!msgp)
+       msgs[msgp++] = "cur";
+    for (msgnum = 0; msgnum < msgp; msgnum++)
+       if (!m_convert (mp, msgs[msgnum]))
+           return;
+    seq_setprev (mp);
+
+    interrupted = 0;
+    for (msgnum = mp->lowsel;
+           msgnum <= mp->hghsel && !interrupted;
+           msgnum++)
+       if (is_selected (mp, msgnum))
+           if (process (msgnum, cmd_name, vecp, vec)) {
+               unset_selected (mp, msgnum);
+               mp->numsel--;
+           }
+
+    seq_setcur (mp, mp->hghsel);
+}
+
+
+static struct swit packswit[] = {
+#define        PAFISW         0
+    { "file name", 0 },
+#define        PAHELP         1
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+static mbx_style = MMDF_FORMAT;
+
+void
+packcmd (char **args)
+{
+    int msgp = 0, md, msgnum;
+    char *cp, *file = NULL;
+    char buf[BUFSIZ], *msgs[MAXARGS];
+    struct stat st;
+
+    if (fmsh) {
+       forkcmd (args, cmd_name);
+       return;
+    }
+
+    while ((cp = *args++)) {
+       if (*cp == '-')
+           switch (smatch (++cp, packswit)) {
+               case AMBIGSW: 
+                   ambigsw (cp, packswit);
+                   return;
+               case UNKWNSW: 
+                   fprintf (stderr, "-%s unknown\n", cp);
+                   return;
+               case PAHELP: 
+                   snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
+                   print_help (buf, packswit, 1);
+                   return;
+
+               case PAFISW: 
+                   if (!(file = *args++) || *file == '-') {
+                       advise (NULL, "missing argument to %s", args[-2]);
+                       return;
+                   }
+                   continue;
+           }
+       if (*cp == '+' || *cp == '@') {
+           advise (NULL, "sorry, no folders allowed!");
+           return;
+       }
+       else
+           msgs[msgp++] = cp;
+    }
+
+    if (!file)
+       file = "./msgbox";
+    file = path (file, TFILE);
+    if (stat (file, &st) == NOTOK) {
+       if (errno != ENOENT) {
+           advise (file, "error on file");
+           goto done_pack;
+       }
+       md = getanswer (cp = concat ("Create file \"", file, "\"? ", NULL));
+       free (cp);
+       if (!md)
+           goto done_pack;
+    }
+
+    if (!msgp)
+       msgs[msgp++] = "all";
+    for (msgnum = 0; msgnum < msgp; msgnum++)
+       if (!m_convert (mp, msgs[msgnum]))
+           goto done_pack;
+    seq_setprev (mp);
+
+    if ((md = mbx_open (file, mbx_style, getuid (), getgid (), m_gmprot ())) == NOTOK) {
+       advise (file, "unable to open");
+       goto done_pack;
+    }
+    for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
+       if (is_selected (mp, msgnum))
+           if (pack (file, md, msgnum) == NOTOK)
+               break;
+    mbx_close (file, md);
+
+    if (mp->hghsel != mp->curmsg)
+       seq_setcur (mp, mp->lowsel);
+
+done_pack: ;
+    free (file);
+}
+
+
+int
+pack (char *mailbox, int md, int msgnum)
+{
+    register FILE *zp;
+
+    if (Msgs[msgnum].m_bboard_id == 0)
+       readid (msgnum);
+
+    zp = msh_ready (msgnum, 1);
+    return mbx_write (mailbox, md, zp, Msgs[msgnum].m_bboard_id,
+           0L, ftell (zp), Msgs[msgnum].m_stop, 1, 1);
+}
+
+
+int
+packhak (char **args)
+{
+    int        result;
+    char *cp, *file = NULL;
+
+    while ((cp = *args++)) {
+       if (*cp == '-')
+           switch (smatch (++cp, packswit)) {
+               case AMBIGSW: 
+               case UNKWNSW: 
+               case PAHELP: 
+                   return NOTOK;
+
+               case PAFISW: 
+                   if (!(file = *args++) || *file == '-') 
+                       return NOTOK;
+                   continue;
+           }
+       if (*cp == '+' || *cp == '@')
+           return NOTOK;
+    }
+
+    file = path (file ? file : "./msgbox", TFILE);
+    result = access (file, F_OK) == NOTOK ? OK : NOTOK;
+    free (file);
+
+    return result;
+}
+
+
+static struct swit pickswit[] = {
+#define        PIANSW                0
+    { "and", 0 },
+#define        PIORSW                1
+    { "or", 0 },
+#define        PINTSW                2
+    { "not", 0 },
+#define        PILBSW                3
+    { "lbrace", 0 },
+#define        PIRBSW                4
+    { "rbrace", 0 },
+#define        PICCSW                5
+    { "cc  pattern", 0 },
+#define        PIDASW                6
+    { "date  pattern", 0 },
+#define        PIFRSW                7
+    { "from  pattern", 0 },
+#define        PISESW                8
+    { "search  pattern", 0 },
+#define        PISUSW                9
+    { "subject  pattern", 0 },
+#define        PITOSW               10
+    { "to  pattern", 0 },
+#define        PIOTSW               11
+    { "-othercomponent  pattern", 15 },
+#define        PIAFSW               12
+    { "after date", 0 },
+#define        PIBFSW               13
+    { "before date", 0 },
+#define        PIDFSW               14
+    { "datefield field", 5 },
+#define        PISQSW               15
+    { "sequence name", 0 },
+#define        PIPUSW               16
+    { "public", 0 },
+#define        PINPUSW              17
+    { "nopublic", 0 },
+#define        PIZRSW               18
+    { "zero", 0 },
+#define        PINZRSW              19
+    { "nozero", 0 },
+#define        PILISW               20
+    { "list", 0 },
+#define        PINLISW              21
+    { "nolist", 0 },
+#define        PIHELP               22
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+
+void
+pickcmd (char **args)
+{
+    int zerosw = 1, msgp = 0, seqp = 0;
+    int vecp = 0, hi, lo, msgnum;
+    char *cp, buf[BUFSIZ], *msgs[MAXARGS];
+    char *seqs[NUMATTRS], *vec[MAXARGS];
+    register FILE *zp;
+
+    while ((cp = *args++)) {
+       if (*cp == '-') {
+           if (*++cp == '-') {
+               vec[vecp++] = --cp;
+               goto pattern;
+           }
+           switch (smatch (cp, pickswit)) {
+               case AMBIGSW: 
+                   ambigsw (cp, pickswit);
+                   return;
+               case UNKWNSW: 
+                   fprintf (stderr, "-%s unknown\n", cp);
+                   return;
+               case PIHELP: 
+                   snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
+                   print_help (buf, pickswit, 1);
+                   return;
+
+               case PICCSW: 
+               case PIDASW: 
+               case PIFRSW: 
+               case PISUSW: 
+               case PITOSW: 
+               case PIDFSW: 
+               case PIAFSW: 
+               case PIBFSW: 
+               case PISESW: 
+                   vec[vecp++] = --cp;
+pattern: ;
+                   if (!(cp = *args++)) {/* allow -xyz arguments */
+                       advise (NULL, "missing argument to %s", args[-2]);
+                       return;
+                   }
+                   vec[vecp++] = cp;
+                   continue;
+               case PIOTSW: 
+                   advise (NULL, "internal error!");
+                   return;
+               case PIANSW: 
+               case PIORSW: 
+               case PINTSW: 
+               case PILBSW: 
+               case PIRBSW: 
+                   vec[vecp++] = --cp;
+                   continue;
+
+               case PISQSW: 
+                   if (!(cp = *args++) || *cp == '-') {
+                       advise (NULL, "missing argument to %s", args[-2]);
+                       return;
+                   }
+                   if (seqp < NUMATTRS)
+                       seqs[seqp++] = cp;
+                   else {
+                       advise (NULL, "only %d sequences allowed!", NUMATTRS);
+                       return;
+                   }
+                   continue;
+               case PIZRSW: 
+                   zerosw++;
+                   continue;
+               case PINZRSW: 
+                   zerosw = 0;
+                   continue;
+
+               case PIPUSW:    /* not implemented */
+               case PINPUSW: 
+               case PILISW: 
+               case PINLISW: 
+                   continue;
+           }
+       }
+       if (*cp == '+' || *cp == '@') {
+           advise (NULL, "sorry, no folders allowed!");
+           return;
+       }
+       else
+           msgs[msgp++] = cp;
+    }
+    vec[vecp] = NULL;
+
+    if (!msgp)
+       msgs[msgp++] = "all";
+    for (msgnum = 0; msgnum < msgp; msgnum++)
+       if (!m_convert (mp, msgs[msgnum]))
+           return;
+    seq_setprev (mp);
+
+    interrupted = 0;
+    if (!pcompile (vec, NULL))
+       return;
+
+    lo = mp->lowsel;
+    hi = mp->hghsel;
+
+    for (msgnum = mp->lowsel;
+           msgnum <= mp->hghsel && !interrupted;
+           msgnum++)
+       if (is_selected (mp, msgnum)) {
+           zp = msh_ready (msgnum, 1);
+           if (pmatches (zp, msgnum, fmsh ? 0L : Msgs[msgnum].m_start,
+                       fmsh ? 0L : Msgs[msgnum].m_stop)) {
+               if (msgnum < lo)
+                   lo = msgnum;
+               if (msgnum > hi)
+                   hi = msgnum;
+           }
+           else {
+               unset_selected (mp, msgnum);
+               mp->numsel--;
+           }
+       }
+
+    if (interrupted)
+       return;
+
+    mp->lowsel = lo;
+    mp->hghsel = hi;
+
+    if (mp->numsel <= 0) {
+       advise (NULL, "no messages match specification");
+       return;
+    }
+
+    seqs[seqp] = NULL;
+    for (seqp = 0; seqs[seqp]; seqp++)
+       if (!seq_addsel (mp, seqs[seqp], 0, zerosw))
+           return;
+
+    printf ("%d hit%s\n", mp->numsel, mp->numsel == 1 ? "" : "s");
+}
+
+
+static struct swit replswit[] = {
+#define        REANSW                  0
+    { "annotate", 0 },
+#define        RENANSW                 1
+    { "noannotate", 0 },
+#define        RECCSW                  2
+    { "cc type", 0 },
+#define        RENCCSW                 3
+    { "nocc type", 0 },
+#define        REDFSW                  4
+    { "draftfolder +folder", 0 },
+#define        REDMSW                  5
+    { "draftmessage msg", 0 },
+#define        RENDFSW                 6
+    { "nodraftfolder", 0 },
+#define        REEDTSW                 7
+    { "editor editor", 0 },
+#define        RENEDSW                 8
+    { "noedit", 0 },
+#define        REFCCSW                 9
+    { "fcc +folder", 0 },
+#define        REFLTSW                10
+    { "filter filterfile", 0 },
+#define        REFRMSW                11
+    { "form formfile", 0 },
+#define        REINSW                 12
+    { "inplace", 0 },
+#define        RENINSW                13
+    { "noinplace", 0 },
+#define        REQUSW                 14
+    { "query", 0 },
+#define        RENQUSW                15
+    { "noquery", 0 },
+#define        REWHTSW                16
+    { "whatnowproc program", 0 },
+#define        RENWTSW                17
+    { "nowhatnow", 0 },
+#define        REWIDSW                19
+    { "width columns", 0 },
+#define        REHELP                 20
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+
+void
+replcmd (char **args)
+{
+    int vecp = 1;
+    char *cp, *msg = NULL;
+    char buf[BUFSIZ], *vec[MAXARGS];
+
+    if (fmsh) {
+       forkcmd (args, cmd_name);
+       return;
+    }
+
+    while ((cp = *args++)) {
+       if (*cp == '-')
+           switch (smatch (++cp, replswit)) {
+               case AMBIGSW: 
+                   ambigsw (cp, replswit);
+                   return;
+               case UNKWNSW: 
+                   fprintf (stderr, "-%s unknown\n", cp);
+                   return;
+               case REHELP: 
+                   snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
+                   print_help (buf, replswit, 1);
+                   return;
+
+               case REANSW:    /* not implemented */
+               case RENANSW: 
+               case REINSW: 
+               case RENINSW: 
+                   continue;
+
+               case REQUSW:
+               case RENQUSW:
+               case RENDFSW:
+               case RENEDSW:
+               case RENWTSW:
+                   vec[vecp++] = --cp;
+                   continue;
+
+               case RECCSW: 
+               case RENCCSW: 
+               case REEDTSW: 
+               case REFCCSW: 
+               case REFLTSW:
+               case REFRMSW: 
+               case REWIDSW: 
+               case REDFSW:
+               case REDMSW:
+               case REWHTSW:
+                   vec[vecp++] = --cp;
+                   if (!(cp = *args++) || *cp == '-') {
+                       advise (NULL, "missing argument to %s", args[-2]);
+                       return;
+                   }
+                   vec[vecp++] = cp;
+                   continue;
+           }
+       if (*cp == '+' || *cp == '@') {
+           advise (NULL, "sorry, no folders allowed!");
+           return;
+       }
+       else
+           if (msg) {
+               advise (NULL, "only one message at a time!");
+               return;
+           }
+           else
+               msg = cp;
+    }
+
+    vec[0] = cmd_name;
+    vec[vecp++] = "-file";
+    vec[vecp] = NULL;
+    if (!msg)
+       msg = "cur";
+    if (!m_convert (mp, msg))
+       return;
+    seq_setprev (mp);
+
+    if (mp->numsel > 1) {
+       advise (NULL, "only one message at a time!");
+       return;
+    }
+    process (mp->hghsel, cmd_name, vecp, vec);
+    seq_setcur (mp, mp->hghsel);
+}
+
+
+static struct swit rmmswit[] = {
+#define        RMHELP    0
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+
+void
+rmmcmd (char **args)
+{
+    int        msgp = 0, msgnum;
+    char *cp, buf[BUFSIZ], *msgs[MAXARGS];
+
+    while ((cp = *args++)) {
+       if (*cp == '-')
+           switch (smatch (++cp, rmmswit)) {
+               case AMBIGSW: 
+                   ambigsw (cp, rmmswit);
+                   return;
+               case UNKWNSW: 
+                   fprintf (stderr, "-%s unknown\n", cp);
+                   return;
+               case RMHELP: 
+                   snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
+                   print_help (buf, rmmswit, 1);
+                   return;
+           }
+       if (*cp == '+' || *cp == '@') {
+           advise (NULL, "sorry, no folders allowed!");
+           return;
+       }
+       else
+           msgs[msgp++] = cp;
+    }
+
+    if (!msgp)
+       msgs[msgp++] = "cur";
+    for (msgnum = 0; msgnum < msgp; msgnum++)
+       if (!m_convert (mp, msgs[msgnum]))
+           return;
+    seq_setprev (mp);
+
+    rmm ();
+}
+
+
+static void
+rmm (void)
+{
+    register int msgnum, vecp;
+    register char *cp;
+    char buffer[BUFSIZ], *vec[MAXARGS];
+
+    if (fmsh) {
+       if (rmmproc) {
+           if (mp->numsel > MAXARGS - 1) {
+               advise (NULL, "more than %d messages for %s exec",
+                       MAXARGS - 1, rmmproc);
+               return;
+           }
+           vecp = 0;
+           for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
+               if (is_selected (mp, msgnum))
+                   vec[vecp++] = getcpy (m_name (msgnum));
+           vec[vecp] = NULL;
+           forkcmd (vec, rmmproc);
+           for (vecp = 0; vec[vecp]; vecp++)
+               free (vec[vecp]);
+       }
+       else
+           for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
+               if (is_selected (mp, msgnum)) {
+                   strncpy (buffer, m_backup (cp = m_name (msgnum)), sizeof(buffer));
+                   if (rename (cp, buffer) == NOTOK)
+                       admonish (buffer, "unable to rename %s to", cp);
+               }
+    }
+
+    for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
+       if (is_selected (mp, msgnum)) {
+           set_deleted (mp, msgnum);
+           unset_exists (mp, msgnum);
+#ifdef MPOP
+#ifdef BPOP
+           if (pmsh && pop_dele (msgnum) != OK)
+               fprintf (stderr, "%s", response);
+#endif
+#endif /* MPOP */
+       }
+
+    if ((mp->nummsg -= mp->numsel) <= 0) {
+       if (fmsh)
+           admonish (NULL, "no messages remaining in +%s", fmsh);
+       else
+           admonish (NULL, "no messages remaining in %s", mp->foldpath);
+       mp->lowmsg = mp->hghmsg = mp->nummsg = 0;
+    }
+    if (mp->lowsel == mp->lowmsg) {
+       for (msgnum = mp->lowmsg + 1; msgnum <= mp->hghmsg; msgnum++)
+           if (does_exist (mp, msgnum))
+               break;
+       mp->lowmsg = msgnum;
+    }
+    if (mp->hghsel == mp->hghmsg) {
+       for (msgnum = mp->hghmsg - 1; msgnum >= mp->lowmsg; msgnum--)
+           if (does_exist (mp, msgnum))
+               break;
+       mp->hghmsg = msgnum;
+    }
+
+    mp->msgflags |= MODIFIED;
+    modified++;
+}
+
+
+static struct swit scanswit[] = {
+#define        SCCLR              0
+    { "clear", 0 },
+#define        SCNCLR             1
+    { "noclear", 0 },
+#define        SCFORM             2
+    { "form formatfile", 0 },
+#define        SCFMT              3
+    { "format string", 5 },
+#define        SCHEAD             4
+    { "header", 0 },
+#define SCNHEAD            5
+    { "noheader", 0 },
+#define        SCWID              6
+    { "width columns", 0 },
+#define        SCHELP             7
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+
+void
+scancmd (char **args)
+{
+#define        equiv(a,b)      (a ? b && !strcmp (a, b) : !b)
+
+    int clearsw = 0, headersw = 0, width = 0, msgp = 0;
+    int msgnum, optim, state;
+    char *cp, *form = NULL, *format = NULL;
+    char buf[BUFSIZ], *nfs, *msgs[MAXARGS];
+    register FILE *zp;
+#ifdef MPOP
+#ifdef BPOP
+    static int p_optim = 0;
+#endif
+#endif /* MPOP */
+    static int s_optim = 0;
+    static char *s_form = NULL, *s_format = NULL;
+
+    while ((cp = *args++)) {
+       if (*cp == '-')
+           switch (smatch (++cp, scanswit)) {
+               case AMBIGSW: 
+                   ambigsw (cp, scanswit);
+                   return;
+               case UNKWNSW: 
+                   fprintf (stderr, "-%s unknown\n", cp);
+                   return;
+               case SCHELP: 
+                   snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
+                   print_help (buf, scanswit, 1);
+                   return;
+
+               case SCCLR: 
+                   clearsw++;
+                   continue;
+               case SCNCLR: 
+                   clearsw = 0;
+                   continue;
+               case SCHEAD: 
+                   headersw++;
+                   continue;
+               case SCNHEAD: 
+                   headersw = 0;
+                   continue;
+               case SCFORM: 
+                   if (!(form = *args++) || *form == '-') {
+                       advise (NULL, "missing argument to %s", args[-2]);
+                       return;
+                   }
+                   format = NULL;
+                   continue;
+               case SCFMT: 
+                   if (!(format = *args++) || *format == '-') {
+                       advise (NULL, "missing argument to %s", args[-2]);
+                       return;
+                   }
+                   form = NULL;
+                   continue;
+               case SCWID: 
+                   if (!(cp = *args++) || *cp == '-') {
+                       advise (NULL, "missing argument to %s", args[-2]);
+                       return;
+                   }
+                   width = atoi (cp);
+                   continue;
+           }
+       if (*cp == '+' || *cp == '@') {
+           advise (NULL, "sorry, no folders allowed!");
+           return;
+       }
+       else
+           msgs[msgp++] = cp;
+    }
+
+    if (!msgp)
+       msgs[msgp++] = "all";
+    for (msgnum = 0; msgnum < msgp; msgnum++)
+       if (!m_convert (mp, msgs[msgnum]))
+           return;
+    seq_setprev (mp);
+
+    /* Get new format string */
+    nfs = new_fs (form, format, FORMAT);
+
+    /* force scansbr to (re)compile format */
+    if (scanl) {
+       free (scanl);
+       scanl = NULL;
+    }
+
+    if (s_optim == 0) {
+       s_optim = optim = 1;
+       s_form = form ? getcpy (form) : NULL;
+       s_format = format ? getcpy (format) : NULL;
+
+#ifdef MPOP
+#ifdef BPOP
+       if (pmsh) {
+           int i;
+           char *dp, *ep, *fp;
+
+           if (width == 0)
+               width = sc_width ();
+
+           for (dp = nfs, i = 0; *dp; dp++, i++)
+               if (*dp == '\\' || *dp == '"' || *dp == '\n')
+                   i++;
+           i++;
+           if ((ep = malloc ((unsigned) i)) == NULL)
+               adios (NULL, "out of memory");
+           for (dp = nfs, fp = ep; *dp; dp++) {
+               if (*dp == '\n') {
+                   *fp++ = '\\', *fp++ = 'n';
+                   continue;
+               }
+               if (*dp == '"' || *dp == '\\')
+                   *fp++ = '\\';
+               *fp++ = *dp;
+           }
+           *fp = NULL;
+
+           if (pop_command ("XTND SCAN %d \"%s\"", width, ep) == OK)
+               p_optim = 1;
+
+           free (ep);
+       }
+#endif
+#endif /* MPOP */
+    }
+    else
+       optim = equiv (s_form, form) && equiv (s_format, format);
+
+#ifdef MPOP
+#ifdef BPOP
+    if (p_optim && optim) {
+       for (msgnum = mp->lowmsg; msgnum <= mp->hghmsg; msgnum++)
+           if (!is_selected(mp, msgnum) || Msgs[msgnum].m_scanl)
+               break;
+       if (msgnum > mp->hghmsg && pop_command ("LIST") == OK) {
+           fprintf (stderr, "Stand-by...");
+           fflush (stderr);
+
+           for (;;) {
+               int     size;
+
+               switch (pop_multiline ()) {
+                   case NOTOK:
+                       fprintf (stderr, "%s", response);
+                       /* and fall... */
+                   case DONE:
+                       fprintf (stderr,"\n");
+                       break;
+
+                   case OK:
+                       if (sscanf (response, "%d %d", &msgnum, &size) == 2
+                               && mp->lowmsg <= msgnum
+                               && msgnum <= mp->hghmsg
+                               && (cp = strchr(response, '#'))
+                               && *++cp)
+                           Msgs[msgnum].m_scanl = concat (cp, "\n", NULL);
+                       continue;
+               }
+               break;
+           }
+       }
+    }
+#endif
+#endif /* MPOP */
+
+    interrupted = 0;
+    for (msgnum = mp->lowsel;
+           msgnum <= mp->hghsel && !interrupted;
+           msgnum++)
+       if (is_selected (mp, msgnum)) {
+           if (optim && Msgs[msgnum].m_scanl)
+               printf ("%s", Msgs[msgnum].m_scanl);
+           else {
+#ifdef MPOP
+#ifdef BPOP
+               if (p_optim
+                       && optim
+                       && is_virtual (mp, msgnum)
+                       && pop_command ("LIST %d", msgnum) == OK
+                       && (cp = strchr(response, '#'))
+                       && *++cp) {
+                   Msgs[msgnum].m_scanl = concat (cp, "\n", NULL);
+                   printf ("%s", Msgs[msgnum].m_scanl);                    
+                   continue;
+               }
+#endif
+#endif /* MPOP */
+
+               zp = msh_ready (msgnum, 0);
+               switch (state = scan (zp, msgnum, 0, nfs, width,
+                       msgnum == mp->curmsg,
+                       is_unseen (mp, msgnum),
+                       headersw ? (fmsh ? fmsh : mp->foldpath) : NULL,
+                       fmsh ? 0L : (long) (Msgs[msgnum].m_stop - Msgs[msgnum].m_start),
+                       1)) {
+                   case SCNMSG:
+                   case SCNENC:
+                   case SCNERR:
+                       if (optim)
+                           Msgs[msgnum].m_scanl = getcpy (scanl);
+                       break;
+
+                   default:
+                       advise (NULL, "scan() botch (%d)", state);
+                       return;
+
+                   case SCNEOF:
+                       printf ("%*d  empty\n", DMAXFOLDER, msgnum);
+                       break;
+                   }
+           }
+           headersw = 0;
+       }
+
+    if (clearsw)
+       clear_screen ();
+}
+
+
+static struct swit showswit[] = {
+#define        SHDRAFT               0
+    { "draft", 5 },
+#define        SHFORM                1
+    { "form formfile", 4 },
+#define        SHPROG                2
+    { "moreproc program", 4 },
+#define        SHNPROG               3
+    { "nomoreproc", 3 },
+#define        SHLEN                 4
+    { "length lines", 4 },
+#define        SHWID                 5
+    { "width columns", 4 },
+#define        SHSHOW                6
+    { "showproc program", 4 },
+#define        SHNSHOW               7
+    { "noshowproc", 3 },
+#define        SHHEAD                8
+    { "header", 4 },
+#define SHNHEAD               9
+    { "noheader", 3 },
+#define        SHHELP               10
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+
+void
+showcmd (char **args)
+{
+    int        headersw = 1, nshow = 0, msgp = 0, vecp = 1;
+    int mhl = 0, seqnum = -1, mode = 0, i, msgnum;
+    char *cp, *proc = showproc, buf[BUFSIZ];
+    char *msgs[MAXARGS], *vec[MAXARGS];
+
+    if (!strcasecmp (cmd_name, "next"))
+       mode = 1;
+    else
+       if (!strcasecmp (cmd_name, "prev"))
+           mode = -1;
+    while ((cp = *args++)) {
+       if (*cp == '-')
+           switch (i = smatch (++cp, showswit)) {
+               case AMBIGSW: 
+                   ambigsw (cp, showswit);
+                   return;
+               case UNKWNSW: 
+               case SHNPROG:
+                   vec[vecp++] = --cp;
+                   continue;
+               case SHHELP: 
+                   snprintf (buf, sizeof(buf), "%s %s[switches] [switches for showproc]",
+                           cmd_name, mode ? NULL : "[msgs] ");
+                   print_help (buf, showswit, 1);
+                   return;
+
+               case SHFORM: 
+               case SHPROG:
+               case SHLEN:
+               case SHWID:
+                   vec[vecp++] = --cp;
+                   if (!(cp = *args++) || *cp == '-') {
+                       advise (NULL, "missing argument to %s", args[-2]);
+                       return;
+                   }
+                   vec[vecp++] = cp;
+                   continue;
+               case SHHEAD: 
+                   headersw++;
+                   continue;
+               case SHNHEAD: 
+                   headersw = 0;
+                   continue;
+               case SHSHOW: 
+                   if (!(proc = *args++) || *proc == '-') {
+                       advise (NULL, "missing argument to %s", args[-2]);
+                       return;
+                   }
+                   nshow = 0;
+                   continue;
+               case SHNSHOW: 
+                   nshow++;
+                   continue;
+
+               case SHDRAFT: 
+                   advise (NULL, "sorry, -%s not allowed!", showswit[i].sw);
+                   return;
+           }
+       if (*cp == '+' || *cp == '@') {
+           advise (NULL, "sorry, no folders allowed!");
+           return;
+       }
+       else
+           if (mode) {
+               fprintf (stderr,
+                       "usage: %s [switches] [switches for showproc]\n",
+                       cmd_name);
+               return;
+           }
+           else
+               msgs[msgp++] = cp;
+    }
+    vec[vecp] = NULL;
+
+    if (!msgp)
+       msgs[msgp++] = mode > 0 ? "next" : mode < 0 ? "prev" : "cur";
+    for (msgnum = 0; msgnum < msgp; msgnum++)
+       if (!m_convert (mp, msgs[msgnum]))
+           return;
+    seq_setprev (mp);
+
+    if (!nshow && !getenv ("NOMHNPROC"))
+       for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
+           if (is_selected (mp, msgnum) && is_nontext (msgnum)) {
+               proc = showmimeproc;
+               vec[vecp++] = "-show";
+               vec[vecp++] = "-file";
+               vec[vecp] = NULL;
+               goto finish;
+           }
+
+    if (nshow)
+       proc = catproc;
+    else
+       if (strcmp (showproc, "mhl") == 0) {
+           proc = mhlproc;
+           mhl++;
+       }
+
+finish: ;
+    seqnum = seq_getnum (mp, "unseen");
+    vec[0] = r1bindex (proc, '/');
+    if (mhl) {
+       msgp = vecp;
+       for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
+           if (is_selected (mp, msgnum)) {
+               vec[vecp++] = getcpy (m_name (msgnum));
+               if (seqnum != -1)
+                   seq_delmsg (mp, "unseen", msgnum);
+           }
+       vec[vecp] = NULL;
+       if (mp->numsel == 1 && headersw)
+           show (mp->lowsel);
+       mhlsbr (vecp, vec, mhl_action);
+       m_eomsbr ((int (*)()) 0);
+       while (msgp < vecp)
+           free (vec[msgp++]);
+    } else {
+       interrupted = 0;
+       for (msgnum = mp->lowsel;
+               msgnum <= mp->hghsel && !interrupted;
+               msgnum++)
+           if (is_selected (mp, msgnum)) {
+               switch (ask (msgnum)) {
+                   case NOTOK: /* QUIT */
+                       break;
+
+                   case OK:    /* INTR */
+                       continue;
+
+                   default:
+                       if (mp->numsel == 1 && headersw)
+                           show (msgnum);
+                       if (nshow)
+                           copy_message (msgnum, stdout);
+                       else
+                           process (msgnum, proc, vecp, vec);
+
+                       if (seqnum != -1)
+                           seq_delmsg (mp, "unseen", msgnum);
+                       continue;
+               }
+               break;
+           }
+    }
+
+    seq_setcur (mp, mp->hghsel);
+}
+
+
+static void
+show (int msgnum)
+{
+    if (Msgs[msgnum].m_bboard_id == 0)
+       readid (msgnum);
+
+    printf ("(Message %d", msgnum);
+    if (Msgs[msgnum].m_bboard_id > 0)
+       printf (", %s: %d", BBoard_ID, Msgs[msgnum].m_bboard_id);
+    printf (")\n");
+}
+
+
+
+static int
+eom_action (int c)
+{
+    return (ftell (mhlfp) >= Msgs[mhlnum].m_stop);
+}
+
+
+static FILE *
+mhl_action (char *name)
+{
+    int msgnum;
+
+    if ((msgnum = m_atoi (name)) < mp->lowmsg
+           || msgnum > mp->hghmsg
+           || !does_exist (mp, msgnum))
+       return NULL;
+    mhlnum = msgnum;
+
+    mhlfp = msh_ready (msgnum, 1);
+    if (!fmsh)
+       m_eomsbr (eom_action);
+
+    return mhlfp;
+}
+
+
+
+static int
+ask (int msgnum)
+{
+    char buf[BUFSIZ];
+
+    if (mp->numsel == 1 || !interactive || redirected)
+       return DONE;
+
+    if (SOprintf ("Press <return> to list \"%d\"...", msgnum)) {
+       if (mp->lowsel != msgnum)
+           printf ("\n\n\n");
+       printf ("Press <return> to list \"%d\"...", msgnum);
+    }
+    fflush (stdout);
+    buf[0] = 0;
+
+#ifndef        BSD42
+    read (fileno (stdout), buf, sizeof buf);
+#else /* BSD42 */
+    switch (setjmp (sigenv)) {
+       case OK: 
+           should_intr = 1;
+           read (fileno (stdout), buf, sizeof buf);/* fall... */
+
+       default: 
+           should_intr = 0;
+           break;
+    }
+#endif /* BSD42 */
+
+    if (strchr(buf, '\n') == NULL)
+       putchar ('\n');
+
+    if (told_to_quit) {
+       told_to_quit = interrupted = 0;
+       return NOTOK;
+    }
+    if (interrupted) {
+       interrupted = 0;
+       return OK;
+    }
+
+    return DONE;
+}
+
+
+#include <h/mime.h>
+
+static int
+is_nontext (int msgnum)
+{
+    int        result, state;
+    char *bp, *cp, *dp;
+    char buf[BUFSIZ], name[NAMESZ];
+    FILE *fp;
+
+    if (Msgs[msgnum].m_flags & MHNCHK)
+       return (Msgs[msgnum].m_flags & MHNYES);
+    Msgs[msgnum].m_flags |= MHNCHK;
+
+    fp = msh_ready (msgnum, 1);
+
+    for (state = FLD;;)
+       switch (state = m_getfld (state, name, buf, sizeof buf, fp)) {
+       case FLD:
+       case FLDPLUS:
+       case FLDEOF:
+           /*
+            * Check Content-Type field
+            */
+           if (!strcasecmp (name, TYPE_FIELD)) {
+               int passno;
+               char c;
+
+               cp = add (buf, NULL);
+               while (state == FLDPLUS) {
+                   state = m_getfld (state, name, buf, sizeof buf, fp);
+                   cp = add (buf, cp);
+               }
+               bp = cp;
+               passno = 1;
+
+again:
+               for (; isspace (*bp); bp++)
+                   continue;
+               if (*bp == '(') {
+                   int i;
+
+                   for (bp++, i = 0;;) {
+                       switch (*bp++) {
+                       case '\0':
+invalid:
+                           result = 0;
+                           goto out;
+                       case '\\':
+                           if (*bp++ == '\0')
+                               goto invalid;
+                           continue;
+                       case '(':
+                           i++;
+                           /* and fall... */
+                       default:
+                           continue;
+                       case ')':
+                           if (--i < 0)
+                               break;
+                           continue;
+                       }
+                       break;
+                   }
+               }
+               if (passno == 2) {
+                   if (*bp != '/')
+                       goto invalid;
+                   bp++;
+                   passno = 3;
+                   goto again;
+               }
+               for (dp = bp; istoken (*dp); dp++)
+                   continue;
+               c = *dp;
+               *dp = '\0';
+               if (!*bp)
+                   goto invalid;
+               if (passno > 1) {
+                   if ((result = (strcasecmp (bp, "plain") != 0)))
+                       goto out;
+                   *dp = c;
+                   for (dp++; isspace (*dp); dp++)
+                       continue;
+                   if (*dp) {
+                       if ((result = !uprf (dp, "charset")))
+                           goto out;
+                       dp += sizeof "charset" - 1;
+                       while (isspace (*dp))
+                           dp++;
+                       if (*dp++ != '=')
+                           goto invalid;
+                       while (isspace (*dp))
+                           dp++;
+                       if (*dp == '"') {
+                           if ((bp = strchr(++dp, '"')))
+                               *bp = '\0';
+                       } else {
+                           for (bp = dp; *bp; bp++)
+                               if (isspace (*bp)) {
+                                   *bp = '\0';
+                                   break;
+                               }
+                       }
+                   } else {
+                       /* Default character set */
+                       dp = "US-ASCII";
+                   }
+                   /* Check the character set */
+                   result = !check_charset (dp, strlen (dp));
+               } else {
+                   if (!(result = (strcasecmp (bp, "text") != 0))) {
+                       *dp = c;
+                       bp = dp;
+                       passno = 2;
+                       goto again;
+                   }
+               }
+out:
+               free (cp);
+               if (result) {
+                   Msgs[msgnum].m_flags |= MHNYES;
+                   return result;
+               }
+               break;
+           }
+
+           /*
+            * Check Content-Transfer-Encoding field
+            */
+           if (!strcasecmp (name, ENCODING_FIELD)) {
+               cp = add (buf, NULL);
+               while (state == FLDPLUS) {
+                   state = m_getfld (state, name, buf, sizeof buf, fp);
+                   cp = add (buf, cp);
+               }
+               for (bp = cp; isspace (*bp); bp++)
+                   continue;
+               for (dp = bp; istoken (*dp); dp++)
+                   continue;
+               *dp = '\0';
+               result = (strcasecmp (bp, "7bit")
+                      && strcasecmp (bp, "8bit")
+                      && strcasecmp (bp, "binary"));
+
+               free (cp);
+               if (result) {
+                   Msgs[msgnum].m_flags |= MHNYES;
+                   return result;
+               }
+               break;
+           }
+
+           /*
+            * Just skip the rest of this header
+            * field and go to next one.
+            */
+           while (state == FLDPLUS)
+               state = m_getfld (state, name, buf, sizeof(buf), fp);
+           break;
+
+           /*
+            * We've passed the message header,
+            * so message is just text.
+            */
+       default:
+           return 0;
+       }
+}
+
+
+static struct swit sortswit[] = {
+#define        SODATE               0
+    { "datefield field", 0 },
+#define        SOSUBJ               1
+    { "textfield field", 0 },
+#define        SONSUBJ              2
+    { "notextfield", 0 },
+#define        SOLIMT               3
+    { "limit days", 0 },
+#define        SONLIMT              4
+    { "nolimit", 0 },
+#define        SOVERB               5
+    { "verbose", 0 },
+#define        SONVERB              6
+    { "noverbose", 0 },
+#define        SOHELP               7
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+
+void
+sortcmd (char **args)
+{
+    int msgp = 0, msgnum;
+    char *cp, *datesw = NULL, *subjsw = NULL;
+    char buf[BUFSIZ], *msgs[MAXARGS];
+    struct tws tb;
+
+    if (fmsh) {
+       forkcmd (args, cmd_name);
+       return;
+    }
+
+    while ((cp = *args++)) {
+       if (*cp == '-')
+           switch (smatch (++cp, sortswit)) {
+               case AMBIGSW: 
+                   ambigsw (cp, sortswit);
+                   return;
+               case UNKWNSW: 
+                   fprintf (stderr, "-%s unknown\n", cp);
+                   return;
+               case SOHELP: 
+                   snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
+                   print_help (buf, sortswit, 1);
+                   return;
+
+               case SODATE: 
+                   if (datesw) {
+                       advise (NULL, "only one date field at a time!");
+                       return;
+                   }
+                   if (!(datesw = *args++) || *datesw == '-') {
+                       advise (NULL, "missing argument to %s", args[-2]);
+                       return;
+                   }
+                   continue;
+
+               case SOSUBJ:
+                   if (subjsw) {
+                       advise (NULL, "only one text field at a time!");
+                       return;
+                   }
+                   if (!(subjsw = *args++) || *subjsw == '-') {
+                       advise (NULL, "missing argument to %s", args[-2]);
+                       return;
+                   }
+                   continue;
+               case SONSUBJ:
+                   subjsw = (char *)0;
+                   continue;
+
+               case SOLIMT:            /* too hard */
+                   if (!(cp = *args++) || *cp == '-') {
+                       advise (NULL, "missing argument to %s", args[-2]);
+                       return;
+                   }
+               case SONLIMT:
+               case SOVERB:            /* not implemented */
+               case SONVERB: 
+                   continue;
+           }
+       if (*cp == '+' || *cp == '@') {
+           advise (NULL, "sorry, no folders allowed!");
+           return;
+       }
+       else
+           msgs[msgp++] = cp;
+    }
+
+    if (!msgp)
+       msgs[msgp++] = "all";
+    if (!datesw)
+       datesw = "Date";
+    for (msgnum = 0; msgnum < msgp; msgnum++)
+       if (!m_convert (mp, msgs[msgnum]))
+           return;
+    seq_setprev (mp);
+
+    twscopy (&tb, dlocaltimenow ());
+
+    for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
+       if (Msgs[msgnum].m_scanl) {
+           free (Msgs[msgnum].m_scanl);
+           Msgs[msgnum].m_scanl = NULL;
+       }
+       if (is_selected (mp, msgnum)) {
+           if (get_fields (datesw, subjsw, msgnum, &Msgs[msgnum]))
+               twscopy (&Msgs[msgnum].m_tb,
+                       msgnum != mp->lowsel ? &Msgs[msgnum - 1].m_tb : &tb);
+       }
+       else                    /* m_scaln is already NULL */
+           twscopy (&Msgs[msgnum].m_tb, &tb);
+       Msgs[msgnum].m_stats = mp->msgstats[msgnum - mp->lowoff];
+       if (mp->curmsg == msgnum)
+           Msgs[msgnum].m_stats |= CUR;
+    }
+
+    qsort ((char *) &Msgs[mp->lowsel], mp->hghsel - mp->lowsel + 1,
+          sizeof(struct Msg), (qsort_comp) (subjsw ? subsort : msgsort));
+
+    for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
+       if (subjsw && Msgs[msgnum].m_scanl) {
+           free (Msgs[msgnum].m_scanl);        /* from subjsort */
+           Msgs[msgnum].m_scanl = NULL;
+       }
+       mp->msgstats[msgnum - mp->lowoff] = Msgs[msgnum].m_stats & ~CUR;
+       if (Msgs[msgnum].m_stats & CUR)
+           seq_setcur (mp, msgnum);
+    }
+           
+    mp->msgflags |= MODIFIED;
+    modified++;
+}
+
+
+/* 
+ * get_fields - parse message, and get date and subject if needed.
+ * We'll use the msgp->m_tb tws struct for the date, and overload
+ * the msgp->m_scanl field with our subject string.
+ */
+static int
+get_fields (char *datesw, char *subjsw, int msgnum, struct Msg *msgp)
+{
+    int        state, gotdate = 0;
+    char *bp, buf[BUFSIZ], name[NAMESZ];
+    struct tws *tw = (struct tws *) 0;
+    register FILE *zp;
+
+    zp = msh_ready (msgnum, 0);
+    for (state = FLD;;) {
+       switch (state = m_getfld (state, name, buf, sizeof buf, zp)) {
+           case FLD: 
+           case FLDEOF: 
+           case FLDPLUS: 
+               if (!strcasecmp (name, datesw)) {
+                   bp = getcpy (buf);
+                   while (state == FLDPLUS) {
+                       state = m_getfld (state, name, buf, sizeof buf, zp);
+                       bp = add (buf, bp);
+                   }
+                   if ((tw = dparsetime (bp)) == NULL)
+                       admonish (NULL,
+                               "unable to parse %s field in message %d",
+                               datesw, msgnum);
+                   else
+                       twscopy (&(msgp->m_tb), tw);
+                   free (bp);
+                   if (!subjsw)        /* not using this, or already done */
+                       break;          /* all done! */
+                   gotdate++;
+               }
+               else if (subjsw && !strcasecmp(name, subjsw)) {
+                   bp = getcpy (buf);
+                   while (state == FLDPLUS) {
+                       state = m_getfld (state, name, buf, sizeof buf, zp);
+                       bp = add (buf, bp);
+                   }
+                   msgp->m_scanl = sosmash(subjsw, bp);
+                   if (gotdate)
+                       break;          /* date done so we're done */
+                   else
+                       subjsw = (char *)0;/* subject done, need date */
+               } else {
+                   while (state == FLDPLUS)    /* flush this one */
+                       state = m_getfld (state, name, buf, sizeof buf, zp);
+               }
+               continue;
+
+           case BODY: 
+           case BODYEOF: 
+           case FILEEOF: 
+               break;
+
+           case LENERR: 
+           case FMTERR: 
+               admonish (NULL, "format error in message %d", msgnum);
+               if (msgp->m_scanl) {    /* this might need free'd */
+                   free (msgp->m_scanl); /* probably can't use subj anyway */
+                   msgp->m_scanl = NULL;
+               }
+               return NOTOK;
+
+           default: 
+               adios (NULL, "internal error -- you lose");
+       }
+       break;
+    }
+    if (tw)
+       return OK;      /* not an error if subj not found */
+
+    admonish (NULL, "no %s field in message %d", datesw, msgnum);
+    return NOTOK;      /* NOTOK means use some other date */
+}
+
+
+/*
+ * sort routines
+ */
+
+static int
+msgsort (struct Msg *a, struct Msg *b)
+{
+    return twsort (&a->m_tb, &b->m_tb);
+}
+
+
+static int
+subsort (struct Msg *a, struct Msg *b)
+{
+       register int i;
+
+       if (a->m_scanl && b->m_scanl)
+           if ((i = strcmp (a->m_scanl, b->m_scanl)))
+               return (i);
+
+       return twsort (&a->m_tb, &b->m_tb);
+}
+
+
+/*
+ * try to make the subject "canonical": delete leading "re:", everything
+ * but letters & smash letters to lower case. 
+ */
+static char *
+sosmash (char *subj, char *s)
+{
+    register char *cp, *dp, c;
+
+    if (s) {
+       cp = s;
+       dp = s; /* dst pointer */
+       if (!strcasecmp (subj, "subject"))
+           while ((c = *cp)) {
+               if (! isspace(c)) {
+                   if(uprf(cp, "re:"))
+                       cp += 2;
+                   else {
+                       if (isalnum(c))
+                           *dp++ = isupper(c) ? tolower(c) : c;
+                       break;
+                   }
+               }
+               cp++;
+           }
+       while ((c = *cp++)) {
+           if (isalnum(c))
+               *dp++ = isupper(c) ? tolower(c) : c;
+
+       }
+       *dp = '\0';
+    }
+    return s;
+}
+
+
+static int
+process (int msgnum, char *proc, int vecp, char **vec)
+{
+    int        child_id, status;
+    char tmpfil[80];
+    FILE *out;
+
+    if (fmsh) {
+       strncpy (tmpfil, m_name (msgnum), sizeof(tmpfil));
+       context_del (pfolder);
+       context_replace (pfolder, fmsh);/* update current folder   */
+       seq_save (mp);
+       context_save ();                /* save the context file   */
+       goto ready;
+    }
+
+    strncpy (tmpfil, m_scratch ("", invo_name), sizeof(tmpfil));
+    if ((out = fopen (tmpfil, "w")) == NULL) {
+       int olderr;
+       extern int errno;
+       char newfil[80];
+
+       olderr = errno;
+       strncpy (newfil, m_tmpfil (invo_name), sizeof(newfil));
+       if ((out = fopen (newfil, "w")) == NULL) {
+           errno = olderr;
+           advise (tmpfil, "unable to create temporary file");
+           return NOTOK;
+       } else {
+           strncpy (tmpfil, newfil, sizeof(tmpfil));
+       }
+    }
+    copy_message (msgnum, out);
+    fclose (out);
+
+ready: ;
+    fflush (stdout);
+    switch (child_id = fork ()) {
+       case NOTOK: 
+           advise ("fork", "unable to");
+           status = NOTOK;
+           break;
+           
+       case OK: 
+           closefds (3);
+           SIGNAL (SIGINT, istat);
+           SIGNAL (SIGQUIT, qstat);
+
+           vec[vecp++] = tmpfil;
+           vec[vecp] = NULL;
+
+           execvp (proc, vec);
+           fprintf (stderr, "unable to exec ");
+           perror (proc);
+           _exit (1);
+
+       default: 
+           status = pidXwait (child_id, NULL);
+           break;
+    }
+
+    if (!fmsh)
+       unlink (tmpfil);
+    return status;
+}
+
+
+static void
+copy_message (int msgnum, FILE *out)
+{
+    long pos;
+    static char buffer[BUFSIZ];
+    register FILE *zp;
+
+    zp = msh_ready (msgnum, 1);
+    if (fmsh) {
+       while (fgets (buffer, sizeof buffer, zp) != NULL) {
+           fputs (buffer, out);
+           if (interrupted && out == stdout)
+               break;
+       }
+    }
+    else {
+       pos = ftell (zp);
+       while (fgets (buffer, sizeof buffer, zp) != NULL
+               && pos < Msgs[msgnum].m_stop) {
+           fputs (buffer, out);
+           pos += (long) strlen (buffer);
+           if (interrupted && out == stdout)
+               break;
+       }
+    }
+}
+
+
+static void
+copy_digest (int msgnum, FILE *out)
+{
+    char c;
+    long pos;
+    static char buffer[BUFSIZ];
+    register FILE *zp;
+
+    c = '\n';
+    zp = msh_ready (msgnum, 1);
+    if (!fmsh)
+       pos = ftell (zp);
+    while (fgets (buffer, sizeof buffer, zp) != NULL
+           && !fmsh && pos < Msgs[msgnum].m_stop) {
+       if (c == '\n' && *buffer == '-')
+           fputc (' ', out);
+       fputs (buffer, out);
+       c = buffer[strlen (buffer) - 1];
+       if (!fmsh)
+           pos += (long) strlen (buffer);
+       if (interrupted && out == stdout)
+           break;
+    }
+}
diff --git a/uip/packf.c b/uip/packf.c
new file mode 100644 (file)
index 0000000..ec2f975
--- /dev/null
@@ -0,0 +1,208 @@
+
+/*
+ * packf.c -- pack a nmh folder into a file
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+#include <h/dropsbr.h>
+#include <errno.h>
+
+/*
+ * We allocate space for messages (msgs array)
+ * this number of elements at a time.
+ */
+#define MAXMSGS  256
+
+
+static struct swit switches[] = {
+#define FILESW         0
+    { "file name", 0 },
+#define MBOXSW         1
+    { "mbox", 0 },
+#define MMDFSW         2
+    { "mmdf", 0 },
+#define VERSIONSW      3
+    { "version", 0 },
+#define        HELPSW         4
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+extern int errno;
+
+static int md = NOTOK;
+static int mbx_style = MBOX_FORMAT;
+static int mapping = 0;
+
+char *file = NULL;
+
+
+int
+main (int argc, char **argv)
+{
+    int nummsgs, maxmsgs, fd, msgnum;
+    char *cp, *maildir, *msgnam, *folder = NULL, buf[BUFSIZ];
+    char **argp, **arguments, **msgs;
+    struct msgs *mp;
+    struct stat st;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    /* Allocate the initial space to record message
+     * names and ranges.
+     */
+    nummsgs = 0;
+    maxmsgs = MAXMSGS;
+    if (!(msgs = (char **) malloc ((size_t) (maxmsgs * sizeof(*msgs)))))
+       adios (NULL, "unable to allocate storage");
+
+    /*
+     * Parse arguments
+     */
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown", cp);
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf), "%s [+folder] [msgs] [switches]",
+                       invo_name);
+                   print_help (buf, switches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case FILESW: 
+                   if (file)
+                       adios (NULL, "only one file at a time!");
+                   if (!(file = *argp++) || *file == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+
+               case MBOXSW:
+                   mbx_style = MBOX_FORMAT;
+                   mapping = 0;
+                   continue;
+               case MMDFSW:
+                   mbx_style = MMDF_FORMAT;
+                   mapping = 1;
+                   continue;
+           }
+       }
+       if (*cp == '+' || *cp == '@') {
+           if (folder)
+               adios (NULL, "only one folder at a time!");
+           folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       } else {
+           /*
+            * Check if we need to allocate more space
+            * for message name/ranges.
+            */
+           if (nummsgs >= maxmsgs) {
+               maxmsgs += MAXMSGS;
+               if (!(msgs = (char **) realloc (msgs,
+                       (size_t) (maxmsgs * sizeof(*msgs)))))
+                   adios (NULL, "unable to reallocate msgs storage");
+           }
+           msgs[nummsgs++] = cp;
+       }
+    }
+
+    if (!file)
+       file = "./msgbox";
+    file = path (file, TFILE);
+
+    /*
+     * Check if file to be created (or appended to)
+     * exists.  If not, ask for confirmation.
+     */
+    if (stat (file, &st) == NOTOK) {
+       if (errno != ENOENT)
+           adios (file, "error on file");
+       cp = concat ("Create file \"", file, "\"? ", NULL);
+       if (!getanswer (cp))
+           done (1);
+       free (cp);
+    }
+
+    if (!context_find ("path"))
+       free (path ("./", TFOLDER));
+
+    /* default is to pack whole folder */
+    if (!nummsgs)
+       msgs[nummsgs++] = "all";
+
+    if (!folder)
+       folder = getfolder (1);
+    maildir = m_maildir (folder);
+
+    if (chdir (maildir) == NOTOK)
+       adios (maildir, "unable to change directory to ");
+
+    /* read folder and create message structure */
+    if (!(mp = folder_read (folder)))
+       adios (NULL, "unable to read folder %s", folder);
+
+    /* check for empty folder */
+    if (mp->nummsg == 0)
+       adios (NULL, "no messages in %s", folder);
+
+    /* parse all the message ranges/sequences and set SELECTED */
+    for (msgnum = 0; msgnum < nummsgs; msgnum++)
+       if (!m_convert (mp, msgs[msgnum]))
+           done (1);
+    seq_setprev (mp);  /* set the previous-sequence */
+
+    /* open and lock new maildrop file */
+    if ((md = mbx_open(file, mbx_style, getuid(), getgid(), m_gmprot())) == NOTOK)
+       adios (file, "unable to open");
+
+    /* copy all the SELECTED messages to the file */
+    for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
+       if (is_selected(mp, msgnum)) {
+           if ((fd = open (msgnam = m_name (msgnum), O_RDONLY)) == NOTOK) {
+               admonish (msgnam, "unable to read message");
+               break;
+           }
+
+           if (mbx_copy (file, mbx_style, md, fd, mapping, NULL, 1) == NOTOK)
+               adios (file, "error writing to file");
+
+           close (fd);
+       }
+
+    /* close and unlock maildrop file */
+    mbx_close (file, md);
+
+    context_replace (pfolder, folder); /* update current folder         */
+    if (mp->hghsel != mp->curmsg)
+       seq_setcur (mp, mp->lowsel);
+    seq_save (mp);
+    context_save ();                   /* save the context file         */
+    folder_free (mp);                  /* free folder/message structure */
+    done (0);
+}
+
+void
+done (int status)
+{
+    mbx_close (file, md);
+    exit (status);
+}
diff --git a/uip/pick.c b/uip/pick.c
new file mode 100644 (file)
index 0000000..d2f72ef
--- /dev/null
@@ -0,0 +1,316 @@
+
+/*
+ * pick.c -- search for messages by content
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <zotnet/tws/tws.h>
+#include <h/picksbr.h>
+
+/*
+ * We allocate space for messages (msgs array)
+ * this number of elements at a time.
+ */
+#define MAXMSGS  256
+
+
+static struct swit switches[] = {
+#define        ANDSW                   0
+    { "and", 0 },
+#define        ORSW                    1
+    { "or", 0 },
+#define        NOTSW                   2
+    { "not", 0 },
+#define        LBRSW                   3
+    { "lbrace", 0 },
+#define        RBRSW                   4
+    { "rbrace", 0 },
+#define        CCSW                    5
+    { "cc  pattern", 0 },
+#define        DATESW                  6
+    { "date  pattern", 0 },
+#define        FROMSW                  7
+    { "from  pattern", 0 },
+#define        SRCHSW                  8
+    { "search  pattern", 0 },
+#define        SUBJSW                  9
+    { "subject  pattern", 0 },
+#define        TOSW                   10
+    { "to  pattern", 0 },
+#define        OTHRSW                 11
+    { "-othercomponent  pattern", 0 },
+#define        AFTRSW                 12
+    { "after date", 0 },
+#define        BEFRSW                 13
+    { "before date", 0 },
+#define        DATFDSW                14
+    { "datefield field", 5 },
+#define        SEQSW                  15
+    { "sequence name", 0 },
+#define        PUBLSW                 16
+    { "public", 0 },
+#define        NPUBLSW                17
+    { "nopublic", 0 },
+#define        ZEROSW                 18
+    { "zero", 0 },
+#define        NZEROSW                19
+    { "nozero", 0 },
+#define        LISTSW                 20
+    { "list", 0 },
+#define        NLISTSW                21
+    { "nolist", 0 },
+#define VERSIONSW              22
+    { "version", 0 },
+#define        HELPSW                 23
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+static int listsw = 0;
+
+
+int
+main (int argc, char **argv)
+{
+    int publicsw = -1, zerosw = 1, seqp = 0, vecp = 0;
+    int nummsgs, maxmsgs, lo, hi, msgnum;
+    char *maildir, *folder = NULL, buf[100];
+    char *cp, **argp, **arguments;
+    char **msgs, *seqs[NUMATTRS + 1], *vec[MAXARGS];
+    struct msgs *mp;
+    register FILE *fp;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    /*
+     * Allocate the initial space to record message
+     * names, ranges, and sequences.
+     */
+    nummsgs = 0;
+    maxmsgs = MAXMSGS;
+    if (!(msgs = (char **) malloc ((size_t) (maxmsgs * sizeof(*msgs)))))
+       adios (NULL, "unable to allocate storage");
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           if (*++cp == '-') {
+               vec[vecp++] = --cp;
+               goto pattern;
+           }
+           switch (smatch (cp, switches)) {
+           case AMBIGSW: 
+               ambigsw (cp, switches);
+               done (1);
+           case UNKWNSW: 
+               adios (NULL, "-%s unknown", cp);
+
+           case HELPSW: 
+               snprintf (buf, sizeof(buf), "%s [+folder] [msgs] [switches]",
+                         invo_name);
+               print_help (buf, switches, 1);
+               listsw = 0;     /* HACK */
+               done (1);
+           case VERSIONSW:
+               print_version(invo_name);
+               done (1);
+
+           case CCSW: 
+           case DATESW: 
+           case FROMSW: 
+           case SUBJSW: 
+           case TOSW: 
+           case DATFDSW: 
+           case AFTRSW: 
+           case BEFRSW: 
+           case SRCHSW: 
+               vec[vecp++] = --cp;
+           pattern:
+               if (!(cp = *argp++))/* allow -xyz arguments */
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               vec[vecp++] = cp;
+               continue;
+           case OTHRSW: 
+               adios (NULL, "internal error!");
+
+           case ANDSW:
+           case ORSW:
+           case NOTSW:
+           case LBRSW:
+           case RBRSW:
+               vec[vecp++] = --cp;
+               continue;
+
+           case SEQSW: 
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+
+               /* check if too many sequences specified */
+               if (seqp >= NUMATTRS)
+                   adios (NULL, "too many sequences (more than %d) specified", NUMATTRS);
+               seqs[seqp++] = cp;
+               listsw = 0;
+               continue;
+           case PUBLSW: 
+               publicsw = 1;
+               continue;
+           case NPUBLSW: 
+               publicsw = 0;
+               continue;
+           case ZEROSW: 
+               zerosw++;
+               continue;
+           case NZEROSW: 
+               zerosw = 0;
+               continue;
+
+           case LISTSW: 
+               listsw++;
+               continue;
+           case NLISTSW: 
+               listsw = 0;
+               continue;
+           }
+       }
+       if (*cp == '+' || *cp == '@') {
+           if (folder)
+               adios (NULL, "only one folder at a time!");
+           else
+               folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       } else {
+           /*
+            * Check if we need to allocate more space
+            * for message name/ranges/sequences.
+            */
+           if (nummsgs >= maxmsgs) {
+               maxmsgs += MAXMSGS;
+               if (!(msgs = (char **) realloc (msgs,
+                                               (size_t) (maxmsgs * sizeof(*msgs)))))
+                   adios (NULL, "unable to reallocate msgs storage");
+           }
+           msgs[nummsgs++] = cp;
+       }
+    }
+    vec[vecp] = NULL;
+
+    if (!context_find ("path"))
+       free (path ("./", TFOLDER));
+
+    /*
+     * If we didn't specify which messages to search,
+     * then search the whole folder.
+     */
+    if (!nummsgs)
+       msgs[nummsgs++] = "all";
+
+    if (!folder)
+       folder = getfolder (1);
+    maildir = m_maildir (folder);
+
+    if (chdir (maildir) == NOTOK)
+       adios (maildir, "unable to change directory to");
+
+    /* read folder and create message structure */
+    if (!(mp = folder_read (folder)))
+       adios (NULL, "unable to read folder %s", folder);
+
+    /* check for empty folder */
+    if (mp->nummsg == 0)
+       adios (NULL, "no messages in %s", folder);
+
+    /* parse all the message ranges/sequences and set SELECTED */
+    for (msgnum = 0; msgnum < nummsgs; msgnum++)
+       if (!m_convert (mp, msgs[msgnum]))
+           done (1);
+    seq_setprev (mp);  /* set the previous-sequence */
+
+    /*
+     * If we aren't saving the results to a sequence,
+     * we need to list the results.
+     */
+    if (seqp == 0)
+       listsw++;
+
+    if (publicsw == 1 && is_readonly(mp))
+       adios (NULL, "folder %s is read-only, so -public not allowed", folder);
+
+    if (!pcompile (vec, NULL))
+       done (1);
+
+    lo = mp->lowsel;
+    hi = mp->hghsel;
+
+    /*
+     * Scan through all the SELECTED messages and check for a
+     * match.  If the message does not match, then unselect it.
+     */
+    for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
+       if (is_selected (mp, msgnum)) {
+           if ((fp = fopen (cp = m_name (msgnum), "r")) == NULL)
+               admonish (cp, "unable to read message");
+           if (fp && pmatches (fp, msgnum, 0L, 0L)) {
+               if (msgnum < lo)
+                   lo = msgnum;
+               if (msgnum > hi)
+                   hi = msgnum;
+           } else {
+               /* if it doesn't match, then unselect it */
+               unset_selected (mp, msgnum);
+               mp->numsel--;
+           }
+           if (fp)
+               fclose (fp);
+       }
+    }
+
+    mp->lowsel = lo;
+    mp->hghsel = hi;
+
+    if (mp->numsel <= 0)
+       adios (NULL, "no messages match specification");
+
+    seqs[seqp] = NULL;
+
+    /*
+     * Add the matching messages to sequences
+     */
+    for (seqp = 0; seqs[seqp]; seqp++)
+       if (!seq_addsel (mp, seqs[seqp], publicsw, zerosw))
+           done (1);
+
+    /*
+     * Print the name of all the matches
+     */
+    if (listsw) {
+       for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
+           if (is_selected (mp, msgnum))
+               printf ("%s\n", m_name (msgnum));
+    } else {
+       printf ("%d hit%s\n", mp->numsel, mp->numsel == 1 ? "" : "s");
+    }
+
+    context_replace (pfolder, folder); /* update current folder         */
+    seq_save (mp);                     /* synchronize message sequences */
+    context_save ();                   /* save the context file         */
+    folder_free (mp);                  /* free folder/message structure */
+    done (0);
+}
+
+
+void
+done (int status)
+{
+    if (listsw && status && !isatty (fileno (stdout)))
+       printf ("0\n");
+    exit (status);
+}
diff --git a/uip/picksbr.c b/uip/picksbr.c
new file mode 100644 (file)
index 0000000..88463ed
--- /dev/null
@@ -0,0 +1,939 @@
+
+/*
+ * picksbr.c -- routines to help pick along...
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <zotnet/tws/tws.h>
+#include <h/picksbr.h>
+
+static struct swit parswit[] = {
+#define        PRAND                   0
+    { "and", 0 },
+#define        PROR                    1
+    { "or", 0 },
+#define        PRNOT                   2
+    { "not", 0 },
+#define        PRLBR                   3
+    { "lbrace", 0 },
+#define        PRRBR                   4
+    { "rbrace", 0 },
+#define        PRCC                    5
+    { "cc  pattern", 0 },
+#define        PRDATE                  6
+    { "date  pattern", 0 },
+#define        PRFROM                  7
+    { "from  pattern", 0 },
+#define        PRSRCH                  8
+    { "search  pattern", 0 },
+#define        PRSUBJ                  9
+    { "subject  pattern", 0 },
+#define        PRTO                   10
+    { "to  pattern", 0 },
+#define        PROTHR                 11
+    { "-othercomponent  pattern", 15 },
+#define        PRAFTR                 12
+    { "after date", 0 },
+#define        PRBEFR                 13
+    { "before date", 0 },
+#define        PRDATF                 14
+    { "datefield field", 5 },
+    { NULL, 0 }
+};
+
+/* DEFINITIONS FOR PATTERN MATCHING */
+
+/*
+ * We really should be using re_comp() and re_exec() here.  Unfortunately,
+ * pick advertises that lowercase characters matches characters of both
+ * cases.  Since re_exec() doesn't exhibit this behavior, we are stuck
+ * with this version.  Furthermore, we need to be able to save and restore
+ * the state of the pattern matcher in order to do things "efficiently".
+ *
+ * The matching power of this algorithm isn't as powerful as the re_xxx()
+ * routines (no \(xxx\) and \n constructs).  Such is life.
+ */
+
+#define        CCHR    2
+#define        CDOT    4
+#define        CCL     6
+#define        NCCL    8
+#define        CDOL    10
+#define        CEOF    11
+
+#define        STAR    01
+
+#define LBSIZE  1024
+#define        ESIZE   256
+
+
+static char linebuf[LBSIZE + 1];
+
+/* the magic array for case-independence */
+static char cc[] = {
+       0000,0001,0002,0003,0004,0005,0006,0007,
+       0010,0011,0012,0013,0014,0015,0016,0017,
+       0020,0021,0022,0023,0024,0025,0026,0027,
+       0030,0031,0032,0033,0034,0035,0036,0037,
+       0040,0041,0042,0043,0044,0045,0046,0047,
+       0050,0051,0052,0053,0054,0055,0056,0057,
+       0060,0061,0062,0063,0064,0065,0066,0067,
+       0070,0071,0072,0073,0074,0075,0076,0077,
+       0100,0141,0142,0143,0144,0145,0146,0147,
+       0150,0151,0152,0153,0154,0155,0156,0157,
+       0160,0161,0162,0163,0164,0165,0166,0167,
+       0170,0171,0172,0133,0134,0135,0136,0137,
+       0140,0141,0142,0143,0144,0145,0146,0147,
+       0150,0151,0152,0153,0154,0155,0156,0157,
+       0160,0161,0162,0163,0164,0165,0166,0167,
+       0170,0171,0172,0173,0174,0175,0176,0177,
+};
+
+/*
+ * DEFINITIONS FOR NEXUS
+ */
+
+#define        nxtarg()        (*argp ? *argp++ : NULL)
+#define        prvarg()        argp--
+
+#define        padvise         if (!talked++) advise
+
+struct nexus {
+    int (*n_action)();
+
+    union {
+       /* for {OR,AND,NOT}action */
+       struct {
+           struct nexus *un_L_child;
+           struct nexus *un_R_child;
+       } st1;
+
+       /* for GREPaction */
+       struct {
+           int   un_header;
+           int   un_circf;
+           char  un_expbuf[ESIZE];
+           char *un_patbuf;
+       } st2;
+
+       /* for TWSaction */
+       struct {
+           char *un_datef;
+           int   un_after;
+           struct tws un_tws;
+       } st3;
+    } un;
+};
+
+#define        n_L_child un.st1.un_L_child
+#define        n_R_child un.st1.un_R_child
+
+#define        n_header un.st2.un_header
+#define        n_circf  un.st2.un_circf
+#define        n_expbuf un.st2.un_expbuf
+#define        n_patbuf un.st2.un_patbuf
+
+#define        n_datef  un.st3.un_datef
+#define        n_after  un.st3.un_after
+#define        n_tws    un.st3.un_tws
+
+static int talked;
+static int pdebug = 0;
+
+static char *datesw;
+static char **argp;
+
+static struct nexus *head;
+
+/*
+ * prototypes for date routines
+ */
+static struct tws *tws_parse();
+static struct tws *tws_special();
+
+/*
+ * static prototypes
+ */
+static void PRaction();
+static int gcompile();
+static int advance();
+static int cclass();
+static int tcompile();
+
+static struct nexus *parse();
+static struct nexus *exp1();
+static struct nexus *exp2();
+static struct nexus *exp3();
+static struct nexus *newnexus();
+
+static int ORaction();
+static int ANDaction();
+static int NOTaction();
+static int GREPaction();
+static int TWSaction();
+
+
+int
+pcompile (char **vec, char *date)
+{
+    register char *cp;
+
+    if ((cp = getenv ("MHPDEBUG")) && *cp)
+       pdebug++;
+
+    argp = vec;
+    if ((datesw = date) == NULL)
+       datesw = "date";
+    talked = 0;
+
+    if ((head = parse ()) == NULL)
+       return (talked ? 0 : 1);
+
+    if (*argp) {
+       padvise (NULL, "%s unexpected", *argp);
+       return 0;
+    }
+
+    return 1;
+}
+
+
+static struct nexus *
+parse (void)
+{
+    register char  *cp;
+    register struct nexus *n, *o;
+
+    if ((n = exp1 ()) == NULL || (cp = nxtarg ()) == NULL)
+       return n;
+
+    if (*cp != '-') {
+       padvise (NULL, "%s unexpected", cp);
+       return NULL;
+    }
+
+    if (*++cp == '-')
+       goto header;
+    switch (smatch (cp, parswit)) {
+       case AMBIGSW: 
+           ambigsw (cp, parswit);
+           talked++;
+           return NULL;
+       case UNKWNSW: 
+           fprintf (stderr, "-%s unknown\n", cp);
+           talked++;
+           return NULL;
+
+       case PROR: 
+           o = newnexus (ORaction);
+           o->n_L_child = n;
+           if ((o->n_R_child = parse ()))
+               return o;
+           padvise (NULL, "missing disjunctive");
+           return NULL;
+
+header: ;
+       default: 
+           prvarg ();
+           return n;
+    }
+}
+
+static struct nexus *
+exp1 (void)
+{
+    register char *cp;
+    register struct nexus *n, *o;
+
+    if ((n = exp2 ()) == NULL || (cp = nxtarg ()) == NULL)
+       return n;
+
+    if (*cp != '-') {
+       padvise (NULL, "%s unexpected", cp);
+       return NULL;
+    }
+
+    if (*++cp == '-')
+       goto header;
+    switch (smatch (cp, parswit)) {
+       case AMBIGSW: 
+           ambigsw (cp, parswit);
+           talked++;
+           return NULL;
+       case UNKWNSW: 
+           fprintf (stderr, "-%s unknown\n", cp);
+           talked++;
+           return NULL;
+
+       case PRAND: 
+           o = newnexus (ANDaction);
+           o->n_L_child = n;
+           if ((o->n_R_child = exp1 ()))
+               return o;
+           padvise (NULL, "missing conjunctive");
+           return NULL;
+
+header: ;
+       default: 
+           prvarg ();
+           return n;
+    }
+}
+
+
+static struct nexus *
+exp2 (void)
+{
+    register char *cp;
+    register struct nexus *n;
+
+    if ((cp = nxtarg ()) == NULL)
+       return NULL;
+
+    if (*cp != '-') {
+       prvarg ();
+       return exp3 ();
+    }
+
+    if (*++cp == '-')
+       goto header;
+    switch (smatch (cp, parswit)) {
+       case AMBIGSW: 
+           ambigsw (cp, parswit);
+           talked++;
+           return NULL;
+       case UNKWNSW: 
+           fprintf (stderr, "-%s unknown\n", cp);
+           talked++;
+           return NULL;
+
+       case PRNOT: 
+           n = newnexus (NOTaction);
+           if ((n->n_L_child = exp3 ()))
+               return n;
+           padvise (NULL, "missing negation");
+           return NULL;
+
+header: ;
+       default: 
+           prvarg ();
+           return exp3 ();
+    }
+}
+
+static struct nexus *
+exp3 (void)
+{
+    int i;
+    register char *cp, *dp;
+    char buffer[BUFSIZ], temp[64];
+    register struct nexus *n;
+
+    if ((cp = nxtarg ()) == NULL)
+       return NULL;
+
+    if (*cp != '-') {
+       padvise (NULL, "%s unexpected", cp);
+       return NULL;
+    }
+
+    if (*++cp == '-') {
+       dp = ++cp;
+       goto header;
+    }
+    switch (i = smatch (cp, parswit)) {
+       case AMBIGSW: 
+           ambigsw (cp, parswit);
+           talked++;
+           return NULL;
+       case UNKWNSW: 
+           fprintf (stderr, "-%s unknown\n", cp);
+           talked++;
+           return NULL;
+
+       case PRLBR: 
+           if ((n = parse ()) == NULL) {
+               padvise (NULL, "missing group");
+               return NULL;
+           }
+           if ((cp = nxtarg ()) == NULL) {
+               padvise (NULL, "missing -rbrace");
+               return NULL;
+           }
+           if (*cp++ == '-' && smatch (cp, parswit) == PRRBR)
+               return n;
+           padvise (NULL, "%s unexpected", --cp);
+           return NULL;
+
+       default: 
+           prvarg ();
+           return NULL;
+
+       case PRCC: 
+       case PRDATE: 
+       case PRFROM: 
+       case PRTO: 
+       case PRSUBJ: 
+           strncpy(temp, parswit[i].sw, sizeof(temp));
+           temp[sizeof(temp) - 1] = '\0';
+           dp = *brkstring (temp, " ", NULL);
+    header: ;
+           if (!(cp = nxtarg ())) {/* allow -xyz arguments */
+               padvise (NULL, "missing argument to %s", argp[-2]);
+               return NULL;
+           }
+           n = newnexus (GREPaction);
+           n->n_header = 1;
+           snprintf (buffer, sizeof(buffer), "^%s[ \t]*:.*%s", dp, cp);
+           dp = buffer;
+           goto pattern;
+
+       case PRSRCH: 
+           n = newnexus (GREPaction);
+           n->n_header = 0;
+           if (!(cp = nxtarg ())) {/* allow -xyz arguments */
+               padvise (NULL, "missing argument to %s", argp[-2]);
+               return NULL;
+           }
+           dp = cp;
+    pattern: ;
+           if (!gcompile (n, dp)) {
+               padvise (NULL, "pattern error in %s %s", argp[-2], cp);
+               return NULL;
+           }
+           n->n_patbuf = getcpy (dp);
+           return n;
+
+       case PROTHR: 
+           padvise (NULL, "internal error!");
+           return NULL;
+
+       case PRDATF: 
+           if (!(datesw = nxtarg ()) || *datesw == '-') {
+               padvise (NULL, "missing argument to %s", argp[-2]);
+               return NULL;
+           }
+           return exp3 ();
+
+       case PRAFTR: 
+       case PRBEFR: 
+           if (!(cp = nxtarg ())) {/* allow -xyz arguments */
+               padvise (NULL, "missing argument to %s", argp[-2]);
+               return NULL;
+           }
+           n = newnexus (TWSaction);
+           n->n_datef = datesw;
+           if (!tcompile (cp, &n->n_tws, n->n_after = i == PRAFTR)) {
+               padvise (NULL, "unable to parse %s %s", argp[-2], cp);
+               return NULL;
+           }
+           return n;
+    }
+}
+
+
+static struct nexus *
+newnexus (int (*action)())
+{
+    register struct nexus *p;
+
+    if ((p = (struct nexus *) calloc ((size_t) 1, sizeof *p)) == NULL)
+       adios (NULL, "unable to allocate component storage");
+
+    p->n_action = action;
+    return p;
+}
+
+
+#define        args(a) a, fp, msgnum, start, stop
+#define        params  args (n)
+#define        plist   \
+           register struct nexus  *n; \
+           register FILE *fp; \
+           int msgnum; \
+           long    start, \
+                   stop;
+
+int
+pmatches (FILE *fp, int msgnum, long start, long stop)
+{
+    if (!head)
+       return 1;
+
+    if (!talked++ && pdebug)
+       PRaction (head, 0);
+
+    return (*head->n_action) (args (head));
+}
+
+
+static void
+PRaction (struct nexus *n, int level)
+{
+    register int i;
+
+    for (i = 0; i < level; i++)
+       fprintf (stderr, "| ");
+
+    if (n->n_action == ORaction) {
+       fprintf (stderr, "OR\n");
+       PRaction (n->n_L_child, level + 1);
+       PRaction (n->n_R_child, level + 1);
+       return;
+    }
+    if (n->n_action == ANDaction) {
+       fprintf (stderr, "AND\n");
+       PRaction (n->n_L_child, level + 1);
+       PRaction (n->n_R_child, level + 1);
+       return;
+    }
+    if (n->n_action == NOTaction) {
+       fprintf (stderr, "NOT\n");
+       PRaction (n->n_L_child, level + 1);
+       return;
+    }
+    if (n->n_action == GREPaction) {
+       fprintf (stderr, "PATTERN(%s) %s\n",
+               n->n_header ? "header" : "body", n->n_patbuf);
+       return;
+    }
+    if (n->n_action == TWSaction) {
+       fprintf (stderr, "TEMPORAL(%s) %s: %s\n",
+               n->n_after ? "after" : "before", n->n_datef,
+               dasctime (&n->n_tws, TW_NULL));
+       return;
+    }
+    fprintf (stderr, "UNKNOWN(0x%x)\n", (unsigned int) (*n->n_action));
+}
+
+
+static int
+ORaction (params)
+plist
+{
+    if ((*n->n_L_child->n_action) (args (n->n_L_child)))
+       return 1;
+    return (*n->n_R_child->n_action) (args (n->n_R_child));
+}
+
+
+static int
+ANDaction (params)
+plist
+{
+    if (!(*n->n_L_child->n_action) (args (n->n_L_child)))
+       return 0;
+    return (*n->n_R_child->n_action) (args (n->n_R_child));
+}
+
+
+static int
+NOTaction (params)
+plist
+{
+    return (!(*n->n_L_child->n_action) (args (n->n_L_child)));
+}
+
+
+static int
+gcompile (struct nexus *n, char *astr)
+{
+    register int c;
+    int cclcnt;
+    register char *ep, *dp, *sp, *lastep;
+
+    dp = (ep = n->n_expbuf) + sizeof n->n_expbuf;
+    sp = astr;
+    if (*sp == '^') {
+       n->n_circf = 1;
+       sp++;
+    }
+    else
+       n->n_circf = 0;
+    for (;;) {
+       if (ep >= dp)
+           goto cerror;
+       if ((c = *sp++) != '*')
+           lastep = ep;
+       switch (c) {
+           case '\0': 
+               *ep++ = CEOF;
+               return 1;
+
+           case '.': 
+               *ep++ = CDOT;
+               continue;
+
+           case '*': 
+               if (lastep == 0)
+                   goto defchar;
+               *lastep |= STAR;
+               continue;
+
+           case '$': 
+               if (*sp != '\0')
+                   goto defchar;
+               *ep++ = CDOL;
+               continue;
+
+           case '[': 
+               *ep++ = CCL;
+               *ep++ = 0;
+               cclcnt = 1;
+               if ((c = *sp++) == '^') {
+                   c = *sp++;
+                   ep[-2] = NCCL;
+               }
+               do {
+                   *ep++ = c;
+                   cclcnt++;
+                   if (c == '\0' || ep >= dp)
+                       goto cerror;
+               } while ((c = *sp++) != ']');
+               lastep[1] = cclcnt;
+               continue;
+
+           case '\\': 
+               if ((c = *sp++) == '\0')
+                   goto cerror;
+       defchar: 
+           default: 
+               *ep++ = CCHR;
+               *ep++ = c;
+       }
+    }
+
+cerror: ;
+    return 0;
+}
+
+
+static int
+GREPaction (params)
+plist
+{
+    int c, body, lf;
+    long pos = start;
+    register char *p1, *p2, *ebp, *cbp;
+    char ibuf[BUFSIZ];
+
+    fseek (fp, start, SEEK_SET);
+    body = 0;
+    ebp = cbp = ibuf;
+    for (;;) {
+       if (body && n->n_header)
+           return 0;
+       p1 = linebuf;
+       p2 = cbp;
+       lf = 0;
+       for (;;) {
+           if (p2 >= ebp) {
+               if (fgets (ibuf, sizeof ibuf, fp) == NULL
+                       || (stop && pos >= stop)) {
+                   if (lf)
+                       break;
+                   return 0;
+               }
+               pos += (long) strlen (ibuf);
+               p2 = ibuf;
+               ebp = ibuf + strlen (ibuf);
+           }
+           c = *p2++;
+           if (lf && c != '\n')
+               if (c != ' ' && c != '\t') {
+                   --p2;
+                   break;
+               }
+               else
+                   lf = 0;
+           if (c == '\n')
+               if (body)
+                   break;
+               else {
+                   if (lf) {
+                       body++;
+                       break;
+                   }
+                   lf++;
+                   c = ' ';
+               }
+           if (c && p1 < &linebuf[LBSIZE - 1])
+               *p1++ = c;
+       }
+
+       *p1++ = 0;
+       cbp = p2;
+       p1 = linebuf;
+       p2 = n->n_expbuf;
+
+       if (n->n_circf) {
+           if (advance (p1, p2))
+               return 1;
+           continue;
+       }
+
+       if (*p2 == CCHR) {
+           c = p2[1];
+           do {
+               if (*p1 == c || cc[*p1] == c)
+                   if (advance (p1, p2))
+                       return 1;
+           } while (*p1++);
+           continue;
+       }
+
+       do {
+           if (advance (p1, p2))
+               return 1;
+       } while (*p1++);
+    }
+}
+
+
+static int
+advance (char *alp, char *aep)
+{
+    register char *lp, *ep, *curlp;
+
+    lp = alp;
+    ep = aep;
+    for (;;)
+       switch (*ep++) {
+           case CCHR: 
+               if (*ep++ == *lp++ || ep[-1] == cc[lp[-1]])
+                   continue;
+               return 0;
+
+           case CDOT: 
+               if (*lp++)
+                   continue;
+               return 0;
+
+           case CDOL: 
+               if (*lp == 0)
+                   continue;
+               return 0;
+
+           case CEOF: 
+               return 1;
+
+           case CCL: 
+               if (cclass (ep, *lp++, 1)) {
+                   ep += *ep;
+                   continue;
+               }
+               return 0;
+
+           case NCCL: 
+               if (cclass (ep, *lp++, 0)) {
+                   ep += *ep;
+                   continue;
+               }
+               return 0;
+
+           case CDOT | STAR: 
+               curlp = lp;
+               while (*lp++)
+                   continue;
+               goto star;
+
+           case CCHR | STAR: 
+               curlp = lp;
+               while (*lp++ == *ep || cc[lp[-1]] == *ep)
+                   continue;
+               ep++;
+               goto star;
+
+           case CCL | STAR: 
+           case NCCL | STAR: 
+               curlp = lp;
+               while (cclass (ep, *lp++, ep[-1] == (CCL | STAR)))
+                   continue;
+               ep += *ep;
+               goto star;
+
+       star: 
+               do {
+                   lp--;
+                   if (advance (lp, ep))
+                       return (1);
+               } while (lp > curlp);
+               return 0;
+
+           default: 
+               admonish (NULL, "advance() botch -- you lose big");
+               return 0;
+       }
+}
+
+
+static int
+cclass (char *aset, int ac, int af)
+{
+    register int    n;
+    register char   c,
+                   *set;
+
+    set = aset;
+    if ((c = ac) == 0)
+       return (0);
+
+    n = *set++;
+    while (--n)
+       if (*set++ == c)
+           return (af);
+
+    return (!af);
+}
+
+
+static int
+tcompile (char *ap, struct tws *tb, int isafter)
+{
+    register struct tws *tw;
+
+    if ((tw = tws_parse (ap, isafter)) == NULL)
+       return 0;
+
+    twscopy (tb, tw);
+    return 1;
+}
+
+
+static struct tws *
+tws_parse (char *ap, int isafter)
+{
+    char buffer[BUFSIZ];
+    register struct tws *tw, *ts;
+
+    if ((tw = tws_special (ap)) != NULL) {
+       tw->tw_sec = tw->tw_min = isafter ? 59 : 0;
+       tw->tw_hour = isafter ? 23 : 0;
+       return tw;
+    }
+    if ((tw = dparsetime (ap)) != NULL)
+       return tw;
+
+    if ((ts = dlocaltimenow ()) == NULL)
+       return NULL;
+
+    snprintf (buffer, sizeof(buffer), "%s %s", ap, dtwszone (ts));
+    if ((tw = dparsetime (buffer)) != NULL)
+       return tw;
+
+    snprintf (buffer, sizeof(buffer), "%s %02d:%02d:%02d %s", ap,
+           ts->tw_hour, ts->tw_min, ts->tw_sec, dtwszone (ts));
+    if ((tw = dparsetime (buffer)) != NULL)
+       return tw;
+
+    snprintf (buffer, sizeof(buffer), "%02d %s %04d %s",
+           ts->tw_mday, tw_moty[ts->tw_mon], ts->tw_year, ap);
+    if ((tw = dparsetime (buffer)) != NULL)
+       return tw;
+
+    snprintf (buffer, sizeof(buffer), "%02d %s %04d %s %s",
+           ts->tw_mday, tw_moty[ts->tw_mon], ts->tw_year,
+           ap, dtwszone (ts));
+    if ((tw = dparsetime (buffer)) != NULL)
+       return tw;
+
+    return NULL;
+}
+
+
+static struct tws *
+tws_special (char *ap)
+{
+    int i;
+    time_t clock;
+    register struct tws *tw;
+
+    time (&clock);
+    if (!strcasecmp (ap, "today"))
+       return dlocaltime (&clock);
+    if (!strcasecmp (ap, "yesterday")) {
+       clock -= (long) (60 * 60 * 24);
+       return dlocaltime (&clock);
+    }
+    if (!strcasecmp (ap, "tomorrow")) {
+       clock += (long) (60 * 60 * 24);
+       return dlocaltime (&clock);
+    }
+
+    for (i = 0; tw_ldotw[i]; i++)
+       if (!strcasecmp (ap, tw_ldotw[i]))
+           break;
+    if (tw_ldotw[i]) {
+       if ((tw = dlocaltime (&clock)) == NULL)
+           return NULL;
+       if ((i -= tw->tw_wday) > 0)
+           i -= 7;
+    }
+    else
+       if (*ap != '-')
+           return NULL;
+       else                    /* -ddd days ago */
+           i = atoi (ap);      /* we should error check this */
+
+    clock += (long) ((60 * 60 * 24) * i);
+    return dlocaltime (&clock);
+}
+
+
+static int
+TWSaction (params)
+plist
+{
+    int state;
+    register char *bp;
+    char buf[BUFSIZ], name[NAMESZ];
+    register struct tws *tw;
+
+    fseek (fp, start, SEEK_SET);
+    for (state = FLD, bp = NULL;;) {
+       switch (state = m_getfld (state, name, buf, sizeof buf, fp)) {
+           case FLD: 
+           case FLDEOF: 
+           case FLDPLUS: 
+               if (bp != NULL)
+                   free (bp), bp = NULL;
+               bp = add (buf, NULL);
+               while (state == FLDPLUS) {
+                   state = m_getfld (state, name, buf, sizeof buf, fp);
+                   bp = add (buf, bp);
+               }
+               if (!strcasecmp (name, n->n_datef))
+                   break;
+               if (state != FLDEOF)
+                   continue;
+
+           case BODY: 
+           case BODYEOF: 
+           case FILEEOF: 
+           case LENERR: 
+           case FMTERR: 
+               if (state == LENERR || state == FMTERR)
+                   advise (NULL, "format error in message %d", msgnum);
+               if (bp != NULL)
+                   free (bp);
+               return 0;
+
+           default: 
+               adios (NULL, "internal error -- you lose");
+       }
+       break;
+    }
+
+    if ((tw = dparsetime (bp)) == NULL)
+       advise (NULL, "unable to parse %s field in message %d, matching...",
+               n->n_datef, msgnum), state = 1;
+    else
+       state = n->n_after ? (twsort (tw, &n->n_tws) > 0)
+           : (twsort (tw, &n->n_tws) < 0);
+
+    if (bp != NULL)
+       free (bp);
+    return state;
+}
diff --git a/uip/popi.c b/uip/popi.c
new file mode 100644 (file)
index 0000000..59ab61a
--- /dev/null
@@ -0,0 +1,644 @@
+
+/*
+ * popi.c -- POP initiator for MPOP
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/fmt_scan.h>
+#include <h/scansbr.h>
+#include <zotnet/mts/mts.h>
+#include <errno.h>
+
+#ifndef        RPOP
+# define RPOPminc(a) (a)
+#else
+# define RPOPminc(a)  0
+#endif
+
+#ifndef        APOP
+# define APOPminc(a) (a)
+#else
+# define APOPminc(a)  0
+#endif
+
+#ifndef        BPOP
+# define BPOPminc(a) (a)
+#else
+# define BPOPminc(a)  0
+#endif
+
+#ifndef        SMTPMTS
+# define BULKminc(a) (a)
+#else
+# define BULKminc(a)  0
+#endif
+
+static struct swit  switches[] = {
+#define        APOPSW                  0
+    { "apop", APOPminc (-4) },
+#define        NAPOPSW                 1
+    { "noapop", APOPminc (-6) },
+#define        AUTOSW                  2
+    { "auto", BPOPminc(-4) },
+#define        NAUTOSW                 3
+    { "noauto", BPOPminc(-6) },
+#define        BULKSW                  4
+    { "bulk directory", BULKminc(-4) },
+#define        FORMSW                  5
+    { "form formatfile", 0 },
+#define        FMTSW                   6
+    { "format string", 5 },
+#define        HOSTSW                  7
+    { "host host", 0 },
+#define        PROGSW                  8
+    { "mshproc program", 0 },
+#define        RPOPSW                  9
+    { "rpop", RPOPminc (-4) },
+#define        NRPOPSW                10
+    { "norpop", RPOPminc (-6) },
+#define        USERSW                 11
+    { "user user", 0 },
+#define        WIDTHSW                12
+    { "width columns", 0 },
+#define VERSIONSW              13
+    { "version", 0 },
+#define        HELPSW                 14
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+static char *bulksw = NULL;
+static int snoop = 0;
+static int width = 0;
+static char mailname[BUFSIZ];
+static char *nfs = NULL;
+static struct msgs *mp;
+
+extern int errno;
+extern char response[];
+
+/*
+ * prototypes
+ */
+int sc_width (void);  /* from termsbr.c */
+
+
+int
+main (int argc, char **argv)
+{
+    int        autosw = 1, noisy = 1, rpop;
+    char *cp, *maildir, *folder = NULL, *form = NULL;
+    char *format = NULL, *host = NULL, *user = NULL;
+    char *pass = NULL, buf[BUFSIZ], **argp;
+    char **arguments;
+    struct stat st;
+
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    mts_init (invo_name);
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    if (pophost && *pophost)
+       host = pophost;
+    if ((cp = getenv ("MHPOPDEBUG")) && *cp)
+       snoop++;
+
+    rpop = getuid() && !geteuid();
+
+    while (cp = *argp++) {
+       if (*cp == '-')
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown", cp);
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf), "%s [+folder] [switches]",
+                       invo_name);
+                   print_help (buf, switches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case AUTOSW:
+                   autosw = 1;
+                   continue;
+               case NAUTOSW:
+                   autosw = 0;
+                   continue;
+
+               case BULKSW: 
+                   if (!(bulksw = *argp++) || *bulksw == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+
+               case FORMSW: 
+                   if (!(form = *argp++) || *form == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   format = NULL;
+                   continue;
+               case FMTSW: 
+                   if (!(format = *argp++) || *format == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   form = NULL;
+                   continue;
+
+               case WIDTHSW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   width = atoi (cp);
+                   continue;
+
+               case HOSTSW:
+                   if (!(host = *argp++) || *host == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+               case USERSW:
+                   if (!(user = *argp++) || *user == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+
+               case APOPSW:
+                   rpop = -1;
+                   continue;
+               case RPOPSW:
+                   rpop = 1;
+                   continue;
+               case NAPOPSW:
+               case NRPOPSW:
+                   rpop = 0;
+                   continue;
+
+               case PROGSW:
+                   if (!(mshproc = *argp++) || *mshproc == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+           }
+       if (*cp == '+' || *cp == '@') {
+           if (folder)
+               adios (NULL, "only one folder at a time!");
+           else
+               folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       }
+       else
+           adios (NULL, "usage: %s [+folder] [switches]", invo_name);
+    }
+
+    if (!host)
+       adios (NULL, "usage: %s -host \"host\"", invo_name);
+
+#ifdef SMTPMTS
+    if (bulksw)
+       do_bulk (host);
+#endif
+
+    if (user == NULL)
+       user = getusername ();
+    if (rpop > 0)
+       pass = getusername ();
+    else {
+       setuid (getuid ());
+       ruserpass (host, &user, &pass);
+    }
+    snprintf (mailname, sizeof(mailname), "PO box for %s@%s", user, host);
+
+    if (pop_init (host, user, pass, snoop, rpop) == NOTOK)
+       adios (NULL, "%s", response);
+    if (rpop > 0)
+       setuid (getuid ());
+
+    /* get new format string */
+    nfs = new_fs (form, format, FORMAT);
+
+    if (!context_find ("path"))
+       free (path ("./", TFOLDER));
+    if (!folder)
+       folder = getfolder (0);
+    maildir = m_maildir (folder);
+
+    if (stat (maildir, &st) == NOTOK) {
+       if (errno != ENOENT)
+           adios (maildir, "error on folder");
+       cp = concat ("Create folder \"", maildir, "\"? ", NULL);
+       if (noisy && !getanswer (cp))
+           done (1);
+       free (cp);
+       if (!makedir (maildir))
+           adios (NULL, "unable to create folder %s", maildir);
+    }
+
+    if (chdir (maildir) == NOTOK)
+       adios (maildir, "unable to change directory to");
+
+    if (!(mp = folder_read (folder)))
+       adios (NULL, "unable to read folder %s", folder);
+
+#ifdef BPOP
+    if (autosw)
+       msh ();
+    else
+#endif
+
+    popi();
+    pop_quit();
+
+    context_replace (pfolder, folder); /* update current folder   */
+    seq_setunseen (mp, 0);             /* set the Unseen-Sequence */
+    seq_save (mp);
+    context_save ();                   /* save the context file   */
+    done (0);
+
+    /* NOTREACHED */
+}
+
+
+static struct swit popicmds[] = {
+#define        DELECMD  0
+    "dele", 0,
+#define        LASTCMD  1
+    "last", 0,
+#define        LISTCMD  2
+    "list", 0,
+#define        NOOPCMD  3
+    "noop", 0,
+#define        QUITCMD  4
+    "quit", 0,
+#define        RETRCMD  5
+    "retr", 0,
+#define        RSETCMD  6
+    "rset", 0,
+#define        SCANCMD  7
+    "scan", 0,
+#define        STATCMD  8
+    "stat", 0,
+#define        TOPCMD   9
+    "top", 0,
+#ifdef BPOP
+#define        MSHCMD  10
+    "msh", 0,
+#endif
+
+    NULL, 0
+};
+
+
+static void
+popi (void)
+{
+    int        eof = 0;
+
+    for (;;) {
+       int i;
+       register char *cp;
+       char buffer[BUFSIZ];
+
+       if (eof)
+           return;
+
+       printf ("(%s) ", invo_name);
+       for (cp = buffer; (i = getchar ()) != '\n'; ) {
+           if (i == EOF) {
+               putchar ('\n');
+               if (cp == buffer)
+                   return;
+               eof = 1;
+               break;
+           }
+
+           if (cp < buffer + sizeof buffer - 2)
+               *cp++ = i;
+       }
+       *cp = '\0';
+       if (buffer[0] == '\0')
+           continue;
+       if (buffer[0] == '?') {
+           printf ("commands:\n");
+           print_sw (ALL, popicmds, "");
+           printf ("type CTRL-D or use \"quit\" to leave %s\n", invo_name);
+           continue;
+       }
+
+       if (cp = strchr (buffer, ' '))
+           *cp = '\0';
+       switch (i = smatch (buffer, popicmds)) {
+           case AMBIGSW:
+               ambigsw (buffer, popicmds);
+               continue;
+           case UNKWNSW:
+               printf ("%s unknown -- type \"?\" for help\n", buffer);
+               continue;
+               
+           case QUITCMD:
+               return;
+
+           case STATCMD:
+           case DELECMD:
+           case NOOPCMD:
+           case LASTCMD:
+           case RSETCMD:
+           case TOPCMD:
+               if (cp)
+                   *cp = ' ';
+               pop_command ("%s%s", popicmds[i].sw, cp ? cp : "");
+               printf ("%s\n", response);
+               break;          
+
+           case LISTCMD:
+               if (cp)
+                   *cp = ' ';
+               if (pop_command ("%s%s", popicmds[i].sw, cp ? cp : "")
+                       == OK) {
+                   printf ("%s\n", response);
+                   if (!cp)
+                       for (;;) {
+                           switch (pop_multiline ()) {
+                               case DONE:
+                                   strcpy (response, ".");
+                                   /* and fall... */
+                               case NOTOK:
+                                   printf ("%s\n", response);
+                                   break;
+
+                               case OK:
+                                   printf ("%s\n", response);
+                                   continue;
+                            }
+                           break;
+                       }
+               }
+               break;
+
+           case RETRCMD:
+               if (!cp) {
+                   advise (NULL, "missing argument to %s", buffer);
+                   break;
+               }
+               retr_action (NULL, OK);
+               pop_retr (atoi (++cp), retr_action);
+               retr_action (NULL, DONE);
+               printf ("%s\n", response);
+               break;
+
+           case SCANCMD:
+               {
+                   char   *dp,
+                          *ep,
+                          *fp;
+
+                   if (width == 0)
+                       width = sc_width ();
+
+                   for (dp = nfs, i = 0; *dp; dp++, i++)
+                       if (*dp == '\\' || *dp == '"' || *dp == '\n')
+                           i++;
+                   i++;
+                   if ((ep = malloc ((unsigned) i)) == NULL)
+                       adios (NULL, "out of memory");
+                   for (dp = nfs, fp = ep; *dp; dp++) {
+                       if (*dp == '\n') {
+                           *fp++ = '\\', *fp++ = 'n';
+                           continue;
+                       }
+                       if (*dp == '"' || *dp == '\\')
+                           *fp++ = '\\';
+                       *fp++ = *dp;
+                   }
+                   *fp = '\0';
+
+                   pop_command ("xtnd scan %d \"%s\"", width, ep);
+                   printf ("%s\n", response);
+
+                   free (ep);
+               }
+               break;
+
+#ifdef BPOP
+           case MSHCMD:
+               msh ();
+               break;
+#endif
+       }
+    }
+}
+
+
+static int
+retr_action (char *rsp, int flag)
+{
+    static FILE *fp;
+
+    if (rsp == NULL) {
+       static int msgnum;
+       static char *cp;
+
+       if (flag == OK) {
+           if (!(mp = folder_realloc (mp, mp->lowoff, msgnum = mp->hghmsg + 1)))
+               adios (NULL, "unable to allocate folder storage");
+
+           cp = getcpy (m_name (mp->hghmsg + 1));
+           if ((fp = fopen (cp, "w+")) == NULL)
+               adios (cp, "unable to write");
+           chmod (cp, m_gmprot ());
+       }
+       else {
+           struct stat st;
+
+           fflush (fp);
+           if (fstat (fileno (fp), &st) != NOTOK && st.st_size > 0) {
+               clear_msg_flags (mp, msgnum);
+               set_exists (mp, msgnum);
+               set_unseen (mp, msgnum);
+               mp->msgflags |= SEQMOD;
+
+               if (ferror (fp))
+                   advise (cp, "write error on");
+               mp->hghmsg = msgnum;
+           }
+           else
+               unlink (cp);
+
+           fclose (fp), fp = NULL;
+           free (cp), cp = NULL;
+       }
+
+       return;
+    }
+
+    fprintf (fp, "%s\n", rsp);
+}
+
+
+#ifdef BPOP
+static void
+msh (void)
+{
+    int        child_id, vecp;
+    char buf1[BUFSIZ], buf2[BUFSIZ], *vec[9];
+
+    if (pop_fd (buf1, sizeof(buf1), buf2, sizeof(buf2)) == NOTOK)
+       adios (NULL, "%s", response);
+
+    vecp = 0;
+    vec[vecp++] = r1bindex (mshproc, '/');
+                   
+    switch (child_id = fork ()) {
+       case NOTOK:
+           adios ("fork", "unable to");
+
+       case OK:
+           vec[vecp++] = "-popread";
+           vec[vecp++] = buf1;
+           vec[vecp++] = "-popwrite";
+           vec[vecp++] = buf2;
+           vec[vecp++] = "-idname";
+           vec[vecp++] = mailname;
+           vec[vecp++] = mailname;
+           vec[vecp] = NULL;
+           execvp (mshproc, vec);
+           fprintf (stderr, "unable to exec ");
+           perror (mshproc);
+           _exit (-1);
+
+       default:
+           pidXwait (child_id, mshproc);
+           break;
+   }
+}
+#endif
+
+
+#ifdef SMTPMTS
+#include <zotnet/mts/mts.h>
+#include <mts/smtp/smtp.h>
+
+static int
+dselect (struct direct *d)
+{
+    int        i;
+
+    if ((i = strlen (d->d_name)) < sizeof "smtp"
+           || strncmp (d->d_name, "smtp", sizeof "smtp" - 1))
+       return 0;
+    return ((i -= (sizeof ".bulk" - 1)) > 0
+               && !strcmp (d->d_name + i, ".bulk"));
+}
+
+
+static int
+dcompar (struct direct *d1, struct direct *d2)
+{
+    struct stat s1, s2;
+
+    if (stat ((*d1)->d_name, &s1) == NOTOK)
+       return 1;
+    if (stat ((*d2)->d_name, &s2) == NOTOK)
+       return -1;
+    return ((int) (s1.st_mtime - s2.st_mtime));
+}
+
+
+static void
+do_bulk (char *host)
+{
+    register int i;
+    int        n, retval, sm;
+    struct direct **namelist;
+
+    if (chdir (bulksw) == NOTOK)
+       adios (bulksw, "unable to change directory to");
+
+    if ((n = scandir (".", &namelist, dselect, dcompar)) == NOTOK)
+       adios (bulksw, "unable to scan directory");
+
+    sm = NOTOK;
+    for (i = 0; i < n; i++) {
+       register struct direct *d = namelist[i];
+
+       if (sm == NOTOK) {
+           if (rp_isbad (retval = sm_init (NULL, host, 1, 1, snoop)))
+               adios (NULL, "problem initializing server: %s",
+                      rp_string (retval));
+           else
+               sm = OK;
+       }
+
+       switch (retval = sm_bulk (d->d_name)) {
+           default:
+               if (rp_isbad (retval))
+                   adios (NULL, "problem delivering msg %s: %s",
+                          d->d_name, rp_string (retval));
+               /* else fall... */
+           case RP_OK:
+           case RP_NO:
+           case RP_NDEL:
+               advise (NULL, "msg %s: %s", d->d_name, rp_string (retval));
+               break;
+       }
+    }
+
+    if (sm == OK) {
+       register int j;
+       int     l,
+               m;
+       struct direct **newlist;
+
+       while ((l = scandir (".", &newlist, dselect, dcompar)) > OK) {
+           m = 0;
+
+           for (j = 0; j < l; j++) {
+               register struct direct *d = newlist[j];
+
+               for (i = 0; i < n; i++)
+                   if (strcmp (d->d_name, namelist[i]->d_name) == 0)
+                       break;
+               if (i >= n) {
+                   switch (retval = sm_bulk (d->d_name)) {
+                       default:
+                           if (rp_isbad (retval))
+                               adios (NULL, "problem delivering msg %s: %s",
+                                      d->d_name, rp_string (retval));
+                           /* else fall... */
+                       case RP_OK:
+                       case RP_NO:
+                       case RP_NDEL:
+                           advise (NULL, "msg %s: %s", d->d_name,
+                                   rp_string (retval));
+                           break;
+                   }
+
+                   m = 1;
+               }
+           }
+
+           for (i = 0; i < n; i++)
+               free ((char *) namelist[i]);
+           free ((char *) namelist);
+           namelist = newlist, n = l;
+
+           if (!m)
+               break;
+           newlist = NULL;
+       }
+    }
+
+    if (sm == OK && rp_isbad (retval = sm_end (OK)))
+       adios (NULL, "problem finalizing server: %s", rp_string (retval));
+
+    for (i = 0; i < n; i++)
+       free ((char *) namelist[i]);
+    free ((char *) namelist);
+
+    free ((char *) namelist);
+
+    done (0);
+}
+#endif
diff --git a/uip/popsbr.c b/uip/popsbr.c
new file mode 100644 (file)
index 0000000..6b026c6
--- /dev/null
@@ -0,0 +1,677 @@
+
+/*
+ * popsbr.c -- POP client subroutines
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+#if defined(NNTP) && !defined(PSHSBR)
+# undef NNTP
+#endif
+
+#ifdef NNTP                    /* building pshsbr.o from popsbr.c */
+# include <h/nntp.h>
+#endif /* NNTP */
+
+#if !defined(NNTP) && defined(APOP)
+# include <h/md5.h>
+#endif
+
+#include <h/popsbr.h>
+#include <h/signals.h>
+#include <signal.h>
+
+#define        TRM     "."
+#define        TRMLEN  (sizeof TRM - 1)
+
+extern int errno;
+
+static int poprint = 0;
+static int pophack = 0;
+
+char response[BUFSIZ];
+
+static FILE *input;
+static FILE *output;
+
+#define        targ_t char *
+
+#if !defined(NNTP) && defined(MPOP)
+# define command pop_command
+# define multiline pop_multiline
+#endif
+
+#ifdef NNTP
+# ifdef BPOP   /* stupid */
+static int xtnd_last = -1;
+static int xtnd_first = 0;
+static char xtnd_name[512];    /* INCREDIBLE HACK!! */
+# endif
+#endif /* NNTP */
+
+/*
+ * static prototypes
+ */
+#if !defined(NNTP) && defined(APOP)
+static char *pop_auth (char *, char *);
+#endif
+
+#if defined(NNTP) || !defined(MPOP)
+/* otherwise they are not static functions */
+static int command(const char *, ...);
+static int multiline(void);
+#endif
+
+static int traverse (int (*)(), const char *, ...);
+static int vcommand(const char *, va_list);
+static int getline (char *, int, FILE *);
+static int putline (char *, FILE *);
+
+
+#if !defined(NNTP) && defined(APOP)
+static char *
+pop_auth (char *user, char *pass)
+{
+    int len, buflen;
+    char *cp, *lp;
+    unsigned char *dp, *ep, digest[16];
+    MD5_CTX mdContext;
+    static char buffer[BUFSIZ];
+
+    if ((cp = strchr (response, '<')) == NULL
+           || (lp = strchr (cp, '>')) == NULL) {
+       snprintf (buffer, sizeof(buffer), "APOP not available: %s", response);
+       strncpy (response, buffer, sizeof(response));
+       return NULL;
+    }
+
+    *++lp = NULL;
+    snprintf (buffer, sizeof(buffer), "%s%s", cp, pass);
+
+    MD5Init (&mdContext);
+    MD5Update (&mdContext, (unsigned char *) buffer,
+              (unsigned int) strlen (buffer));
+    MD5Final (digest, &mdContext);
+
+    cp = buffer;
+    buflen = sizeof(buffer);
+
+    snprintf (cp, buflen, "%s ", user);
+    len = strlen (cp);
+    cp += len;
+    buflen -= len;
+
+    for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]); dp < ep; ) {
+       snprintf (cp, buflen, "%02x", *dp++ & 0xff);
+       cp += 2;
+       buflen -= 2;
+    }
+    *cp = NULL;
+
+    return buffer;
+}
+#endif /* !NNTP && APOP */
+
+
+int
+pop_init (char *host, char *user, char *pass, int snoop, int rpop)
+{
+    int fd1, fd2;
+    char buffer[BUFSIZ];
+
+#ifdef APOP
+    int apop;
+
+    if ((apop = rpop) < 0)
+       rpop = 0;
+#endif
+
+#ifndef NNTP
+# ifndef KPOP
+    if ((fd1 = client (host, "tcp", POPSERVICE, rpop, response, sizeof(response))) == NOTOK)
+# else /* KPOP */
+    snprintf (buffer, sizeof(buffer), "%s/%s", KPOP_PRINCIPAL, POPSERVICE);
+    if ((fd1 = client (host, "tcp", buffer, rpop, response, sizeof(response))) == NOTOK)
+# endif
+#else  /* NNTP */
+    if ((fd1 = client (host, "tcp", "nntp", rpop, response, sizeof(response))) == NOTOK)
+#endif
+       return NOTOK;
+
+    if ((fd2 = dup (fd1)) == NOTOK) {
+       char *s;
+
+       if ((s = strerror(errno)))
+           snprintf (response, sizeof(response),
+               "unable to dup connection descriptor: %s", s);
+       else
+           snprintf (response, sizeof(response),
+               "unable to dup connection descriptor: unknown error");
+       close (fd1);
+       return NOTOK;
+    }
+#ifndef NNTP
+    if (pop_set (fd1, fd2, snoop) == NOTOK)
+#else  /* NNTP */
+    if (pop_set (fd1, fd2, snoop, (char *)0) == NOTOK)
+#endif /* NNTP */
+       return NOTOK;
+
+    SIGNAL (SIGPIPE, SIG_IGN);
+
+    switch (getline (response, sizeof response, input)) {
+       case OK: 
+           if (poprint)
+               fprintf (stderr, "<--- %s\n", response);
+#ifndef        NNTP
+           if (*response == '+') {
+# ifndef KPOP
+#  ifdef APOP
+               if (apop < 0) {
+                   char *cp = pop_auth (user, pass);
+
+                   if (cp && command ("APOP %s", cp) != NOTOK)
+                       return OK;
+               }
+               else
+#  endif /* APOP */
+               if (command ("USER %s", user) != NOTOK
+                   && command ("%s %s", rpop ? "RPOP" : (pophack++, "PASS"),
+                                       pass) != NOTOK)
+               return OK;
+# else /* KPOP */
+               if (command ("USER %s", user) != NOTOK
+                   && command ("PASS %s", pass) != NOTOK)
+               return OK;
+# endif
+           }
+#else /* NNTP */
+           if (*response < CHAR_ERR) {
+               command ("MODE READER");
+               return OK;
+           }
+#endif
+           strncpy (buffer, response, sizeof(buffer));
+           command ("QUIT");
+           strncpy (response, buffer, sizeof(response));
+                               /* and fall */
+
+       case NOTOK: 
+       case DONE: 
+           if (poprint)            
+               fprintf (stderr, "%s\n", response);
+           fclose (input);
+           fclose (output);
+           return NOTOK;
+    }
+
+    return NOTOK;      /* NOTREACHED */
+}
+
+#ifdef NNTP
+int
+pop_set (int in, int out, int snoop, char *myname)
+#else
+int
+pop_set (int in, int out, int snoop)
+#endif
+{
+
+#ifdef NNTP
+    if (myname && *myname) {
+       /* interface from bbc to msh */
+       strncpy (xtnd_name, myname, sizeof(xtnd_name));
+    }
+#endif /* NNTP */
+
+    if ((input = fdopen (in, "r")) == NULL
+           || (output = fdopen (out, "w")) == NULL) {
+       strncpy (response, "fdopen failed on connection descriptor", sizeof(response));
+       if (input)
+           fclose (input);
+       else
+           close (in);
+       close (out);
+       return NOTOK;
+    }
+
+    poprint = snoop;
+
+    return OK;
+}
+
+
+int
+pop_fd (char *in, int inlen, char *out, int outlen)
+{
+    snprintf (in, inlen, "%d", fileno (input));
+    snprintf (out, outlen, "%d", fileno (output));
+    return OK;
+}
+
+
+/*
+ * Find out number of messages available
+ * and their total size.
+ */
+
+int
+pop_stat (int *nmsgs, int *nbytes)
+{
+#ifdef NNTP
+    char **ap;
+#endif /* NNTP */
+
+#ifndef        NNTP
+    if (command ("STAT") == NOTOK)
+       return NOTOK;
+
+    *nmsgs = *nbytes = 0;
+    sscanf (response, "+OK %d %d", nmsgs, nbytes);
+
+#else /* NNTP */
+    if (xtnd_last < 0) {       /* in msh, xtnd_name is set from myname */
+       if (command("GROUP %s", xtnd_name) == NOTOK)
+           return NOTOK;
+
+       ap = brkstring (response, " ", "\n"); /* "211 nart first last ggg" */
+       xtnd_first = atoi (ap[2]);
+       xtnd_last  = atoi (ap[3]);
+    }
+
+    /* nmsgs is not the real nart, but an incredible simuation */
+    if (xtnd_last > 0)
+       *nmsgs = xtnd_last - xtnd_first + 1;    /* because of holes... */
+    else
+       *nmsgs = 0;
+    *nbytes = xtnd_first;      /* for subtracting offset in msh() */
+#endif /* NNTP */
+
+    return OK;
+}
+
+#ifdef NNTP
+int
+pop_exists (int (*action)())
+{
+#ifdef XMSGS           /* hacked into NNTP 1.5 */
+    if (traverse (action, "XMSGS %d-%d", (targ_t) xtnd_first, (targ_t) xtnd_last) == OK)
+       return OK;
+#endif
+    /* provided by INN 1.4 */
+    if (traverse (action, "LISTGROUP") == OK)
+       return OK;
+    return traverse (action, "XHDR NONAME %d-%d", (targ_t) xtnd_first, (targ_t) xtnd_last);
+}
+#endif /* NNTP */
+
+
+#ifdef BPOP
+int
+pop_list (int msgno, int *nmsgs, int *msgs, int *bytes, int *ids)
+#else
+int
+pop_list (int msgno, int *nmsgs, int *msgs, int *bytes)
+#endif
+{
+    int i;
+#ifndef        BPOP
+    int *ids = NULL;
+#endif
+
+    if (msgno) {
+#ifndef NNTP
+       if (command ("LIST %d", msgno) == NOTOK)
+           return NOTOK;
+       *msgs = *bytes = 0;
+       if (ids) {
+           *ids = 0;
+           sscanf (response, "+OK %d %d %d", msgs, bytes, ids);
+       }
+       else
+           sscanf (response, "+OK %d %d", msgs, bytes);
+#else /* NNTP */
+       *msgs = *bytes = 0;
+       if (command ("STAT %d", msgno) == NOTOK) 
+           return NOTOK;
+       if (ids) {
+           *ids = msgno;
+       }
+#endif /* NNTP */
+       return OK;
+    }
+
+#ifndef NNTP
+    if (command ("LIST") == NOTOK)
+       return NOTOK;
+
+    for (i = 0; i < *nmsgs; i++)
+       switch (multiline ()) {
+           case NOTOK: 
+               return NOTOK;
+           case DONE: 
+               *nmsgs = ++i;
+               return OK;
+           case OK: 
+               *msgs = *bytes = 0;
+               if (ids) {
+                   *ids = 0;
+                   sscanf (response, "%d %d %d",
+                           msgs++, bytes++, ids++);
+               }
+               else
+                   sscanf (response, "%d %d", msgs++, bytes++);
+               break;
+       }
+    for (;;)
+       switch (multiline ()) {
+           case NOTOK: 
+               return NOTOK;
+           case DONE: 
+               return OK;
+           case OK: 
+               break;
+       }
+#else /* NNTP */
+    return NOTOK;
+#endif /* NNTP */
+}
+
+
+int
+pop_retr (int msgno, int (*action)())
+{
+#ifndef NNTP
+    return traverse (action, "RETR %d", (targ_t) msgno);
+#else /* NNTP */
+    return traverse (action, "ARTICLE %d", (targ_t) msgno);
+#endif /* NNTP */
+}
+
+
+static int
+traverse (int (*action)(), const char *fmt, ...)
+{
+    int result;
+    va_list ap;
+    char buffer[sizeof(response)];
+
+    va_start(ap, fmt);
+    result = vcommand (fmt, ap);
+    va_end(ap);
+
+    if (result == NOTOK)
+       return NOTOK;
+    strncpy (buffer, response, sizeof(buffer));
+
+    for (;;)
+       switch (multiline ()) {
+           case NOTOK: 
+               return NOTOK;
+
+           case DONE: 
+               strncpy (response, buffer, sizeof(response));
+               return OK;
+
+           case OK: 
+               (*action) (response);
+               break;
+       }
+}
+
+
+int
+pop_dele (int msgno)
+{
+    return command ("DELE %d", msgno);
+}
+
+
+int
+pop_noop (void)
+{
+    return command ("NOOP");
+}
+
+
+#if defined(MPOP) && !defined(NNTP)
+int
+pop_last (void)
+{
+    return command ("LAST");
+}
+#endif
+
+
+int
+pop_rset (void)
+{
+    return command ("RSET");
+}
+
+
+int
+pop_top (int msgno, int lines, int (*action)())
+{
+#ifndef NNTP
+    return traverse (action, "TOP %d %d", (targ_t) msgno, (targ_t) lines);
+#else  /* NNTP */
+    return traverse (action, "HEAD %d", (targ_t) msgno);
+#endif /* NNTP */
+}
+
+
+#ifdef BPOP
+int
+pop_xtnd (int (*action)(), char *fmt, ...)
+{
+    int result;
+    va_list ap;
+    char buffer[BUFSIZ];
+
+#ifdef NNTP
+    char **ap;
+#endif
+
+    va_start(ap, fmt);
+#ifndef NNTP
+    /* needs to be fixed... va_end needs to be added */
+    snprintf (buffer, sizeof(buffer), "XTND %s", fmt);
+    result = traverse (action, buffer, a, b, c, d);
+    va_end(ap);
+    return result;
+#else /* NNTP */
+    snprintf (buffer, sizeof(buffer), fmt, a, b, c, d);
+    ap = brkstring (buffer, " ", "\n");        /* a hack, i know... */
+
+    if (!strcasecmp(ap[0], "x-bboards")) {     /* XTND "X-BBOARDS group */
+       /* most of these parameters are meaningless under NNTP. 
+        * bbc.c was modified to set AKA and LEADERS as appropriate,
+        * the rest are left blank.
+        */
+       return OK;
+    }
+    if (!strcasecmp (ap[0], "archive") && ap[1]) {
+       snprintf (xtnd_name, sizeof(xtnd_name), "%s", ap[1]);   /* save the name */
+       xtnd_last = 0;
+       xtnd_first = 1;         /* setup to fail in pop_stat */
+       return OK;
+    }
+    if (!strcasecmp (ap[0], "bboards")) {
+
+       if (ap[1]) {                    /* XTND "BBOARDS group" */
+           snprintf (xtnd_name, sizeof(xtnd_name), "%s", ap[1]);       /* save the name */
+           if (command("GROUP %s", xtnd_name) == NOTOK)
+               return NOTOK;
+
+           /* action must ignore extra args */
+           strncpy (buffer, response, sizeof(buffer));
+           ap = brkstring (response, " ", "\n");/* "211 nart first last g" */
+           xtnd_first = atoi (ap[2]);
+           xtnd_last  = atoi (ap[3]);
+
+           (*action) (buffer);         
+           return OK;
+
+       } else {                /* XTND "BBOARDS" */
+           return traverse (action, "LIST", a, b, c, d);
+       }
+    }
+    return NOTOK;      /* unknown XTND command */
+#endif /* NNTP */
+}
+#endif BPOP
+
+
+int
+pop_quit (void)
+{
+    int i;
+
+    i = command ("QUIT");
+    pop_done ();
+
+    return i;
+}
+
+
+int
+pop_done (void)
+{
+    fclose (input);
+    fclose (output);
+
+    return OK;
+}
+
+
+#if !defined(MPOP) || defined(NNTP)
+static
+#endif
+int
+command(const char *fmt, ...)
+{
+    va_list ap;
+    int result;
+
+    va_start(ap, fmt);
+    result = vcommand(fmt, ap);
+    va_end(ap);
+
+    return result;
+}
+
+
+static int
+vcommand (const char *fmt, va_list ap)
+{
+    char *cp, buffer[BUFSIZ];
+
+    vsnprintf (buffer, sizeof(buffer), fmt, ap);
+    if (poprint)
+       if (pophack) {
+           if ((cp = strchr (buffer, ' ')))
+               *cp = 0;
+           fprintf (stderr, "---> %s ********\n", buffer);
+           if (cp)
+               *cp = ' ';
+           pophack = 0;
+       }
+       else
+           fprintf (stderr, "---> %s\n", buffer);
+
+    if (putline (buffer, output) == NOTOK)
+       return NOTOK;
+
+    switch (getline (response, sizeof response, input)) {
+       case OK: 
+           if (poprint)
+               fprintf (stderr, "<--- %s\n", response);
+#ifndef NNTP
+           return (*response == '+' ? OK : NOTOK);
+#else  /* NNTP */
+           return (*response < CHAR_ERR ? OK : NOTOK);
+#endif /* NNTP */
+
+       case NOTOK: 
+       case DONE: 
+           if (poprint)            
+               fprintf (stderr, "%s\n", response);
+           return NOTOK;
+    }
+
+    return NOTOK;      /* NOTREACHED */
+}
+
+
+#if defined(MPOP) && !defined(NNTP)
+int
+multiline (void)
+#else
+static int
+multiline (void)
+#endif
+{
+    char buffer[BUFSIZ + TRMLEN];
+
+    if (getline (buffer, sizeof buffer, input) != OK)
+       return NOTOK;
+#ifdef DEBUG
+    if (poprint)
+       fprintf (stderr, "<--- %s\n", response);
+#endif DEBUG
+    if (strncmp (buffer, TRM, TRMLEN) == 0) {
+       if (buffer[TRMLEN] == 0)
+           return DONE;
+       else
+           strncpy (response, buffer + TRMLEN, sizeof(response));
+    }
+    else
+       strncpy (response, buffer, sizeof(response));
+
+    return OK;
+}
+
+
+static int
+getline (char *s, int n, FILE *iop)
+{
+    int c;
+    char *p;
+
+    p = s;
+    while (--n > 0 && (c = fgetc (iop)) != EOF)
+       if ((*p++ = c) == '\n')
+           break;
+    if (ferror (iop) && c != EOF) {
+       strncpy (response, "error on connection", sizeof(response));
+       return NOTOK;
+    }
+    if (c == EOF && p == s) {
+       strncpy (response, "connection closed by foreign host", sizeof(response));
+       return DONE;
+    }
+    *p = 0;
+    if (*--p == '\n')
+       *p = 0;
+    if (*--p == '\r')
+       *p = 0;
+
+    return OK;
+}
+
+
+static int
+putline (char *s, FILE *iop)
+{
+    fprintf (iop, "%s\r\n", s);
+    fflush (iop);
+    if (ferror (iop)) {
+       strncpy (response, "lost connection", sizeof(response));
+       return NOTOK;
+    }
+
+    return OK;
+}
diff --git a/uip/post.c b/uip/post.c
new file mode 100644 (file)
index 0000000..4602dc0
--- /dev/null
@@ -0,0 +1,1965 @@
+
+/*
+ * post.c -- enter messages into the mail transport system
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+#include <h/signals.h>
+#include <h/addrsbr.h>
+#include <h/aliasbr.h>
+#include <h/dropsbr.h>
+#include <h/mime.h>
+
+#include <zotnet/tws/tws.h>
+#include <zotnet/mts/mts.h>
+
+#include <errno.h>
+#include <setjmp.h>
+#include <signal.h>
+
+#ifdef MMDFMTS
+# include <mts/mmdf/util.h>
+# include <mts/mmdf/mmdf.h>
+#endif
+
+/*
+ * Currently smtp and sendmail use
+ * the same interface for posting.
+ */
+#ifdef SMTPMTS
+# define SENDMTS
+#endif
+
+#ifdef SENDMTS
+# include <mts/smtp/smtp.h>
+#endif
+
+#ifndef        MMDFMTS
+# define uptolow(c) ((isalpha(c) && isupper (c)) ? tolower (c) : (c))
+#endif
+
+#define FCCS           10      /* max number of fccs allowed */
+
+static struct swit switches[] = {
+#define        ALIASW                    0
+    { "alias aliasfile", 0 },
+#define        CHKSW                     1
+    { "check", -5 },                   /* interface from whom */
+#define        NCHKSW                    2
+    { "nocheck", -7 },                 /* interface from whom */
+#define        DEBUGSW                   3
+    { "debug", -5 },
+#define        DISTSW                    4
+    { "dist", -4 },                    /* interface from dist */
+#define        FILTSW                    5
+    { "filter filterfile", 0 },
+#define        NFILTSW                   6
+    { "nofilter", 0 },
+#define        FRMTSW                    7
+    { "format", 0 },
+#define        NFRMTSW                   8
+    { "noformat", 0 },
+#define        LIBSW                     9
+    { "library directory", -7 },       /* interface from send, whom */
+#define        MIMESW                   10
+    { "mime", 0 },
+#define        NMIMESW                  11
+    { "nomime", 0 },
+#define        MSGDSW                   12
+    { "msgid", 0 },
+#define        NMSGDSW                  13
+    { "nomsgid", 0 },
+#define        VERBSW                   14
+    { "verbose", 0 },
+#define        NVERBSW                  15
+    { "noverbose", 0 },
+#define        WATCSW                   16
+    { "watch", 0 },
+#define        NWATCSW                  17
+    { "nowatch", 0 },
+#define        WHOMSW                   18
+    { "whom", -4 },                    /* interface from whom */
+#define        WIDTHSW                  19
+    { "width columns", 0 },
+#define VERSIONSW                20
+    { "version", 0 },
+#define        HELPSW                   21
+    { "help", 4 },
+#define BITSTUFFSW               22
+    { "dashstuffing", -12 },           /* should we dashstuff BCC messages? */
+#define NBITSTUFFSW              23
+    { "nodashstuffing", -14 },
+#define        MAILSW                   24
+    { "mail", -4 },                    /* specify MAIL smtp mode */
+#define        SAMLSW                   25
+    { "saml", -4 },                    /* specify SAML smtp mode */
+#define        SENDSW                   26
+    { "send", -4 },                    /* specify SEND smtp mode */
+#define        SOMLSW                   27
+    { "soml", -4 },                    /* specify SOML smtp mode */
+#define        ANNOSW                   28
+    { "idanno number", -6 },           /* interface from send    */
+#define        DLVRSW                   29
+    { "deliver address-list", -7 },
+#define        CLIESW                   30
+    { "client host", -6 },
+#define        SERVSW                   31
+    { "server host", -6 },             /* specify alternate SMTP server */
+#define        SNOOPSW                  32
+    { "snoop", -5 },                   /* snoop the SMTP transaction */
+#define        FILLSW                   33
+    { "fill-in file", -7 },
+#define        FILLUSW                  34
+    { "fill-up", -7 },
+#define        PARTSW                   35
+    { "partno", -6 },
+#define        QUEUESW                  36
+    { "queued", -6 },
+    { NULL, 0 }
+};
+
+
+struct headers {
+    char *value;
+    unsigned int flags;
+    unsigned int set;
+};
+
+/*
+ * flags for headers->flags
+ */
+#define        HNOP  0x0000            /* just used to keep .set around          */
+#define        HBAD  0x0001            /* bad header - don't let it through      */
+#define        HADR  0x0002            /* header has an address field            */
+#define        HSUB  0x0004            /* Subject: header                        */
+#define        HTRY  0x0008            /* try to send to addrs on header         */
+#define        HBCC  0x0010            /* don't output this header               */
+#define        HMNG  0x0020            /* munge this header                      */
+#define        HNGR  0x0040            /* no groups allowed in this header       */
+#define        HFCC  0x0080            /* FCC: type header                       */
+#define        HNIL  0x0100            /* okay for this header not to have addrs */
+#define        HIGN  0x0200            /* ignore this header                     */
+#define        HDCC  0x0400            /* another undocumented feature           */
+
+/*
+ * flags for headers->set
+ */
+#define        MFRM  0x0001            /* we've seen a From:        */
+#define        MDAT  0x0002            /* we've seen a Date:        */
+#define        MRFM  0x0004            /* we've seen a Resent-From: */
+#define        MVIS  0x0008            /* we've seen sighted addrs  */
+#define        MINV  0x0010            /* we've seen blind addrs    */
+
+
+static struct headers NHeaders[] = {
+    { "Return-Path", HBAD,                0 },
+    { "Received",    HBAD,                0 },
+    { "Reply-To",    HADR|HNGR,           0 },
+    { "From",        HADR|HNGR,           MFRM },
+    { "Sender",      HADR|HBAD,           0 },
+    { "Date",        HBAD,                0 },
+    { "Subject",     HSUB,                0 },
+    { "To",          HADR|HTRY,           MVIS },
+    { "cc",          HADR|HTRY,           MVIS },
+    { "Bcc",         HADR|HTRY|HBCC|HNIL, MINV },
+    { "Dcc",         HADR|HTRY|HDCC|HNIL, MVIS },      /* sorta cc & bcc combined */
+    { "Message-ID",  HBAD,                0 },
+    { "Fcc",         HFCC,                0 },
+    { NULL,          0,                   0 }
+};
+
+static struct headers RHeaders[] = {
+    { "Resent-Reply-To",   HADR|HNGR,           0 },
+    { "Resent-From",       HADR|HNGR,           MRFM },
+    { "Resent-Sender",     HADR|HBAD,           0 },
+    { "Resent-Date",       HBAD,                0 },
+    { "Resent-Subject",    HSUB,                0 },
+    { "Resent-To",         HADR|HTRY,           MVIS },
+    { "Resent-cc",         HADR|HTRY,           MVIS },
+    { "Resent-Bcc",        HADR|HTRY|HBCC,      MINV },
+    { "Resent-Message-ID", HBAD,                0 },
+    { "Resent-Fcc",        HFCC,                0 },
+    { "Reply-To",          HADR,                0 },
+    { "From",              HADR|HNGR,           MFRM },
+#ifdef MMDFI
+    { "Sender",            HADR|HNGR|HMNG,      0 },
+#else
+    { "Sender",            HADR|HNGR,           0 },
+#endif
+    { "Date",              HNOP,                MDAT },
+    { "To",                HADR|HNIL,           0 },
+    { "cc",                HADR|HNIL,           0 },
+    { "Bcc",               HADR|HTRY|HBCC|HNIL, 0 },
+    { "Fcc",               HIGN,                0 },
+    { NULL,                0,                   0 }
+};
+
+static short fccind = 0;       /* index into fccfold[] */
+static short outputlinelen = OUTPUTLINELEN;
+
+static int pfd = NOTOK;                /* fd to write annotation list to        */
+static uid_t myuid= -1;                /* my user id                            */
+static gid_t mygid= -1;                /* my group id                           */
+static int recipients = 0;     /* how many people will get a copy       */
+static int unkadr = 0;         /* how many of those were unknown        */
+static int badadr = 0;         /* number of bad addrs                   */
+static int badmsg = 0;         /* message has bad semantics             */
+static int verbose = 0;                /* spell it out                          */
+static int format = 1;         /* format addresses                      */
+static int mime = 0;           /* use MIME-style encapsulations for Bcc */
+static int msgid = 0;          /* add msgid                             */
+static int debug = 0;          /* debugging post                        */
+static int watch = 0;          /* watch the delivery process            */
+static int whomsw = 0;         /* we are whom not post                  */
+static int checksw = 0;                /* whom -check                           */
+static int linepos=0;          /* putadr()'s position on the line       */
+static int nameoutput=0;       /* putadr() has output header name       */
+
+static unsigned msgflags = 0;  /* what we've seen */
+
+#define        NORMAL 0
+#define        RESENT 1
+static int msgstate = NORMAL;
+
+static time_t tclock = 0;      /* the time we started (more or less) */
+
+static SIGNAL_HANDLER hstat, istat, qstat, tstat;
+
+static char tmpfil[BUFSIZ];
+static char bccfil[BUFSIZ];
+
+static char from[BUFSIZ];      /* my network address            */
+static char signature[BUFSIZ]; /* my signature                  */
+static char *filter = NULL;    /* the filter for BCC'ing        */
+static char *subject = NULL;   /* the subject field for BCC'ing */
+static char *fccfold[FCCS];    /* foldernames for FCC'ing       */
+
+static struct headers  *hdrtab;        /* table for the message we're doing */
+
+static struct mailname localaddrs={NULL};      /* local addrs     */
+static struct mailname netaddrs={NULL};                /* network addrs   */
+static struct mailname uuaddrs={NULL};         /* uucp addrs      */
+static struct mailname tmpaddrs={NULL};                /* temporary queue */
+
+#ifdef MMDFMTS
+static char *submitmode = "m";         /* deliver to mailbox only    */
+static char submitopts[6] = "vl";      /* initial options for submit */
+#endif /* MMDFMTS */
+
+#ifdef SENDMTS
+static int snoop      = 0;
+static int smtpmode   = S_MAIL;
+static char *clientsw = NULL;
+static char *serversw = NULL;
+
+extern struct smtp sm_reply;
+#endif /* SENDMTS */
+
+static char prefix[] = "----- =_aaaaaaaaaa";
+
+static int fill_up = 0;
+static char *fill_in = NULL;
+static char *partno = NULL;
+static int queued = 0;
+
+/*
+ * static prototypes
+ */
+static void putfmt (char *, char *, FILE *);
+static void start_headers (void);
+static void finish_headers (FILE *);
+static int get_header (char *, struct headers *);
+static int putadr (char *, char *, struct mailname *, FILE *, unsigned int);
+static void putgrp (char *, char *, FILE *, unsigned int);
+static int insert (struct mailname *);
+static void pl (void);
+static void anno (void);
+static int annoaux (struct mailname *);
+static void insert_fcc (struct headers *, char *);
+static void make_bcc_file (int);
+static void verify_all_addresses (int);
+static void chkadr (void);
+static void sigon (void);
+static void sigoff (void);
+static void p_refile (char *);
+static void fcc (char *, char *);
+static void die (char *, char *, ...);
+static void post (char *, int, int);
+static void do_text (char *file, int fd);
+static void do_an_address (struct mailname *, int);
+static void do_addresses (int, int);
+static int find_prefix (void);
+
+
+int
+main (int argc, char **argv)
+{
+    int state, compnum, dashstuff = 0;
+    char *cp, *msg = NULL, **argp, **arguments;
+    char buf[BUFSIZ], name[NAMESZ];
+    FILE *in, *out;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* foil search of user profile/context */
+    if (context_foil (NULL) == -1)
+       done (1);
+
+    mts_init (invo_name);
+    arguments = getarguments (invo_name, argc, argv, 0);
+    argp = arguments;
+
+#if defined(MMDFMTS) && defined(MMDFII)
+    mmdf_init (invo_name);
+#endif /* MMDFMTS and MMDFII */
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown", cp);
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf), "%s [switches] file", invo_name);
+                   print_help (buf, switches, 0);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case LIBSW:
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   /* create a minimal context */
+                   if (context_foil (cp) == -1)
+                       done (1);
+                   continue;
+
+               case ALIASW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   if ((state = alias (cp)) != AK_OK)
+                       adios (NULL, "aliasing error in %s - %s",
+                               cp, akerror (state));
+                   continue;
+
+               case CHKSW: 
+                   checksw++;
+                   continue;
+               case NCHKSW: 
+                   checksw = 0;
+                   continue;
+
+               case DEBUGSW: 
+                   debug++;
+                   continue;
+
+               case DISTSW:
+                   msgstate = RESENT;
+                   continue;
+
+               case FILTSW:
+                   if (!(filter = *argp++) || *filter == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   mime = 0;
+                   continue;
+               case NFILTSW:
+                   filter = NULL;
+                   continue;
+               
+               case FRMTSW: 
+                   format++;
+                   continue;
+               case NFRMTSW: 
+                   format = 0;
+                   continue;
+
+               case BITSTUFFSW:
+                   dashstuff = 1;
+                   continue;
+               case NBITSTUFFSW:
+                   dashstuff = -1;
+                   continue;
+
+               case MIMESW:
+                   mime++;
+                   filter = NULL;
+                   continue;
+               case NMIMESW: 
+                   mime = 0;
+                   continue;
+
+               case MSGDSW: 
+                   msgid++;
+                   continue;
+               case NMSGDSW: 
+                   msgid = 0;
+                   continue;
+
+               case VERBSW: 
+                   verbose++;
+                   continue;
+               case NVERBSW: 
+                   verbose = 0;
+                   continue;
+
+               case WATCSW: 
+                   watch++;
+                   continue;
+               case NWATCSW: 
+                   watch = 0;
+                   continue;
+
+               case WHOMSW: 
+                   whomsw++;
+                   continue;
+
+               case WIDTHSW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   if ((outputlinelen = atoi (cp)) < 10)
+                       adios (NULL, "impossible width %d", outputlinelen);
+                   continue;
+
+               case ANNOSW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   if ((pfd = atoi (cp)) <= 2)
+                       adios (NULL, "bad argument %s %s", argp[-2], cp);
+                   continue;
+
+#ifdef MMDFMTS
+               case MAILSW:
+                   submitmode = "m";
+                   continue;
+               case SOMLSW:    /* for right now, sigh... */
+               case SAMLSW:
+                   submitmode = "b";
+                   continue;
+               case SENDSW:
+                   submitmode = "y";
+                   continue;
+#endif /* MMDFMTS */
+
+               case DLVRSW:
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+
+#ifndef        SENDMTS
+               case CLIESW:
+               case SERVSW:
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+
+               case SNOOPSW:
+                   continue;
+#else /* SENDMTS */
+               case MAILSW:
+                   smtpmode = S_MAIL;
+                   continue;
+               case SAMLSW:
+                   smtpmode = S_SAML;
+                   continue;
+               case SOMLSW:
+                   smtpmode = S_SOML;
+                   continue;
+               case SENDSW:
+                   smtpmode = S_SEND;
+                   continue;
+               case CLIESW:
+                   if (!(clientsw = *argp++) || *clientsw == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+               case SERVSW:
+                   if (!(serversw = *argp++) || *serversw == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+               case SNOOPSW:
+                   snoop++;
+                   continue;
+#endif /* SENDMTS */
+
+               case FILLSW:
+                   if (!(fill_in = *argp++) || *fill_in == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+               case FILLUSW:
+                   fill_up++;
+                   continue;
+               case PARTSW:
+                   if (!(partno = *argp++) || *partno == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+
+               case QUEUESW:
+                   queued++;
+                   continue;
+           }
+       }
+       if (msg)
+           adios (NULL, "only one message at a time!");
+       else
+           msg = cp;
+    }
+
+    alias (AliasFile);
+
+    if (!msg)
+       adios (NULL, "usage: %s [switches] file", invo_name);
+
+    if (outputlinelen < 10)
+       adios (NULL, "impossible width %d", outputlinelen);
+
+    if ((in = fopen (msg, "r")) == NULL)
+       adios (msg, "unable to open");
+
+    start_headers ();
+    if (debug) {
+       verbose++;
+       discard (out = stdout); /* XXX: reference discard() to help loader */
+    } else {
+       if (whomsw) {
+           if ((out = fopen (fill_in ? fill_in : "/dev/null", "w")) == NULL)
+               adios ("/dev/null", "unable to open");
+       } else {
+           strncpy (tmpfil, m_scratch ("", m_maildir (invo_name)),
+               sizeof(tmpfil));
+           if ((out = fopen (tmpfil, "w")) == NULL) {
+               strncpy (tmpfil, m_tmpfil (invo_name), sizeof(tmpfil));
+               if ((out = fopen (tmpfil, "w")) == NULL)
+                   adios (tmpfil, "unable to create");
+           }
+           chmod (tmpfil, 0600);
+       }
+    }
+
+    hdrtab = msgstate == NORMAL ? NHeaders : RHeaders;
+
+    for (compnum = 1, state = FLD;;) {
+       switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
+           case FLD: 
+           case FLDEOF: 
+           case FLDPLUS: 
+               compnum++;
+               cp = add (buf, NULL);
+               while (state == FLDPLUS) {
+                   state = m_getfld (state, name, buf, sizeof(buf), in);
+                   cp = add (buf, cp);
+               }
+               putfmt (name, cp, out);
+               free (cp);
+               if (state != FLDEOF)
+                   continue;
+               finish_headers (out);
+               break;
+
+           case BODY: 
+           case BODYEOF: 
+               finish_headers (out);
+               if (whomsw && !fill_in)
+                   break;
+               fprintf (out, "\n%s", buf);
+               while (state == BODY) {
+                   state = m_getfld (state, name, buf, sizeof(buf), in);
+                   fputs (buf, out);
+               }
+               break;
+
+           case FILEEOF: 
+               finish_headers (out);
+               break;
+
+           case LENERR: 
+           case FMTERR: 
+               adios (NULL, "message format error in component #%d", compnum);
+
+           default: 
+               adios (NULL, "getfld() returned %d", state);
+       }
+       break;
+    }
+
+    if (pfd != NOTOK)
+       anno ();
+    fclose (in);
+
+    if (debug) {
+       pl ();
+       done (0);
+    } else {
+       fclose (out);
+    }
+
+    /* If we are doing a "whom" check */
+    if (whomsw) {
+       if (!fill_up)
+           verify_all_addresses (1);
+       done (0);
+    }
+
+#ifdef MMDFMTS
+    strcat (submitopts, submitmode);
+    if (watch)
+       strcat (submitopts, "nw");
+#endif /* MMDFMTS */
+
+    if (msgflags & MINV) {
+       make_bcc_file (dashstuff);
+       if (msgflags & MVIS) {
+           verify_all_addresses (verbose);
+           post (tmpfil, 0, verbose);
+       }
+       post (bccfil, 1, verbose);
+       unlink (bccfil);
+    } else {
+       post (tmpfil, 0, isatty (1));
+    }
+
+    p_refile (tmpfil);
+    unlink (tmpfil);
+
+    if (verbose)
+       printf (partno ? "Partial Message #%s Processed\n" : "Message Processed\n",
+               partno);
+    done (0);
+}
+
+
+/*
+ * DRAFT GENERATION
+ */
+
+static void
+putfmt (char *name, char *str, FILE *out)
+{
+    int count, grp, i, keep;
+    char *cp, *pp, *qp;
+    char namep[BUFSIZ];
+    struct mailname *mp, *np;
+    struct headers *hdr;
+
+    while (*str == ' ' || *str == '\t')
+       str++;
+
+    if (msgstate == NORMAL && uprf (name, "resent")) {
+       advise (NULL, "illegal header line -- %s:", name);
+       badmsg++;
+       return;
+    }
+
+    if ((i = get_header (name, hdrtab)) == NOTOK) {
+       fprintf (out, "%s: %s", name, str);
+       return;
+    }
+
+    hdr = &hdrtab[i];
+    if (hdr->flags & HIGN) {
+       if (fill_in)
+           fprintf (out, "%s: %s", name, str);
+       return;
+    }
+    if (hdr->flags & HBAD) {
+       if (fill_in)
+           fprintf (out, "%s: %s", name, str);
+       else {
+           advise (NULL, "illegal header line -- %s:", name);
+           badmsg++;
+       }
+       return;
+    }
+    msgflags |= (hdr->set & ~(MVIS | MINV));
+
+    if (hdr->flags & HSUB)
+       subject = subject ? add (str, add ("\t", subject)) : getcpy (str);
+    if (hdr->flags & HFCC) {
+       if (fill_in) {
+           fprintf (out, "%s: %s", name, str);
+           return;
+       }
+
+       if ((cp = strrchr(str, '\n')))
+           *cp = 0;
+       for (cp = pp = str; cp = strchr(pp, ','); pp = cp) {
+           *cp++ = 0;
+           insert_fcc (hdr, pp);
+       }
+       insert_fcc (hdr, pp);
+       return;
+    }
+
+    if (!(hdr->flags & HADR)) {
+       fprintf (out, "%s: %s", name, str);
+       return;
+    }
+
+    tmpaddrs.m_next = NULL;
+    for (count = 0; cp = getname (str); count++)
+       if ((mp = getm (cp, NULL, 0, AD_HOST, NULL))) {
+           if (tmpaddrs.m_next)
+               np->m_next = mp;
+           else
+               tmpaddrs.m_next = mp;
+           np = mp;
+       }
+       else
+           if (hdr->flags & HTRY)
+               badadr++;
+           else
+               badmsg++;
+
+    if (count < 1) {
+       if (hdr->flags & HNIL)
+           fprintf (out, "%s: %s", name, str);
+       else {
+#ifdef notdef
+           advise (NULL, "%s: field requires at least one address", name);
+           badmsg++;
+#endif /* notdef */
+       }
+       return;
+    }
+
+    nameoutput = linepos = 0;
+    snprintf (namep, sizeof(namep), "%s%s",
+               !fill_in && (hdr->flags & HMNG) ? "Original-" : "", name);
+
+    for (grp = 0, mp = tmpaddrs.m_next; mp; mp = np)
+       if (mp->m_nohost) {     /* also used to test (hdr->flags & HTRY) */
+           pp = akvalue (mp->m_mbox);
+           qp = akvisible () ? mp->m_mbox : "";
+           np = mp;
+           if (np->m_gname)
+               putgrp (namep, np->m_gname, out, hdr->flags);
+           while ((cp = getname (pp))) {
+               if (!(mp = getm (cp, NULL, 0, AD_HOST, NULL))) {
+                   badadr++;
+                   continue;
+               }
+               if (hdr->flags & HBCC)
+                   mp->m_bcc++;
+               if (np->m_ingrp)
+                   mp->m_ingrp = np->m_ingrp;
+               else
+                   if (mp->m_gname)
+                       putgrp (namep, mp->m_gname, out, hdr->flags);
+               if (mp->m_ingrp)
+                   grp++;
+               if (putadr (namep, qp, mp, out, hdr->flags))
+                   msgflags |= (hdr->set & (MVIS | MINV));
+               else
+                   mnfree (mp);
+           }
+           mp = np;
+           np = np->m_next;
+           mnfree (mp);
+       }
+       else {
+           if (hdr->flags & HBCC)
+               mp->m_bcc++;
+           if (mp->m_gname)
+               putgrp (namep, mp->m_gname, out, hdr->flags);
+           if (mp->m_ingrp)
+               grp++;
+           keep = putadr (namep, "", mp, out, hdr->flags);
+           np = mp->m_next;
+           if (keep) {
+               mp->m_next = NULL;
+               msgflags |= (hdr->set & (MVIS | MINV));
+           }
+           else
+               mnfree (mp);
+       }
+
+    if (grp > 0 && (hdr->flags & HNGR)) {
+       advise (NULL, "%s: field does not allow groups", name);
+       badmsg++;
+    }
+    if (linepos) {
+       if (fill_in && grp > 0)
+           putc (';', out);
+       putc ('\n', out);
+    }
+}
+
+
+static void
+start_headers (void)
+{
+    char  *cp;
+    char myhost[BUFSIZ], sigbuf[BUFSIZ];
+    struct mailname *mp;
+
+    myuid = getuid ();
+    mygid = getgid ();
+    time (&tclock);
+
+    strncpy (from, adrsprintf (NULL, NULL), sizeof(from));
+    strncpy (myhost, LocalName (), sizeof(myhost));
+
+    for (cp = myhost; *cp; cp++)
+       *cp = uptolow (*cp);
+
+    if ((cp = getfullname ()) && *cp) {
+       strncpy (sigbuf, cp, sizeof(sigbuf));
+       snprintf (signature, sizeof(signature), "%s <%s>",
+               sigbuf, adrsprintf (NULL, NULL));
+       if ((cp = getname (signature)) == NULL)
+           adios (NULL, "getname () failed -- you lose extraordinarily big");
+       if ((mp = getm (cp, NULL, 0, AD_HOST, NULL)) == NULL)
+           adios (NULL, "bad signature '%s'", sigbuf);
+       mnfree (mp);
+       while (getname (""))
+           continue;
+    } else {
+       strncpy (signature, adrsprintf (NULL, NULL), sizeof(signature));
+    }
+}
+
+
+/*
+ * Now that we've outputted the header fields in the draft
+ * message, we will now output any remaining header fields
+ * that we need to add/create.
+ */
+
+static void
+finish_headers (FILE *out)
+{
+    switch (msgstate) {
+       case NORMAL: 
+           if (whomsw && !fill_up)
+               break;
+
+           fprintf (out, "Date: %s\n", dtime (&tclock, 0));
+           if (msgid)
+               fprintf (out, "Message-ID: <%d.%ld@%s>\n",
+                       (int) getpid (), tclock, LocalName ());
+           if (msgflags & MFRM)
+               fprintf (out, "Sender: %s\n", from);
+           else
+               fprintf (out, "From: %s\n", signature);
+           if (whomsw)
+               break;
+
+           if (!(msgflags & MVIS))
+               fprintf (out, "Bcc: Blind Distribution List: ;\n");
+           break;
+
+       case RESENT: 
+           if (!(msgflags & MDAT)) {
+               advise (NULL, "message has no Date: header");
+               badmsg++;
+           }
+           if (!(msgflags & MFRM)) {
+               advise (NULL, "message has no From: header");
+               badmsg++;
+           }
+           if (whomsw && !fill_up)
+               break;
+
+#ifdef MMDFI                   /* sigh */
+           fprintf (out, "Sender: %s\n", from);
+#endif /* MMDFI */
+
+           fprintf (out, "Resent-Date: %s\n", dtime (&tclock, 0));
+           if (msgid)
+               fprintf (out, "Resent-Message-ID: <%d.%ld@%s>\n",
+                       (int) getpid (), tclock, LocalName ());
+           if (msgflags & MRFM)
+               fprintf (out, "Resent-Sender: %s\n", from);
+           else
+               fprintf (out, "Resent-From: %s\n", signature);
+           if (whomsw)
+               break;
+           if (!(msgflags & MVIS))
+               fprintf (out, "Resent-Bcc: Blind Re-Distribution List: ;\n");
+           break;
+    }
+
+    if (badmsg)
+       adios (NULL, "re-format message and try again");
+    if (!recipients)
+       adios (NULL, "no addressees");
+}
+
+
+static int
+get_header (char *header, struct headers *table)
+{
+    struct headers *h;
+
+    for (h = table; h->value; h++)
+       if (!strcasecmp (header, h->value))
+           return (h - table);
+
+    return NOTOK;
+}
+
+
+static int
+putadr (char *name, char *aka, struct mailname *mp, FILE *out, unsigned int flags)
+{
+    int len;
+    char *cp;
+    char buffer[BUFSIZ];
+
+    if (mp->m_mbox == NULL || ((flags & HTRY) && !insert (mp)))
+       return 0;
+    if (!fill_in && (flags & (HBCC | HDCC)) || mp->m_ingrp)
+       return 1;
+
+    if (!nameoutput) {
+       fprintf (out, "%s: ", name);
+       linepos += (nameoutput = strlen (name) + 2);
+    }
+
+    if (*aka && mp->m_type != UUCPHOST && !mp->m_pers)
+       mp->m_pers = getcpy (aka);
+    if (format) {
+       if (mp->m_gname && !fill_in) {
+           snprintf (buffer, sizeof(buffer), "%s;", mp->m_gname);
+           cp = buffer;
+       } else {
+           cp = adrformat (mp);
+       }
+    } else {
+       cp = mp->m_text;
+    }
+    len = strlen (cp);
+
+    if (linepos != nameoutput)
+       if (len + linepos + 2 > outputlinelen)
+           fprintf (out, ",\n%*s", linepos = nameoutput, "");
+       else {
+           fputs (", ", out);
+           linepos += 2;
+       }
+
+    fputs (cp, out);
+    linepos += len;
+
+    return (flags & HTRY);
+}
+
+
+static void
+putgrp (char *name, char *group, FILE *out, unsigned int flags)
+{
+    int len;
+    char *cp;
+
+    if (!fill_in && (flags & HBCC))
+       return;
+
+    if (!nameoutput) {
+       fprintf (out, "%s: ", name);
+       linepos += (nameoutput = strlen (name) + 2);
+       if (fill_in)
+           linepos -= strlen (group);
+    }
+
+    cp = fill_in ? group : concat (group, ";", NULL);
+    len = strlen (cp);
+
+    if (linepos > nameoutput)
+       if (len + linepos + 2 > outputlinelen) {
+           fprintf (out, ",\n%*s", nameoutput, "");
+           linepos = nameoutput;
+       }
+       else {
+           fputs (", ", out);
+           linepos += 2;
+       }
+
+    fputs (cp, out);
+    linepos += len;
+}
+
+
+static int
+insert (struct mailname *np)
+{
+    struct mailname *mp;
+
+    if (np->m_mbox == NULL)
+       return 0;
+
+    for (mp = np->m_type == LOCALHOST ? &localaddrs
+           : np->m_type == UUCPHOST ? &uuaddrs
+           : &netaddrs;
+           mp->m_next;
+           mp = mp->m_next)
+       if (!strcasecmp (np->m_host, mp->m_next->m_host)
+               && !strcasecmp (np->m_mbox, mp->m_next->m_mbox)
+               && np->m_bcc == mp->m_next->m_bcc)
+           return 0;
+
+    mp->m_next = np;
+    recipients++;
+    return 1;
+}
+
+
+static void
+pl (void)
+{
+    int i;
+    struct mailname *mp;
+
+    printf ("-------\n\t-- Addresses --\nlocal:\t");
+    for (mp = localaddrs.m_next; mp; mp = mp->m_next)
+       printf ("%s%s%s", mp->m_mbox,
+               mp->m_bcc ? "[BCC]" : "",
+               mp->m_next ? ",\n\t" : "");
+
+    printf ("\nnet:\t");
+    for (mp = netaddrs.m_next; mp; mp = mp->m_next)
+       printf ("%s%s@%s%s%s", mp->m_path ? mp->m_path : "",
+               mp->m_mbox, mp->m_host,
+               mp->m_bcc ? "[BCC]" : "",
+               mp->m_next ? ",\n\t" : "");
+
+    printf ("\nuucp:\t");
+    for (mp = uuaddrs.m_next; mp; mp = mp->m_next)
+       printf ("%s!%s%s%s", mp->m_host, mp->m_mbox,
+               mp->m_bcc ? "[BCC]" : "",
+               mp->m_next ? ",\n\t" : "");
+
+    printf ("\n\t-- Folder Copies --\nfcc:\t");
+    for (i = 0; i < fccind; i++)
+       printf ("%s%s", fccfold[i], i + 1 < fccind ? ",\n\t" : "");
+    printf ("\n");
+}
+
+
+static void
+anno (void)
+{
+    struct mailname *mp;
+
+    for (mp = localaddrs.m_next; mp; mp = mp->m_next)
+       if (annoaux (mp) == NOTOK)
+           goto oops;
+
+    for (mp = netaddrs.m_next; mp; mp = mp->m_next)
+       if (annoaux (mp) == NOTOK)
+           goto oops;
+
+    for (mp = uuaddrs.m_next; mp; mp = mp->m_next)
+       if (annoaux (mp) == NOTOK)
+           break;
+
+oops: ;
+    close (pfd);
+    pfd = NOTOK;
+}
+
+
+static int
+annoaux (struct mailname *mp)
+{
+    int i;
+    char buffer[BUFSIZ];
+
+    snprintf (buffer, sizeof(buffer), "%s\n", adrformat (mp));
+    i = strlen (buffer);
+
+    return (write (pfd, buffer, i) == i ? OK : NOTOK);
+}
+
+
+static void
+insert_fcc (struct headers *hdr, char *pp)
+{
+    char *cp;
+
+    for (cp = pp; isspace (*cp); cp++)
+       continue;
+    for (pp += strlen (pp) - 1; pp > cp && isspace (*pp); pp--)
+       continue;
+    if (pp >= cp)
+       *++pp = 0;
+    if (*cp == 0)
+       return;
+
+    if (fccind >= FCCS)
+       adios (NULL, "too many %ss", hdr->value);
+    fccfold[fccind++] = getcpy (cp);
+}
+
+/*
+ * BCC GENERATION
+ */
+
+static void
+make_bcc_file (int dashstuff)
+{
+    int fd, i;
+    pid_t child_id;
+    char *vec[6];
+    FILE *out;
+
+    strncpy (bccfil, m_tmpfil ("bccs"), sizeof(bccfil));
+    if ((out = fopen (bccfil, "w")) == NULL)
+       adios (bccfil, "unable to create");
+    chmod (bccfil, 0600);
+
+    fprintf (out, "Date: %s\n", dtime (&tclock, 0));
+    if (msgid)
+       fprintf (out, "Message-ID: <%d.%ld@%s>\n",
+               (int) getpid (), tclock, LocalName ());
+    fprintf (out, "From: %s\n", signature);
+    if (subject)
+       fprintf (out, "Subject: %s", subject);
+    fprintf (out, "BCC:\n");
+
+    /*
+     * Use MIME encapsulation for Bcc messages
+     */
+    if (mime) {
+       char *cp;
+
+       /*
+        * Check if any lines in the message clash with the
+        * prefix for the MIME multipart separator.  If there
+        * is a clash, increment one of the letters in the
+        * prefix and check again.
+        */
+       if ((cp = strchr(prefix, 'a')) == NULL)
+           adios (NULL, "lost prefix start");
+       while (find_prefix () == NOTOK) {
+           if (*cp < 'z')
+               (*cp)++;
+           else
+               if (*++cp == 0)
+                   adios (NULL, "can't find a unique delimiter string");
+               else
+                   (*cp)++;
+       }
+
+       fprintf (out, "%s: %s\n%s: multipart/digest; boundary=\"",
+                VRSN_FIELD, VRSN_VALUE, TYPE_FIELD);
+       fprintf (out, "%s\"\n\n--%s\n\n", prefix, prefix);
+    } else {
+       fprintf (out, "\n------- Blind-Carbon-Copy\n\n");
+    }
+
+    fflush (out);
+
+    /*
+     * Do mhl filtering of Bcc messages instead
+     * of MIME encapsulation.
+     */
+    if (filter != NULL) {
+       vec[0] = r1bindex (mhlproc, '/');
+
+       for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
+           sleep (5);
+       switch (child_id) {
+           case NOTOK: 
+               adios ("fork", "unable to");
+
+           case OK: 
+               dup2 (fileno (out), 1);
+
+               i = 1;
+               vec[i++] = "-forward";
+               vec[i++] = "-form";
+               vec[i++] = filter;
+               vec[i++] = tmpfil;
+
+               /* was the flag -[no]dashstuffing specified? */
+               if (dashstuff > 0)
+                   vec[i++] = "-dashstuffing";
+               else if (dashstuff < 0)
+                   vec[i++] = "-nodashstuffing";
+               vec[i] = NULL;
+
+               execvp (mhlproc, vec);
+               fprintf (stderr, "unable to exec ");
+               perror (mhlproc);
+               _exit (-1);
+
+           default: 
+               pidXwait (child_id, mhlproc);
+               break;
+       }
+    } else {
+       if ((fd = open (tmpfil, O_RDONLY)) == NOTOK)
+           adios (tmpfil, "unable to re-open");
+
+       /*
+        * If using MIME encapsulation, or if the -nodashstuffing
+        * flag was given, then just copy message.  Else do
+        * RFC934 quoting (dashstuffing).
+        */
+       if (mime || dashstuff < 0)
+           cpydata (fd, fileno (out), tmpfil, bccfil);
+       else
+           cpydgst (fd, fileno (out), tmpfil, bccfil);
+       close (fd);
+    }
+
+    fseek (out, 0L, SEEK_END);
+    if (mime)
+       fprintf (out, "\n--%s--\n", prefix);
+    else
+       fprintf (out, "\n------- End of Blind-Carbon-Copy\n");
+    fclose (out);
+}
+
+
+/*
+ * Scan message to check if any lines clash with
+ * the prefix of the MIME multipart separator.
+ */
+
+static int
+find_prefix (void)
+{
+    int        len, result;
+    char buffer[BUFSIZ];
+    FILE *in;
+
+    if ((in = fopen (tmpfil, "r")) == NULL)
+       adios (tmpfil, "unable to re-open");
+
+    len = strlen (prefix);
+
+    result = OK;
+    while (fgets (buffer, sizeof(buffer) - 1, in))
+       if (buffer[0] == '-' && buffer[1] == '-') {
+           char *cp;
+
+           for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
+               if (!isspace (*cp))
+                   break;
+           *++cp = '\0';
+           if (strcmp (buffer + 2, prefix) == 0) {
+               result = NOTOK;
+               break;
+           }
+       }
+
+    fclose (in);
+    return result;
+}
+
+
+#define        plural(x) (x == 1 ? "" : "s")
+
+static void
+chkadr (void)
+{
+    if (badadr && unkadr)
+       die (NULL, "%d address%s unparsable, %d addressee%s undeliverable",
+               badadr, plural (badadr), unkadr, plural (badadr));
+    if (badadr)
+       die (NULL, "%d address%s unparsable", badadr, plural (badadr));
+    if (unkadr)
+       die (NULL, "%d addressee%s undeliverable", unkadr, plural (unkadr));
+}
+
+
+static void
+do_addresses (int bccque, int talk)
+{
+    int retval;
+    int        state;
+    struct mailname *lp;
+
+    state = 0;
+    for (lp = localaddrs.m_next; lp; lp = lp->m_next)
+       if (lp->m_bcc ? bccque : !bccque) {
+           if (talk && !state)
+               printf ("  -- Local Recipients --\n");
+           do_an_address (lp, talk);
+           state++;
+       }
+
+    state = 0;
+    for (lp = uuaddrs.m_next; lp; lp = lp->m_next)
+       if (lp->m_bcc ? bccque : !bccque) {
+           if (talk && !state)
+               printf ("  -- UUCP Recipients --\n");
+           do_an_address (lp, talk);
+           state++;
+       }
+
+    state = 0;
+    for (lp = netaddrs.m_next; lp; lp = lp->m_next)
+       if (lp->m_bcc ? bccque : !bccque) {
+           if (talk && !state)
+               printf ("  -- Network Recipients --\n");
+           do_an_address (lp, talk);
+           state++;
+       }
+
+    chkadr ();
+
+#ifdef MMDFMTS
+    if (rp_isbad (retval = mm_waend ()))
+       die (NULL, "problem ending addresses [%s]\n", rp_valstr (retval));
+#endif /* MMDFMTS */
+
+#ifdef SENDMTS
+    if (rp_isbad (retval = sm_waend ()))
+       die (NULL, "problem ending addresses; %s", rp_string (retval));
+#endif /* SENDMTS */
+}
+
+
+/*
+ * MTS-SPECIFIC INTERACTION
+ */
+
+
+/*
+ * SENDMAIL/SMTP routines
+ */
+
+#ifdef SENDMTS
+
+static void
+post (char *file, int bccque, int talk)
+{
+    int fd, onex;
+    int        retval;
+
+    onex = !(msgflags & MINV) || bccque;
+    if (verbose) {
+       if (msgflags & MINV)
+           printf (" -- Posting for %s Recipients --\n",
+                   bccque ? "Blind" : "Sighted");
+       else
+           printf (" -- Posting for All Recipients --\n");
+    }
+
+    sigon ();
+
+    if (rp_isbad (retval = sm_init (clientsw, serversw, watch, verbose,
+                                   snoop, onex, queued))
+           || rp_isbad (retval = sm_winit (smtpmode, from)))
+       die (NULL, "problem initializing server; %s", rp_string (retval));
+
+    do_addresses (bccque, talk && verbose);
+    if ((fd = open (file, O_RDONLY)) == NOTOK)
+       die (file, "unable to re-open");
+    do_text (file, fd);
+    close (fd);
+    fflush (stdout);
+
+    sm_end (onex ? OK : DONE);
+    sigoff ();
+
+    if (verbose) {
+       if (msgflags & MINV)
+           printf (" -- %s Recipient Copies Posted --\n",
+                   bccque ? "Blind" : "Sighted");
+       else
+           printf (" -- Recipient Copies Posted --\n");
+    }
+
+    fflush (stdout);
+}
+
+
+/* Address Verification */
+
+static void
+verify_all_addresses (int talk)
+{
+    int retval;
+    struct mailname *lp;
+
+    sigon ();
+
+    if (!whomsw || checksw)
+       if (rp_isbad (retval = sm_init (clientsw, serversw, 0, 0, snoop, 0, 0))
+               || rp_isbad (retval = sm_winit (smtpmode, from)))
+           die (NULL, "problem initializing server; %s", rp_string (retval));
+
+    if (talk && !whomsw)
+       printf (" -- Address Verification --\n");
+    if (talk && localaddrs.m_next)
+       printf ("  -- Local Recipients --\n");
+    for (lp = localaddrs.m_next; lp; lp = lp->m_next)
+       do_an_address (lp, talk);
+
+    if (talk && uuaddrs.m_next)
+       printf ("  -- UUCP Recipients --\n");
+    for (lp = uuaddrs.m_next; lp; lp = lp->m_next)
+       do_an_address (lp, talk);
+
+    if (talk && netaddrs.m_next)
+       printf ("  -- Network Recipients --\n");
+    for (lp = netaddrs.m_next; lp; lp = lp->m_next)
+       do_an_address (lp, talk);
+
+    chkadr ();
+    if (talk && !whomsw)
+       printf (" -- Address Verification Successful --\n");
+
+    if (!whomsw || checksw)
+       sm_end (DONE);
+
+    fflush (stdout);
+    sigoff ();
+}
+
+
+static void
+do_an_address (struct mailname *lp, int talk)
+{
+    int retval;
+    char *mbox, *host;
+    char addr[BUFSIZ];
+
+    switch (lp->m_type) {
+       case LOCALHOST: 
+           mbox = lp->m_mbox;
+           host = lp->m_host;
+           strncpy (addr, mbox, sizeof(addr));
+           break;
+
+       case UUCPHOST: 
+           mbox = auxformat (lp, 0);
+           host = NULL;
+           snprintf (addr, sizeof(addr), "%s!%s", lp->m_host, lp->m_mbox);
+           break;
+
+       default:                /* let SendMail decide if the host is bad  */
+           mbox = lp->m_mbox;
+           host = lp->m_host;
+           snprintf (addr, sizeof(addr), "%s at %s", mbox, host);
+           break;
+    }
+
+    if (talk)
+       printf ("  %s%s", addr, whomsw && lp->m_bcc ? "[BCC]" : "");
+
+    if (whomsw && !checksw) {
+       putchar ('\n');
+       return;
+    }
+    if (talk)
+       printf (": ");
+    fflush (stdout);
+
+    switch (retval = sm_wadr (mbox, host,
+                        lp->m_type != UUCPHOST ? lp->m_path : NULL)) {
+       case RP_OK: 
+           if (talk)
+               printf ("address ok\n");
+           break;
+
+       case RP_NO: 
+       case RP_USER: 
+           if (!talk)
+               fprintf (stderr, "  %s: ", addr);
+           fprintf (talk ? stdout : stderr, "loses; %s\n",
+                       rp_string (retval));
+           unkadr++;
+           break;
+
+       default: 
+           if (!talk)
+               fprintf (stderr, "  %s: ", addr);
+           die (NULL, "unexpected response; %s", rp_string (retval));
+    }
+
+    fflush (stdout);
+}
+
+
+static void
+do_text (char *file, int fd)
+{
+    int retval, state;
+    char buf[BUFSIZ];
+
+    lseek (fd, (off_t) 0, SEEK_SET);
+
+    while ((state = read (fd, buf, sizeof(buf))) > 0) {
+       if (rp_isbad (retval = sm_wtxt (buf, state)))
+           die (NULL, "problem writing text; %s\n", rp_string (retval));
+    }
+
+    if (state == NOTOK)
+       die (file, "problem reading from");
+
+    switch (retval = sm_wtend ()) {
+       case RP_OK: 
+           break;
+
+       case RP_NO: 
+       case RP_NDEL: 
+           die (NULL, "posting failed; %s", rp_string (retval));
+
+       default: 
+           die (NULL, "unexpected response; %s", rp_string (retval));
+    }
+}
+
+#endif /* SENDMTS */
+
+/*
+ * MMDF routines
+ */
+
+#ifdef MMDFMTS
+
+static void
+post (char *file, int bccque, int talk)
+{
+    int fd, onex;
+    int        retval;
+#ifdef RP_NS
+    int        len;
+    struct rp_bufstruct reply;
+#endif /* RP_NS */
+
+    onex = !(msgflags & MINV) || bccque;
+    if (verbose) {
+       if (msgflags & MINV)
+           printf (" -- Posting for %s Recipients --\n",
+                   bccque ? "Blind" : "Sighted");
+       else
+           printf (" -- Posting for All Recipients --\n");
+    }
+
+    sigon ();
+
+    if (rp_isbad (retval = mm_init ())
+           || rp_isbad (retval = mm_sbinit ())
+           || rp_isbad (retval = mm_winit (NULL, submitopts, from)))
+       die (NULL, "problem initializing MMDF system [%s]",
+               rp_valstr (retval));
+#ifdef RP_NS
+       if (rp_isbad (retval = mm_rrply (&reply, &len)))
+           die (NULL, "problem with sender address [%s]",
+                   rp_valstr (retval));
+#endif /* RP_NS */
+
+    do_addresses (bccque, talk && verbose);
+    if ((fd = open (file, O_RDONLY)) == NOTOK)
+       die (file, "unable to re-open");
+    do_text (file, fd);
+    close (fd);
+    fflush (stdout);
+
+    mm_sbend ();
+    mm_end (OK);
+    sigoff ();
+
+    if (verbose)
+       if (msgflags & MINV)
+           printf (" -- %s Recipient Copies Posted --\n",
+                   bccque ? "Blind" : "Sighted");
+       else
+           printf (" -- Recipient Copies Posted --\n");
+    fflush (stdout);
+}
+
+
+/* Address Verification */
+
+static void
+verify_all_addresses (int talk)
+{
+    int retval;
+    struct mailname *lp;
+
+#ifdef RP_NS
+    int        len;
+    struct rp_bufstruct reply;
+#endif /* RP_NS */
+
+    sigon ();
+
+    if (!whomsw || checksw) {
+       if (rp_isbad (retval = mm_init ())
+               || rp_isbad (retval = mm_sbinit ())
+               || rp_isbad (retval = mm_winit (NULL, submitopts, from)))
+           die (NULL, "problem initializing MMDF system [%s]",
+                   rp_valstr (retval));
+#ifdef RP_NS
+       if (rp_isbad (retval = mm_rrply (&reply, &len)))
+           die (NULL, "problem with sender address [%s]", rp_valstr (retval));
+#endif /* RP_NS */
+    }
+
+    if (talk && !whomsw)
+       printf (" -- Address Verification --\n");
+    if (talk && localaddrs.m_next)
+       printf ("  -- Local Recipients --\n");
+    for (lp = localaddrs.m_next; lp; lp = lp->m_next)
+       do_an_address (lp, talk);
+
+    if (talk && uuaddrs.m_next)
+       printf ("  -- UUCP Recipients --\n");
+    for (lp = uuaddrs.m_next; lp; lp = lp->m_next)
+       do_an_address (lp, talk);
+
+    if (talk && netaddrs.m_next)
+       printf ("  -- Network Recipients --\n");
+    for (lp = netaddrs.m_next; lp; lp = lp->m_next)
+       do_an_address (lp, talk);
+
+    chkadr ();
+    if (talk && !whomsw)
+       printf (" -- Address Verification Successful --\n");
+
+    if (!whomsw || checksw)
+       mm_end (NOTOK);
+
+    fflush (stdout);
+    sigoff ();
+}
+
+
+static void
+do_an_address (struct mailname *lp, int talk)
+{
+    int len, retval;
+    char *mbox, *host, *text, *path;
+    char addr[BUFSIZ];
+    struct rp_bufstruct reply;
+
+    switch (lp->m_type) {
+       case LOCALHOST: 
+           mbox = lp->m_mbox;
+           host = LocalName ();
+           strncpy (addr, mbox, sizeof(addr));
+           break;
+
+       case UUCPHOST: 
+           fprintf (talk ? stdout : stderr, "  %s!%s: %s\n",
+               lp->m_host, lp->m_mbox, "not supported; UUCP address");
+           unkadr++;
+           fflush (stdout);
+           return;
+
+       default:                /* let MMDF decide if the host is bad */
+           mbox = lp->m_mbox;
+           host = lp->m_host;
+           snprintf (addr, sizeof(addr), "%s at %s", mbox, host);
+           break;
+    }
+
+    if (talk)
+       printf ("  %s%s", addr, whomsw && lp->m_bcc ? "[BCC]" : "");
+
+    if (whomsw && !checksw) {
+       putchar ('\n');
+       return;
+    }
+    if (talk)
+       printf (": ");
+    fflush (stdout);
+
+#ifdef MMDFII
+    if (lp->m_path)
+       path = concat (lp->m_path, mbox, "@", host, NULL);
+    else
+#endif /* MMDFII */
+       path = NULL;
+    if (rp_isbad (retval = mm_wadr (path ? NULL : host, path ? path : mbox))
+           || rp_isbad (retval = mm_rrply (&reply, &len)))
+       die (NULL, "problem submitting address [%s]", rp_valstr (retval));
+
+    switch (rp_gval (reply.rp_val)) {
+       case RP_AOK: 
+           if (talk)
+               printf ("address ok\n");
+           fflush (stdout);
+           return;
+
+#ifdef RP_DOK
+       case RP_DOK: 
+           if (talk)
+               printf ("nameserver timeout - queued for checking\n");
+           fflush (stdout);
+           return;
+#endif /* RP_DOK */
+
+       case RP_NO: 
+           text = "you lose";
+           break;
+
+#ifdef RP_NS
+       case RP_NS: 
+           text = "temporary nameserver failure";
+           break;
+
+#endif /* RP_NS */
+
+       case RP_USER: 
+       case RP_NDEL: 
+           text = "not deliverable";
+           break;
+
+       case RP_AGN: 
+           text = "try again later";
+           break;
+
+       case RP_NOOP: 
+           text = "nothing done";
+           break;
+
+       default: 
+           if (!talk)
+               fprintf (stderr, "  %s: ", addr);
+           text = "unexpected response";
+           die (NULL, "%s;\n    [%s] -- %s", text,
+                   rp_valstr (reply.rp_val), reply.rp_line);
+    }
+
+    if (!talk)
+       fprintf (stderr, "  %s: ", addr);
+    fprintf (talk ? stdout : stderr, "%s;\n    %s\n", text, reply.rp_line);
+    unkadr++;
+
+    fflush (stdout);
+}
+
+
+static void
+do_text (char *file, int fd)
+{
+    int retval, state;
+    char buf[BUFSIZ];
+    struct rp_bufstruct reply;
+
+    lseek (fd, (off_t) 0, SEEK_SET);
+
+    while ((state = read (fd, buf, sizeof(buf))) > 0) {
+       if (rp_isbad (mm_wtxt (buf, state)))
+           die (NULL, "problem writing text [%s]\n", rp_valstr (retval));
+    }
+
+    if (state == NOTOK)
+       die (file, "problem reading from");
+
+    if (rp_isbad (retval = mm_wtend ()))
+       die (NULL, "problem ending text [%s]\n", rp_valstr (retval));
+
+    if (rp_isbad (retval = mm_rrply (&reply, &state)))
+       die (NULL, "problem getting submission status [%s]\n",
+               rp_valstr (retval));
+
+    switch (rp_gval (reply.rp_val)) {
+       case RP_OK: 
+       case RP_MOK: 
+           break;
+
+       case RP_NO: 
+           die (NULL, "you lose; %s", reply.rp_line);
+
+       case RP_NDEL: 
+           die (NULL, "no delivery occurred; %s", reply.rp_line);
+
+       case RP_AGN: 
+           die (NULL, "try again later; %s", reply.rp_line);
+
+       case RP_NOOP: 
+           die (NULL, "nothing done; %s", reply.rp_line);
+
+       default: 
+           die (NULL, "unexpected response;\n\t[%s] -- %s",
+                   rp_valstr (reply.rp_val), reply.rp_line);
+    }
+}
+
+#endif /* MMDFMTS */
+
+
+/*
+ * SIGNAL HANDLING
+ */
+
+static RETSIGTYPE
+sigser (int i)
+{
+#ifndef RELIABLE_SIGNALS
+    SIGNAL (i, SIG_IGN);
+#endif
+
+    unlink (tmpfil);
+    if (msgflags & MINV)
+       unlink (bccfil);
+
+#ifdef MMDFMTS
+    if (!whomsw || checksw)
+       mm_end (NOTOK);
+#endif /* MMDFMTS */
+
+#ifdef SENDMTS
+    if (!whomsw || checksw)
+       sm_end (NOTOK);
+#endif /* SENDMTS */
+
+    done (1);
+}
+
+
+static void
+sigon (void)
+{
+    if (debug)
+       return;
+
+    hstat = SIGNAL2 (SIGHUP, sigser);
+    istat = SIGNAL2 (SIGINT, sigser);
+    qstat = SIGNAL2 (SIGQUIT, sigser);
+    tstat = SIGNAL2 (SIGTERM, sigser);
+}
+
+
+static void
+sigoff (void)
+{
+    if (debug)
+       return;
+
+    SIGNAL (SIGHUP, hstat);
+    SIGNAL (SIGINT, istat);
+    SIGNAL (SIGQUIT, qstat);
+    SIGNAL (SIGTERM, tstat);
+}
+
+/*
+ * FCC INTERACTION
+ */
+
+static void
+p_refile (char *file)
+{
+    int i;
+
+    if (fccind == 0)
+       return;
+
+    if (verbose)
+       printf (" -- Filing Folder Copies --\n");
+    for (i = 0; i < fccind; i++)
+       fcc (file, fccfold[i]);
+    if (verbose)
+       printf (" -- Folder Copies Filed --\n");
+}
+
+
+/*
+ * Call the `fileproc' to add the file to the folder.
+ */
+
+static void
+fcc (char *file, char *folder)
+{
+    pid_t child_id;
+    int i, status;
+    char fold[BUFSIZ];
+
+    if (verbose)
+       printf ("  %sFcc %s: ", msgstate == RESENT ? "Resent-" : "", folder);
+    fflush (stdout);
+
+    for (i = 0; (child_id = fork ()) == NOTOK && i < 5; i++)
+       sleep (5);
+
+    switch (child_id) {
+       case NOTOK: 
+           if (!verbose)
+               fprintf (stderr, "  %sFcc %s: ",
+                       msgstate == RESENT ? "Resent-" : "", folder);
+           fprintf (verbose ? stdout : stderr, "no forks, so not ok\n");
+           break;
+
+       case OK: 
+           /* see if we need to add `+' */
+           snprintf (fold, sizeof(fold), "%s%s",
+                   *folder == '+' || *folder == '@' ? "" : "+", folder);
+
+           /* now exec the fileproc */
+           execlp (fileproc, r1bindex (fileproc, '/'),
+                   "-link", "-file", file, fold, NULL);
+           _exit (-1);
+
+       default: 
+           if ((status = pidwait (child_id, OK))) {
+               if (!verbose)
+                   fprintf (stderr, "  %sFcc %s: ",
+                           msgstate == RESENT ? "Resent-" : "", folder);
+               pidstatus (status, verbose ? stdout : stderr, NULL);
+           } else {
+               if (verbose)
+                   printf ("folder ok\n");
+           }
+    }
+
+    fflush (stdout);
+}
+
+/*
+ * TERMINATION
+ */
+
+static void
+die (char *what, char *fmt, ...)
+{
+    va_list ap;
+
+    unlink (tmpfil);
+    if (msgflags & MINV)
+       unlink (bccfil);
+
+#ifdef MMDFMTS
+    if (!whomsw || checksw)
+       mm_end (NOTOK);
+#endif /* MMDFMTS */
+
+#ifdef SENDMTS
+    if (!whomsw || checksw)
+       sm_end (NOTOK);
+#endif /* SENDMTS */
+
+    va_start(ap, fmt);
+    advertise (what, NULL, fmt, ap);
+    va_end(ap);
+    done (1);
+}
+
+
+#ifdef MMDFMTS
+/* 
+ * err_abrt() is used by the mm_ routines
+ *      do not, under *ANY* circumstances, remove it from post,
+ *      or you will lose *BIG*
+ */
+
+void
+err_abrt (int code, char *fmt, ...)
+{
+    char buffer[BUFSIZ];
+    va_list ap;
+
+    snprintf (buffer, sizeof(buffer), "[%s]", rp_valstr (code));
+
+    va_start(ap, fmt);
+    advertise (buffer, NULL, fmt, ap);
+    va_end(ap);
+
+    done (1);
+}
+#endif /* MMDFMTS */
diff --git a/uip/prompter.c b/uip/prompter.c
new file mode 100644 (file)
index 0000000..fa1bfae
--- /dev/null
@@ -0,0 +1,481 @@
+
+/*
+ * prompter.c -- simple prompting editor front-end
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+#include <h/signals.h>
+#include <errno.h>
+#include <signal.h>
+#include <setjmp.h>
+
+#ifdef HAVE_TERMIOS_H
+# include <termios.h>
+#else
+# ifdef HAVE_TERMIO_H
+#  include <termio.h>
+# else
+#  include <sgtty.h>
+# endif
+#endif
+
+#define        QUOTE '\\'
+
+#ifndef        CKILL
+# define CKILL '@'
+#endif
+
+#ifndef        CERASE
+# define CERASE '#'
+#endif
+
+static struct swit switches[] = {
+#define        ERASESW 0
+    { "erase chr", 0 },
+#define        KILLSW  1
+    { "kill chr", 0 },
+#define        PREPSW  2
+    { "prepend", 0 },
+#define        NPREPSW 3
+    { "noprepend", 0 },        
+#define        RAPDSW  4
+    { "rapid", 0 },
+#define        NRAPDSW 5
+    { "norapid", 0 },
+#define        BODYSW  6
+    { "body", -4 },
+#define        NBODYSW 7
+    { "nobody", -6 },
+#define        DOTSW   8
+    { "doteof", 0 },
+#define        NDOTSW  9
+    { "nodoteof", 0 },
+#define VERSIONSW 10
+    { "version", 0 },
+#define        HELPSW  11
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+extern int errno;
+
+#ifdef HAVE_TERMIOS_H
+static struct termios tio;
+# define ERASE tio.c_cc[VERASE]
+# define KILL  tio.c_cc[VKILL]
+# define INTR  tio.c_cc[VINTR]
+#else
+# ifdef HAVE_TERMIO_H
+static struct termio tio;
+#  define ERASE tio.c_cc[VERASE]
+#  define KILL  tio.c_cc[VKILL]
+#  define INTR  tio.c_cc[VINTR]
+# else
+static struct sgttyb tio;
+static struct tchars tc;
+#  define ERASE tio.sg_erase
+#  define KILL  tio.sg_kill
+#  define INTR  tc.t_intrc
+# endif
+#endif
+
+static int wtuser = 0;
+static int sigint = 0;
+static jmp_buf sigenv;
+
+/*
+ * prototypes
+ */
+int getln (char *, int);
+static int chrcnv (char *);
+static void chrdsp (char *, char);
+static RETSIGTYPE intrser (int);
+
+
+int
+main (int argc, char **argv)
+{
+    int body = 1, prepend = 1, rapid = 0;
+    int doteof = 0, fdi, fdo, i, state;
+    char *cp, *drft = NULL, *erasep = NULL;
+    char *killp = NULL, name[NAMESZ], field[BUFSIZ];
+    char buffer[BUFSIZ], tmpfil[BUFSIZ];
+    char **arguments, **argp;
+    FILE *in, *out;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    while ((cp = *argp++))
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown", cp);
+
+               case HELPSW: 
+                   snprintf (buffer, sizeof(buffer), "%s [switches] file",
+                       invo_name);
+                   print_help (buffer, switches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case ERASESW: 
+                   if (!(erasep = *argp++) || *erasep == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+               case KILLSW: 
+                   if (!(killp = *argp++) || *killp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+
+               case PREPSW: 
+                   prepend++;
+                   continue;
+               case NPREPSW: 
+                   prepend = 0;
+                   continue;
+
+               case RAPDSW: 
+                   rapid++;
+                   continue;
+               case NRAPDSW: 
+                   rapid = 0;
+                   continue;
+
+               case BODYSW: 
+                   body++;
+                   continue;
+               case NBODYSW: 
+                   body = 0;
+                   continue;
+
+               case DOTSW: 
+                   doteof++;
+                   continue;
+               case NDOTSW: 
+                   doteof = 0;
+                   continue;
+           }
+       } else {
+           if (!drft)
+               drft = cp;
+       }
+
+    if (!drft)
+       adios (NULL, "usage: %s [switches] file", invo_name);
+    if ((in = fopen (drft, "r")) == NULL)
+       adios (drft, "unable to open");
+
+    strncpy (tmpfil, m_tmpfil (invo_name), sizeof(tmpfil));
+    if ((out = fopen (tmpfil, "w")) == NULL)
+       adios (tmpfil, "unable to create");
+    chmod (tmpfil, 0600);
+
+    /*
+     * Are we changing the kill or erase character?
+     */
+    if (killp || erasep) {
+#ifdef HAVE_TERMIOS_H
+       cc_t save_erase, save_kill;
+#else
+       int save_erase, save_kill;
+#endif
+
+       /* get the current terminal attributes */
+#ifdef HAVE_TERMIOS_H
+       tcgetattr(0, &tio);
+#else
+# ifdef HAVE_TERMIO_H
+       ioctl(0, TCGETA, &tio);
+# else
+       ioctl (0, TIOCGETP, (char *) &tio);
+       ioctl (0, TIOCGETC, (char *) &tc);
+# endif
+#endif
+
+       /* save original kill, erase character for later */
+       save_kill = KILL;
+       save_erase = ERASE;
+
+       /* set new kill, erase character in terminal structure */
+       KILL = killp ? chrcnv (killp) : save_kill;
+       ERASE = erasep ? chrcnv (erasep) : save_erase;
+
+       /* set the new terminal attributes */
+#ifdef HAVE_TERMIOS_H
+        tcsetattr(0, TCSADRAIN, &tio);
+#else
+# ifdef HAVE_TERMIO_H
+       ioctl(0, TCSETAW, &tio);
+# else
+       ioctl (0, TIOCSETN, (char *) &tio);
+# endif
+#endif
+
+       /* print out new kill erase characters */
+       chrdsp ("erase", ERASE);
+       chrdsp (", kill", KILL);
+       chrdsp (", intr", INTR);
+       putchar ('\n');
+       fflush (stdout);
+
+       /*
+        * We set the kill and erase character back to original
+        * setup in terminal structure so we can easily
+        * restore it upon exit.
+        */
+       KILL = save_kill;
+       ERASE = save_erase;
+    }
+
+    sigint = 0;
+    SIGNAL2 (SIGINT, intrser);
+
+    /*
+     * Loop through the lines of the draft skeleton.
+     */
+    for (state = FLD;;) {
+       switch (state = m_getfld (state, name, field, sizeof(field), in)) {
+           case FLD: 
+           case FLDEOF: 
+           case FLDPLUS: 
+               /*
+                * Check if the value of field contains anything
+                * other than space or tab.
+                */
+               for (cp = field; *cp; cp++)
+                   if (*cp != ' ' && *cp != '\t')
+                       break;
+
+               /* If so, just add header line to draft */
+               if (*cp++ != '\n' || *cp != 0) {
+                   printf ("%s:%s", name, field);
+                   fprintf (out, "%s:%s", name, field);
+                   while (state == FLDPLUS) {
+                       state =
+                           m_getfld (state, name, field, sizeof(field), in);
+                       printf ("%s", field);
+                       fprintf (out, "%s", field);
+                   }
+               } else {
+                   /* Else, get value of header field */
+                   printf ("%s: ", name);
+                   fflush (stdout);
+                   i = getln (field, sizeof(field));
+                   if (i == -1) {
+abort:
+                       if (killp || erasep) {
+#ifdef HAVE_TERMIOS_H
+                           tcsetattr(0, TCSADRAIN, &tio);
+#else
+# ifdef HAVE_TERMIO
+                           ioctl (0, TCSETA, &tio);
+# else
+                           ioctl (0, TIOCSETN, (char *) &tio);
+# endif
+#endif
+                       }
+                       unlink (tmpfil);
+                       done (1);
+                   }
+                   if (i != 0 || (field[0] != '\n' && field[0] != 0)) {
+                       fprintf (out, "%s:", name);
+                       do {
+                           if (field[0] != ' ' && field[0] != '\t')
+                               putc (' ', out);
+                           fprintf (out, "%s", field);
+                       } while (i == 1
+                                   && (i = getln (field, sizeof(field))) >= 0);
+                       if (i == -1)
+                           goto abort;
+                   }
+               }
+
+               if (state == FLDEOF) {  /* moby hack */
+                   fprintf (out, "--------\n");
+                   printf ("--------\n");
+                   if (!body)
+                       break;
+                   goto no_body;
+               }
+               continue;
+
+           case BODY: 
+           case BODYEOF:
+           case FILEEOF: 
+               if (!body)
+                   break;
+               fprintf (out, "--------\n");
+               if (field[0] == 0 || !prepend)
+                   printf ("--------\n");
+               if (field[0]) {
+                   if (prepend && body) {
+                       printf ("\n--------Enter initial text\n\n");
+                       fflush (stdout);
+                       for (;;) {
+                           getln (buffer, sizeof(buffer));
+                           if (doteof && buffer[0] == '.' && buffer[1] == '\n')
+                               break;
+                           if (buffer[0] == 0)
+                               break;
+                           fprintf (out, "%s", buffer);
+                       }
+                   }
+
+                   do {
+                       fprintf (out, "%s", field);
+                       if (!rapid && !sigint)
+                           printf ("%s", field);
+                   } while (state == BODY &&
+                           (state = m_getfld (state, name, field, sizeof(field), in)));
+                   if (prepend || !body)
+                       break;
+                   else
+                       printf ("\n--------Enter additional text\n\n");
+               }
+no_body:
+               fflush (stdout);
+               for (;;) {
+                   getln (field, sizeof(field));
+                   if (doteof && field[0] == '.' && field[1] == '\n')
+                       break;
+                   if (field[0] == 0)
+                       break;
+                   fprintf (out, "%s", field);
+               }
+               break;
+
+           default: 
+               adios (NULL, "skeleton is poorly formatted");
+       }
+       break;
+    }
+
+    if (body)
+       printf ("--------\n");
+
+    fflush (stdout);
+    fclose (in);
+    fclose (out);
+    SIGNAL (SIGINT, SIG_IGN);
+
+    if (killp || erasep) {
+#ifdef HAVE_TERMIOS_H
+        tcsetattr(0, TCSADRAIN, &tio);
+#else
+# ifdef HAVE_TERMIO_H
+       ioctl (0, TCSETAW, &tio);
+# else
+       ioctl (0, TIOCSETN, (char *) &tio);
+# endif
+#endif
+    }
+
+    if ((fdi = open (tmpfil, O_RDONLY)) == NOTOK)
+       adios (tmpfil, "unable to re-open");
+    if ((fdo = creat (drft, m_gmprot ())) == NOTOK)
+       adios (drft, "unable to write");
+    cpydata (fdi, fdo, tmpfil, drft);
+    close (fdi);
+    close (fdo);
+    unlink (tmpfil);
+
+    context_save ();   /* save the context file */
+    done (0);
+}
+
+
+int
+getln (char *buffer, int n)
+{
+    int c;
+    char *cp;
+
+    cp = buffer;
+    *cp = 0;
+
+    switch (setjmp (sigenv)) {
+       case OK: 
+           wtuser = 1;
+           break;
+
+       case DONE: 
+           wtuser = 0;
+           return 0;
+
+       default: 
+           wtuser = 0;
+           return NOTOK;
+    }
+
+    for (;;) {
+       switch (c = getchar ()) {
+           case EOF: 
+               clearerr (stdin);
+               longjmp (sigenv, DONE);
+
+           case '\n': 
+               if (cp[-1] == QUOTE) {
+                   cp[-1] = c;
+                   wtuser = 0;
+                   return 1;
+               }
+               *cp++ = c;
+               *cp = 0;
+               wtuser = 0;
+               return 0;
+
+           default: 
+               if (cp < buffer + n)
+                   *cp++ = c;
+               *cp = 0;
+       }
+    }
+}
+
+
+static RETSIGTYPE
+intrser (int i)
+{
+#ifndef        RELIABLE_SIGNALS
+    SIGNAL (SIGINT, intrser);
+#endif
+
+    if (wtuser)
+       longjmp (sigenv, NOTOK);
+    sigint++;
+}
+
+
+static int
+chrcnv (char *cp)
+{
+    return (*cp != QUOTE ? *cp : m_atoi (++cp));
+}
+
+
+static void
+chrdsp (char *s, char c)
+{
+    printf ("%s ", s);
+    if (c < ' ' || c == 0177)
+       printf ("^%c", c ^ 0100);
+    else
+       printf ("%c", c);
+}
diff --git a/uip/rcvdist.c b/uip/rcvdist.c
new file mode 100644 (file)
index 0000000..b210696
--- /dev/null
@@ -0,0 +1,275 @@
+
+/*
+ * rcvdist.c -- asynchronously redistribute messages
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/fmt_scan.h>
+#include <h/rcvmail.h>
+#include <zotnet/tws/tws.h>
+
+static struct swit switches[] = {
+#define        FORMSW       0
+    { "form formfile",  4 },
+#define VERSIONSW    1
+    { "version", 0 },
+#define        HELPSW       2
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+static char backup[BUFSIZ] = "";
+static char drft[BUFSIZ] = "";
+static char tmpfil[BUFSIZ] = "";
+
+/*
+ * prototypes
+ */
+static void rcvdistout (FILE *, char *, char *);
+void done (int);
+
+
+int
+main (int argc, char **argv)
+{
+    pid_t child_id;
+    int i, vecp = 1;
+    char *addrs = NULL, *cp, *form = NULL, buf[BUFSIZ];
+    char **argp, **arguments, *vec[MAXARGS];
+    register FILE *fp;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    mts_init (invo_name);
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   vec[vecp++] = --cp;
+                   continue;
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf),
+                       "%s [switches] [switches for postproc] address ...",
+                       invo_name);
+                   print_help (buf, switches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case FORMSW: 
+                   if (!(form = *argp++) || *form == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+           }
+       }
+       addrs = addrs ? add (cp, add (", ", addrs)) : getcpy (cp);
+    }
+
+    if (addrs == NULL)
+       adios (NULL, "usage: %s [switches] [switches for postproc] address ...",
+           invo_name);
+
+    umask (~m_gmprot ());
+    strncpy (tmpfil, m_tmpfil (invo_name), sizeof(tmpfil));
+    if ((fp = fopen (tmpfil, "w+")) == NULL)
+       adios (tmpfil, "unable to create");
+    cpydata (fileno (stdin), fileno (fp), "message", tmpfil);
+    fseek (fp, 0L, SEEK_SET);
+    strncpy (drft, m_tmpfil (invo_name), sizeof(drft));
+    rcvdistout (fp, form, addrs);
+    fclose (fp);
+
+    if (distout (drft, tmpfil, backup) == NOTOK)
+       done (1);
+
+    vec[0] = r1bindex (postproc, '/');
+    vec[vecp++] = "-dist";
+    vec[vecp++] = drft;
+    vec[vecp] = NULL;
+
+    for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
+       sleep (5);
+    switch (child_id) {
+       case NOTOK: 
+           admonish (NULL, "unable to fork");/* fall */
+       case OK: 
+           execvp (postproc, vec);
+           fprintf (stderr, "unable to exec ");
+           perror (postproc);
+           _exit (1);
+
+       default: 
+           done (pidXwait(child_id, postproc));
+    }
+/* NOTREACHED */
+}
+
+/* very similar to routine in replsbr.c */
+
+#define        SBUFSIZ 256
+
+static int outputlinelen = OUTPUTLINELEN;
+
+static struct format *fmt;
+
+static int ncomps = 0;
+static char **compbuffers = 0;
+static struct comp **used_buf = 0;
+
+static int dat[5];
+
+static char *addrcomps[] = {
+    "from",
+    "sender",
+    "reply-to",
+    "to",
+    "cc",
+    "bcc",
+    "resent-from",
+    "resent-sender",
+    "resent-reply-to",
+    "resent-to",
+    "resent-cc",
+    "resent-bcc",
+    NULL
+};
+
+
+static void
+rcvdistout (FILE *inb, char *form, char *addrs)
+{
+    register int char_read = 0, format_len, i, state;
+    register char *tmpbuf, **nxtbuf, **ap;
+    char *cp, *scanl, name[NAMESZ];
+    register struct comp *cptr, **savecomp;
+    FILE *out;
+
+    if (!(out = fopen (drft, "w")))
+       adios (drft, "unable to create");
+
+    /* get new format string */
+    cp = new_fs (form ? form : rcvdistcomps, NULL, NULL);
+    format_len = strlen (cp);
+    ncomps = fmt_compile (cp, &fmt) + 1;
+    if (!(nxtbuf = compbuffers = (char **) calloc ((size_t) ncomps, sizeof(char *))))
+       adios (NULL, "unable to allocate component buffers");
+    if (!(savecomp = used_buf = (struct comp **) calloc ((size_t) (ncomps + 1), sizeof(struct comp *))))
+       adios (NULL, "unable to allocate component buffer stack");
+    savecomp += ncomps + 1;
+    *--savecomp = 0;
+
+    for (i = ncomps; i--;)
+       if (!(*nxtbuf++ = malloc (SBUFSIZ)))
+           adios (NULL, "unable to allocate component buffer");
+    nxtbuf = compbuffers;
+    tmpbuf = *nxtbuf++;
+
+    for (ap = addrcomps; *ap; ap++) {
+       FINDCOMP (cptr, *ap);
+       if (cptr)
+           cptr->c_type |= CT_ADDR;
+    }
+
+    FINDCOMP (cptr, "addresses");
+    if (cptr)
+       cptr->c_text = addrs;
+
+    for (state = FLD;;) {
+       switch (state = m_getfld (state, name, tmpbuf, SBUFSIZ, inb)) {
+           case FLD: 
+           case FLDPLUS: 
+               if ((cptr = wantcomp[CHASH (name)]))
+                   do {
+                       if (!strcasecmp (name, cptr->c_name)) {
+                           char_read += msg_count;
+                           if (!cptr->c_text) {
+                               cptr->c_text = tmpbuf;
+                               *--savecomp = cptr;
+                               tmpbuf = *nxtbuf++;
+                           }
+                           else {
+                               i = strlen (cp = cptr->c_text) - 1;
+                               if (cp[i] == '\n')
+                                   if (cptr->c_type & CT_ADDR) {
+                                       cp[i] = 0;
+                                       cp = add (",\n\t", cp);
+                                   }
+                                   else
+                                       cp = add ("\t", cp);
+                               cptr->c_text = add (tmpbuf, cp);
+                           }
+                           while (state == FLDPLUS) {
+                               state = m_getfld (state, name, tmpbuf,
+                                                 SBUFSIZ, inb);
+                               cptr->c_text = add (tmpbuf, cptr->c_text);
+                               char_read += msg_count;
+                           }
+                           break;
+                       }
+                   } while ((cptr = cptr->c_next));
+
+               while (state == FLDPLUS)
+                   state = m_getfld (state, name, tmpbuf, SBUFSIZ, inb);
+               break;
+
+           case LENERR: 
+           case FMTERR: 
+           case BODY: 
+           case FILEEOF: 
+               goto finished;
+
+           default: 
+               adios (NULL, "m_getfld() returned %d", state);
+       }
+    }
+finished: ;
+
+    i = format_len + char_read + 256;
+    scanl = malloc ((size_t) i + 2);
+    dat[0] = dat[1] = dat[2] = dat[4] = 0;
+    dat[3] = outputlinelen;
+    fmt_scan (fmt, scanl, i, dat);
+    fputs (scanl, out);
+
+    if (ferror (out))
+       adios (drft, "error writing");
+    fclose (out);
+
+    free (scanl);
+    for (nxtbuf = compbuffers, i = ncomps; cptr = *savecomp++; nxtbuf++, i--)
+       free (cptr->c_text);
+    while (i-- > 0)
+        free (*nxtbuf++);
+    free ((char *) compbuffers);
+    free ((char *) used_buf);
+}
+
+
+void
+done (int status)
+{
+    if (backup[0])
+       unlink (backup);
+    if (drft[0])
+       unlink (drft);
+    if (tmpfil[0])
+       unlink (tmpfil);
+
+    exit (status ? RCV_MBX : RCV_MOK);
+}
diff --git a/uip/rcvpack.c b/uip/rcvpack.c
new file mode 100644 (file)
index 0000000..1520cbe
--- /dev/null
@@ -0,0 +1,103 @@
+
+/*
+ * rcvpack.c -- append message to a file
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/dropsbr.h>
+#include <h/rcvmail.h>
+#include <zotnet/tws/tws.h>
+#include <zotnet/mts/mts.h>
+
+static struct swit switches[] = {
+#define MBOXSW       0
+    { "mbox", 0 },
+#define MMDFSW       1
+    { "mmdf", 0 },
+#define VERSIONSW    2
+    { "version", 0 },
+#define        HELPSW       3
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+/*
+ * default format in which to save messages
+ */
+static int mbx_style = MBOX_FORMAT;
+
+
+int
+main (int argc, char **argv)
+{
+    int md;
+    char *cp, *file = NULL, buf[BUFSIZ];
+    char **argp, **arguments;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    mts_init (invo_name);
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    /* parse arguments */
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown", cp);
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf), "%s [switches] file", invo_name);
+                   print_help (buf, switches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case MBOXSW:
+                   mbx_style = MBOX_FORMAT;
+                   continue;
+               case MMDFSW:
+                   mbx_style = MMDF_FORMAT;
+                   continue;
+           }
+       }
+       if (file)
+           adios (NULL, "only one file at a time!");
+       else
+           file = cp;
+    }
+
+    if (!file)
+       adios (NULL, "%s [switches] file", invo_name);
+
+    rewind (stdin);
+
+    /* open and lock the file */
+    if ((md = mbx_open (file, mbx_style, getuid(), getgid(), m_gmprot())) == NOTOK)
+       done (RCV_MBX);
+
+    /* append the message */
+    if (mbx_copy (file, mbx_style, md, fileno(stdin), 1, NULL, 0) == NOTOK) {
+       mbx_close (file, md);
+       done (RCV_MBX);
+    }
+
+    /* close and unlock the file */
+    if (mbx_close (file, md) == NOTOK)
+       done (RCV_MBX);
+
+    done (RCV_MOK);
+}
diff --git a/uip/rcvstore.c b/uip/rcvstore.c
new file mode 100644 (file)
index 0000000..81a03e8
--- /dev/null
@@ -0,0 +1,233 @@
+
+/*
+ * rcvstore.c -- asynchronously add mail to a folder
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+#include <h/signals.h>
+#include <errno.h>
+#include <signal.h>
+
+static struct swit switches[] = {
+#define CRETSW         0
+    { "create",        0 },
+#define NCRETSW        1
+    { "nocreate", 0 },
+#define UNSEENSW       2
+    { "unseen", 0 },
+#define NUNSEENSW      3
+    { "nounseen", 0 },
+#define PUBSW          4
+    { "public",        0 },
+#define NPUBSW         5
+    { "nopublic",  0 },
+#define ZEROSW         6
+    { "zero",  0 },
+#define NZEROSW        7
+    { "nozero",        0 },
+#define SEQSW          8
+    { "sequence name", 0 },
+#define VERSIONSW      9
+    { "version", 0 },
+#define HELPSW        10
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+extern int errno;
+
+/*
+ * name of temporary file to store incoming message
+ */
+static char *tmpfilenam = NULL;
+
+
+int
+main (int argc, char **argv)
+{
+    int publicsw = -1, zerosw = 0;
+    int create = 1, unseensw = 1;
+    int fd, msgnum, seqp = 0;
+    char *cp, *maildir, *folder = NULL, buf[BUFSIZ];
+    char **argp, **arguments, *seqs[NUMATTRS+1];
+    struct msgs *mp;
+    struct stat st;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    mts_init (invo_name);
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    /* parse arguments */
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+           case AMBIGSW: 
+               ambigsw (cp, switches);
+               done (1);
+           case UNKWNSW: 
+               adios (NULL, "-%s unknown", cp);
+
+           case HELPSW: 
+               snprintf (buf, sizeof(buf), "%s [+folder] [switches]",
+                         invo_name);
+               print_help (buf, switches, 1);
+               done (1);
+           case VERSIONSW:
+               print_version(invo_name);
+               done (1);
+
+           case SEQSW: 
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument name to %s", argp[-2]);
+
+               /* check if too many sequences specified */
+               if (seqp >= NUMATTRS)
+                   adios (NULL, "too many sequences (more than %d) specified", NUMATTRS);
+               seqs[seqp++] = cp;
+               continue;
+
+           case UNSEENSW:
+               unseensw = 1;
+               continue;
+           case NUNSEENSW:
+               unseensw = 0;
+               continue;
+
+           case PUBSW: 
+               publicsw = 1;
+               continue;
+           case NPUBSW: 
+               publicsw = 0;
+               continue;
+
+           case ZEROSW: 
+               zerosw++;
+               continue;
+           case NZEROSW: 
+               zerosw = 0;
+               continue;
+
+           case CRETSW: 
+               create++;
+               continue;
+           case NCRETSW: 
+               create = 0;
+               continue;
+           }
+       }
+       if (*cp == '+' || *cp == '@') {
+           if (folder)
+               adios (NULL, "only one folder at a time!");
+           else
+               folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       } else {
+           adios (NULL, "usage: %s [+folder] [switches]", invo_name);
+       }
+    }
+
+    seqs[seqp] = NULL; /* NULL terminate list of sequences */
+
+    if (!context_find ("path"))
+       free (path ("./", TFOLDER));
+
+    /* if no folder is given, use default folder */
+    if (!folder)
+       folder = getfolder (0);
+    maildir = m_maildir (folder);
+
+    /* check if folder exists */
+    if (stat (maildir, &st) == NOTOK) {
+       if (errno != ENOENT)
+           adios (maildir, "error on folder");
+       if (!create)
+           adios (NULL, "folder %s doesn't exist", maildir);
+       if (!makedir (maildir))
+           adios (NULL, "unable to create folder %s", maildir);
+    }
+
+    if (chdir (maildir) == NOTOK)
+       adios (maildir, "unable to change directory to");
+
+    /* ignore a few signals */
+    SIGNAL (SIGHUP, SIG_IGN);
+    SIGNAL (SIGINT, SIG_IGN);
+    SIGNAL (SIGQUIT, SIG_IGN);
+    SIGNAL (SIGTERM, SIG_IGN);
+
+    /* create a temporary file */
+    tmpfilenam = m_scratch ("", invo_name);
+    if ((fd = creat (tmpfilenam, m_gmprot ())) == NOTOK)
+       adios (tmpfilenam, "unable to create");
+    chmod (tmpfilenam, m_gmprot());
+
+    /* copy the message from stdin into temp file */
+    cpydata (fileno (stdin), fd, "standard input", tmpfilenam);
+
+    if (fstat (fd, &st) == NOTOK) {
+       unlink (tmpfilenam);
+       adios (tmpfilenam, "unable to fstat");
+    }
+    if (close (fd) == NOTOK)
+       adios (tmpfilenam, "error closing");
+
+    /* don't add file if it is empty */
+    if (st.st_size == 0) {
+       unlink (tmpfilenam);
+       advise (NULL, "empty file");
+       done (0);
+    }
+
+    /*
+     * read folder and create message structure
+     */
+    if (!(mp = folder_read (folder)))
+       adios (NULL, "unable to read folder %s", folder);
+
+    /*
+     * Link message into folder, and possibly add
+     * to the Unseen-Sequence's.
+     */
+    if ((msgnum = folder_addmsg (&mp, tmpfilenam, 0, unseensw, 0)) == -1)
+       done (1);
+
+    /*
+     * Add the message to any extra sequences
+     * that have been specified.
+     */
+    for (seqp = 0; seqs[seqp]; seqp++) {
+       if (!seq_addmsg (mp, seqs[seqp], msgnum, publicsw, zerosw))
+           done (1);
+    }
+
+    seq_setunseen (mp, 0);     /* synchronize any Unseen-Sequence's      */
+    seq_save (mp);             /* synchronize and save message sequences */
+    folder_free (mp);          /* free folder/message structure          */
+
+    context_save ();           /* save the global context file           */
+    unlink (tmpfilenam);       /* remove temporary file                  */
+    tmpfilenam = NULL;
+
+    done (0);
+}
+
+/*
+ * Clean up and exit
+ */
+void
+done(int status)
+{
+    if (tmpfilenam && *tmpfilenam)
+       unlink (tmpfilenam);
+    exit (status);
+}
diff --git a/uip/rcvtty.c b/uip/rcvtty.c
new file mode 100644 (file)
index 0000000..9eb365f
--- /dev/null
@@ -0,0 +1,308 @@
+
+/*
+ * rcvtty.c -- a rcvmail program (a lot like rcvalert) handling IPC ttys
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/signals.h>
+#include <h/rcvmail.h>
+#include <h/scansbr.h>
+#include <zotnet/tws/tws.h>
+#include <signal.h>
+#include <fcntl.h>
+
+#include <utmp.h>
+#ifndef UTMP_FILE
+# ifdef _PATH_UTMP
+#  define UTMP_FILE _PATH_UTMP
+# else
+#  define UTMP_FILE "/etc/utmp"
+# endif
+#endif
+
+#define        SCANFMT \
+"%2(hour{dtimenow}):%02(min{dtimenow}): %<(size)%5(size) %>%<{encrypted}E%>\
+%<(mymbox{from})%<{to}To:%14(friendly{to})%>%>%<(zero)%17(friendly{from})%>  \
+%{subject}%<{body}<<%{body}>>%>"
+
+static struct swit switches[] = {
+#define        BIFFSW  0
+    { "biff", 0 },
+#define        FORMSW  1
+    { "form formatfile", 0 },
+#define        FMTSW   2
+    { "format string", 5 },
+#define WIDTHSW 3
+    { "width columns", 0 },
+#define NLSW    4
+    { "newline", 0 },
+#define NNLSW   5
+    { "nonewline", 0 },
+#define BELSW  6
+    { "bell", 0 },
+#define        NBELSW  7
+    { "nobell", 0 },
+#define VERSIONSW 8
+    { "version", 0 },
+#define        HELPSW  9
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+static jmp_buf myctx;
+static int bell = 1;
+static int newline = 1;
+static int biff = 0;
+static int width = 0;
+static char *form = NULL;
+static char *format = NULL;
+
+/*
+ * external prototypes
+ */
+char *getusername(void);
+
+/*
+ * static prototypes
+ */
+static RETSIGTYPE alrmser (int);
+static int message_fd (char **);
+static int header_fd (void);
+static void alert (char *, int);
+
+
+int
+main (int argc, char **argv)
+{
+    int md, vecp = 0;
+    char *cp, *user, buf[BUFSIZ], tty[BUFSIZ];
+    char **argp, **arguments, *vec[MAXARGS];
+    struct utmp ut;
+    register FILE *uf;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    mts_init (invo_name);
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   vec[vecp++] = --cp;
+                   continue;
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf), "%s [command ...]", invo_name);
+                   print_help (buf, switches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case BIFFSW:
+                   biff = 1;
+                   continue;
+
+               case FORMSW: 
+                   if (!(form = *argp++) || *form == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   format = NULL;
+                   continue;
+               case FMTSW: 
+                   if (!(format = *argp++) || *format == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   form = NULL;
+                   continue;
+
+               case WIDTHSW:
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios(NULL, "missing argument to %s", argp[-2]);
+                   width = atoi(cp);
+                   continue;
+                case NLSW:
+                    newline = 1;
+                    continue;
+                case NNLSW:
+                    newline = 0;
+                    continue;
+                case BELSW:
+                    bell = 1;
+                    continue;
+                case NBELSW:
+                    bell = 0;
+                    continue;
+
+           }
+       }
+       vec[vecp++] = cp;
+    }
+    vec[vecp] = 0;
+
+    if ((md = vecp ? message_fd (vec) : header_fd ()) == NOTOK)
+       exit (RCV_MBX);
+
+    user = getusername();
+    if ((uf = fopen (UTMP_FILE, "r")) == NULL)
+       exit (RCV_MBX);
+
+    while (fread ((char *) &ut, sizeof(ut), 1, uf) == 1)
+       if (ut.ut_name[0] != 0
+               && strncmp (user, ut.ut_name, sizeof(ut.ut_name)) == 0) {
+           strncpy (tty, ut.ut_line, sizeof(ut.ut_line));
+           alert (tty, md);
+       }
+
+    fclose (uf);
+    exit (RCV_MOK);
+}
+
+
+static RETSIGTYPE
+alrmser (int i)
+{
+#ifndef RELIABLE_SIGNALS
+    SIGNAL (SIGALRM, alrmser);
+#endif
+
+    longjmp (myctx, 1);
+}
+
+
+static int
+message_fd (char **vec)
+{
+    pid_t child_id;
+    int bytes, fd, seconds;
+    char tmpfil[BUFSIZ];
+    struct stat st;
+
+    unlink (mktemp (strncpy (tmpfil, "/tmp/rcvttyXXXXX", sizeof(tmpfil))));
+    if ((fd = open (tmpfil, O_RDWR | O_CREAT | O_TRUNC, 0600)) == NOTOK)
+       return header_fd ();
+    unlink (tmpfil);
+
+    if ((child_id = vfork()) == NOTOK) {
+       /* fork error */
+       close (fd);
+       return header_fd ();
+    } else if (child_id) {
+       /* parent process */
+       if (!setjmp (myctx)) {
+           SIGNAL (SIGALRM, alrmser);
+           bytes = fstat(fileno (stdin), &st) != NOTOK ? (int) st.st_size : 100;
+
+           /* amount of time to wait depends on message size */
+           if (bytes <= 100) {
+               /* give at least 5 minutes */
+               seconds = 300;
+           } else if (bytes >= 90000) {
+               /* but 30 minutes should be long enough */
+               seconds = 1800;
+           } else {
+               seconds = (bytes / 60) + 300;
+           }
+           alarm ((unsigned int) seconds);
+           pidwait(child_id, OK);
+           alarm (0);
+
+           if (fstat (fd, &st) != NOTOK && st.st_size > (off_t) 0)
+               return fd;
+       } else {
+           /*
+            * Ruthlessly kill the child and anything
+            * else in its process group.
+            */
+           KILLPG(child_id, SIGKILL);
+       }
+       close (fd);
+       return header_fd ();
+    }
+
+    /* child process */
+    rewind (stdin);
+    if (dup2 (fd, 1) == NOTOK || dup2 (fd, 2) == NOTOK)
+       _exit (-1);
+    closefds (3);
+    setpgid ((pid_t) 0, getpid ());    /* put in own process group */
+    execvp (vec[0], vec);
+    _exit (-1);
+}
+
+
+static int
+header_fd (void)
+{
+    int fd;
+    char *nfs, tmpfil[BUFSIZ];
+
+    strncpy (tmpfil, m_tmpfil (invo_name), sizeof(tmpfil));
+    if ((fd = open (tmpfil, O_RDWR | O_CREAT | O_TRUNC, 0600)) == NOTOK)
+       return NOTOK;
+    unlink (tmpfil);
+
+    rewind (stdin);
+
+    /* get new format string */
+    nfs = new_fs (form, format, SCANFMT);
+    scan (stdin, 0, 0, nfs, width, 0, 0, NULL, 0L, 0);
+    if (newline)
+        write (fd, "\n\r", 2);
+    write (fd, scanl, strlen (scanl));
+    if (bell)
+        write (fd, "\007", 1);
+
+    return fd;
+}
+
+
+static void
+alert (char *tty, int md)
+{
+    int i, td, mask;
+    char buffer[BUFSIZ], ttyspec[BUFSIZ];
+    struct stat st;
+
+    snprintf (ttyspec, sizeof(ttyspec), "/dev/%s", tty);
+
+    /*
+     * The mask depends on whether we are checking for
+     * write permission based on `biff' or `mesg'.
+     */
+    mask = biff ? S_IEXEC : (S_IWRITE >> 3);
+    if (stat (ttyspec, &st) == NOTOK || (st.st_mode & mask) == 0)
+       return;
+
+    if (!setjmp (myctx)) {
+       SIGNAL (SIGALRM, alrmser);
+       alarm (2);
+       td = open (ttyspec, O_WRONLY);
+       alarm (0);
+       if (td == NOTOK)
+           return;
+    } else {
+       alarm (0);
+       return;
+    }
+
+    lseek (md, (off_t) 0, SEEK_SET);
+
+    while ((i = read (md, buffer, sizeof(buffer))) > 0)
+       if (write (td, buffer, i) != i)
+           break;
+
+    close (td);
+}
+
diff --git a/uip/refile.c b/uip/refile.c
new file mode 100644 (file)
index 0000000..83e5639
--- /dev/null
@@ -0,0 +1,396 @@
+
+/*
+ * refile.c -- move or link message(s) from a source folder
+ *          -- into one or more destination folders
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+#include <errno.h>
+
+/*
+ * We allocate spaces for messages (msgs array)
+ * this number of elements at a time.
+ */
+#define MAXMSGS  256
+
+
+static struct swit switches[] = {
+#define        DRAFTSW          0
+    { "draft", 0 },
+#define        LINKSW           1
+    { "link", 0 },
+#define        NLINKSW          2
+    { "nolink", 0 },
+#define        PRESSW           3
+    { "preserve", 0 },
+#define        NPRESSW          4
+    { "nopreserve", 0 },
+#define UNLINKSW         5
+    { "unlink", 0 },
+#define NUNLINKSW        6
+    { "nounlink", 0 },
+#define        SRCSW            7
+    { "src +folder", 0 },
+#define        FILESW           8
+    { "file file", 0 },
+#define        RPROCSW          9
+    { "rmmproc program", 0 },
+#define        NRPRCSW         10
+    { "normmproc", 0 },
+#define VERSIONSW       11
+    { "version", 0 },
+#define        HELPSW          12
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+extern int errno;
+
+static char maildir[BUFSIZ];
+
+struct st_fold {
+    char *f_name;
+    struct msgs *f_mp;
+};
+
+/*
+ * static prototypes
+ */
+static void opnfolds (struct st_fold *, int);
+static void clsfolds (struct st_fold *, int);
+static void remove_files (int, char **);
+static int m_file (char *, struct st_fold *, int, int);
+
+
+int
+main (int argc, char **argv)
+{
+    int        linkf = 0, preserve = 0, filep = 0;
+    int foldp = 0, isdf = 0, unlink_msgs = 0;
+    int i, msgnum, nummsgs, maxmsgs;
+    char *cp, *folder = NULL, buf[BUFSIZ];
+    char **argp, **arguments, **msgs;
+    char *filevec[NFOLDERS + 2];
+    char **files = &filevec[1];           /* leave room for remove_files:vec[0] */
+    struct st_fold folders[NFOLDERS + 1];
+    struct msgs *mp;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    /*
+     * Allocate the initial space to record message
+     * names, ranges, and sequences.
+     */
+    nummsgs = 0;
+    maxmsgs = MAXMSGS;
+    if (!(msgs = (char **) malloc ((size_t) (maxmsgs * sizeof(*msgs)))))
+       adios (NULL, "unable to allocate storage");
+
+    /*
+     * Parse arguments
+     */
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+           case AMBIGSW: 
+               ambigsw (cp, switches);
+               done (1);
+           case UNKWNSW: 
+               adios (NULL, "-%s unknown\n", cp);
+
+           case HELPSW: 
+               snprintf (buf, sizeof(buf), "%s [msgs] [switches] +folder ...",
+                         invo_name);
+               print_help (buf, switches, 1);
+               done (1);
+           case VERSIONSW:
+               print_version(invo_name);
+               done (1);
+
+           case LINKSW: 
+               linkf++;
+               continue;
+           case NLINKSW: 
+               linkf = 0;
+               continue;
+
+           case PRESSW: 
+               preserve++;
+               continue;
+           case NPRESSW: 
+               preserve = 0;
+               continue;
+
+           case UNLINKSW:
+               unlink_msgs++;
+               continue;
+           case NUNLINKSW:
+               unlink_msgs = 0;
+               continue;
+
+           case SRCSW: 
+               if (folder)
+                   adios (NULL, "only one source folder at a time!");
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               folder = path (*cp == '+' || *cp == '@' ? cp + 1 : cp,
+                              *cp != '@' ? TFOLDER : TSUBCWF);
+               continue;
+           case DRAFTSW:
+               if (filep > NFOLDERS)
+                   adios (NULL, "only %d files allowed!", NFOLDERS);
+               isdf = 0;
+               files[filep++] = getcpy (m_draft (NULL, NULL, 1, &isdf));
+               continue;
+           case FILESW: 
+               if (filep > NFOLDERS)
+                   adios (NULL, "only %d files allowed!", NFOLDERS);
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               files[filep++] = path (cp, TFILE);
+               continue;
+
+           case RPROCSW: 
+               if (!(rmmproc = *argp++) || *rmmproc == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               continue;
+           case NRPRCSW: 
+               rmmproc = NULL;
+               continue;
+           }
+       }
+       if (*cp == '+' || *cp == '@') {
+           if (foldp > NFOLDERS)
+               adios (NULL, "only %d folders allowed!", NFOLDERS);
+           folders[foldp++].f_name =
+               path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       } else {
+           /*
+            * Check if we need to allocate more space
+            * for message names, ranges, and sequences.
+            */
+           if (nummsgs >= maxmsgs) {
+               maxmsgs += MAXMSGS;
+               if (!(msgs = (char **) realloc (msgs,
+                                               (size_t) (maxmsgs * sizeof(*msgs)))))
+                   adios (NULL, "unable to reallocate msgs storage");
+           }
+           msgs[nummsgs++] = cp;
+       }
+    }
+
+    if (!context_find ("path"))
+       free (path ("./", TFOLDER));
+    if (foldp == 0)
+       adios (NULL, "no folder specified");
+
+#ifdef WHATNOW
+    if (!nummsgs && !foldp && !filep && (cp = getenv ("mhdraft")) && *cp)
+       files[filep++] = cp;
+#endif /* WHATNOW */
+
+    /*
+     * We are refiling a file to the folders
+     */
+    if (filep > 0) {
+       if (folder || nummsgs)
+           adios (NULL, "use -file or some messages, not both");
+       opnfolds (folders, foldp);
+       for (i = 0; i < filep; i++)
+           if (m_file (files[i], folders, foldp, preserve))
+               done (1);
+       /* If -nolink, then "remove" files */
+       if (!linkf)
+           remove_files (filep, filevec);
+       done (0);
+    }
+
+    if (!nummsgs)
+       msgs[nummsgs++] = "cur";
+    if (!folder)
+       folder = getfolder (1);
+    strncpy (maildir, m_maildir (folder), sizeof(maildir));
+
+    if (chdir (maildir) == NOTOK)
+       adios (maildir, "unable to change directory to");
+
+    /* read source folder and create message structure */
+    if (!(mp = folder_read (folder)))
+       adios (NULL, "unable to read folder %s", folder);
+
+    /* check for empty folder */
+    if (mp->nummsg == 0)
+       adios (NULL, "no messages in %s", folder);
+
+    /* parse the message range/sequence/name and set SELECTED */
+    for (msgnum = 0; msgnum < nummsgs; msgnum++)
+       if (!m_convert (mp, msgs[msgnum]))
+           done (1);
+    seq_setprev (mp);  /* set the previous-sequence */
+
+    /* create folder structures for each destination folder */
+    opnfolds (folders, foldp);
+
+    /* Link all the selected messages into destination folders */
+    for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
+       if (is_selected (mp, msgnum)) {
+           cp = getcpy (m_name (msgnum));
+           if (m_file (cp, folders, foldp, preserve))
+               done (1);
+           free (cp);
+       }
+    }
+
+    /*
+     * This is a hack.  If we are using an external rmmproc,
+     * then save the current folder to the context file,
+     * so the external rmmproc will remove files from the correct
+     * directory.  This should be moved to folder_delmsgs().
+     */
+    if (rmmproc) {
+       context_replace (pfolder, folder);
+       context_save ();
+       fflush (stdout);
+    }
+
+    /* If -nolink, then "remove" messages from source folder */
+    if (!linkf) {
+       folder_delmsgs (mp, unlink_msgs);
+    }
+
+    clsfolds (folders, foldp);
+
+    if (mp->hghsel != mp->curmsg
+       && (mp->numsel != mp->nummsg || linkf))
+       seq_setcur (mp, mp->hghsel);
+    seq_save (mp);     /* synchronize message sequences */
+
+    context_replace (pfolder, folder); /* update current folder   */
+    context_save ();                   /* save the context file   */
+    folder_free (mp);                  /* free folder structure   */
+    done (0);
+}
+
+
+/*
+ * Read all the destination folders and
+ * create folder structures for all of them.
+ */
+
+static void
+opnfolds (struct st_fold *folders, int nfolders)
+{
+    register char *cp;
+    char nmaildir[BUFSIZ];
+    register struct st_fold *fp, *ep;
+    register struct msgs *mp;
+    struct stat st;
+
+    for (fp = folders, ep = folders + nfolders; fp < ep; fp++) {
+       chdir (m_maildir (""));
+       strncpy (nmaildir, m_maildir (fp->f_name), sizeof(nmaildir));
+
+       if (stat (nmaildir, &st) == NOTOK) {
+           if (errno != ENOENT)
+               adios (nmaildir, "error on folder");
+           cp = concat ("Create folder \"", nmaildir, "\"? ", NULL);
+           if (!getanswer (cp))
+               done (1);
+           free (cp);
+           if (!makedir (nmaildir))
+               adios (NULL, "unable to create folder %s", nmaildir);
+       }
+
+       if (chdir (nmaildir) == NOTOK)
+           adios (nmaildir, "unable to change directory to");
+       if (!(mp = folder_read (fp->f_name)))
+           adios (NULL, "unable to read folder %s", fp->f_name);
+       mp->curmsg = 0;
+
+       fp->f_mp = mp;
+
+       chdir (maildir);
+    }
+}
+
+
+/*
+ * Set the Previous-Sequence and then sychronize the
+ * sequence file, for each destination folder.
+ */
+
+static void
+clsfolds (struct st_fold *folders, int nfolders)
+{
+    register struct st_fold *fp, *ep;
+    register struct msgs *mp;
+
+    for (fp = folders, ep = folders + nfolders; fp < ep; fp++) {
+       mp = fp->f_mp;
+       seq_setprev (mp);
+       seq_save (mp);
+    }
+}
+
+
+/*
+ * If you have a "rmmproc" defined, we called that
+ * to remove all the specified files.  If "rmmproc"
+ * is not defined, then just unlink the files.
+ */
+
+static void
+remove_files (int filep, char **files)
+{
+    int i;
+    char **vec;
+
+    /* If rmmproc is defined, we use that */
+    if (rmmproc) {
+       vec = files++;          /* vec[0] = filevec[0] */
+       files[filep] = NULL;    /* NULL terminate list */
+
+       fflush (stdout);
+       vec[0] = r1bindex (rmmproc, '/');
+       execvp (rmmproc, vec);
+       adios (rmmproc, "unable to exec");
+    }
+
+    /* Else just unlink the files */
+    files++;   /* advance past filevec[0] */
+    for (i = 0; i < filep; i++) {
+       if (unlink (files[i]) == NOTOK)
+           admonish (files[i], "unable to unlink");
+    }
+}
+
+
+/*
+ * Link (or copy) the message into each of
+ * the destination folders.
+ */
+
+static int
+m_file (char *msgfile, struct st_fold *folders, int nfolders, int preserve)
+{
+    int msgnum;
+    struct st_fold *fp, *ep;
+
+    for (fp = folders, ep = folders + nfolders; fp < ep; fp++) {
+       if ((msgnum = folder_addmsg (&fp->f_mp, msgfile, 1, 0, preserve)) == -1)
+           return 1;
+    }
+    return 0;
+}
diff --git a/uip/repl.c b/uip/repl.c
new file mode 100644 (file)
index 0000000..af58c59
--- /dev/null
@@ -0,0 +1,462 @@
+
+/*
+ * repl.c -- reply to a message
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+static struct swit switches[] = {
+#define GROUPSW                0
+    { "group", 0 },
+#define NGROUPSW               1
+    { "nogroup", 0 },
+#define        ANNOSW                 2
+    { "annotate", 0 },
+#define        NANNOSW                3
+    { "noannotate", 0 },
+#define        CCSW                   4
+    { "cc all|to|cc|me", 0 },
+#define        NCCSW                  5
+    { "nocc type", 0 },
+#define        DFOLDSW                6
+    { "draftfolder +folder", 0 },
+#define        DMSGSW                 7
+    { "draftmessage msg", 0 },
+#define        NDFLDSW                8
+    { "nodraftfolder", 0 },
+#define        EDITRSW                9
+    { "editor editor", 0 },
+#define        NEDITSW               10
+    { "noedit", 0 },
+#define        FCCSW                 11
+    { "fcc folder", 0 },
+#define        FILTSW                12
+    { "filter filterfile", 0 },
+#define        FORMSW                13
+    { "form formfile", 0 },
+#define        FRMTSW                14
+    { "format", 5 },
+#define        NFRMTSW               15
+    { "noformat", 7 },
+#define        INPLSW                16
+    { "inplace", 0 },
+#define        NINPLSW               17
+    { "noinplace", 0 },
+#define MIMESW                18
+    { "mime", 0 },
+#define NMIMESW               19
+    { "nomime", 0 },
+#define        QURYSW                20
+    { "query", 0 },
+#define        NQURYSW               21
+    { "noquery", 0 },
+#define        WHATSW                22
+    { "whatnowproc program", 0 },
+#define        NWHATSW               23
+    { "nowhatnowproc", 0 },
+#define        WIDTHSW               24
+    { "width columns", 0 },
+#define VERSIONSW             25
+    { "version", 0 },
+#define        HELPSW                26
+    { "help", 4 },
+#define        FILESW                27
+    { "file file", -4 },               /* interface from msh */
+
+#ifdef MHE
+#define        BILDSW                28
+    { "build", -5 },                   /* interface from mhe */
+#endif
+
+    { NULL, 0 }
+};
+
+static struct swit ccswitches[] = {
+#define        CTOSW   0
+    { "to", 0 },
+#define        CCCSW   1
+    { "cc", 0 },
+#define        CMESW   2
+    { "me", 0 },
+#define        CALSW   3
+    { "all", 0 },
+    { NULL, 0 }
+};
+
+static struct swit aqrnl[] = {
+#define        NOSW         0
+    { "quit", 0 },
+#define        YESW         1
+    { "replace", 0 },
+#define        LISTDSW      2
+    { "list", 0 },
+#define        REFILSW      3
+    { "refile +folder", 0 },
+#define NEWSW        4
+    { "new", 0 },
+    { NULL, 0 }
+};
+
+static struct swit aqrl[] = {
+    { "quit", 0 },
+    { "replace", 0 },
+    { "list", 0 },
+    { "refile +folder", 0 },
+    { NULL, 0 }
+};
+
+short ccto = 1;                /* global for replsbr */
+short cccc = 1;
+short ccme = 1;
+short querysw = 0;
+
+short outputlinelen = OUTPUTLINELEN;
+short groupreply = 0;          /* Is this a group reply?        */
+
+int mime = 0;                  /* include original as MIME part */
+char *form   = NULL;           /* form (components) file        */
+char *filter = NULL;           /* message filter file           */
+char *fcc    = NULL;           /* folders to add to Fcc: header */
+
+
+/*
+ * prototypes
+ */
+void docc (char *, int);
+
+
+int
+main (int argc, char **argv)
+{
+    int        i, isdf = 0;
+    int anot = 0, inplace = 1;
+    int nedit = 0, nwhat = 0;
+    char *cp, *cwd, *dp, *maildir, *file = NULL;
+    char *folder = NULL, *msg = NULL, *dfolder = NULL;
+    char *dmsg = NULL, *ed = NULL, drft[BUFSIZ], buf[BUFSIZ];
+    char **argp, **arguments;
+    struct msgs *mp = NULL;
+    struct stat st;
+    FILE *in;
+
+#ifdef MHE
+    int buildsw = 0;
+#endif /* MHE */
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown", cp);
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf), "%s: [+folder] [msg] [switches]",
+                       invo_name);
+                   print_help (buf, switches, 1);
+                   done (0);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case GROUPSW:
+                   groupreply++;
+                   continue;
+               case NGROUPSW:
+                   groupreply = 0;
+                   continue;
+
+               case ANNOSW: 
+                   anot++;
+                   continue;
+               case NANNOSW: 
+                   anot = 0;
+                   continue;
+
+               case CCSW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   docc (cp, 1);
+                   continue;
+               case NCCSW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   docc (cp, 0);
+                   continue;
+
+               case EDITRSW: 
+                   if (!(ed = *argp++) || *ed == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   nedit = 0;
+                   continue;
+               case NEDITSW:
+                   nedit++;
+                   continue;
+                   
+               case WHATSW: 
+                   if (!(whatnowproc = *argp++) || *whatnowproc == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   nwhat = 0;
+                   continue;
+#ifdef MHE
+               case BILDSW: 
+                   buildsw++;  /* fall... */
+#endif /* MHE */
+               case NWHATSW: 
+                   nwhat++;
+                   continue;
+
+               case FCCSW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   dp = NULL;
+                   if (*cp == '@')
+                       cp = dp = path (cp + 1, TSUBCWF);
+                   if (fcc)
+                       fcc = add (", ", fcc);
+                   fcc = add (cp, fcc);
+                   if (dp)
+                       free (dp);
+                   continue;
+
+               case FILESW: 
+                   if (file)
+                       adios (NULL, "only one file at a time!");
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   file = path (cp, TFILE);
+                   continue;
+               case FILTSW:
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   filter = getcpy (etcpath (cp));
+                   mime = 0;
+                   continue;
+               case FORMSW: 
+                   if (!(form = *argp++) || *form == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+
+               case FRMTSW: 
+                   filter = getcpy (etcpath (mhlreply));
+                   mime = 0;
+                   continue;
+               case NFRMTSW: 
+                   filter = NULL;
+                   continue;
+
+               case INPLSW: 
+                   inplace++;
+                   continue;
+               case NINPLSW: 
+                   inplace = 0;
+                   continue;
+
+               case MIMESW:
+                   mime++;
+                   filter = NULL;
+                   continue;
+               case NMIMESW:
+                   mime = 0;
+                   continue;
+
+               case QURYSW: 
+                   querysw++;
+                   continue;
+               case NQURYSW: 
+                   querysw = 0;
+                   continue;
+
+               case WIDTHSW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   if ((outputlinelen = atoi (cp)) < 10)
+                       adios (NULL, "impossible width %d", outputlinelen);
+                   continue;
+
+               case DFOLDSW: 
+                   if (dfolder)
+                       adios (NULL, "only one draft folder at a time!");
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   dfolder = path (*cp == '+' || *cp == '@' ? cp + 1 : cp,
+                                   *cp != '@' ? TFOLDER : TSUBCWF);
+                   continue;
+               case DMSGSW:
+                   if (dmsg)
+                       adios (NULL, "only one draft message at a time!");
+                   if (!(dmsg = *argp++) || *dmsg == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+               case NDFLDSW: 
+                   dfolder = NULL;
+                   isdf = NOTOK;
+                   continue;
+           }
+       }
+       if (*cp == '+' || *cp == '@') {
+           if (folder)
+               adios (NULL, "only one folder at a time!");
+           else
+               folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       } else {
+           if (msg)
+               adios (NULL, "only one message at a time!");
+           else
+               msg = cp;
+       }
+    }
+
+    cwd = getcpy (pwd ());
+
+    if (!context_find ("path"))
+       free (path ("./", TFOLDER));
+    if (file && (msg || folder))
+       adios (NULL, "can't mix files and folders/msgs");
+
+try_it_again:
+
+#ifdef MHE
+    strncpy (drft, buildsw ? m_maildir ("reply")
+                         : m_draft (dfolder, NULL, NOUSE, &isdf), sizeof(drft));
+
+    /* Check if a draft exists */
+    if (!buildsw && stat (drft, &st) != NOTOK) {
+#else
+    strncpy (drft, m_draft (dfolder, dmsg, NOUSE, &isdf), sizeof(drft));
+
+    /* Check if a draft exists */
+    if (stat (drft, &st) != NOTOK) {
+#endif /* MHE */
+       printf ("Draft \"%s\" exists (%ld bytes).", drft, (long) st.st_size);
+       for (i = LISTDSW; i != YESW;) {
+           if (!(argp = getans ("\nDisposition? ", isdf ? aqrnl : aqrl)))
+               done (1);
+           switch (i = smatch (*argp, isdf ? aqrnl : aqrl)) {
+               case NOSW: 
+                   done (0);
+               case NEWSW: 
+                   dmsg = NULL;
+                   goto try_it_again;
+               case YESW: 
+                   break;
+               case LISTDSW: 
+                   showfile (++argp, drft);
+                   break;
+               case REFILSW: 
+                   if (refile (++argp, drft) == 0)
+                       i = YESW;
+                   break;
+               default: 
+                   advise (NULL, "say what?");
+                   break;
+           }
+       }
+    }
+
+    if (file) {
+       /*
+        * We are replying to a file.
+        */
+       anot = 0;       /* we don't want to annotate a file */
+    } else {
+       /*
+        * We are replying to a message.
+        */
+       if (!msg)
+           msg = "cur";
+       if (!folder)
+           folder = getfolder (1);
+       maildir = m_maildir (folder);
+
+       if (chdir (maildir) == NOTOK)
+           adios (maildir, "unable to change directory to");
+
+       /* read folder and create message structure */
+       if (!(mp = folder_read (folder)))
+           adios (NULL, "unable to read folder %s", folder);
+
+       /* check for empty folder */
+       if (mp->nummsg == 0)
+           adios (NULL, "no messages in %s", folder);
+
+       /* parse the message range/sequence/name and set SELECTED */
+       if (!m_convert (mp, msg))
+           done (1);
+       seq_setprev (mp);       /* set the previous-sequence */
+
+       if (mp->numsel > 1)
+           adios (NULL, "only one message at a time!");
+
+       context_replace (pfolder, folder);      /* update current folder   */
+       seq_setcur (mp, mp->lowsel);    /* update current message  */
+       seq_save (mp);                  /* synchronize sequences   */
+       context_save ();                        /* save the context file   */
+    }
+
+    msg = file ? file : getcpy (m_name (mp->lowsel));
+
+    if ((in = fopen (msg, "r")) == NULL)
+       adios (msg, "unable to open");
+
+    /* find form (components) file */
+    if (!form) {
+       if (groupreply)
+           form = etcpath (replgroupcomps);
+       else
+           form = etcpath (replcomps);
+    }
+
+    replout (in, msg, drft, mp, outputlinelen, mime, form, filter, fcc);
+    fclose (in);
+
+    if (nwhat)
+       done (0);
+    what_now (ed, nedit, NOUSE, drft, msg, 0, mp,
+           anot ? "Replied" : NULL, inplace, cwd);
+    done (1);
+}
+
+void
+docc (char *cp, int ccflag)
+{
+    switch (smatch (cp, ccswitches)) {
+       case AMBIGSW: 
+           ambigsw (cp, ccswitches);
+           done (1);
+       case UNKWNSW: 
+           adios (NULL, "-%scc %s unknown", ccflag ? "" : "no", cp);
+
+       case CTOSW: 
+           ccto = ccflag;
+           break;
+
+       case CCCSW: 
+           cccc = ccflag;
+           break;
+
+       case CMESW: 
+           ccme = ccflag;
+           break;
+
+       case CALSW: 
+           ccto = cccc = ccme = ccflag;
+           break;
+    }
+}
diff --git a/uip/replsbr.c b/uip/replsbr.c
new file mode 100644 (file)
index 0000000..57eb6cd
--- /dev/null
@@ -0,0 +1,448 @@
+
+/*
+ * replsbr.c -- routines to help repl along...
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/addrsbr.h>
+#include <h/fmt_scan.h>
+#include <sys/file.h>          /* L_SET */
+
+extern short ccto;             /* from repl.c */
+extern short cccc;
+extern short ccme;
+extern short querysw;
+
+static int dftype=0;
+
+static char *badaddrs = NULL;
+static char *dfhost = NULL;
+
+static struct mailname mq = { NULL };
+
+/*
+ * Buffer size for content part of header fields.
+ * We want this to be large enough so that we don't
+ * do a lot of extra FLDPLUS calls on m_getfld but
+ * small enough so that we don't snarf the entire
+ * message body when we're not going to use any of it.
+ */
+#define SBUFSIZ 256            
+
+static struct format *fmt;
+
+static int ncomps = 0;                 /* # of interesting components */
+static char **compbuffers = NULL;      /* buffers for component text */
+static struct comp **used_buf = NULL;  /* stack for comp that use buffers */
+
+static int dat[5];                     /* aux. data for format routine */
+
+static char *addrcomps[] = {
+    "from",
+    "sender",
+    "reply-to",
+    "to",
+    "cc",
+    "bcc",
+    "resent-from",
+    "resent-sender",
+    "resent-reply-to",
+    "resent-to",
+    "resent-cc",
+    "resent-bcc",
+    NULL
+};
+
+/*
+ * static prototypes
+ */
+static int insert (struct mailname *);
+static void replfilter (FILE *, FILE *, char *);
+
+
+void
+replout (FILE *inb, char *msg, char *drft, struct msgs *mp, int outputlinelen,
+       int mime, char *form, char *filter, char *fcc)
+{
+    register int state, i;
+    register struct comp *cptr;
+    register char *tmpbuf;
+    register char **nxtbuf;
+    register char **ap;
+    register struct comp **savecomp;
+    int        char_read = 0, format_len;
+    char name[NAMESZ], *scanl, *cp;
+    FILE *out;
+
+    umask(~m_gmprot());
+    if ((out = fopen (drft, "w")) == NULL)
+       adios (drft, "unable to create");
+
+    /* get new format string */
+    cp = new_fs (form, NULL, NULL);
+    format_len = strlen (cp);
+
+    /* compile format string */
+    ncomps = fmt_compile (cp, &fmt) + 1;
+
+    if (!(nxtbuf = compbuffers = (char **)
+           calloc((size_t) ncomps, sizeof(char *))))
+       adios (NULL, "unable to allocate component buffers");
+    if (!(savecomp = used_buf = (struct comp **)
+           calloc((size_t) (ncomps+1), sizeof(struct comp *))))
+       adios (NULL, "unable to allocate component buffer stack");
+    savecomp += ncomps + 1;
+    *--savecomp = NULL;                /* point at zero'd end minus 1 */
+
+    for (i = ncomps; i--; )
+       if (!(*nxtbuf++ = malloc(SBUFSIZ)))
+           adios (NULL, "unable to allocate component buffer");
+
+    nxtbuf = compbuffers;              /* point at start */
+    tmpbuf = *nxtbuf++;
+
+    for (ap = addrcomps; *ap; ap++) {
+       FINDCOMP (cptr, *ap);
+       if (cptr)
+           cptr->c_type |= CT_ADDR;
+    }
+
+    /*
+     * ignore any components killed by command line switches
+     */
+    if (!ccto) {
+       FINDCOMP (cptr, "to");
+       if (cptr)
+           cptr->c_name = "";
+    }
+    if (!cccc) {
+       FINDCOMP (cptr, "cc");
+       if (cptr)
+           cptr->c_name = "";
+    }
+    /* set up the "fcc" pseudo-component */
+    if (fcc) {
+       FINDCOMP (cptr, "fcc");
+       if (cptr)
+           cptr->c_text = getcpy (fcc);
+    }
+    if ((cp = getenv("USER"))) {
+       FINDCOMP (cptr, "user");
+       if (cptr)
+           cptr->c_text = getcpy(cp);
+    }
+    if (!ccme)
+       ismymbox (NULL);
+
+    /*
+     * pick any interesting stuff out of msg "inb"
+     */
+    for (state = FLD;;) {
+       state = m_getfld (state, name, tmpbuf, SBUFSIZ, inb);
+       switch (state) {
+           case FLD: 
+           case FLDPLUS: 
+               /*
+                * if we're interested in this component, save a pointer
+                * to the component text, then start using our next free
+                * buffer as the component temp buffer (buffer switching
+                * saves an extra copy of the component text).
+                */
+               if ((cptr = wantcomp[CHASH(name)]))
+                   do {
+                       if (!strcasecmp(name, cptr->c_name)) {
+                           char_read += msg_count;
+                           if (! cptr->c_text) {
+                               cptr->c_text = tmpbuf;
+                               *--savecomp = cptr;
+                               tmpbuf = *nxtbuf++;
+                           } else {
+                               i = strlen (cp = cptr->c_text) - 1;
+                               if (cp[i] == '\n')
+                                   if (cptr->c_type & CT_ADDR) {
+                                       cp[i] = '\0';
+                                       cp = add (",\n\t", cp);
+                                   } else {
+                                       cp = add ("\t", cp);
+                                   }
+                               cptr->c_text = add (tmpbuf, cp);
+                           }
+                           while (state == FLDPLUS) {
+                               state = m_getfld (state, name, tmpbuf,
+                                                 SBUFSIZ, inb);
+                               cptr->c_text = add (tmpbuf, cptr->c_text);
+                               char_read += msg_count;
+                           }
+                           break;
+                       }
+                   } while ((cptr = cptr->c_next));
+
+               while (state == FLDPLUS)
+                   state = m_getfld (state, name, tmpbuf, SBUFSIZ, inb);
+               break;
+
+           case LENERR: 
+           case FMTERR: 
+           case BODY: 
+           case FILEEOF:
+               goto finished;
+
+           default: 
+               adios (NULL, "m_getfld() returned %d", state);
+       }
+    }
+
+    /*
+     * format and output the header lines.
+     */
+finished:
+
+    /*
+     * if there's a "Subject" component, strip any "Re:"s off it
+     */
+    FINDCOMP (cptr, "subject")
+    if (cptr && (cp = cptr->c_text)) {
+       register char *sp = cp;
+
+       for (;;) {
+           while (isspace(*cp))
+               cp++;
+           if(uprf(cp, "re:"))
+               cp += 3;
+           else
+               break;
+           sp = cp;
+       }
+       if (sp != cptr->c_text) {
+           cp = cptr->c_text;
+           cptr->c_text = getcpy (sp);
+           free (cp);
+       }
+    }
+    i = format_len + char_read + 256;
+    scanl = malloc ((size_t) i + 2);
+    dat[0] = 0;
+    dat[1] = 0;
+    dat[2] = 0;
+    dat[3] = outputlinelen;
+    dat[4] = 0;
+    fmt_scan (fmt, scanl, i, dat);
+    fputs (scanl, out);
+    if (badaddrs) {
+       fputs ("\nrepl: bad addresses:\n", out);
+       fputs ( badaddrs, out);
+    }
+
+    /*
+     * Check if we should filter the message
+     * or add mhn directives
+     */
+    if (filter) {
+       replfilter (inb, out, filter);
+    } else if (mime && mp) {
+           fprintf (out, "#forw [original message] +%s %s\n",
+                    mp->foldpath, m_name (mp->lowsel));
+    }
+
+    if (ferror (out))
+       adios (drft, "error writing");
+    fclose (out);
+
+    /* return dynamically allocated buffers */
+    free (scanl);
+    for (nxtbuf = compbuffers, i = ncomps; cptr = *savecomp++; nxtbuf++, i--)
+       free (cptr->c_text);    /* if not nxtbuf, nxtbuf already freed */
+    while ( i-- > 0)
+        free (*nxtbuf++);      /* free unused nxtbufs */
+    free ((char *) compbuffers);
+    free ((char *) used_buf);
+}
+
+static char *buf;              /* our current working buffer */
+static char *bufend;           /* end of working buffer */
+static char *last_dst;         /* buf ptr at end of last call */
+static unsigned int bufsiz=0;  /* current size of buf */
+
+#define BUFINCR 512            /* how much to expand buf when if fills */
+
+#define CPY(s) { cp = (s); while ((*dst++ = *cp++)) ; --dst; }
+
+/*
+ * check if there's enough room in buf for str.
+ * add more mem if needed
+ */
+#define CHECKMEM(str) \
+           if ((len = strlen (str)) >= bufend - dst) {\
+               int i = dst - buf;\
+               int n = last_dst - buf;\
+               bufsiz += ((dst + len - bufend) / BUFINCR + 1) * BUFINCR;\
+               buf = realloc (buf, bufsiz);\
+               dst = buf + i;\
+               last_dst = buf + n;\
+               if (! buf)\
+                   adios (NULL, "formataddr: couldn't get buffer space");\
+               bufend = buf + bufsiz;\
+           }
+
+
+/*
+ * fmt_scan will call this routine if the user includes the function
+ * "(formataddr {component})" in a format string.  "orig" is the
+ * original contents of the string register.  "str" is the address
+ * string to be formatted and concatenated onto orig.  This routine
+ * returns a pointer to the concatenated address string.
+ *
+ * We try to not do a lot of malloc/copy/free's (which is why we
+ * don't call "getcpy") but still place no upper limit on the
+ * length of the result string.
+ */
+char *
+formataddr (char *orig, char *str)
+{
+    register int len;
+    char baddr[BUFSIZ], error[BUFSIZ];
+    register int isgroup;
+    register char *dst;
+    register char *cp;
+    register char *sp;
+    register struct mailname *mp = NULL;
+
+    /* if we don't have a buffer yet, get one */
+    if (bufsiz == 0) {
+       buf = malloc (BUFINCR);
+       if (! buf)
+           adios (NULL, "formataddr: couldn't allocate buffer space");
+       last_dst = buf;         /* XXX */
+       bufsiz = BUFINCR - 6;  /* leave some slop */
+       bufend = buf + bufsiz;
+    }
+    /*
+     * If "orig" points to our buffer we can just pick up where we
+     * left off.  Otherwise we have to copy orig into our buffer.
+     */
+    if (orig == buf)
+       dst = last_dst;
+    else if (!orig || !*orig) {
+       dst = buf;
+       *dst = '\0';
+    } else {
+       dst = last_dst;         /* XXX */
+       CHECKMEM (orig);
+       CPY (orig);
+    }
+
+    /* concatenate all the new addresses onto 'buf' */
+    for (isgroup = 0; cp = getname (str); ) {
+       if ((mp = getm (cp, dfhost, dftype, AD_NAME, error)) == NULL) {
+           snprintf (baddr, sizeof(baddr), "\t%s -- %s\n", cp, error);
+           badaddrs = add (baddr, badaddrs);
+           continue;
+       }
+       if (isgroup && (mp->m_gname || !mp->m_ingrp)) {
+           *dst++ = ';';
+           isgroup = 0;
+       }
+       if (insert (mp)) {
+           /* if we get here we're going to add an address */
+           if (dst != buf) {
+               *dst++ = ',';
+               *dst++ = ' ';
+           }
+           if (mp->m_gname) {
+               CHECKMEM (mp->m_gname);
+               CPY (mp->m_gname);
+               isgroup++;
+           }
+           sp = adrformat (mp);
+           CHECKMEM (sp);
+           CPY (sp);
+       }
+    }
+
+    if (isgroup)
+       *dst++ = ';';
+
+    *dst = '\0';
+    last_dst = dst;
+    return (buf);
+}
+
+
+static int
+insert (struct mailname *np)
+{
+    char buffer[BUFSIZ];
+    register struct mailname *mp;
+
+    if (np->m_mbox == NULL)
+       return 0;
+
+    for (mp = &mq; mp->m_next; mp = mp->m_next) {
+       if (!strcasecmp (np->m_host, mp->m_next->m_host)
+               && !strcasecmp (np->m_mbox, mp->m_next->m_mbox))
+           return 0;
+    }
+    if (!ccme && ismymbox (np))
+       return 0;
+
+    if (querysw) {
+       snprintf (buffer, sizeof(buffer), "Reply to %s? ", adrformat (np));
+       if (!gans (buffer, anoyes))
+       return 0;
+    }
+    mp->m_next = np;
+
+#ifdef ISI
+    if (ismymbox (np))
+       ccme = 0;
+#endif
+
+    return 1;
+}
+
+
+/*
+ * Call the mhlproc
+ */
+
+static void
+replfilter (FILE *in, FILE *out, char *filter)
+{
+    int        pid;
+    char *mhl;
+
+    if (filter == NULL)
+       return;
+
+    if (access (filter, R_OK) == NOTOK)
+       adios (filter, "unable to read");
+
+    mhl = r1bindex (mhlproc, '/');
+
+    rewind (in);
+    lseek (fileno(in), (off_t) 0, SEEK_SET);
+    fflush (out);
+
+    switch (pid = vfork ()) {
+       case NOTOK: 
+           adios ("fork", "unable to");
+
+       case OK: 
+           dup2 (fileno (in), fileno (stdin));
+           dup2 (fileno (out), fileno (stdout));
+           closefds (3);
+
+           execlp (mhlproc, mhl, "-form", filter, "-noclear", NULL);
+           fprintf (stderr, "unable to exec ");
+           perror (mhlproc);
+           _exit (-1);
+
+       default: 
+           if (pidXwait (pid, mhl))
+               done (1);
+           fseek (out, 0L, SEEK_END);
+           break;
+    }
+}
diff --git a/uip/rmf.c b/uip/rmf.c
new file mode 100644 (file)
index 0000000..059e607
--- /dev/null
+++ b/uip/rmf.c
@@ -0,0 +1,245 @@
+
+/*
+ * rmf.c -- remove a folder
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+static struct swit switches[] = {
+#define        INTRSW            0
+    { "interactive", 0 },
+#define        NINTRSW           1
+    { "nointeractive", 0 },
+#define VERSIONSW         2
+    { "version", 0 },
+#define        HELPSW            3
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+/*
+ * static prototypes
+ */
+static int rmf(char *);
+static void rma (char *);
+
+
+int
+main (int argc, char **argv)
+{
+    int defolder = 0, interactive = -1;
+    char *cp, *folder = NULL, newfolder[BUFSIZ];
+    char buf[BUFSIZ], **argp, **arguments;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown", cp);
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf), "%s [+folder] [switches]",
+                       invo_name);
+                   print_help (buf, switches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case INTRSW: 
+                   interactive = 1;
+                   continue;
+               case NINTRSW: 
+                   interactive = 0;
+                   continue;
+           }
+       }
+       if (*cp == '+' || *cp == '@') {
+           if (folder)
+               adios (NULL, "only one folder at a time!");
+           else
+               folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       } else {
+           adios (NULL, "usage: %s [+folder] [switches]", invo_name);
+       }
+    }
+
+    if (!context_find ("path"))
+       free (path ("./", TFOLDER));
+    if (!folder) {
+       folder = getfolder (1);
+       defolder++;
+    }
+    if (strcmp (m_mailpath (folder), pwd ()) == 0)
+       adios (NULL, "sorry, you can't remove the current working directory");
+
+    if (interactive == -1)
+       interactive = defolder;
+
+    if (strchr (folder, '/') && (*folder != '/') && (*folder != '.')) {
+       for (cp = copy (folder, newfolder); cp > newfolder && *cp != '/'; cp--)
+           continue;
+       if (cp > newfolder)
+           *cp = '\0';
+       else
+           strncpy (newfolder, getfolder(0), sizeof(newfolder));
+    } else {
+       strncpy (newfolder, getfolder(0), sizeof(newfolder));
+    }
+
+    if (interactive) {
+       cp = concat ("Remove folder \"", folder, "\"? ", NULL);
+       if (!getanswer (cp))
+           done (0);
+       free (cp);
+    }
+
+    if (rmf (folder) == OK && strcmp (context_find (pfolder), newfolder)) {
+       printf ("[+%s now current]\n", newfolder);
+       context_replace (pfolder, newfolder);   /* update current folder */
+    }
+    context_save ();   /* save the context file */
+    done (0);
+}
+
+static int
+rmf (char *folder)
+{
+    int i, j, others;
+    register char *maildir;
+    char cur[BUFSIZ];
+    register struct dirent *dp;
+    register DIR *dd;
+
+    switch (i = chdir (maildir = m_maildir (folder))) {
+       case OK: 
+           if (access (".", W_OK) != NOTOK && access ("..", W_OK) != NOTOK)
+               break;          /* fall otherwise */
+
+       case NOTOK: 
+           snprintf (cur, sizeof(cur), "atr-%s-%s",
+                       current, m_mailpath (folder));
+           if (!context_del (cur)) {
+               printf ("[+%s de-referenced]\n", folder);
+               return OK;
+           }
+           advise (NULL, "you have no profile entry for the %s folder +%s",
+                   i == NOTOK ? "unreadable" : "read-only", folder);
+           return NOTOK;
+    }
+
+    if ((dd = opendir (".")) == NULL)
+       adios (NULL, "unable to read folder +%s", folder);
+    others = 0;
+
+    j = strlen(BACKUP_PREFIX);
+    while ((dp = readdir (dd))) {
+       switch (dp->d_name[0]) {
+           case '.': 
+               if (strcmp (dp->d_name, ".") == 0
+                       || strcmp (dp->d_name, "..") == 0)
+                   continue;   /* else fall */
+
+           case ',': 
+#ifdef MHE
+           case '+': 
+#endif /* MHE */
+#ifdef UCI
+           case '_': 
+           case '#': 
+#endif /* UCI */
+               break;
+
+           default: 
+               if (m_atoi (dp->d_name))
+                   break;
+               if (strcmp (dp->d_name, LINK) == 0
+                       || strncmp (dp->d_name, BACKUP_PREFIX, j) == 0)
+                   break;
+
+               admonish (NULL, "file \"%s/%s\" not deleted",
+                       folder, dp->d_name);
+               others++;
+               continue;
+       }
+       if (unlink (dp->d_name) == NOTOK) {
+           admonish (dp->d_name, "unable to unlink %s:", folder);
+           others++;
+       }
+    }
+
+    closedir (dd);
+
+    /*
+     * Remove any relevant private sequences
+     * or attributes from context file.
+     */
+    rma (folder);
+
+    chdir ("..");
+    if (others == 0 && remdir (maildir))
+       return OK;
+
+    advise (NULL, "folder +%s not removed", folder);
+    return NOTOK;
+}
+
+
+/*
+ * Remove all the (private) sequence information for
+ * this folder from the profile/context list.
+ */
+
+static void
+rma (char *folder)
+{
+    register int alen, j, plen;
+    register char *cp;
+    register struct node *np, *pp;
+
+    /* sanity check - check that context has been read */
+    if (defpath == NULL)
+       adios (NULL, "oops, context hasn't been read yet");
+
+    alen = strlen ("atr-");
+    plen = strlen (cp = m_mailpath (folder)) + 1;
+
+    /*
+     * Search context list for keys that look like
+     * "atr-something-folderpath", and remove them.
+     */
+    for (np = m_defs, pp = NULL; np; np = np->n_next) {
+       if (ssequal ("atr-", np->n_name)
+               && (j = strlen (np->n_name) - plen) > alen
+               && *(np->n_name + j) == '-'
+               && strcmp (cp, np->n_name + j + 1) == 0) {
+           if (!np->n_context)
+               admonish (NULL, "bug: context_del(key=\"%s\")", np->n_name);
+           if (pp) {
+               pp->n_next = np->n_next;
+               np = pp;
+           } else {
+               m_defs = np->n_next;
+           }
+           ctxflags |= CTXMOD;
+       } else {
+           pp = np;
+       }
+    }
+}
diff --git a/uip/rmm.c b/uip/rmm.c
new file mode 100644 (file)
index 0000000..1b09907
--- /dev/null
+++ b/uip/rmm.c
@@ -0,0 +1,151 @@
+
+/*
+ * rmm.c -- remove a message(s)
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+/*
+ * We allocate space for message names and ranges
+ * (msgs array) this number of elements at a time.
+ */
+#define MAXMSGS  256
+
+static struct swit switches[] = {
+#define UNLINKSW      0
+    { "unlink", 0 },
+#define NUNLINKSW    1
+    { "nounlink", 0 },
+#define VERSIONSW     2
+    { "version", 0 },
+#define        HELPSW        3
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+
+int
+main (int argc, char **argv)
+{
+    int nummsgs, maxmsgs, msgnum, unlink_msgs = 0;
+    char *cp, *maildir, *folder = NULL;
+    char buf[BUFSIZ], **argp;
+    char **arguments, **msgs;
+    struct msgs *mp;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    /*
+     * Allocate the initial space to record message
+     * names and ranges.
+     */
+    nummsgs = 0;
+    maxmsgs = MAXMSGS;
+    if (!(msgs = (char **) malloc ((size_t) (maxmsgs * sizeof(*msgs)))))
+       adios (NULL, "unable to allocate storage");
+
+    /* parse arguments */
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+           case AMBIGSW: 
+               ambigsw (cp, switches);
+               done (1);
+           case UNKWNSW: 
+               adios (NULL, "-%s unknown\n", cp);
+
+           case HELPSW: 
+               snprintf (buf, sizeof(buf), "%s [+folder] [msgs] [switches]",
+                         invo_name);
+               print_help (buf, switches, 1);
+               done (1);
+           case VERSIONSW:
+               print_version(invo_name);
+               done (1);
+
+           case UNLINKSW:
+               unlink_msgs++;
+               continue;
+           case NUNLINKSW:
+               unlink_msgs = 0;
+               continue;
+           }
+       }
+       if (*cp == '+' || *cp == '@') {
+           if (folder)
+               adios (NULL, "only one folder at a time!");
+           else
+               folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       } else {
+           /*
+            * Check if we need to allocate more space
+            * for message names/ranges.
+            */
+           if (nummsgs >= maxmsgs){
+               maxmsgs += MAXMSGS;
+               if (!(msgs = (char **) realloc (msgs,
+                            (size_t) (maxmsgs * sizeof(*msgs)))))
+                   adios (NULL, "unable to reallocate msgs storage");
+           }
+           msgs[nummsgs++] = cp;
+       }
+    }
+
+    if (!context_find ("path"))
+       free (path ("./", TFOLDER));
+    if (!nummsgs)
+       msgs[nummsgs++] = "cur";
+    if (!folder)
+       folder = getfolder (1);
+    maildir = m_maildir (folder);
+
+    if (chdir (maildir) == NOTOK)
+       adios (maildir, "unable to change directory to");
+
+    /* read folder and create message structure */
+    if (!(mp = folder_read (folder)))
+       adios (NULL, "unable to read folder %s", folder);
+
+    /* check for empty folder */
+    if (mp->nummsg == 0)
+       adios (NULL, "no messages in %s", folder);
+
+    /* parse all the message ranges/sequences and set SELECTED */
+    for (msgnum = 0; msgnum < nummsgs; msgnum++)
+       if (!m_convert (mp, msgs[msgnum]))
+           done (1);
+    seq_setprev (mp);          /* set the previous-sequence      */
+
+    /*
+     * This is hackish.  If we are using a external rmmproc,
+     * then we need to update the current folder in the
+     * context so the external rmmproc will remove files
+     * from the correct directory.  This should be moved to
+     * folder_delmsgs().
+     */
+    if (rmmproc) {
+       context_replace (pfolder, folder);
+       context_save ();
+       fflush (stdout);
+    }
+
+    /* "remove" the SELECTED messages */
+    folder_delmsgs (mp, unlink_msgs);
+
+    seq_save (mp);             /* synchronize message sequences  */
+    context_replace (pfolder, folder); /* update current folder   */
+    context_save ();                   /* save the context file   */
+    folder_free (mp);                  /* free folder structure   */
+    done (0);
+}
diff --git a/uip/scan.c b/uip/scan.c
new file mode 100644 (file)
index 0000000..1f9b8c0
--- /dev/null
@@ -0,0 +1,351 @@
+
+/*
+ * scan.c -- display a one-line "scan" listing of folder or messages
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/fmt_scan.h>
+#include <h/scansbr.h>
+#include <zotnet/tws/tws.h>
+#include <errno.h>
+
+/*
+ * We allocate space for message names (msgs array)
+ * this number of elements at a time.
+ */
+#define MAXMSGS  256
+
+
+static struct swit switches[] = {
+#define        CLRSW   0
+    { "clear", 0 },
+#define        NCLRSW  1
+    { "noclear", 0 },
+#define        FORMSW  2
+    { "form formatfile", 0 },
+#define        FMTSW   3
+    { "format string", 5 },
+#define        HEADSW  4
+    { "header", 0 },
+#define        NHEADSW 5
+    { "noheader", 0 },
+#define        WIDTHSW 6
+    { "width columns", 0 },
+#define        REVSW   7
+    { "reverse", 0 },
+#define        NREVSW  8
+    { "noreverse", 0 },
+#define        FILESW  9
+    { "file file", 4 },
+#define VERSIONSW 10
+    { "version", 0 },
+#define        HELPSW  11
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+extern int errno;
+
+/*
+ * global for sbr/formatsbr.c - yech!
+ */
+#ifdef LBL
+extern struct msgs *fmt_current_folder;        
+#endif
+
+/*
+ * prototypes
+ */
+void clear_screen(void);  /* from termsbr.c */
+
+
+int
+main (int argc, char **argv)
+{
+    int clearflag = 0, hdrflag = 0, ontty;
+    int width = 0, revflag = 0;
+    int i, state, msgnum, nummsgs, maxmsgs;
+    int seqnum[NUMATTRS], unseen, num_unseen_seq = 0;
+    char *cp, *maildir, *file = NULL, *folder = NULL;
+    char *form = NULL, *format = NULL, buf[BUFSIZ];
+    char **argp, *nfs, **arguments, **msgs;
+    struct msgs *mp;
+    FILE *in;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    mts_init (invo_name);
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    /*
+     * Allocate the initial space to record message
+     * names, ranges, and sequences.
+     */
+    nummsgs = 0;
+    maxmsgs = MAXMSGS;
+    if (!(msgs = (char **) malloc ((size_t) (maxmsgs * sizeof(*msgs)))))
+       adios (NULL, "unable to allocate storage");
+
+    /*
+     * Parse arguments
+     */
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown", cp);
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf), "%s [+folder] [msgs] [switches]",
+                       invo_name);
+                   print_help (buf, switches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case CLRSW: 
+                   clearflag++;
+                   continue;
+               case NCLRSW: 
+                   clearflag = 0;
+                   continue;
+
+               case FORMSW: 
+                   if (!(form = *argp++) || *form == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   format = NULL;
+                   continue;
+               case FMTSW: 
+                   if (!(format = *argp++) || *format == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   form = NULL;
+                   continue;
+
+               case HEADSW: 
+                   hdrflag++;
+                   continue;
+               case NHEADSW: 
+                   hdrflag = 0;
+                   continue;
+
+               case WIDTHSW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   width = atoi (cp);
+                   continue;
+               case REVSW:
+                   revflag++;
+                   continue;
+               case NREVSW:
+                   revflag = 0;
+                   continue;
+
+               case FILESW:
+                   if (!(cp = *argp++) || (cp[0] == '-' && cp[1]))
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   if (strcmp (file = cp, "-"))
+                       file = path (cp, TFILE);
+                   continue;
+           }
+       }
+       if (*cp == '+' || *cp == '@') {
+           if (folder)
+               adios (NULL, "only one folder at a time!");
+           else
+               folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       } else {
+           /*
+            * Check if we need to allocate more space
+            * for message names/ranges/sequences.
+            */
+           if (nummsgs >= maxmsgs) {
+               maxmsgs += MAXMSGS;
+               if (!(msgs = (char **) realloc (msgs,
+                       (size_t) (maxmsgs * sizeof(*msgs)))))
+                   adios (NULL, "unable to reallocate msgs storage");
+           }
+           msgs[nummsgs++] = cp;
+       }
+    }
+
+    if (!context_find ("path"))
+       free (path ("./", TFOLDER));
+
+    /*
+     * Get new format string.  Must be before chdir().
+     */
+    nfs = new_fs (form, format, FORMAT);
+
+    /*
+     * We are scanning a maildrop file
+     */
+    if (file) {
+       if (nummsgs)
+           adios (NULL, "\"msgs\" not allowed with -file");
+       if (folder)
+           adios (NULL, "\"+folder\" not allowed with -file");
+
+       /* check if "file" is really stdin */
+       if (strcmp (file, "-") == 0) {
+           in = stdin;
+           file = "stdin";
+       } else {
+           if ((in = fopen (file, "r")) == NULL)
+               adios (file, "unable to open");
+       }
+
+#ifndef        JLR
+       if (hdrflag) {
+           printf ("FOLDER %s\t%s\n", file, dtimenow (1));
+       }
+#endif /* JLR */
+
+       m_unknown (in);
+       for (msgnum = 1; ; ++msgnum) {
+           state = scan (in, msgnum, -1, nfs, width, 0, 0,
+                   hdrflag ? file : NULL, 0L, 1);
+           if (state != SCNMSG && state != SCNENC)
+               break;
+       }
+       fclose (in);
+       done (0);
+    }
+
+    /*
+     * We are scanning a folder
+     */
+
+    if (!nummsgs)
+       msgs[nummsgs++] = "all";
+    if (!folder)
+       folder = getfolder (1);
+    maildir = m_maildir (folder);
+
+    if (chdir (maildir) == NOTOK)
+       adios (maildir, "unable to change directory to");
+
+    /* read folder and create message structure */
+    if (!(mp = folder_read (folder)))
+       adios (NULL, "unable to read folder %s", folder);
+
+    /* check for empty folder */
+    if (mp->nummsg == 0)
+       adios (NULL, "no messages in %s", folder);
+
+    /* parse all the message ranges/sequences and set SELECTED */
+    for (msgnum = 0; msgnum < nummsgs; msgnum++)
+       if (!m_convert (mp, msgs[msgnum]))
+           done(1);
+    seq_setprev (mp);                  /* set the Previous-Sequence */
+
+    context_replace (pfolder, folder); /* update current folder         */
+    seq_save (mp);                     /* synchronize message sequences */
+    context_save ();                   /* save the context file         */
+
+    /*
+     * Get the sequence number for each sequence
+     * specified by Unseen-Sequence
+     */
+    if ((cp = context_find (usequence)) && *cp) {
+       char **ap, *dp;
+
+       dp = getcpy(cp);
+       ap = brkstring (dp, " ", "\n");
+       for (i = 0; ap && *ap; i++, ap++)
+           seqnum[i] = seq_getnum (mp, *ap);
+
+       num_unseen_seq = i;
+       if (dp)
+           free(dp);
+    }
+
+    ontty = isatty (fileno (stdout));
+
+#ifdef LBL
+    else
+       fmt_current_folder = mp;
+#endif
+
+    for (msgnum = revflag ? mp->hghsel : mp->lowsel;
+        (revflag ? msgnum >= mp->lowsel : msgnum <= mp->hghsel);
+        msgnum += (revflag ? -1 : 1)) {
+       if (is_selected(mp, msgnum)) {
+           if ((in = fopen (cp = m_name (msgnum), "r")) == NULL) {
+#if 0
+               if (errno != EACCES)
+#endif
+                   admonish (cp, "unable to open message");
+#if 0
+               else
+                   printf ("%*d  unreadable\n", DMAXFOLDER, msgnum);
+#endif
+               continue;
+           }
+
+#ifndef JLR
+           if (hdrflag) {
+               printf ("FOLDER %s\t%s\n", folder, dtimenow(1));
+           }
+#endif /* JLR */
+
+           /*
+            * Check if message is in any sequence given
+            * by Unseen-Sequence profile entry.
+            */
+           unseen = 0;
+           for (i = 0; i < num_unseen_seq; i++) {
+               if (in_sequence(mp, seqnum[i], msgnum)) {
+                   unseen = 1;
+                   break;
+               }
+           }
+
+           switch (state = scan (in, msgnum, 0, nfs, width,
+                       msgnum == mp->curmsg, unseen,
+                       hdrflag ? folder : NULL, 0L, 1)) {
+               case SCNMSG: 
+               case SCNENC: 
+               case SCNERR: 
+                   break;
+
+               default: 
+                   adios (NULL, "scan() botch (%d)", state);
+
+               case SCNEOF: 
+#if 0
+                   printf ("%*d  empty\n", DMAXFOLDER, msgnum);
+#else
+                   advise (NULL, "message %d: empty", msgnum);
+#endif
+                   break;
+           }
+           hdrflag = 0;
+           fclose (in);
+           if (ontty)
+               fflush (stdout);
+       }
+    }
+
+#ifdef LBL
+    seq_save (mp);     /* because formatsbr might have made changes */
+#endif
+
+    folder_free (mp);  /* free folder/message structure */
+    if (clearflag)
+       clear_screen ();
+
+    done (0);
+}
diff --git a/uip/scansbr.c b/uip/scansbr.c
new file mode 100644 (file)
index 0000000..44cbf0d
--- /dev/null
@@ -0,0 +1,386 @@
+
+/*
+ * scansbr.c -- routines to help scan along...
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/addrsbr.h>
+#include <h/fmt_scan.h>
+#include <h/scansbr.h>
+#include <zotnet/tws/tws.h>
+
+#ifdef _FSTDIO
+# define _ptr _p                /* Gag    */
+# define _cnt _w                /* Wretch */
+#endif
+
+#ifdef SCO_5_STDIO
+# define _ptr  __ptr
+# define _cnt  __cnt
+# define _base __base
+# define _filbuf(fp)  ((fp)->__cnt = 0, __filbuf(fp))
+#endif
+
+#define MAXSCANL 256           /* longest possible scan line */
+
+/*
+ * Buffer size for content part of header fields.  We want this
+ * to be large enough so that we don't do a lot of extra FLDPLUS
+ * calls on m_getfld but small enough so that we don't snarf
+ * the entire message body when we're only going to display 30
+ * characters of it.
+ */
+#define SBUFSIZ 512
+
+static struct format *fmt;
+#ifdef JLR
+static struct format *fmt_top;
+#endif /* JLR */
+
+static struct comp *datecomp;          /* pntr to "date" comp             */
+static struct comp *bodycomp;          /* pntr to "body" pseudo-comp      *
+                                        * (if referenced)                 */
+static int ncomps = 0;                 /* # of interesting components     */
+static char **compbuffers = 0;                 /* buffers for component text      */
+static struct comp **used_buf = 0;     /* stack for comp that use buffers */
+
+static int dat[5];                     /* aux. data for format routine    */
+
+char *scanl = 0;                       /* text of most recent scanline    */
+
+#define FPUTS(buf) {\
+               if (mh_fputs(buf,scnout) == EOF)\
+                   adios (scnmsg, "write error on");\
+               }
+
+/*
+ * prototypes
+ */
+int sc_width (void);                   /* from termsbr.c */
+static int mh_fputs(char *, FILE *);
+
+
+int
+scan (FILE *inb, int innum, int outnum, char *nfs, int width, int curflg,
+      int unseen, char *folder, long size, int noisy)
+{
+    int i, compnum, encrypted, state;
+    char *cp, *tmpbuf, **nxtbuf;
+    char *saved_c_text;
+    struct comp *cptr;
+    struct comp **savecomp;
+    char *scnmsg;
+    FILE *scnout;
+    char name[NAMESZ];
+    static int rlwidth, slwidth;
+
+#ifdef RPATHS
+    char returnpath[BUFSIZ];
+    char deliverydate[BUFSIZ];
+#endif
+
+    /* first-time only initialization */
+    if (!scanl) {
+       if (width == 0) {
+           if ((width = sc_width ()) < WIDTH/2)
+               width = WIDTH/2;
+           else if (width > MAXSCANL)
+               width = MAXSCANL;
+       }
+       dat[3] = slwidth = width;
+       if ((scanl = (char *) malloc((size_t) (slwidth + 2) )) == NULL)
+           adios (NULL, "unable to malloc scan line (%d bytes)", slwidth+2);
+       if (outnum)
+           umask(~m_gmprot());
+
+       /* Compile format string */
+       ncomps = fmt_compile (nfs, &fmt) + 1;
+
+#ifdef JLR
+       fmt_top = fmt;
+#endif /* JLR */
+       FINDCOMP(bodycomp, "body");
+       FINDCOMP(datecomp, "date");
+       FINDCOMP(cptr, "folder");
+       if (cptr && folder)
+           cptr->c_text = folder;
+       FINDCOMP(cptr, "encrypted");
+       if (!cptr)
+           if ((cptr = (struct comp *) calloc (1, sizeof(*cptr)))) {
+               cptr->c_name = "encrypted";
+               cptr->c_next = wantcomp[i = CHASH (cptr->c_name)];
+               wantcomp[i] = cptr;
+               ncomps++;
+       }
+       FINDCOMP (cptr, "dtimenow");
+       if (cptr)
+           cptr->c_text = getcpy(dtimenow (0));
+       nxtbuf = compbuffers = (char **) calloc((size_t) ncomps, sizeof(char *));
+       if (nxtbuf == NULL)
+           adios (NULL, "unable to allocate component buffers");
+       used_buf = (struct comp **) calloc((size_t) (ncomps+1),
+           sizeof(struct comp *));
+       if (used_buf == NULL)
+           adios (NULL, "unable to allocate component buffer stack");
+       used_buf += ncomps+1; *--used_buf = 0;
+       rlwidth = bodycomp && (width > SBUFSIZ) ? width : SBUFSIZ;
+       for (i = ncomps; i--; )
+           if ((*nxtbuf++ = malloc(rlwidth)) == NULL)
+               adios (NULL, "unable to allocate component buffer");
+    }
+
+    /*
+     * each-message initialization
+     */
+    nxtbuf = compbuffers;
+    savecomp = used_buf;
+    tmpbuf = *nxtbuf++;
+    dat[0] = innum ? innum : outnum;
+    dat[1] = curflg;
+    dat[4] = unseen;
+
+    /*
+     * Get the first field.  If the message is non-empty
+     * and we're doing an "inc", open the output file.
+     */
+    if ((state = m_getfld (FLD, name, tmpbuf, rlwidth, inb)) == FILEEOF) {
+       if (ferror(inb)) {
+           advise("read", "unable to"); /* "read error" */
+           return SCNFAT;
+       } else {
+           return SCNEOF;
+       }
+    }
+
+    if (outnum) {
+       if (outnum > 0) {
+           scnmsg = m_name (outnum);
+           if (*scnmsg == '?')         /* msg num out of range */
+               return SCNNUM;
+       } else {
+           scnmsg = "/dev/null";
+       }
+       if ((scnout = fopen (scnmsg, "w")) == NULL)
+           adios (scnmsg, "unable to write");
+#ifdef RPATHS
+       /*
+        * Add the Return-Path and Delivery-Date
+        * header fields to message.
+        */
+       if (get_returnpath (returnpath, sizeof(returnpath),
+               deliverydate, sizeof(deliverydate))) {
+           FPUTS ("Return-Path: ");
+           FPUTS (returnpath);
+           FPUTS ("Delivery-Date: ");
+           FPUTS (deliverydate);
+       }
+#endif /* RPATHS */
+    }
+
+    /* scan - main loop */
+    for (compnum = 1; ; state = m_getfld (state, name, tmpbuf, rlwidth, inb)) {
+       switch (state) {
+           case FLD: 
+           case FLDPLUS: 
+               compnum++;
+               if (outnum) {
+                   FPUTS (name);
+                   putc (':', scnout);
+                   FPUTS (tmpbuf);
+               }
+               /*
+                * if we're interested in this component, save a pointer
+                * to the component text, then start using our next free
+                * buffer as the component temp buffer (buffer switching
+                * saves an extra copy of the component text).
+                */
+               if ((cptr = wantcomp[CHASH(name)])) {
+                   do {
+                       if (!strcasecmp(name, cptr->c_name)) {
+                           if (! cptr->c_text) {
+                               cptr->c_text = tmpbuf;
+                               for (cp = tmpbuf + strlen (tmpbuf) - 1; 
+                                       cp >= tmpbuf; cp--)
+                                   if (isspace (*cp))
+                                       *cp = 0;
+                                   else
+                                       break;
+                               *--savecomp = cptr;
+                               tmpbuf = *nxtbuf++;
+                           }
+                           break;
+                       }
+                   } while ((cptr = cptr->c_next));
+               }
+
+               while (state == FLDPLUS) {
+                   state = m_getfld (state, name, tmpbuf, rlwidth, inb);
+                   if (outnum)
+                       FPUTS (tmpbuf);
+               }
+               break;
+
+           case BODY: 
+               compnum = -1;
+               if (! outnum) {
+                   state = FILEEOF; /* stop now if scan cmd */
+                   goto finished;
+               }
+               putc ('\n', scnout);
+               FPUTS (tmpbuf);
+               /*
+                * performance hack: some people like to run "inc" on
+                * things like net.sources or large digests.  We do a
+                * copy directly into the output buffer rather than
+                * going through an intermediate buffer.
+                *
+                * We need the amount of data m_getfld found & don't
+                * want to do a strlen on the long buffer so there's
+                * a hack in m_getfld to save the amount of data it
+                * returned in the global "msg_count".
+                */
+body:;
+               while (state == BODY) {
+#ifdef LINUX_STDIO
+                   if (scnout->_IO_write_ptr == scnout->_IO_write_end) {
+#else
+                   if (scnout->_cnt <= 0) {
+#endif
+                       if (fflush(scnout) == EOF)
+                           adios (scnmsg, "write error on");
+                   }
+#ifdef LINUX_STDIO
+                   state = m_getfld(state, name, scnout->_IO_write_ptr,
+                       (long)scnout->_IO_write_ptr-(long)scnout->_IO_write_end , inb);
+                   scnout->_IO_write_ptr += msg_count;
+#else
+                   state = m_getfld( state, name, scnout->_ptr, -(scnout->_cnt), inb );
+                   scnout->_cnt -= msg_count;
+                   scnout->_ptr += msg_count;
+#endif
+               }
+               goto finished;
+
+           case LENERR: 
+           case FMTERR: 
+               fprintf (stderr, 
+                       innum ? "??Format error (message %d) in "
+                             : "??Format error in ",
+                       outnum ? outnum : innum);
+               fprintf (stderr, "component %d\n", compnum);
+
+               if (outnum) {
+                   FPUTS ("\n\nBAD MSG:\n");
+                   FPUTS (name);
+                   putc ('\n', scnout);
+                   state = BODY;
+                   goto body;
+               }
+               /* fall through */
+
+           case FILEEOF:
+               goto finished;
+
+           default: 
+               adios (NULL, "getfld() returned %d", state);
+       }
+    }
+
+    /*
+     * format and output the scan line.
+     */
+finished:
+    if (ferror(inb)) {
+       advise("read", "unable to"); /* "read error" */
+       return SCNFAT;
+    }
+
+    /* Save and restore buffer so we don't trash our dynamic pool! */
+    if (bodycomp) {
+       saved_c_text = bodycomp->c_text;
+       bodycomp->c_text = tmpbuf;
+    }
+
+    if (size)
+       dat[2] = size;
+    else if (outnum > 0)
+       dat[2] = ftell(scnout);
+
+    if ((datecomp && !datecomp->c_text) || (!size && !outnum)) {
+       struct stat st;
+
+       fstat (fileno(inb), &st);
+       if (!size && !outnum)
+           dat[2] = st.st_size;
+       if (datecomp) {
+           if (! datecomp->c_text) {
+               if (datecomp->c_tws == NULL)
+                   datecomp->c_tws = (struct tws *)
+                       calloc((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);
+               datecomp->c_flags = -1;
+           } else {
+               datecomp->c_flags = 0;
+           }
+       }
+    }
+
+    fmt_scan (fmt, scanl, slwidth, dat);
+
+#if 0
+    fmt = fmt_scan (fmt, scanl, slwidth, dat);
+    if (!fmt)
+       fmt = fmt_top;          /* reset for old format files */
+#endif
+
+    if (bodycomp)
+       bodycomp->c_text = saved_c_text;
+
+    if (noisy)
+       fputs (scanl, stdout);
+
+    FINDCOMP (cptr, "encrypted");
+    encrypted = cptr && cptr->c_text;
+
+    /* return dynamically allocated buffers to pool */
+    while ((cptr = *savecomp++)) {
+       *--nxtbuf = cptr->c_text;
+       cptr->c_text = NULL;
+    }
+    *--nxtbuf = tmpbuf;
+
+    if (outnum && fclose (scnout) == EOF)
+       adios (scnmsg, "write error on");
+
+    return (state != FILEEOF ? SCNERR : encrypted ? SCNENC : SCNMSG);
+}
+
+
+/*
+ * Cheat:  we are loaded with adrparse, which wants a routine called
+ * OfficialName().  We call adrparse:getm() with the correct arguments
+ * to prevent OfficialName() from being called.  Hence, the following
+ * is to keep the loader happy.
+ */
+char *
+OfficialName (char *name)
+{
+    return name;
+}
+
+
+static int
+mh_fputs(char *s, FILE *stream)
+{
+    char c;
+
+    while ((c = *s++)) 
+       if (putc (c,stream) == EOF )
+           return(EOF);
+    return (0);
+}
+
diff --git a/uip/send.c b/uip/send.c
new file mode 100644 (file)
index 0000000..2370ea4
--- /dev/null
@@ -0,0 +1,432 @@
+
+/*
+ * send.c -- send a composed message
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+
+
+static struct swit switches[] = {
+#define        ALIASW                 0
+    { "alias aliasfile", 0 },
+#define        DEBUGSW                1
+    { "debug", -5 },
+#define        DRAFTSW                2
+    { "draft", 0 },
+#define        DFOLDSW                3
+    { "draftfolder +folder", 6 },
+#define        DMSGSW                 4
+    { "draftmessage msg", 6 },
+#define        NDFLDSW                5
+    { "nodraftfolder", 0 },
+#define        FILTSW                 6
+    { "filter filterfile", 0 },
+#define        NFILTSW                7
+    { "nofilter", 0 },
+#define        FRMTSW                 8
+    { "format", 0 },
+#define        NFRMTSW                9
+    { "noformat", 0 },
+#define        FORWSW                10
+    { "forward", 0 },
+#define        NFORWSW               11
+    { "noforward", 0 },
+#define MIMESW                12
+    { "mime", 0 },
+#define NMIMESW               13
+    { "nomime", 0 },
+#define        MSGDSW                14
+    { "msgid", 0 },
+#define        NMSGDSW               15
+    { "nomsgid", 0 },
+#define        PUSHSW                16
+    { "push", 0 },
+#define        NPUSHSW               17
+    { "nopush", 0 },
+#define        SPLITSW               18
+    { "split seconds", 0 },
+#define        UNIQSW                19
+    { "unique", -6 },
+#define        NUNIQSW               20
+    { "nounique", -8 },
+#define        VERBSW                21
+    { "verbose", 0 },
+#define        NVERBSW               22
+    { "noverbose", 0 },
+#define        WATCSW                23
+    { "watch", 0 },
+#define        NWATCSW               24
+    { "nowatch", 0 },
+#define        WIDTHSW               25
+    { "width columns", 0 },
+#define VERSIONSW             26
+    { "version", 0 },
+#define        HELPSW                27
+    { "help", 4 },
+#define BITSTUFFSW            28
+    { "dashstuffing", -12 },
+#define NBITSTUFFSW           29
+    { "nodashstuffing", -14 },
+#define        MAILSW                30
+    { "mail", -4 },
+#define        SAMLSW                31
+    { "saml", -4 },
+#define        SENDSW                32
+    { "send", -4 },
+#define        SOMLSW                33
+    { "soml", -4 },
+#define        CLIESW                34
+    { "client host", -6 },
+#define        SERVSW                35
+    { "server host", -6 },
+#define        SNOOPSW               36
+    { "snoop", -5 },
+    { NULL, 0 }
+};
+
+static struct swit anyl[] = {
+#define        NOSW     0
+    { "no", 0 },
+#define        YESW     1
+    { "yes", 0 },
+#define        LISTDSW  2
+    { "list", 0 },
+    { NULL, 0 }
+};
+
+extern int debugsw;            /* from sendsbr.c */
+extern int forwsw;
+extern int inplace;
+extern int pushsw;
+extern int splitsw;
+extern int unique;
+extern int verbsw;
+
+extern char *altmsg;           /*  .. */
+extern char *annotext;
+extern char *distfile;
+
+extern int   errno;
+
+int
+main (int argc, char **argv)
+{
+    int msgp = 0, distsw = 0, vecp = 1;
+    int isdf = 0, mime = 0;
+    int msgnum, status;
+    char *cp, *dfolder = NULL, *maildir = NULL;
+    char buf[BUFSIZ], **ap, **argp, **arguments;
+    char *msgs[MAXARGS], *vec[MAXARGS];
+    struct msgs *mp;
+    struct stat st;
+#ifdef UCI
+    FILE *fp;
+#endif /* UCI */
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    vec[vecp++] = "-library";
+    vec[vecp++] = getcpy (m_maildir (""));
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown\n", cp);
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf), "%s [file] [switches]", invo_name);
+                   print_help (buf, switches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case DRAFTSW: 
+                   msgs[msgp++] = draft;
+                   continue;
+
+               case DFOLDSW: 
+                   if (dfolder)
+                       adios (NULL, "only one draft folder at a time!");
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   dfolder = path (*cp == '+' || *cp == '@' ? cp + 1 : cp,
+                           *cp != '@' ? TFOLDER : TSUBCWF);
+                   continue;
+               case DMSGSW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   msgs[msgp++] = cp;
+                   continue;
+               case NDFLDSW: 
+                   dfolder = NULL;
+                   isdf = NOTOK;
+                   continue;
+
+               case PUSHSW: 
+                   pushsw++;
+                   continue;
+               case NPUSHSW: 
+                   pushsw = 0;
+                   continue;
+
+               case SPLITSW: 
+                   if (!(cp = *argp++) || sscanf (cp, "%d", &splitsw) != 1)
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+
+               case UNIQSW: 
+                   unique++;
+                   continue;
+               case NUNIQSW: 
+                   unique = 0;
+                   continue;
+
+               case FORWSW:
+                   forwsw++;
+                   continue;
+               case NFORWSW:
+                   forwsw = 0;
+                   continue;
+
+               case VERBSW: 
+                   verbsw++;
+                   vec[vecp++] = --cp;
+                   continue;
+               case NVERBSW:
+                   verbsw = 0;
+                   vec[vecp++] = --cp;
+                   continue;
+
+               case MIMESW:
+                   mime++;
+                   vec[vecp++] = --cp;
+                   continue;
+               case NMIMESW:
+                   mime = 0;
+                   vec[vecp++] = --cp;
+                   continue;
+
+               case DEBUGSW: 
+                   debugsw++;  /* fall */
+               case NFILTSW: 
+               case FRMTSW: 
+               case NFRMTSW: 
+               case BITSTUFFSW:
+               case NBITSTUFFSW:
+               case MSGDSW: 
+               case NMSGDSW: 
+               case WATCSW: 
+               case NWATCSW: 
+               case MAILSW: 
+               case SAMLSW: 
+               case SENDSW: 
+               case SOMLSW: 
+               case SNOOPSW: 
+                   vec[vecp++] = --cp;
+                   continue;
+
+               case ALIASW: 
+               case FILTSW: 
+               case WIDTHSW: 
+               case CLIESW: 
+               case SERVSW: 
+                   vec[vecp++] = --cp;
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   vec[vecp++] = cp;
+                   continue;
+           }
+       } else {
+           msgs[msgp++] = cp;
+       }
+    }
+
+    /*
+     * check for "Aliasfile:" profile entry
+     */
+    if ((cp = context_find ("Aliasfile"))) {
+       char *dp = NULL;
+
+       for (ap = brkstring(dp = getcpy(cp), " ", "\n"); ap && *ap; ap++) {
+           vec[vecp++] = "-alias";
+           vec[vecp++] = *ap;
+       }
+    }
+
+    if (dfolder == NULL) {
+       if (msgp == 0) {
+#ifdef WHATNOW
+           if ((cp = getenv ("mhdraft")) && *cp) {
+               msgs[msgp++] = cp;
+               goto go_to_it;
+           }
+#endif /* WHATNOW */
+           msgs[msgp++] = getcpy (m_draft (NULL, NULL, 1, &isdf));
+           if (stat (msgs[0], &st) == NOTOK)
+               adios (msgs[0], "unable to stat draft file");
+           cp = concat ("Use \"", msgs[0], "\"? ", NULL);
+           for (status = LISTDSW; status != YESW;) {
+               if (!(argp = getans (cp, anyl)))
+                   done (1);
+               switch (status = smatch (*argp, anyl)) {
+                   case NOSW: 
+                       done (0);
+                   case YESW: 
+                       break;
+                   case LISTDSW: 
+                       showfile (++argp, msgs[0]);
+                       break;
+                   default:
+                       advise (NULL, "say what?");
+                       break;
+               }
+           }
+       } else {
+           for (msgnum = 0; msgnum < msgp; msgnum++)
+               msgs[msgnum] = getcpy (m_maildir (msgs[msgnum]));
+       }
+    } else {
+       if (!context_find ("path"))
+           free (path ("./", TFOLDER));
+
+       if (!msgp)
+           msgs[msgp++] = "cur";
+       maildir = m_maildir (dfolder);
+
+       if (chdir (maildir) == NOTOK)
+           adios (maildir, "unable to change directory to");
+
+       /* read folder and create message structure */
+       if (!(mp = folder_read (dfolder)))
+           adios (NULL, "unable to read folder %s", dfolder);
+
+       /* check for empty folder */
+       if (mp->nummsg == 0)
+           adios (NULL, "no messages in %s", dfolder);
+
+       /* parse all the message ranges/sequences and set SELECTED */
+       for (msgnum = 0; msgnum < msgp; msgnum++)
+           if (!m_convert (mp, msgs[msgnum]))
+               done (1);
+       seq_setprev (mp);       /* set the previous-sequence */
+
+       for (msgp = 0, msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
+           if (is_selected (mp, msgnum)) {
+               msgs[msgp++] = getcpy (m_name (msgnum));
+               unset_exists (mp, msgnum);
+           }
+       }
+
+       mp->msgflags |= SEQMOD;
+       seq_save (mp);
+    }
+
+#ifdef WHATNOW
+go_to_it:
+#endif /* WHATNOW */
+
+    if ((cp = getenv ("SIGNATURE")) == NULL || *cp == 0)
+       if ((cp = context_find ("signature")) && *cp)
+           m_putenv ("SIGNATURE", cp);
+#ifdef UCI
+       else {
+           snprintf (buf, sizeof(buf), "%s/.signature", mypath);
+           if ((fp = fopen (buf, "r")) != NULL
+               && fgets (buf, sizeof buf, fp) != NULL) {
+                   fclose (fp);
+                   if (cp = strchr (buf, '\n'))
+                       *cp = 0;
+                   m_putenv ("SIGNATURE", buf);
+           }
+       }
+#endif /* UCI */
+
+    for (msgnum = 0; msgnum < msgp; msgnum++)
+       if (stat (msgs[msgnum], &st) == NOTOK)
+           adios (msgs[msgnum], "unable to stat draft file");
+
+    if ((annotext = getenv ("mhannotate")) == NULL || *annotext == 0)
+       annotext = NULL;
+    if (annotext && ((cp = getenv ("mhinplace")) != NULL && *cp != 0))
+       inplace = atoi (cp);
+    if ((altmsg = getenv ("mhaltmsg")) == NULL || *altmsg == 0)
+       altmsg = NULL;  /* used by dist interface - see below */
+
+    if ((cp = getenv ("mhdist"))
+           && *cp
+           && (distsw = atoi (cp))
+           && altmsg) {
+       vec[vecp++] = "-dist";
+       distfile = getcpy (m_scratch (altmsg, invo_name));
+       if (link (altmsg, distfile) == NOTOK) {
+           if (errno != EXDEV
+#ifdef EISREMOTE
+                   && errno != EISREMOTE
+#endif /* EISREMOTE */
+               )
+               adios (distfile, "unable to link %s to", altmsg);
+           free (distfile);
+           distfile = getcpy (m_tmpfil (invo_name));
+           {
+               int in, out;
+               struct stat st;
+
+               if ((in = open (altmsg, O_RDONLY)) == NOTOK)
+                   adios (altmsg, "unable to open");
+               fstat(in, &st);
+               if ((out = creat (distfile, (int) st.st_mode & 0777)) == NOTOK)
+                   adios (distfile, "unable to write");
+               cpydata (in, out, altmsg, distfile);
+               close (in);
+               close (out);
+           }   
+       }
+    } else {
+       distfile = NULL;
+    }
+
+    if (altmsg == NULL || stat (altmsg, &st) == NOTOK) {
+       st.st_mtime = 0;
+       st.st_dev = 0;
+       st.st_ino = 0;
+    }
+    if (pushsw)
+       push ();
+
+    status = 0;
+    vec[0] = r1bindex (postproc, '/');
+    closefds (3);
+
+    for (msgnum = 0; msgnum < msgp; msgnum++) {
+       switch (sendsbr (vec, vecp, msgs[msgnum], &st, 1)) {
+           case DONE: 
+               done (++status);
+           case NOTOK: 
+               status++;       /* fall */
+           case OK:
+               break;
+       }
+    }
+
+    context_save ();   /* save the context file */
+    done (status);
+}
diff --git a/uip/sendsbr.c b/uip/sendsbr.c
new file mode 100644 (file)
index 0000000..245a538
--- /dev/null
@@ -0,0 +1,650 @@
+
+/*
+ * sendsbr.c -- routines to help WhatNow/Send along
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/signals.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <h/mime.h>
+
+int debugsw = 0;               /* global */
+int forwsw  = 1;
+int inplace = 1;
+int pushsw  = 0;
+int splitsw = -1;
+int unique  = 0;
+int verbsw  = 0;
+
+char *altmsg   = NULL;         /*  .. */
+char *annotext = NULL;
+char *distfile = NULL;
+
+static int armed = 0;
+static jmp_buf env;
+
+/*
+ * external prototypes
+ */
+int sendsbr (char **, int, char *, struct stat *, int);
+void done (int);
+char *getusername (void);
+
+/*
+ * static prototypes
+ */
+static void alert (char *, int);
+static int tmp_fd (void);
+static void anno (int, struct stat *);
+static void annoaux (int);
+static int splitmsg (char **, int, char *, struct stat *, int);
+static int sendaux (char **, int, char *, struct stat *);
+
+
+/*
+ * Entry point into (back-end) routines to send message.
+ */
+
+int
+sendsbr (char **vec, int vecp, char *drft, struct stat *st, int rename_drft)
+{
+    int status;
+    char buffer[BUFSIZ], file[BUFSIZ];
+    struct stat sts;
+
+    armed++;
+    switch (setjmp (env)) {
+    case OK: 
+       /*
+        * If given -push and -unique (which is undocumented), then
+        * rename the draft file.  I'm not quite sure why.
+        */
+       if (pushsw && unique) {
+           if (rename (drft, strncpy (file, m_scratch (drft, invo_name), sizeof(file)))
+                   == NOTOK)
+               adios (file, "unable to rename %s to", drft);
+           drft = file;
+       }
+
+       /*
+        * Check if we need to split the message into
+        * multiple messages of type "message/partial".
+        */
+       if (splitsw >= 0 && !distfile && stat (drft, &sts) != NOTOK
+               && sts.st_size >= CPERMSG) {
+           status = splitmsg (vec, vecp, drft, st, splitsw) ? NOTOK : OK;
+       } else {
+           status = sendaux (vec, vecp, drft, st) ? NOTOK : OK;
+       }
+
+       /* rename the original draft */
+       if (rename_drft && status == OK &&
+               rename (drft, strncpy (buffer, m_backup (drft), sizeof(buffer))) == NOTOK)
+           advise (buffer, "unable to rename %s to", drft);
+       break;
+
+    default: 
+       status = DONE;
+       break;
+    }
+
+    armed = 0;
+    if (distfile)
+       unlink (distfile);
+
+    return status;
+}
+
+
+/*
+ * Split large message into several messages of
+ * type "message/partial" and send them.
+ */
+
+static int
+splitmsg (char **vec, int vecp, char *drft, struct stat *st, int delay)
+{
+    int        compnum, nparts, partno, state, status;
+    long pos, start;
+    time_t clock;
+    char *cp, *dp, buffer[BUFSIZ], msgid[BUFSIZ];
+    char subject[BUFSIZ];
+    char name[NAMESZ], partnum[BUFSIZ];
+    FILE *in;
+
+    if ((in = fopen (drft, "r")) == NULL)
+       adios (drft, "unable to open for reading");
+
+    cp = dp = NULL;
+    start = 0L;
+
+    /*
+     * Scan through the message and examine the various header fields,
+     * as well as locate the beginning of the message body.
+     */
+    for (compnum = 1, state = FLD;;) {
+       switch (state = m_getfld (state, name, buffer, sizeof(buffer), in)) {
+           case FLD:
+           case FLDPLUS:
+           case FLDEOF:
+               compnum++;
+
+               /*
+                * This header field is discarded.
+                */
+               if (!strcasecmp (name, "Message-ID")) {
+                   while (state == FLDPLUS)
+                       state = m_getfld (state, name, buffer, sizeof(buffer), in);
+               } else if (uprf (name, XXX_FIELD_PRF)
+                       || !strcasecmp (name, VRSN_FIELD)
+                       || !strcasecmp (name, "Subject")
+                       || !strcasecmp (name, "Encrypted")) {
+                   /*
+                    * These header fields are copied to the enclosed
+                    * header of the first message in the collection
+                    * of message/partials.  For the "Subject" header
+                    * field, we also record it, so that a modified
+                    * version of it, can be copied to the header
+                    * of each messsage/partial in the collection.
+                    */
+                   if (!strcasecmp (name, "Subject")) {
+                       size_t sublen;
+
+                       strncpy (subject, buffer, BUFSIZ);
+                       sublen = strlen (subject);
+                       if (sublen > 0 && subject[sublen - 1] == '\n')
+                           subject[sublen - 1] = '\0';
+                   }
+
+                   dp = add (concat (name, ":", buffer, NULL), dp);
+                   while (state == FLDPLUS) {
+                       state = m_getfld (state, name, buffer, sizeof(buffer), in);
+                       dp = add (buffer, dp);
+                   }
+               } else {
+                   /*
+                    * These header fields are copied to the header of
+                    * each message/partial in the collection.
+                    */
+                   cp = add (concat (name, ":", buffer, NULL), cp);
+                   while (state == FLDPLUS) {
+                       state = m_getfld (state, name, buffer, sizeof(buffer), in);
+                       cp = add (buffer, cp);
+                   }
+               }
+
+               if (state != FLDEOF) {
+                   start = ftell (in) + 1;
+                   continue;
+               }
+               /* else fall... */
+
+          case BODY:
+          case BODYEOF:
+          case FILEEOF:
+               break;
+
+          case LENERR:
+          case FMTERR:
+               adios (NULL, "message format error in component #%d", compnum);
+
+          default:
+               adios (NULL, "getfld () returned %d", state);
+       }
+
+       break;
+    }
+    if (cp == NULL)
+       adios (NULL, "headers missing from draft");
+
+    nparts = 1;
+    pos = start;
+    while (fgets (buffer, sizeof(buffer) - 1, in)) {
+       long len;
+
+       if ((pos += (len = strlen (buffer))) > CPERMSG) {
+           nparts++;
+           pos = len;
+       }
+    }
+
+    /* Only one part, nothing to split */
+    if (nparts == 1) {
+       free (cp);
+       if (dp)
+           free (dp);
+
+       fclose (in);
+       return sendaux (vec, vecp, drft, st);
+    }
+
+    if (!pushsw) {
+       printf ("Sending as %d Partial Messages\n", nparts);
+       fflush (stdout);
+    }
+    status = OK;
+
+    vec[vecp++] = "-partno";
+    vec[vecp++] = partnum;
+    if (delay == 0)
+       vec[vecp++] = "-queued";
+
+    time (&clock);
+    snprintf (msgid, sizeof(msgid), "<%d.%ld@%s>",
+               (int) getpid(), (long) clock, LocalName());
+
+    fseek (in, start, SEEK_SET);
+    for (partno = 1; partno <= nparts; partno++) {
+       char tmpdrf[BUFSIZ];
+       FILE *out;
+
+       strncpy (tmpdrf, m_scratch (drft, invo_name), sizeof(tmpdrf));
+       if ((out = fopen (tmpdrf, "w")) == NULL)
+           adios (tmpdrf, "unable to open for writing");
+       chmod (tmpdrf, 0600);
+
+       /*
+        * Output the header fields
+        */
+       fputs (cp, out);
+       fprintf (out, "Subject: %s (part %d of %d)\n", subject, partno, nparts);
+       fprintf (out, "%s: %s\n", VRSN_FIELD, VRSN_VALUE);
+       fprintf (out, "%s: message/partial; id=\"%s\";\n", TYPE_FIELD, msgid);
+       fprintf (out, "\tnumber=%d; total=%d\n", partno, nparts);
+       fprintf (out, "%s: part %d of %d\n\n", DESCR_FIELD, partno, nparts);
+
+       /*
+        * If this is the first in the collection, output the
+        * header fields we are encapsulating at the beginning
+        * of the body of the first message.
+        */
+       if (partno == 1) {
+           if (dp)
+               fputs (dp, out);
+           fprintf (out, "Message-ID: %s\n", msgid);
+           fprintf (out, "\n");
+       }
+
+       pos = 0;
+       for (;;) {
+           long len;
+
+           if (!fgets (buffer, sizeof(buffer) - 1, in)) {
+               if (partno == nparts)
+                   break;
+               adios (NULL, "premature eof");
+           }
+           
+           if ((pos += (len = strlen (buffer))) > CPERMSG) {
+               fseek (in, -len, SEEK_CUR);
+               break;
+           }
+
+           fputs (buffer, out);
+       }
+
+       if (fflush (out))
+           adios (tmpdrf, "error writing to");
+
+       fclose (out);
+
+       if (!pushsw && verbsw) {
+           printf ("\n");
+           fflush (stdout);
+       }
+
+       /* Pause here, if a delay is specified */
+       if (delay > 0 && 1 < partno && partno <= nparts) {
+           if (!pushsw) {
+               printf ("pausing %d seconds before sending part %d...\n",
+                       delay, partno);
+               fflush (stdout);
+           }
+           sleep ((unsigned int) delay);
+       }
+
+       snprintf (partnum, sizeof(partnum), "%d", partno);
+       status = sendaux (vec, vecp, tmpdrf, st);
+       unlink (tmpdrf);
+       if (status != OK)
+           break;
+
+       /*
+        * This is so sendaux will only annotate
+        * the altmsg the first time it is called.
+        */
+       annotext = NULL;
+    }
+
+    free (cp);
+    if (dp)
+       free (dp);
+
+    fclose (in);       /* close the draft */
+    return status;
+}
+
+
+/*
+ * Annotate original message, and
+ * call `postproc' to send message.
+ */
+
+static int
+sendaux (char **vec, int vecp, char *drft, struct stat *st)
+{
+    pid_t child_id;
+    int i, status, fd, fd2;
+    char backup[BUFSIZ], buf[BUFSIZ];
+
+    fd = pushsw ? tmp_fd () : NOTOK;
+    fd2 = NOTOK;
+
+    vec[vecp++] = drft;
+    if (annotext) {
+       if ((fd2 = tmp_fd ()) != NOTOK) {
+           vec[vecp++] = "-idanno";
+           snprintf (buf, sizeof(buf), "%d", fd2);
+           vec[vecp++] = buf;
+       } else {
+           admonish (NULL, "unable to create file for annotation list");
+       }
+    }
+    if (distfile && distout (drft, distfile, backup) == NOTOK)
+       done (1);
+    vec[vecp] = NULL;
+
+    for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
+       sleep (5);
+
+    switch (child_id) {
+    case -1:
+       /* oops -- fork error */
+       adios ("fork", "unable to");
+       break;  /* NOT REACHED */
+
+    case 0:
+       /*
+        * child process -- send it
+        *
+        * If fd is ok, then we are pushing and fd points to temp
+        * file, so capture anything on stdout and stderr there.
+        */
+       if (fd != NOTOK) {
+           dup2 (fd, fileno (stdout));
+           dup2 (fd, fileno (stderr));
+           close (fd);
+       }
+       execvp (postproc, vec);
+       fprintf (stderr, "unable to exec ");
+       perror (postproc);
+       _exit (-1);
+       break;  /* NOT REACHED */
+
+    default:
+       /*
+        * parent process -- wait for it
+        */
+       if ((status = pidwait(child_id, NOTOK)) == OK) {
+           if (annotext && fd2 != NOTOK)
+               anno (fd2, st);
+       } else {
+           /*
+            * If postproc failed, and we have good fd (which means
+            * we pushed), then mail error message (and possibly the
+            * draft) back to the user.
+            */
+           if (fd != NOTOK) {
+               alert (drft, fd);
+               close (fd);
+           } else {
+               advise (NULL, "message not delivered to anyone");
+           }
+           if (annotext && fd2 != NOTOK)
+               close (fd2);
+           if (distfile) {
+               unlink (drft);
+               if (rename (backup, drft) == NOTOK)
+                   advise (drft, "unable to rename %s to", backup);
+           }
+       }
+       break;
+    }
+
+    return status;
+}
+
+
+/*
+ * Mail error notification (and possibly a copy of the
+ * message) back to the user, using the mailproc
+ */
+
+static void
+alert (char *file, int out)
+{
+    pid_t child_id;
+    int i, in;
+    char buf[BUFSIZ];
+
+    for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
+       sleep (5);
+
+    switch (child_id) {
+       case NOTOK:
+           /* oops -- fork error */
+           advise ("fork", "unable to");
+
+       case OK:
+           /* child process -- send it */
+           SIGNAL (SIGHUP, SIG_IGN);
+           SIGNAL (SIGINT, SIG_IGN);
+           SIGNAL (SIGQUIT, SIG_IGN);
+           SIGNAL (SIGTERM, SIG_IGN);
+           if (forwsw) {
+               if ((in = open (file, O_RDONLY)) == NOTOK) {
+                   admonish (file, "unable to re-open");
+               } else {
+                   lseek (out, (off_t) 0, SEEK_END);
+                   strncpy (buf, "\nMessage not delivered to anyone.\n", sizeof(buf));
+                   write (out, buf, strlen (buf));
+                   strncpy (buf, "\n------- Unsent Draft\n\n", sizeof(buf));
+                   write (out, buf, strlen (buf));
+                   cpydgst (in, out, file, "temporary file");
+                   close (in);
+                   strncpy (buf, "\n------- End of Unsent Draft\n", sizeof(buf));
+                   write (out, buf, strlen (buf));
+                   if (rename (file, strncpy (buf, m_backup (file), sizeof(buf))) == NOTOK)
+                       admonish (buf, "unable to rename %s to", file);
+               }
+           }
+           lseek (out, (off_t) 0, SEEK_SET);
+           dup2 (out, fileno (stdin));
+           close (out);
+           /* create subject for error notification */
+           snprintf (buf, sizeof(buf), "send failed on %s",
+                       forwsw ? "enclosed draft" : file);
+
+           execlp (mailproc, r1bindex (mailproc, '/'), getusername (),
+                   "-subject", buf, NULL);
+           fprintf (stderr, "unable to exec ");
+           perror (mailproc);
+           _exit (-1);
+
+       default:                /* no waiting... */
+           break;
+    }
+}
+
+
+static int
+tmp_fd (void)
+{
+    int fd;
+    char tmpfil[BUFSIZ];
+
+    strncpy (tmpfil, m_tmpfil (invo_name), sizeof(tmpfil));
+    if ((fd = open (tmpfil, O_RDWR | O_CREAT | O_TRUNC, 0600)) == NOTOK)
+       return NOTOK;
+    if (debugsw)
+       advise (NULL, "temporary file %s selected", tmpfil);
+    else
+       if (unlink (tmpfil) == NOTOK)
+           advise (tmpfil, "unable to remove");
+
+    return fd;
+}
+
+
+static void
+anno (int fd, struct stat *st)
+{
+    pid_t child_id;
+    sigset_t set, oset;
+    static char *cwd = NULL;
+    struct stat st2;
+
+    if (altmsg &&
+           (stat (altmsg, &st2) == NOTOK
+               || st->st_mtime != st2.st_mtime
+               || st->st_dev != st2.st_dev
+               || st->st_ino != st2.st_ino)) {
+       if (debugsw)
+           admonish (NULL, "$mhaltmsg mismatch");
+       return;
+    }
+
+    child_id = debugsw ? NOTOK : fork ();
+    switch (child_id) {
+       case NOTOK:             /* oops */
+           if (!debugsw)
+               advise (NULL,
+                           "unable to fork, so doing annotations by hand...");
+           if (cwd == NULL)
+               cwd = getcpy (pwd ());
+
+       case OK: 
+           /* block a few signals */
+           sigemptyset (&set);
+           sigaddset (&set, SIGHUP);
+           sigaddset (&set, SIGINT);
+           sigaddset (&set, SIGQUIT);
+           sigaddset (&set, SIGTERM);
+           SIGPROCMASK (SIG_BLOCK, &set, &oset);
+
+           annoaux (fd);
+           if (child_id == OK)
+               _exit (0);
+
+           /* reset the signal mask */
+           SIGPROCMASK (SIG_SETMASK, &oset, &set);
+
+           chdir (cwd);
+           break;
+
+       default:                /* no waiting... */
+           close (fd);
+           break;
+    }
+}
+
+
+static void
+annoaux (int fd)
+{
+    int        fd2, fd3, msgnum;
+    char *cp, *folder, *maildir;
+    char buffer[BUFSIZ], **ap;
+    FILE *fp;
+    struct msgs *mp;
+
+    if ((folder = getenv ("mhfolder")) == NULL || *folder == 0) {
+       if (debugsw)
+           admonish (NULL, "$mhfolder not set");
+       return;
+    }
+    maildir = m_maildir (folder);
+    if (chdir (maildir) == NOTOK) {
+       if (debugsw)
+           admonish (maildir, "unable to change directory to");
+       return;
+    }
+    if (!(mp = folder_read (folder))) {
+       if (debugsw)
+           admonish (NULL, "unable to read folder %s");
+       return;
+    }
+
+    /* check for empty folder */
+    if (mp->nummsg == 0) {
+       if (debugsw)
+           admonish (NULL, "no messages in %s", folder);
+       goto oops;
+    }
+
+    if ((cp = getenv ("mhmessages")) == NULL || *cp == 0) {
+       if (debugsw)
+           admonish (NULL, "$mhmessages not set");
+       goto oops;
+    }
+    if (!debugsw                       /* MOBY HACK... */
+           && pushsw
+           && (fd3 = open ("/dev/null", O_RDWR)) != NOTOK
+           && (fd2 = dup (fileno (stderr))) != NOTOK) {
+       dup2 (fd3, fileno (stderr));
+       close (fd3);
+    }
+    else
+       fd2 = NOTOK;
+    for (ap = brkstring (cp = getcpy (cp), " ", NULL); *ap; ap++)
+       m_convert (mp, *ap);
+    free (cp);
+    if (fd2 != NOTOK)
+       dup2 (fd2, fileno (stderr));
+    if (mp->numsel == 0) {
+       if (debugsw)
+           admonish (NULL, "no messages to annotate");
+       goto oops;
+    }
+
+    lseek (fd, (off_t) 0, SEEK_SET);
+    if ((fp = fdopen (fd, "r")) == NULL) {
+       if (debugsw)
+           admonish (NULL, "unable to fdopen annotation list");
+       goto oops;
+    }
+    cp = NULL;
+    while (fgets (buffer, sizeof(buffer), fp) != NULL)
+       cp = add (buffer, cp);
+    fclose (fp);
+
+    if (debugsw)
+       advise (NULL, "annotate%s with %s: \"%s\"",
+               inplace ? " inplace" : "", annotext, cp);
+    for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
+       if (is_selected(mp, msgnum)) {
+           if (debugsw)
+               advise (NULL, "annotate message %d", msgnum);
+           annotate (m_name (msgnum), annotext, cp, inplace, 1);
+       }
+    }
+
+    free (cp);
+
+oops:
+    folder_free (mp);  /* free folder/message structure */
+}
+
+
+void
+done (int status)
+{
+    if (armed)
+       longjmp (env, status ? status : NOTOK);
+
+    exit (status);
+}
diff --git a/uip/show.c b/uip/show.c
new file mode 100644 (file)
index 0000000..5ab6370
--- /dev/null
@@ -0,0 +1,532 @@
+
+/*
+ * show.c -- show/list messages
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/mime.h>
+
+static struct swit switches[] = {
+#define CHECKMIMESW          0
+    { "checkmime", 0 },
+#define NOCHECKMIMESW        1
+    { "nocheckmime", 0 },
+#define        HEADSW               2
+    { "header", 0 },
+#define        NHEADSW              3
+    { "noheader", 0 },
+#define        FORMSW               4
+    { "form formfile", 0 },
+#define        PROGSW               5
+    { "moreproc program", 0 },
+#define        NPROGSW              6
+    { "nomoreproc", 0 },
+#define        LENSW                7
+    { "length lines", 0 },
+#define        WIDTHSW              8
+    { "width columns", 0 },
+#define        SHOWSW               9
+    { "showproc program", 0 },
+#define SHOWMIMESW          10
+    { "showmimeproc program", 0 },
+#define        NSHOWSW             11
+    { "noshowproc", 0 },
+#define        DRFTSW              12
+    { "draft", 0 },
+#define        FILESW              13
+    { "file file", -4 },               /* interface from showfile */
+#define VERSIONSW           14
+    { "version", 0 },
+#define        HELPSW              15
+    { "help", 0 },
+    { NULL, 0 }
+};
+
+/*
+ * static prototypes
+ */
+static int is_nontext(char *);
+
+#define        SHOW  0
+#define        NEXT  1
+#define        PREV  2
+
+
+int
+main (int argc, char **argv)
+{
+    int draftsw = 0, headersw = 1, msgp = 0;
+    int nshow = 0, checkmime = 1, mime;
+    int vecp = 1, procp = 1, isdf = 0, mode = SHOW, msgnum;
+    char *cp, *maildir, *file = NULL, *folder = NULL, *proc;
+    char buf[BUFSIZ], **argp, **arguments;
+    char *msgs[MAXARGS], *vec[MAXARGS];
+    struct msgs *mp;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    if (!strcasecmp (invo_name, "next")) {
+       mode = NEXT;
+    } else if (!strcasecmp (invo_name, "prev")) {
+       mode = PREV;
+    }
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+               case NPROGSW:
+                   vec[vecp++] = --cp;
+                   continue;
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf),
+                       "%s [+folder] %s[switches] [switches for showproc]",
+                       invo_name, mode == SHOW ? "[msgs] ": "");
+                   print_help (buf, switches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case DRFTSW: 
+                   if (file)
+                       adios (NULL, "only one file at a time!");
+                   draftsw++;
+                   if (mode == SHOW)
+                       continue;
+usage:
+                   adios (NULL,
+                           "usage: %s [+folder] [switches] [switches for showproc]",
+                           invo_name);
+               case FILESW: 
+                   if (mode != SHOW)
+                       goto usage;
+                   if (draftsw || file)
+                       adios (NULL, "only one file at a time!");
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   file = path (cp, TFILE);
+                   continue;
+
+               case HEADSW: 
+                   headersw++;
+                   continue;
+               case NHEADSW: 
+                   headersw = 0;
+                   continue;
+
+               case FORMSW:
+                   vec[vecp++] = --cp;
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   vec[vecp++] = getcpy (etcpath(cp));
+                   continue;
+
+               case PROGSW:
+               case LENSW:
+               case WIDTHSW:
+                   vec[vecp++] = --cp;
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   vec[vecp++] = cp;
+                   continue;
+
+               case SHOWSW: 
+                   if (!(showproc = *argp++) || *showproc == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   nshow = 0;
+                   continue;
+               case NSHOWSW: 
+                   nshow++;
+                   continue;
+
+               case SHOWMIMESW:
+                   if (!(showmimeproc = *argp++) || *showmimeproc == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   nshow = 0;
+                   continue;
+               case CHECKMIMESW:
+                   checkmime++;
+                   continue;
+               case NOCHECKMIMESW:
+                   checkmime = 0;
+                   continue;
+           }
+       }
+       if (*cp == '+' || *cp == '@') {
+           if (folder)
+               adios (NULL, "only one folder at a time!");
+           else
+               folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       } else {
+           if (mode != SHOW)
+               goto usage;
+           else
+               msgs[msgp++] = cp;
+       }
+    }
+    procp = vecp;
+
+    if (!context_find ("path"))
+       free (path ("./", TFOLDER));
+
+    if (draftsw || file) {
+       if (msgp)
+           adios (NULL, "only one file at a time!");
+       vec[vecp++] = draftsw
+           ? getcpy (m_draft (folder, msgp ? msgs[0] : NULL, 1, &isdf))
+           : file;
+       goto go_to_it;
+    }
+
+#ifdef WHATNOW
+    if (!msgp && !folder && mode == SHOW && (cp = getenv ("mhdraft")) && *cp) {
+       draftsw++;
+       vec[vecp++] = cp;
+       goto go_to_it;
+    }
+#endif /* WHATNOW */
+
+    if (!msgp) {
+       switch (mode) {
+           case NEXT:
+               msgs[msgp++] = "next";
+               break;
+           case PREV:
+               msgs[msgp++] = "prev";
+               break;
+           default:
+               msgs[msgp++] = "cur";
+               break;
+       }
+    }
+
+    if (!folder)
+       folder = getfolder (1);
+    maildir = m_maildir (folder);
+
+    if (chdir (maildir) == NOTOK)
+       adios (maildir, "unable to change directory to");
+
+    /* read folder and create message structure */
+    if (!(mp = folder_read (folder)))
+       adios (NULL, "unable to read folder %s", folder);
+
+    /* check for empty folder */
+    if (mp->nummsg == 0)
+       adios (NULL, "no messages in %s", folder);
+
+    /* parse all the message ranges/sequences and set SELECTED */
+    for (msgnum = 0; msgnum < msgp; msgnum++)
+       if (!m_convert (mp, msgs[msgnum]))
+           done (1);
+
+    /*
+     * Set the SELECT_UNSEEN bit for all the SELECTED messages,
+     * since we will use that as a tag to know which messages
+     * to remove from the "unseen" sequence.
+     */
+    for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
+       if (is_selected(mp, msgnum))
+           set_unseen (mp, msgnum);
+
+    seq_setprev (mp);          /* set the Previous-Sequence */
+    seq_setunseen (mp, 1);     /* unset the Unseen-Sequence */
+
+    if (mp->numsel > MAXARGS - 2)
+       adios (NULL, "more than %d messages for show exec", MAXARGS - 2);
+
+    for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
+       if (is_selected(mp, msgnum))
+           vec[vecp++] = getcpy (m_name (msgnum));
+
+    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   */
+
+    if (headersw && vecp == 2)
+       printf ("(Message %s:%s)\n", folder, vec[1]);
+
+go_to_it: ;
+    fflush (stdout);
+
+    vec[vecp] = NULL;
+
+    /*
+     * Decide which "proc" to use
+     */
+    mime = 0;
+    if (nshow) {
+       proc = catproc;
+    } else {
+       /* check if any messages are non-text MIME messages */
+       if (checkmime && !getenv ("NOMHNPROC")) {
+           if (!draftsw && !file) {
+               /* loop through selected messages and check for MIME */
+               for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
+                   if (is_selected (mp, msgnum) && is_nontext (m_name (msgnum))) {
+                       mime = 1;
+                       break;
+                   }
+           } else {
+               /* check the file or draft for MIME */
+               if (is_nontext (vec[vecp - 1]))
+                   mime = 1;
+           }
+       }
+
+       /* Set the "proc" */
+       if (mime)
+           proc = showmimeproc;
+       else
+           proc = showproc;
+    }
+
+    if (folder && !draftsw && !file)
+       m_putenv ("mhfolder", folder);
+
+    /*
+     * For backward compatibility, if the "proc" is mhn,
+     * then add "-show" option.  Add "-file" if showing
+     * file or draft.
+     */
+    if (strcmp (r1bindex (proc, '/'), "mhn") == 0) {
+       if (draftsw || file) {
+           vec[vecp] = vec[vecp - 1];
+           vec[vecp - 1] = "-file";
+           vecp++;
+       }
+       vec[vecp++] = "-show";
+       vec[vecp] = NULL;
+    }
+
+    /*
+     * If "proc" is mhl, then run it internally
+     * rather than exec'ing it.
+     */
+    if (strcmp (r1bindex (proc, '/'), "mhl") == 0) {
+       vec[0] = "mhl";
+       mhl (vecp, vec);
+       done (0);
+    }
+
+    /*
+     * If you are not using a nmh command as your "proc", then
+     * add the path to the message names.  Currently, we are just
+     * checking for mhn here, since we've already taken care of mhl.
+     */
+    if (!strcmp (r1bindex (proc, '/'), "mhl")
+           && !draftsw
+           && !file
+           && chdir (maildir = concat (m_maildir (""), "/", NULL)) != NOTOK) {
+       mp->foldpath = concat (mp->foldpath, "/", NULL);
+       cp = ssequal (maildir, mp->foldpath)
+           ? mp->foldpath + strlen (maildir)
+           : mp->foldpath;
+       for (msgnum = procp; msgnum < vecp; msgnum++)
+           vec[msgnum] = concat (cp, vec[msgnum], NULL);
+    }
+
+    vec[0] = r1bindex (proc, '/');
+    execvp (proc, vec);
+    adios (proc, "unable to exec");
+}
+
+/*
+ * Cheat:  we are loaded with adrparse, which wants a routine called
+ * OfficialName().  We call adrparse:getm() with the correct arguments
+ * to prevent OfficialName() from being called.  Hence, the following
+ * is to keep the loader happy.
+ */
+
+char *
+OfficialName (char *name)
+{
+    return name;
+}
+
+
+/*
+ * Check if a message or file contains any non-text parts
+ */
+static int
+is_nontext (char *msgnam)
+{
+    int        result, state;
+    char *bp, *cp, *dp;
+    char buf[BUFSIZ], name[NAMESZ];
+    FILE *fp;
+
+    if ((fp = fopen (msgnam, "r")) == NULL)
+       return 0;
+
+    for (state = FLD;;) {
+       switch (state = m_getfld (state, name, buf, sizeof(buf), fp)) {
+       case FLD:
+       case FLDPLUS:
+       case FLDEOF:
+           /*
+            * Check Content-Type field
+            */
+           if (!strcasecmp (name, TYPE_FIELD)) {
+               int passno;
+               char c;
+
+               cp = add (buf, NULL);
+               while (state == FLDPLUS) {
+                   state = m_getfld (state, name, buf, sizeof(buf), fp);
+                   cp = add (buf, cp);
+               }
+               bp = cp;
+               passno = 1;
+
+again:
+               for (; isspace (*bp); bp++)
+                   continue;
+               if (*bp == '(') {
+                   int i;
+
+                   for (bp++, i = 0;;) {
+                       switch (*bp++) {
+                       case '\0':
+invalid:
+                           result = 0;
+                           goto out;
+                       case '\\':
+                           if (*bp++ == '\0')
+                               goto invalid;
+                           continue;
+                       case '(':
+                           i++;
+                           /* and fall... */
+                       default:
+                           continue;
+                       case ')':
+                           if (--i < 0)
+                               break;
+                           continue;
+                       }
+                       break;
+                   }
+               }
+               if (passno == 2) {
+                   if (*bp != '/')
+                       goto invalid;
+                   bp++;
+                   passno = 3;
+                   goto again;
+               }
+               for (dp = bp; istoken (*dp); dp++)
+                   continue;
+               c = *dp;
+               *dp = '\0';
+               if (!*bp)
+                   goto invalid;
+               if (passno > 1) {
+                   if ((result = (strcasecmp (bp, "plain") != 0)))
+                       goto out;
+                   *dp = c;
+                   for (dp++; isspace (*dp); dp++)
+                       continue;
+                   if (*dp) {
+                       if ((result = !uprf (dp, "charset")))
+                           goto out;
+                       dp += sizeof("charset") - 1;
+                       while (isspace (*dp))
+                           dp++;
+                       if (*dp++ != '=')
+                           goto invalid;
+                       while (isspace (*dp))
+                           dp++;
+                       if (*dp == '"') {
+                           if ((bp = strchr(++dp, '"')))
+                               *bp = '\0';
+                       } else {
+                           for (bp = dp; *bp; bp++)
+                               if (isspace (*bp)) {
+                                   *bp = '\0';
+                                   break;
+                               }
+                       }
+                   } else {
+                       /* Default character set */
+                       dp = "US-ASCII";
+                   }
+                   /* Check the character set */
+                   result = !check_charset (dp, strlen (dp));
+               } else {
+                   if (!(result = (strcasecmp (bp, "text") != 0))) {
+                       *dp = c;
+                       bp = dp;
+                       passno = 2;
+                       goto again;
+                   }
+               }
+out:
+               free (cp);
+               if (result) {
+                   fclose (fp);
+                   return result;
+               }
+               break;
+           }
+
+           /*
+            * Check Content-Transfer-Encoding field
+            */
+           if (!strcasecmp (name, ENCODING_FIELD)) {
+               cp = add (buf, NULL);
+               while (state == FLDPLUS) {
+                   state = m_getfld (state, name, buf, sizeof(buf), fp);
+                   cp = add (buf, cp);
+               }
+               for (bp = cp; isspace (*bp); bp++)
+                   continue;
+               for (dp = bp; istoken (*dp); dp++)
+                   continue;
+               *dp = '\0';
+               result = (strcasecmp (bp, "7bit")
+                      && strcasecmp (bp, "8bit")
+                      && strcasecmp (bp, "binary"));
+
+               free (cp);
+               if (result) {
+                   fclose (fp);
+                   return result;
+               }
+               break;
+           }
+
+           /*
+            * Just skip the rest of this header
+            * field and go to next one.
+            */
+           while (state == FLDPLUS)
+               state = m_getfld (state, name, buf, sizeof(buf), fp);
+           break;
+
+           /*
+            * We've passed the message header,
+            * so message is just text.
+            */
+       default:
+           fclose (fp);
+           return 0;
+       }
+    }
+}
diff --git a/uip/slocal.c b/uip/slocal.c
new file mode 100644 (file)
index 0000000..c1ee80f
--- /dev/null
@@ -0,0 +1,1543 @@
+
+/*
+ * slocal.c -- asynchronously filter and deliver new mail
+ *
+ * $Id$
+ */
+
+/*
+ *  Under sendmail, users should add the line
+ *
+ *     "| /usr/local/nmh/lib/slocal"
+ *
+ *  to their $HOME/.forward file.
+ *
+ *  Under MMDF-I, users should (symbolically) link
+ *  /usr/local/nmh/lib/slocal to $HOME/bin/rcvmail.
+ *
+ */
+
+#include <h/mh.h>
+#include <h/dropsbr.h>
+#include <h/rcvmail.h>
+#include <h/signals.h>
+#include <zotnet/tws/tws.h>
+#include <zotnet/mts/mts.h>
+
+#include <pwd.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <ndbm.h>
+#include <fcntl.h>
+
+#include <utmp.h>
+
+#ifndef UTMP_FILE
+# ifdef _PATH_UTMP
+#  define UTMP_FILE _PATH_UTMP
+# else
+#  define UTMP_FILE "/etc/utmp"
+# endif
+#endif
+
+static struct swit switches[] = {
+#define        ADDRSW         0
+    { "addr address", 0 },
+#define        USERSW         1
+    { "user name", 0 },
+#define        FILESW         2
+    { "file file", 0 },
+#define        SENDERSW       3
+    { "sender address", 0 },
+#define        MAILBOXSW      4
+    { "mailbox file", 0 },
+#define        HOMESW         5
+    { "home directory", -4 },
+#define        INFOSW         6
+    { "info data", 0 },
+#define        MAILSW         7
+    { "maildelivery file", 0 },
+#define        VERBSW         8
+    { "verbose", 0 },
+#define        NVERBSW        9
+    { "noverbose", 0 },
+#define SUPPRESSDUP   10
+    { "suppressdup", 0 },
+#define NSUPPRESSDUP 11
+    { "nosuppressdup", 0 },
+#define        DEBUGSW       12
+    { "debug", 0 },
+#define VERSIONSW     13
+    { "version", 0 },
+#define        HELPSW        14
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+static int globbed = 0;                /* have we built "vars" table yet?        */
+static int parsed = 0;         /* have we built header field table yet   */
+static int utmped = 0;         /* have we scanned umtp(x) file yet       */
+static int suppressdup = 0;    /* are we suppressing duplicate messages? */
+
+static int verbose = 0;
+static int debug = 0;
+
+static char *addr = NULL;
+static char *user = NULL;
+static char *info = NULL;
+static char *file = NULL;
+static char *sender = NULL;
+static char *envelope = NULL;  /* envelope information ("From " line)  */
+static char *mbox = NULL;
+static char *home = NULL;
+
+static struct passwd *pw;      /* passwd file entry */
+
+static char ddate[BUFSIZ];     /* record the delivery date */
+struct tws *now;
+
+static jmp_buf myctx;
+
+/* flags for pair->p_flags */
+#define        P_NIL  0x00
+#define        P_ADR  0x01     /* field is address     */
+#define        P_HID  0x02     /* special (fake) field */
+#define        P_CHK  0x04
+
+struct pair {
+    char *p_name;
+    char *p_value;
+    char  p_flags;
+};
+
+#define        NVEC 100
+
+/*
+ * Lookup table for matching fields and patterns
+ * in messages.  The rest of the table is added
+ * when the message is parsed.
+ */
+static struct pair hdrs[NVEC + 1] = {
+    { "source",          NULL, P_HID },
+    { "addr",            NULL, P_HID },
+    { "Return-Path",     NULL, P_ADR },
+    { "Reply-To",        NULL, P_ADR },
+    { "From",            NULL, P_ADR },
+    { "Sender",          NULL, P_ADR },
+    { "To",              NULL, P_ADR },
+    { "cc",              NULL, P_ADR },
+    { "Resent-Reply-To", NULL, P_ADR },
+    { "Resent-From",     NULL, P_ADR },
+    { "Resent-Sender",   NULL, P_ADR },
+    { "Resent-To",       NULL, P_ADR },
+    { "Resent-cc",       NULL, P_ADR },
+    { NULL, NULL, 0 }
+};
+
+/*
+ * The list of builtin variables to expand in a string
+ * before it is executed by the "pipe" or "qpipe" action.
+ */
+static struct pair vars[] = {
+    { "sender",   NULL, P_NIL },
+    { "address",  NULL, P_NIL },
+    { "size",     NULL, P_NIL },
+    { "reply-to", NULL, P_CHK },
+    { "info",     NULL, P_NIL },
+    { NULL, NULL, 0 }
+};
+
+extern char **environ;
+
+/*
+ * static prototypes
+ */
+static int localmail (int, char *);
+static int usr_delivery (int, char *, int);
+static int split (char *, char **);
+static int parse (int);
+static void expand (char *, char *, int);
+static void glob (int);
+static struct pair *lookup (struct pair *, char *);
+static int logged_in (void);
+static int timely (char *, char *);
+static int usr_file (int, char *, int);
+static int usr_pipe (int, char *, char *, char **, int);
+static int usr_folder (int, char *);
+static RETSIGTYPE alrmser (int);
+static void get_sender (char *, char **);
+static int copy_message (int, char *, int);
+static void verbose_printf (char *fmt, ...);
+static void adorn (char *, char *, ...);
+static void debug_printf (char *fmt, ...);
+static int suppress_duplicates (int, char *);
+static char *trim (char *);
+
+
+int
+main (int argc, char **argv)
+{
+    int fd, status;
+    FILE *fp = stdin;
+    char *cp, *mdlvr = NULL, buf[BUFSIZ];
+    char mailbox[BUFSIZ], tmpfil[BUFSIZ];
+    char **argp, **arguments;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (*argv, '/');
+
+    /* foil search of user profile/context */
+    if (context_foil (NULL) == -1)
+       done (1);
+
+    mts_init (invo_name);
+    arguments = getarguments (invo_name, argc, argv, 0);
+    argp = arguments;
+
+    /* Parse arguments */
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown", cp);
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf),
+                       "%s [switches] [address info sender]", invo_name);
+                   print_help (buf, switches, 0);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case ADDRSW: 
+                   if (!(addr = *argp++))/* allow -xyz arguments */
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+               case INFOSW: 
+                   if (!(info = *argp++))/* allow -xyz arguments */
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+               case USERSW: 
+                   if (!(user = *argp++))/* allow -xyz arguments */
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+               case FILESW: 
+                   if (!(file = *argp++) || *file == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+               case SENDERSW: 
+                   if (!(sender = *argp++))/* allow -xyz arguments */
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+               case MAILBOXSW: 
+                   if (!(mbox = *argp++) || *mbox == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+               case HOMESW: 
+                   if (!(home = *argp++) || *home == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+
+               case MAILSW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   if (mdlvr)
+                       adios (NULL, "only one maildelivery file at a time!");
+                   mdlvr = cp;
+                   continue;
+
+               case VERBSW: 
+                   verbose++;
+                   continue;
+               case NVERBSW: 
+                   verbose = 0;
+                   continue;
+
+               case SUPPRESSDUP:
+                   suppressdup++;
+                   continue;
+               case NSUPPRESSDUP:
+                   suppressdup = 0;
+                   continue;
+               case DEBUGSW: 
+                   debug++;
+                   continue;
+           }
+       }
+
+       switch (argp - (argv + 1)) {
+           case 1: 
+               addr = cp;
+               break;
+
+           case 2: 
+               info = cp;
+               break;
+
+           case 3: 
+               sender = cp;
+               break;
+       }
+    }
+
+    if (addr == NULL)
+       addr = getusername ();
+    if (user == NULL)
+       user = (cp = strchr(addr, '.')) ? ++cp : addr;
+    if ((pw = getpwnam (user)) == NULL)
+       adios (NULL, "no such local user as %s", user);
+
+    if (chdir (pw->pw_dir) == -1)
+       chdir ("/");
+    umask (0077);
+
+    if (geteuid() == 0) {
+       setgid (pw->pw_gid);
+       initgroups (pw->pw_name, pw->pw_gid);
+       setuid (pw->pw_uid);
+    }
+    
+    if (info == NULL)
+       info = "";
+
+    setbuf (stdin, NULL);
+
+    /* Record the delivery time */
+    if ((now = dlocaltimenow ()) == NULL)
+       adios (NULL, "unable to ascertain local time");
+    snprintf (ddate, sizeof(ddate), "Delivery-Date: %s\n", dtimenow (0));
+
+    /*
+     * Copy the message to a temporary file
+     */
+    if (file) {
+       int tempfd;
+
+       /* getting message from file */
+       if ((tempfd = open (file, O_RDONLY)) == -1)
+           adios (file, "unable to open");
+       if (debug)
+           debug_printf ("retrieving message from file \"%s\"\n", file);
+       if ((fd = copy_message (tempfd, tmpfil, 1)) == -1)
+           adios (NULL, "unable to create temporary file");
+       close (tempfd);
+    } else {
+       /* getting message from stdin */
+       if (debug)
+           debug_printf ("retrieving message from stdin\n");
+       if ((fd = copy_message (fileno (stdin), tmpfil, 1)) == -1)
+           adios (NULL, "unable to create temporary file");
+    }
+    if (debug)
+       debug_printf ("temporary file=\"%s\"\n", tmpfil);
+    else
+       unlink (tmpfil);
+
+    if (!(fp = fdopen (fd, "r+")))
+       adios (NULL, "unable to access temporary file");
+
+    /*
+     * If no sender given, extract it
+     * from envelope information.
+     */
+    if (sender == NULL)
+       get_sender (envelope, &sender);
+
+    if (mbox == NULL) {
+       snprintf (mailbox, sizeof(mailbox), "%s/%s",
+               mmdfldir[0] ? mmdfldir : pw->pw_dir,
+               mmdflfil[0] ? mmdflfil : pw->pw_name);
+       mbox = mailbox;
+    }
+    if (home == NULL)
+       home = pw->pw_dir;
+
+    if (debug) {
+       debug_printf ("addr=\"%s\"\n", trim(addr));
+       debug_printf ("user=\"%s\"\n", trim(user));
+       debug_printf ("info=\"%s\"\n", trim(info));
+       debug_printf ("sender=\"%s\"\n", trim(sender));
+       debug_printf ("envelope=\"%s\"\n", envelope ? trim(envelope) : "");
+       debug_printf ("mbox=\"%s\"\n", trim(mbox));
+       debug_printf ("home=\"%s\"\n", trim(home));
+       debug_printf ("ddate=\"%s\"\n", trim(ddate));
+       debug_printf ("now=%02d:%02d\n\n", now->tw_hour, now->tw_min);
+    }
+
+    /* deliver the message */
+    status = localmail (fd, mdlvr);
+
+    done (status != -1 ? RCV_MOK : RCV_MBX);
+}
+
+
+/*
+ * Main routine for delivering message.
+ */
+
+static int
+localmail (int fd, char *mdlvr)
+{
+    /* check if this message is a duplicate */
+    if (suppressdup &&
+        suppress_duplicates(fd, mdlvr ? mdlvr : ".maildelivery") == DONE)
+       return 0;
+
+    /* delivery according to personal Maildelivery file */
+    if (usr_delivery (fd, mdlvr ? mdlvr : ".maildelivery", 0) != -1)
+       return 0;
+
+    /* delivery according to global Maildelivery file */
+    if (usr_delivery (fd, maildelivery, 1) != -1)
+       return 0;
+
+    if (verbose)
+       verbose_printf ("(delivering to standard mail spool)\n");
+
+    /* last resort - deliver to standard mail spool */
+#ifdef SLOCAL_MBOX
+    return usr_file (fd, mbox, MBOX_FORMAT);
+#else
+    return usr_file (fd, mbox, MMDF_FORMAT);
+#endif
+}
+
+
+#define        matches(a,b) (stringdex (b, a) >= 0)
+
+/*
+ * Parse the delivery file, and process incoming message.
+ */
+
+static int
+usr_delivery (int fd, char *delivery, int su)
+{
+    int i, accept, status, won, vecp, next;
+    char *field, *pattern, *action, *result, *string;
+    char buffer[BUFSIZ], tmpbuf[BUFSIZ];
+    char *cp, *vec[NVEC];
+    struct stat st;
+    struct pair *p;
+    FILE *fp;
+
+    /* open the delivery file */
+    if ((fp = fopen (delivery, "r")) == NULL)
+       return -1;
+
+    /* check if delivery file has bad ownership or permissions */
+    if (fstat (fileno (fp), &st) == -1
+           || (st.st_uid != 0 && (su || st.st_uid != pw->pw_uid))
+           || st.st_mode & (S_IWGRP|S_IWOTH)) {
+       if (verbose) {
+           verbose_printf ("WARNING: %s has bad ownership/modes (su=%d,uid=%d,owner=%d,mode=0%o)\n",
+                   delivery, su, (int) pw->pw_uid, (int) st.st_uid, (int) st.st_mode);
+       }
+       return -1;
+    }
+
+    won = 0;
+    next = 1;
+
+    /* read and process delivery file */
+    while (fgets (buffer, sizeof(buffer), fp)) {
+       /* skip comments and empty lines */
+       if (*buffer == '#' || *buffer == '\n')
+           continue;
+
+       /* zap trailing newline */
+       if ((cp = strchr(buffer, '\n')))
+           *cp = 0;
+
+       /* split buffer into fields */
+       vecp = split (buffer, vec);
+
+       /* check for too few fields */
+       if (vecp < 5) {
+           if (debug)
+               debug_printf ("WARNING: entry with only %d fields, skipping.\n", vecp);
+           continue;
+       }
+
+       if (debug) {
+           for (i = 0; vec[i]; i++)
+               debug_printf ("vec[%d]: \"%s\"\n", i, trim(vec[i]));
+       }
+
+       field   = vec[0];
+       pattern = vec[1];
+       action  = vec[2];
+       result  = vec[3];
+       string  = vec[4];
+
+       /* find out how to perform the action */
+       switch (result[0]) {
+           case 'N':
+           case 'n':
+               /*
+                * If previous condition failed, don't
+                * do this - else fall through
+                */
+               if (!next)
+                   continue;   /* else fall */
+
+           case '?': 
+               /*
+                * If already delivered, skip this action.  Else
+                * consider delivered if action is successful.
+                */
+               if (won)
+                   continue;   /* else fall */
+
+           case 'A': 
+           case 'a': 
+               /*
+                * Take action, and consider delivered if
+                * action is successful.
+                */
+               accept = 1;
+               break;
+
+           case 'R': 
+           case 'r': 
+           default: 
+               /*
+                * Take action, but don't consider delivered, even
+                * if action is successful
+                */
+               accept = 0;
+               break;
+       }
+
+       if (vecp > 5) {
+           if (!strcasecmp (vec[5], "select")) {
+               if (logged_in () != -1)
+                   continue;
+               if (vecp > 7 && timely (vec[6], vec[7]) == -1)
+                   continue;
+           }
+       }
+
+       /* check if the field matches */
+       switch (*field) {
+           case '*': 
+           /* always matches */
+               break;
+
+           case 'd': 
+           /*
+            * "default" matches only if the message hasn't
+            * been delivered yet.
+            */
+               if (!strcasecmp (field, "default")) {
+                   if (won)
+                       continue;
+                   break;
+               }               /* else fall */
+
+           default: 
+               /* parse message and build lookup table */
+               if (!parsed && parse (fd) == -1) {
+                   fclose (fp);
+                   return -1;
+               }
+               /*
+                * find header field in lookup table, and
+                * see if the pattern matches.
+                */
+               if ((p = lookup (hdrs, field)) && (p->p_value != NULL)
+                       && matches (p->p_value, pattern)) {
+                   next = 1;
+               } else {
+                   next = 0;
+                   continue;
+               }
+               break;
+       }
+
+       /* find out the action to perform */
+       switch (*action) {
+           case 'q':
+               /* deliver to quoted pipe */
+               if (strcasecmp (action, "qpipe"))
+                   continue;   /* else fall */
+           case '^':
+               expand (tmpbuf, string, fd);
+               if (split (tmpbuf, vec) < 1)
+                   continue;
+               status = usr_pipe (fd, tmpbuf, vec[0], vec, 0);
+               break;
+
+           case 'p': 
+               /* deliver to pipe */
+               if (strcasecmp (action, "pipe"))
+                   continue;   /* else fall */
+           case '|': 
+               vec[2] = "sh";
+               vec[3] = "-c";
+               expand (tmpbuf, string, fd);
+               vec[4] = tmpbuf;
+               vec[5] = NULL;
+               status = usr_pipe (fd, tmpbuf, "/bin/sh", vec + 2, 0);
+               break;
+
+           case 'f': 
+               /* mbox format */
+               if (!strcasecmp (action, "file")) {
+                   status = usr_file (fd, string, MBOX_FORMAT);
+                   break;
+               }
+               /* deliver to nmh folder */
+               else if (strcasecmp (action, "folder"))
+                   continue;   /* else fall */
+           case '+':
+               status = usr_folder (fd, string);
+               break;
+
+           case 'm':
+               /* mmdf format */
+               if (!strcasecmp (action, "mmdf")) {
+                   status = usr_file (fd, string, MMDF_FORMAT);
+                   break;
+               }
+               /* mbox format */
+               else if (strcasecmp (action, "mbox"))
+                   continue;   /* else fall */
+
+           case '>': 
+               /* mbox format */
+               status = usr_file (fd, string, MBOX_FORMAT);
+               break;
+
+           case 'd': 
+               /* ignore message */
+               if (strcasecmp (action, "destroy"))
+                   continue;
+               status = 0;
+               break;
+       }
+
+       if (accept && status == 0)
+           won++;
+    }
+
+    fclose (fp);
+    return (won ? 0 : -1);
+}
+
+
+#define        QUOTE   '\\'
+
+/*
+ * Split buffer into fields (delimited by whitespace or
+ * comma's).  Return the number of fields found.
+ */
+
+static int
+split (char *cp, char **vec)
+{
+    int i;
+    char *s;
+
+    s = cp;
+
+    /* split into a maximum of NVEC fields */
+    for (i = 0; i <= NVEC;) {
+       vec[i] = NULL;
+
+       /* zap any whitespace and comma's */
+       while (isspace (*s) || *s == ',')
+           *s++ = 0;
+
+       /* end of buffer, time to leave */
+       if (*s == 0)
+           break;
+
+       /* get double quote text as a single field */
+       if (*s == '"') {
+           for (vec[i++] = ++s; *s && *s != '"'; s++) {
+               /*
+                * Check for escaped double quote.  We need
+                * to shift the string to remove slash.
+                */
+               if (*s == QUOTE) {
+                   if (*++s == '"')
+                       strcpy (s - 1, s);
+                   s--;
+               }
+           }
+           if (*s == '"')      /* zap trailing double quote */
+               *s++ = 0;
+           continue;
+       }
+
+       if (*s == QUOTE && *++s != '"')
+           s--;
+       vec[i++] = s++;
+
+       /* move forward to next field delimiter */
+       while (*s && !isspace (*s) && *s != ',')
+           s++;
+    }
+    vec[i] = NULL;
+
+    return i;
+}
+
+
+/*
+ * Parse the headers of a message, and build the
+ * lookup table for matching fields and patterns.
+ */
+
+static int
+parse (int fd)
+{
+    int i, state;
+    int fd1;
+    char *cp, *dp, *lp;
+    char name[NAMESZ], field[BUFSIZ];
+    struct pair *p, *q;
+    FILE  *in;
+
+    if (parsed++)
+       return 0;
+
+    /* get a new FILE pointer to message */
+    if ((fd1 = dup (fd)) == -1)
+       return -1;
+    if ((in = fdopen (fd1, "r")) == NULL) {
+       close (fd1);
+       return -1;
+    }
+    rewind (in);
+
+    /* add special entries to lookup table */
+    if ((p = lookup (hdrs, "source")))
+       p->p_value = getcpy (sender);
+    if ((p = lookup (hdrs, "addr")))
+       p->p_value = getcpy (addr);
+
+    /*
+     * Scan the headers of the message and build
+     * a lookup table.
+     */
+    for (i = 0, state = FLD;;) {
+       switch (state = m_getfld (state, name, field, sizeof(field), in)) {
+           case FLD: 
+           case FLDEOF: 
+           case FLDPLUS: 
+               lp = add (field, NULL);
+               while (state == FLDPLUS) {
+                   state = m_getfld (state, name, field, sizeof(field), in);
+                   lp = add (field, lp);
+               }
+               for (p = hdrs; p->p_name; p++) {
+                   if (!strcasecmp (p->p_name, name)) {
+                       if (!(p->p_flags & P_HID)) {
+                           if ((cp = p->p_value))
+                               if (p->p_flags & P_ADR) {
+                                   dp = cp + strlen (cp) - 1;
+                                   if (*dp == '\n')
+                                       *dp = 0;
+                                   cp = add (",\n\t", cp);
+                               } else {
+                                   cp = add ("\t", cp);
+                               }
+                           p->p_value = add (lp, cp);
+                       }
+                       free (lp);
+                       break;
+                   }
+               }
+               if (p->p_name == NULL && i < NVEC) {
+                   p->p_name = getcpy (name);
+                   p->p_value = lp;
+                   p->p_flags = P_NIL;
+                   p++, i++;
+                   p->p_name = NULL;
+               }
+               if (state != FLDEOF)
+                   continue;
+               break;
+
+           case BODY: 
+           case BODYEOF: 
+           case FILEEOF: 
+               break;
+
+           case LENERR: 
+           case FMTERR: 
+               advise (NULL, "format error in message");
+               break;
+
+           default: 
+               advise (NULL, "internal error in m_getfld");
+               fclose (in);
+               return -1;
+       }
+       break;
+    }
+    fclose (in);
+
+    if ((p = lookup (vars, "reply-to"))) {
+       if ((q = lookup (hdrs, "reply-to")) == NULL || q->p_value == NULL)
+           q = lookup (hdrs, "from");
+       p->p_value = getcpy (q ? q->p_value : "");
+       p->p_flags &= ~P_CHK;
+       if (debug)
+           debug_printf ("vars[%d]: name=\"%s\" value=\"%s\"\n",
+                   p - vars, p->p_name, trim(p->p_value));
+    }
+    if (debug) {
+       for (p = hdrs; p->p_name; p++)
+           debug_printf ("hdrs[%d]: name=\"%s\" value=\"%s\"\n",
+               p - hdrs, p->p_name, p->p_value ? trim(p->p_value) : "");
+    }
+
+    return 0;
+}
+
+
+#define        LPAREN  '('
+#define        RPAREN  ')'
+
+/*
+ * Expand any builtin variables such as $(sender),
+ * $(address), etc., in a string.
+ */
+
+static void
+expand (char *s1, char *s2, int fd)
+{
+    char c, *cp;
+    struct pair *p;
+
+    if (!globbed)
+       glob (fd);
+
+    while ((c = *s2++)) {
+       if (c != '$' || *s2 != LPAREN) {
+           *s1++ = c;
+       } else {
+           for (cp = ++s2; *s2 && *s2 != RPAREN; s2++)
+               continue;
+           if (*s2 != RPAREN) {
+               s2 = --cp;
+               continue;
+           }
+           *s2++ = 0;
+           if ((p = lookup (vars, cp))) {
+               if (!parsed && (p->p_flags & P_CHK))
+                   parse (fd);
+
+               strcpy (s1, p->p_value);
+               s1 += strlen (s1);
+           }
+       }
+    }
+    *s1 = 0;
+}
+
+
+/*
+ * Fill in the information missing from the "vars"
+ * table, which is necessary to expand any builtin
+ * variables in the string for a "pipe" or "qpipe"
+ * action.
+ */
+
+static void
+glob (int fd)
+{
+    char buffer[BUFSIZ];
+    struct stat st;
+    struct pair *p;
+
+    if (globbed++)
+       return;
+
+    if ((p = lookup (vars, "sender")))
+       p->p_value = getcpy (sender);
+    if ((p = lookup (vars, "address")))
+       p->p_value = getcpy (addr);
+    if ((p = lookup (vars, "size"))) {
+       snprintf (buffer, sizeof(buffer), "%d",
+               fstat (fd, &st) != -1 ? (int) st.st_size : 0);
+       p->p_value = getcpy (buffer);
+    }
+    if ((p = lookup (vars, "info")))
+       p->p_value = getcpy (info);
+
+    if (debug) {
+       for (p = vars; p->p_name; p++)
+           debug_printf ("vars[%d]: name=\"%s\" value=\"%s\"\n",
+                   p - vars, p->p_name, trim(p->p_value));
+    }
+}
+
+
+/*
+ * Find a matching name in a lookup table.  If found,
+ * return the "pairs" entry, else return NULL.
+ */
+
+static struct pair *
+lookup (struct pair *pairs, char *key)
+{
+    for (; pairs->p_name; pairs++)
+       if (!strcasecmp (pairs->p_name, key))
+           return pairs;
+
+    return NULL;
+}
+
+
+/*
+ * Check utmp(x) file to see if user is currently
+ * logged in.
+ */
+
+static int
+logged_in (void)
+{
+    struct utmp ut;
+    FILE *uf;
+
+    if (utmped)
+       return utmped;
+
+    if ((uf = fopen (UTMP_FILE, "r")) == NULL)
+       return NOTOK;
+
+    while (fread ((char *) &ut, sizeof(ut), 1, uf) == 1) {
+       if (ut.ut_name[0] != 0
+           && strncmp (user, ut.ut_name, sizeof(ut.ut_name)) == 0) {
+           if (debug)
+               continue;
+           fclose (uf);
+           return (utmped = DONE);
+       }
+    }
+
+    fclose (uf);
+    return (utmped = NOTOK);
+}
+
+
+#define        check(t,a,b)            if (t < a || t > b) return -1
+#define        cmpar(h1,m1,h2,m2)      if (h1 < h2 || (h1 == h2 && m1 < m2)) return 0
+
+static int
+timely (char *t1, char *t2)
+{
+    int t1hours, t1mins, t2hours, t2mins;
+
+    if (sscanf (t1, "%d:%d", &t1hours, &t1mins) != 2)
+       return -1;
+    check (t1hours, 0, 23);
+    check (t1mins, 0, 59);
+
+    if (sscanf (t2, "%d:%d", &t2hours, &t2mins) != 2)
+       return -1;
+    check (t2hours, 0, 23);
+    check (t2mins, 0, 59);
+
+    cmpar (now->tw_hour, now->tw_min, t1hours, t1mins);
+    cmpar (t2hours, t2mins, now->tw_hour, now->tw_min);
+
+    return -1;
+}
+
+
+/*
+ * Deliver message by appending to a file.
+ */
+
+static int
+usr_file (int fd, char *mailbox, int mbx_style)
+{
+    int        md, mapping;
+
+    if (verbose)
+       verbose_printf ("delivering to file \"%s\"", mailbox);
+
+    if (mbx_style == MBOX_FORMAT) {
+       if (verbose)
+           verbose_printf (" (mbox style)");
+       mapping = 0;
+    } else {
+       if (verbose)
+           verbose_printf (" (mmdf style)");
+       mapping = 1;
+    }
+
+    /* open and lock the file */
+    if ((md = mbx_open (mailbox, mbx_style, pw->pw_uid, pw->pw_gid, m_gmprot())) == -1) {
+       if (verbose)
+           adorn ("", "unable to open:");
+       return -1;
+    }
+
+    lseek (fd, (off_t) 0, SEEK_SET);
+
+    /* append message to file */
+    if (mbx_copy (mailbox, mbx_style, md, fd, mapping, NULL, verbose) == -1) {
+       if (verbose)
+           adorn ("", "error writing to:");
+       return -1;
+    }
+
+    /* close and unlock file */
+    mbx_close (mailbox, md);
+
+    if (verbose)
+       verbose_printf (", success.\n");
+    return 0;
+}
+
+
+/*
+ * Deliver message to a nmh folder.
+ */
+
+static int
+usr_folder (int fd, char *string)
+{
+    int status;
+    char folder[BUFSIZ], *vec[3];
+
+    /* get folder name ready */
+    if (*string == '+')
+       strncpy(folder, string, sizeof(folder));
+    else
+       snprintf(folder, sizeof(folder), "+%s", string);
+
+    if (verbose)
+       verbose_printf ("delivering to folder \"%s\"", folder + 1);
+
+    vec[0] = "rcvstore";
+    vec[1] = folder;
+    vec[2] = NULL;
+
+    /* use rcvstore to put message in folder */
+    status = usr_pipe (fd, "rcvstore", rcvstoreproc, vec, 1);
+
+#if 0
+    /*
+     * Currently, verbose status messages are handled by usr_pipe().
+     */
+    if (verbose) {
+       if (status == 0)
+           verbose_printf (", success.\n");
+       else
+           verbose_printf (", failed.\n");
+    }
+#endif
+
+    return status;
+}
+
+/*
+ * Deliver message to a process.
+ */
+
+static int
+usr_pipe (int fd, char *cmd, char *pgm, char **vec, int suppress)
+{
+    pid_t child_id;
+    int i, bytes, seconds, status;
+    struct stat st;
+
+    if (verbose && !suppress)
+       verbose_printf ("delivering to pipe \"%s\"", cmd);
+
+    lseek (fd, (off_t) 0, SEEK_SET);
+
+    for (i = 0; (child_id = fork()) == -1 && i < 5; i++)
+       sleep (5);
+
+    switch (child_id) {
+       case -1: 
+           /* fork error */
+           if (verbose)
+               adorn ("fork", "unable to");
+           return -1;
+
+       case 0: 
+           /* child process */
+           if (fd != 0)
+               dup2 (fd, 0);
+           freopen ("/dev/null", "w", stdout);
+           freopen ("/dev/null", "w", stderr);
+           if (fd != 3)
+               dup2 (fd, 3);
+           closefds (4);
+
+#ifdef TIOCNOTTY
+           if ((fd = open ("/dev/tty", O_RDWR)) != -1) {
+               ioctl (fd, TIOCNOTTY, NULL);
+               close (fd);
+           }
+#endif /* TIOCNOTTY */
+
+           setpgid ((pid_t) 0, getpid ());     /* put in own process group */
+
+           *environ = NULL;
+           m_putenv ("USER", pw->pw_name);
+           m_putenv ("HOME", pw->pw_dir);
+           m_putenv ("SHELL", pw->pw_shell);
+
+           execvp (pgm, vec);
+           _exit (-1);
+
+       default: 
+           /* parent process */
+           if (!setjmp (myctx)) {
+               SIGNAL (SIGALRM, alrmser);
+               bytes = fstat (fd, &st) != -1 ? (int) st.st_size : 100;
+
+               /* amount of time to wait depends on message size */
+               if (bytes <= 100) {
+                   /* give at least 5 minutes */
+                   seconds = 300;
+               } else if (bytes >= 90000) {
+                   /* a half hour is long enough */
+                   seconds = 1800;
+               } else {
+                   seconds = (bytes / 60) + 300;
+               }
+               alarm ((unsigned int) seconds);
+               status = pidwait (child_id, 0);
+               alarm (0);
+
+#ifdef MMDFI
+               if (status == RP_MOK || status == RP_OK)
+                   status = 0;
+#endif
+               if (verbose) {
+                   if (status == 0)
+                       verbose_printf (", success.\n");
+                   else
+                       if ((status & 0xff00) == 0xff00)
+                           verbose_printf (", system error\n");
+                       else
+                           pidstatus (status, stdout, ", failed");
+               }
+               return (status == 0 ? 0 : -1);
+           } else {
+               /*
+                * Ruthlessly kill the child and anything
+                * else in its process group.
+                */
+               KILLPG(child_id, SIGKILL);
+               if (verbose)
+                   verbose_printf (", timed-out; terminated\n");
+               return -1;
+           }
+    }
+}
+
+
+static RETSIGTYPE
+alrmser (int i)
+{
+#ifndef RELIABLE_SIGNALS
+    SIGNAL (SIGALRM, alrmser);
+#endif
+
+    longjmp (myctx, DONE);
+}
+
+
+/*
+ * Get the `sender' from the envelope
+ * information ("From " line).
+ */
+
+static void
+get_sender (char *envelope, char **sender)
+{
+    int i;
+    char *cp;
+    char buffer[BUFSIZ];
+
+    if (envelope == NULL) {
+       *sender = getcpy ("");
+       return;
+    }
+
+    i = strlen ("From ");
+    strncpy (buffer, envelope + i, sizeof(buffer));
+    if ((cp = strchr(buffer, '\n'))) {
+       *cp = 0;
+       cp -= 24;
+       if (cp < buffer)
+           cp = buffer;
+    } else {
+       cp = buffer;
+    }
+    *cp = 0;
+
+    for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
+       if (isspace (*cp))
+           *cp = 0;
+       else
+           break;
+    *sender = getcpy (buffer);
+}
+
+
+/*
+ * Copy message into a temporary file.
+ * While copying, it will do some header processing
+ * including the extraction of the envelope information.
+ */
+
+static int
+copy_message (int qd, char *tmpfil, int fold)
+{
+    int i, first = 1, fd1, fd2;
+    char buffer[BUFSIZ];
+    FILE *qfp, *ffp;
+
+    strcpy (tmpfil, m_tmpfil (invo_name));
+
+    /* open temporary file to put message in */
+    if ((fd1 = open (tmpfil, O_RDWR | O_CREAT | O_TRUNC, 0600)) == -1)
+       return -1;
+
+    if (!fold) {
+       while ((i = read (qd, buffer, sizeof(buffer))) > 0)
+           if (write (fd1, buffer, i) != i) {
+you_lose:
+               close (fd1);
+               unlink (tmpfil);
+               return -1;
+           }
+       if (i == -1)
+           goto you_lose;
+       lseek (fd1, (off_t) 0, SEEK_SET);
+       return fd1;
+    }
+
+    /* dup the fd for incoming message */
+    if ((fd2 = dup (qd)) == -1) {
+       close (fd1);
+       return -1;
+    }
+
+    /* now create a FILE pointer for it */
+    if ((qfp = fdopen (fd2, "r")) == NULL) {
+       close (fd1);
+       close (fd2);
+       return -1;
+    }
+
+    /* dup the fd for temporary file */
+    if ((fd2 = dup (fd1)) == -1) {
+       close (fd1);
+       fclose (qfp);
+       return -1;
+    }
+
+    /* now create a FILE pointer for it */
+    if ((ffp = fdopen (fd2, "r+")) == NULL) {
+       close (fd1);
+       close (fd2);
+       fclose (qfp);
+       return -1;
+    }
+
+    /*
+     * copy message into temporary file
+     * and massage the headers.  Save
+     * a copy of the "From " line for later.
+     */
+    i = strlen ("From ");
+    while (fgets (buffer, sizeof(buffer), qfp)) {
+       if (first) {
+           first = 0;
+           if (!strncmp (buffer, "From ", i)) {
+#ifdef RPATHS
+               char *fp, *cp, *hp, *ep;
+#endif
+               /* get copy of envelope information ("From " line) */
+               envelope = getcpy (buffer);
+
+#if 0
+               /* First go ahead and put "From " line in message */
+               fputs (buffer, ffp);
+               if (ferror (ffp))
+                   goto fputs_error;
+#endif
+
+#ifdef RPATHS
+               /*
+                * Now create a "Return-Path:" line
+                * from the "From " line.
+                */
+               hp = cp = strchr(fp = envelope + i, ' ');
+               while ((hp = strchr(++hp, 'r')))
+                   if (uprf (hp, "remote from")) {
+                       hp = strrchr(hp, ' ');
+                       break;
+                   }
+               if (hp) {
+                   /* return path for UUCP style addressing */
+                   ep = strchr(++hp, '\n');
+                   snprintf (buffer, sizeof(buffer), "Return-Path: %.*s!%.*s\n",
+                       ep - hp, hp, cp - fp, fp);
+               } else {
+                   /* return path for standard domain addressing */
+                   snprintf (buffer, sizeof(buffer), "Return-Path: %.*s\n",
+                       cp - fp, fp);
+               }
+
+               /* Add Return-Path header to message */
+               fputs (buffer, ffp);
+               if (ferror (ffp))
+                   goto fputs_error;
+#endif
+               /* Put the delivery date in message */
+               fputs (ddate, ffp);
+               if (ferror (ffp))
+                   goto fputs_error;
+
+               continue;
+           }
+       }
+
+       fputs (buffer, ffp);
+       if (ferror (ffp))
+           goto fputs_error;
+    }
+
+    fclose (ffp);
+    if (ferror (qfp)) {
+       close (fd1);
+       fclose (qfp);
+       return -1;
+    }
+    fclose (qfp);
+    lseek (fd1, (off_t) 0, SEEK_SET);
+    return fd1;
+
+
+fputs_error:
+    close (fd1);
+    fclose (ffp);
+    fclose (qfp);
+    return -1;
+}
+
+/*
+ * Trim strings for pretty printing of debugging output
+ */
+
+static char *
+trim (char *cp)
+{
+    char buffer[BUFSIZ*4];
+    char *bp, *sp;
+
+    if (cp == NULL)
+       return NULL;
+
+    /* copy string into temp buffer */
+    strncpy (buffer, cp, sizeof(buffer));
+    bp = buffer;
+
+    /* skip over leading whitespace */
+    while (isspace(*bp))
+       bp++;
+
+    /* start at the end and zap trailing whitespace */
+    for (sp = bp + strlen(bp) - 1; sp >= bp; sp--) {
+       if (isspace(*sp))
+           *sp = 0;
+       else
+           break;
+    }
+
+    /* replace remaining whitespace with spaces */
+    for (sp = bp; *sp; sp++)
+       if (isspace(*sp))
+           *sp = ' ';
+
+    /* now return a copy */
+    return getcpy(bp);
+}
+
+/*
+ * Function for printing `verbose' messages.
+ */
+
+static void
+verbose_printf (char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    vfprintf (stdout, fmt, ap);
+    va_end(ap);
+
+    fflush (stdout);   /* now flush output */
+}
+
+
+/*
+ * Function for printing `verbose' delivery
+ * error messages.
+ */
+
+static void
+adorn (char *what, char *fmt, ...)
+{
+    va_list ap;
+    int eindex;
+    char *s;
+
+    eindex = errno;    /* save the errno */
+    fprintf (stdout, ", ");
+
+    va_start(ap, fmt);
+    vfprintf (stdout, fmt, ap);
+    va_end(ap);
+
+    if (what) {
+       if (*what)
+           fprintf (stdout, " %s: ", what);
+       if ((s = strerror (eindex)))
+           fprintf (stdout, "%s", s);
+       else
+           fprintf (stdout, "Error %d", eindex);
+    }
+
+    fputc ('\n', stdout);
+    fflush (stdout);
+}
+
+
+/*
+ * Function for printing `debug' messages.
+ */
+
+static void
+debug_printf (char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    vfprintf (stderr, fmt, ap);
+    va_end(ap);
+}
+
+
+/*
+ * Check ndbm/db file(s) to see if the Message-Id of this
+ * message matches the Message-Id of a previous message,
+ * so we can discard it.  If it doesn't match, we add the
+ * Message-Id of this message to the ndbm/db file.
+ */
+static int
+suppress_duplicates (int fd, char *file)
+{
+    int        fd1, lockfd, state, result;
+    char *cp, buf[BUFSIZ], name[NAMESZ];
+    datum key, value;
+    DBM *db;
+    FILE *in;
+
+    if ((fd1 = dup (fd)) == -1)
+       return -1;
+    if (!(in = fdopen (fd1, "r"))) {
+       close (fd1);
+       return -1;
+    }
+    rewind (in);
+
+    for (state = FLD;;) {
+       state = m_getfld (state, name, buf, sizeof(buf), in);
+       switch (state) {
+           case FLD:
+           case FLDPLUS:
+           case FLDEOF:
+               /* Search for the message ID */
+               if (strcasecmp (name, "Message-ID")) {
+                   while (state == FLDPLUS)
+                       state = m_getfld (state, name, buf, sizeof(buf), in);
+                   continue;
+               }
+
+               cp = add (buf, NULL);
+               while (state == FLDPLUS) {
+                   state = m_getfld (state, name, buf, sizeof(buf), in);
+                   cp = add (buf, cp);
+               }
+               key.dptr = trimcpy (cp);
+               key.dsize = strlen (key.dptr) + 1;
+               free (cp);
+               cp = key.dptr;
+
+               if (!(db = dbm_open (file, O_RDWR | O_CREAT, 0600))) {
+                   advise (file, "unable to perform dbm_open on");
+                   free (cp);
+                   fclose (in);
+                   return -1;
+               }
+               /*
+                * Since it is difficult to portable lock a ndbm file,
+                * we will open and lock the Maildelivery file instead.
+                * This will fail if your Maildelivery file doesn't
+                * exist.
+                */
+               if ((lockfd = lkopen(file, O_RDWR, 0)) == -1) {
+                   advise (file, "unable to perform file locking on");
+                   free (cp);
+                   fclose (in);
+                   return -1;
+               }
+               value = dbm_fetch (db, key);
+               if (value.dptr) {
+                   if (verbose)
+                       verbose_printf ("Message-ID: %s\n            already received on %s",
+                                cp, value.dptr);
+                   result = DONE;
+               } else {
+                   value.dptr  = ddate + sizeof("Delivery-Date:");
+                   value.dsize = strlen(value.dptr) + 1;
+                   if (dbm_store (db, key, value, DBM_INSERT))
+                       advise (file, "possibly corrupt file");
+                   result = 0;
+               }
+
+               dbm_close (db);
+               lkclose(lockfd, file);
+               free (cp);
+               fclose (in);
+               return result;
+               break;
+
+          case BODY:
+          case BODYEOF:
+          case FILEEOF:
+               break;
+
+          case LENERR:
+          case FMTERR:
+          default:
+               break;
+       }
+
+       break;
+    }
+
+    fclose (in);
+    return 0;
+}
diff --git a/uip/sortm.c b/uip/sortm.c
new file mode 100644 (file)
index 0000000..398045b
--- /dev/null
@@ -0,0 +1,583 @@
+
+/*
+ * sortm.c -- sort messages in a folder by date/time
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <zotnet/tws/tws.h>
+
+/*
+ * We allocate space for messages (msgs array)
+ * this number of elements at a time.
+ */
+#define MAXMSGS  256
+
+
+static struct swit switches[] = {
+#define DATESW                 0
+     { "datefield field", 0 },
+#define        TEXTSW                 1
+     { "textfield field", 0 },
+#define        NSUBJSW                2
+     { "notextfield", 0 },
+#define SUBJSW                 3
+     { "subject", -3 },                   /* backward-compatibility */
+#define LIMSW                  4
+     { "limit days", 0 },
+#define        NLIMSW                 5
+     { "nolimit", 0 },
+#define VERBSW                 6
+     { "verbose", 0 },
+#define NVERBSW                7
+     { "noverbose", 0 },
+#define VERSIONSW              8
+     { "version", 0 },
+#define HELPSW                 9
+     { "help", 4 },
+     { NULL, 0 }
+};
+
+struct smsg {
+    int s_msg;
+    time_t s_clock;
+    char *s_subj;
+};
+
+static struct smsg *smsgs;
+int nmsgs;
+
+char *subjsort = (char *) 0;    /* sort on subject if != 0 */
+unsigned long datelimit = 0;
+int submajor = 0;              /* if true, sort on subject-major */
+int verbose;
+
+/* This keeps compiler happy on calls to qsort */
+typedef int (*qsort_comp) (const void *, const void *);
+
+/*
+ * static prototypes
+ */
+static int read_hdrs (struct msgs *, char *);
+static int get_fields (char *, int, struct smsg *);
+static int dsort (struct smsg **, struct smsg **);
+static int subsort (struct smsg **, struct smsg **);
+static int txtsort (struct smsg **, struct smsg **);
+static void rename_chain (struct msgs *, struct smsg **, int, int);
+static void rename_msgs (struct msgs *, struct smsg **);
+
+
+int
+main (int argc, char **argv)
+{
+    int        nummsgs, maxmsgs, i, msgnum;
+    char *cp, *maildir, *datesw = NULL;
+    char *folder = NULL, buf[BUFSIZ], **argp;
+    char **arguments, **msgs;
+    struct msgs *mp;
+    struct smsg **dlist;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    /*
+     * Allocate the initial space to record message
+     * names and ranges.
+     */
+    nummsgs = 0;
+    maxmsgs = MAXMSGS;
+    if (!(msgs = (char **) malloc ((size_t) (maxmsgs * sizeof(*msgs)))))
+       adios (NULL, "unable to allocate storage");
+
+    /*
+     * Parse arguments
+     */
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+           case AMBIGSW:
+               ambigsw (cp, switches);
+               done (1);
+           case UNKWNSW:
+               adios (NULL, "-%s unknown", cp);
+
+           case HELPSW:
+               snprintf(buf, sizeof(buf), "%s [+folder] [msgs] [switches]",
+                       invo_name);
+               print_help (buf, switches, 1);
+               done (1);
+           case VERSIONSW:
+               print_version(invo_name);
+               done (1);
+
+           case DATESW:
+               if (datesw)
+                   adios (NULL, "only one date field at a time");
+               if (!(datesw = *argp++) || *datesw == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               continue;
+
+           case TEXTSW:
+               if (subjsort)
+                   adios (NULL, "only one text field at a time");
+               if (!(subjsort = *argp++) || *subjsort == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               continue;
+
+           case SUBJSW:
+               subjsort = "subject";
+               continue;
+           case NSUBJSW:
+               subjsort = (char *)0;
+               continue;
+
+           case LIMSW:
+               if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+               while (*cp == '0')
+                   cp++;               /* skip any leading zeros */
+               if (!*cp) {             /* hit end of string */
+                   submajor++;         /* sort subject-major */
+                   continue;
+               }
+               if (!isdigit(*cp) || !(datelimit = atoi(cp)))
+                   adios (NULL, "impossible limit %s", cp);
+               datelimit *= 60*60*24;
+               continue;
+           case NLIMSW:
+               submajor = 0;   /* use date-major, but */
+               datelimit = 0;  /* use no limit */
+               continue;
+
+           case VERBSW:
+               verbose++;
+               continue;
+           case NVERBSW:
+               verbose = 0;
+               continue;
+           }
+       }
+       if (*cp == '+' || *cp == '@') {
+           if (folder)
+               adios (NULL, "only one folder at a time!");
+           else
+               folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
+       } else {
+           /*
+            * Check if we need to allocate more space
+            * for message names/ranges.
+            */
+           if (nummsgs >= maxmsgs) {
+               maxmsgs += MAXMSGS;
+               if (!(msgs = (char **) realloc (msgs,
+                       (size_t) (maxmsgs * sizeof(*msgs)))))
+                   adios (NULL, "unable to reallocate msgs storage");
+           }
+           msgs[nummsgs++] = cp;
+       }
+    }
+
+    if (!context_find ("path"))
+       free (path ("./", TFOLDER));
+    if (!nummsgs)
+       msgs[nummsgs++] = "all";
+    if (!datesw)
+       datesw = "date";
+    if (!folder)
+       folder = getfolder (1);
+    maildir = m_maildir (folder);
+
+    if (chdir (maildir) == NOTOK)
+       adios (maildir, "unable to change directory to");
+
+    /* read folder and create message structure */
+    if (!(mp = folder_read (folder)))
+       adios (NULL, "unable to read folder %s", folder);
+
+    /* check for empty folder */
+    if (mp->nummsg == 0)
+       adios (NULL, "no messages in %s", folder);
+
+    /* parse all the message ranges/sequences and set SELECTED */
+    for (msgnum = 0; msgnum < nummsgs; msgnum++)
+       if (!m_convert (mp, msgs[msgnum]))
+           done (1);
+    seq_setprev (mp);  /* set the previous sequence */
+
+    if ((nmsgs = read_hdrs (mp, datesw)) <= 0)
+       adios (NULL, "no messages to sort");
+
+    /*
+     * sort a list of pointers to our "messages to be sorted".
+     */
+    dlist = (struct smsg **) malloc ((nmsgs+1) * sizeof(*dlist));
+    if (! dlist)
+       adios (NULL, "couldn't allocate sort memory");
+    for (i = 0; i < nmsgs; i++)
+       dlist[i] = &smsgs[i];
+    dlist[nmsgs] = 0;
+
+    if (verbose)       /* announce what we're doing */
+       if (subjsort)
+           printf ("sorting by %s-major %s-minor\n", 
+               submajor ? subjsort : datesw,
+               submajor ? datesw : subjsort);
+       else
+           printf ("sorting by datefield %s\n", datesw);
+
+    /* first sort by date, or by subject-major, date-minor */
+    qsort ((char *) dlist, nmsgs, sizeof(*dlist), 
+           (qsort_comp) (submajor && subjsort ? txtsort : dsort));
+
+    /*
+     * if we're sorting on subject, we need another list
+     * in subject order, then a merge pass to collate the
+     * two sorts.
+     */
+    if (!submajor && subjsort) {       /* already date sorted */
+       struct smsg **slist, **flist;
+       register struct smsg ***il, **fp, **dp;
+
+       slist = (struct smsg **) malloc ((nmsgs+1) * sizeof(*slist));
+       if (! slist)
+           adios (NULL, "couldn't allocate sort memory");
+       memcpy((char *)slist, (char *)dlist, (nmsgs+1)*sizeof(*slist));
+       qsort((char *)slist, nmsgs, sizeof(*slist), (qsort_comp) subsort);
+
+       /*
+        * make an inversion list so we can quickly find
+        * the collection of messages with the same subj
+        * given a message number.
+        */
+       il = (struct smsg ***) calloc (mp->hghsel+1, sizeof(*il));
+       if (! il)
+           adios (NULL, "couldn't allocate msg list");
+       for (i = 0; i < nmsgs; i++)
+           il[slist[i]->s_msg] = &slist[i];
+       /*
+        * make up the final list, chronological but with
+        * all the same subjects grouped together.
+        */
+       flist = (struct smsg **) malloc ((nmsgs+1) * sizeof(*flist));
+       if (! flist)
+           adios (NULL, "couldn't allocate msg list");
+       fp = flist;
+       for (dp = dlist; *dp;) {
+           register struct smsg **s = il[(*dp++)->s_msg];
+
+           /* see if we already did this guy */
+           if (! s)
+               continue;
+
+           *fp++ = *s++;
+           /*
+            * take the next message(s) if there is one,
+            * its subject isn't null and its subject
+            * is the same as this one and it's not too
+            * far away in time.
+            */
+           while (*s && (*s)->s_subj[0] &&
+                  strcmp((*s)->s_subj, s[-1]->s_subj) == 0 &&
+                  (datelimit == 0 || 
+                  (*s)->s_clock - s[-1]->s_clock <= datelimit)) {
+               il[(*s)->s_msg] = 0;
+               *fp++ = *s++;
+           }
+       }
+       *fp = 0;
+       free (slist);
+       free (dlist);
+       dlist = flist;
+    }
+    rename_msgs (mp, dlist);
+
+    context_replace (pfolder, folder); /* update current folder         */
+    seq_save (mp);                     /* synchronize message sequences */
+    context_save ();                   /* save the context file         */
+    folder_free (mp);                  /* free folder/message structure */
+    done (0);
+}
+
+static int 
+read_hdrs (struct msgs *mp, char *datesw)
+{
+    int msgnum;
+    struct tws tb;
+    register struct smsg *s;
+
+    twscopy (&tb, dlocaltimenow ());
+
+    smsgs = (struct smsg *)
+       calloc ((size_t) (mp->hghsel - mp->lowsel + 2),
+           sizeof(*smsgs));
+    if (smsgs == NULL)
+       adios (NULL, "unable to allocate sort storage");
+
+    s = smsgs;
+    for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
+       if (is_selected(mp, msgnum)) {
+           if (get_fields (datesw, msgnum, s)) {
+               s->s_msg = msgnum;
+               s++;
+           }
+       }
+    }
+    s->s_msg = 0;
+    return(s - smsgs);
+}
+
+
+/*
+ * Parse the message and get the data or subject field,
+ * if needed.
+ */
+
+static int
+get_fields (char *datesw, int msg, struct smsg *smsg)
+{
+    register int state;
+    int compnum;
+    char *msgnam, buf[BUFSIZ], nam[NAMESZ];
+    register struct tws *tw;
+    register char *datecomp = NULL, *subjcomp = NULL;
+    register FILE *in;
+
+    if ((in = fopen (msgnam = m_name (msg), "r")) == NULL) {
+       admonish (msgnam, "unable to read message");
+       return (0);
+    }
+    for (compnum = 1, state = FLD;;) {
+       switch (state = m_getfld (state, nam, buf, sizeof(buf), in)) {
+       case FLD:
+       case FLDEOF:
+       case FLDPLUS:
+           compnum++;
+           if (!strcasecmp (nam, datesw)) {
+               datecomp = add (buf, datecomp);
+               while (state == FLDPLUS) {
+                   state = m_getfld (state, nam, buf, sizeof(buf), in);
+                   datecomp = add (buf, datecomp);
+               }
+               if (!subjsort || subjcomp)
+                   break;
+           } else if (subjsort && !strcasecmp (nam, subjsort)) {
+               subjcomp = add (buf, subjcomp);
+               while (state == FLDPLUS) {
+                   state = m_getfld (state, nam, buf, sizeof(buf), in);
+                   subjcomp = add (buf, subjcomp);
+               }
+               if (datecomp)
+                   break;
+           } else {
+               /* just flush this guy */
+               while (state == FLDPLUS)
+                   state = m_getfld (state, nam, buf, sizeof(buf), in);
+           }
+           continue;
+
+       case BODY:
+       case BODYEOF:
+       case FILEEOF:
+           break;
+
+       case LENERR:
+       case FMTERR:
+           if (state == LENERR || state == FMTERR)
+               admonish (NULL, "format error in message %d (header #%d)",
+                     msg, compnum);
+           if (datecomp)
+               free (datecomp);
+           if (subjcomp)
+               free (subjcomp);
+           fclose (in);
+           return (0);
+
+       default:
+           adios (NULL, "internal error -- you lose");
+       }
+       break;
+    }
+
+    /*
+     * If no date component, then use the modification
+     * time of the file as its date
+     */
+    if (!datecomp || (tw = dparsetime (datecomp)) == NULL) {
+       struct stat st;
+
+       admonish (NULL, "can't parse %s field in message %d", datesw, msg);
+       fstat (fileno (in), &st);
+       smsg->s_clock = st.st_mtime;
+    } else {
+       smsg->s_clock = dmktime (tw);
+    }
+
+    if (subjsort) {
+       if (subjcomp) {
+           /*
+            * try to make the subject "canonical": delete
+            * leading "re:", everything but letters & smash
+            * letters to lower case. 
+            */
+           register char  *cp, *cp2, c;
+
+           cp = subjcomp;
+           cp2 = subjcomp;
+           if (strcmp (subjsort, "subject") == 0)
+               while ((c = *cp)) {
+                   if (! isspace(c)) {
+                       if(uprf(cp, "re:"))
+                           cp += 2;
+                       else {
+                           if (isalnum(c))
+                               *cp2++ = isupper(c) ? tolower(c) : c;
+                           break;
+                       }
+                   }
+                   cp++;
+               }
+           while ((c = *cp++)) {
+               if (isalnum(c))
+                   *cp2++ = isupper(c) ? tolower(c) : c;
+
+           }
+           *cp2 = '\0';
+       }
+       else
+           subjcomp = "";
+
+       smsg->s_subj = subjcomp;
+    }
+    fclose (in);
+    if (datecomp)
+       free (datecomp);
+
+    return (1);
+}
+
+/*
+ * sort on dates.
+ */
+static int 
+dsort (struct smsg **a, struct smsg **b)
+{
+    if ((*a)->s_clock < (*b)->s_clock)
+       return (-1);
+    else if ((*a)->s_clock > (*b)->s_clock)
+       return (1);
+    else if ((*a)->s_msg < (*b)->s_msg)
+       return (-1);
+    else
+       return (1);
+}
+
+/*
+ * sort on subjects.
+ */
+static int 
+subsort (struct smsg **a, struct smsg **b)
+{
+    register int i;
+
+    if ((i = strcmp ((*a)->s_subj, (*b)->s_subj)))
+       return (i);
+
+    return (dsort (a, b));
+}
+
+static int 
+txtsort (struct smsg **a, struct smsg **b)
+{
+    register int i;
+
+    if ((i = strcmp ((*a)->s_subj, (*b)->s_subj)))
+       return (i);
+    else if ((*a)->s_msg < (*b)->s_msg)
+       return (-1);
+    else
+       return (1);
+}
+
+static void
+rename_chain (struct msgs *mp, struct smsg **mlist, int msg, int endmsg)
+{
+    int nxt, old, new;
+    char *newname, oldname[BUFSIZ];
+
+    for (;;) {
+       nxt = mlist[msg] - smsgs;       /* mlist[msg] is a ptr into smsgs */
+       mlist[msg] = (struct smsg *)0;
+       old = smsgs[nxt].s_msg;
+       new = smsgs[msg].s_msg;
+       strncpy (oldname, m_name (old), sizeof(oldname));
+       newname = m_name (new);
+       if (verbose)
+           printf ("message %d becomes message %d\n", old, new);
+
+       if (rename (oldname, newname) == NOTOK)
+           adios (newname, "unable to rename %s to", oldname);
+
+       copy_msg_flags (mp, new, old);
+       if (mp->curmsg == old)
+           seq_setcur (mp, new);
+
+       if (nxt == endmsg) 
+           break;
+
+       msg = nxt;
+    }
+/*     if (nxt != endmsg); */
+/*     rename_chain (mp, mlist, nxt, endmsg); */
+}
+
+static void
+rename_msgs (struct msgs *mp, struct smsg **mlist)
+{
+    int i, j, old, new;
+    seqset_t tmpset;
+    char f1[BUFSIZ], tmpfil[BUFSIZ];
+    struct smsg *sp;
+
+    strncpy (tmpfil, m_name (mp->hghmsg + 1), sizeof(tmpfil));
+
+    for (i = 0; i < nmsgs; i++) {
+       if (! (sp = mlist[i])) 
+           continue;   /* did this one */
+
+       j = sp - smsgs;
+       if (j == i)
+           continue;   /* this one doesn't move */
+
+       /*
+        * the guy that was msg j is about to become msg i.
+        * rename 'j' to make a hole, then recursively rename
+        * guys to fill up the hole.
+        */
+       old = smsgs[j].s_msg;
+       new = smsgs[i].s_msg;
+       strncpy (f1, m_name (old), sizeof(f1));
+
+       if (verbose)
+           printf ("renaming message chain from %d to %d\n", old, new);
+
+       if (rename (f1, tmpfil) == NOTOK)
+           adios (tmpfil, "unable to rename %s to ", f1);
+       get_msg_flags (mp, &tmpset, old);
+
+       rename_chain (mp, mlist, j, i);
+       if (rename (tmpfil, m_name(new)) == NOTOK)
+           adios (m_name(new), "unable to rename %s to", tmpfil);
+
+       set_msg_flags (mp, &tmpset, new);
+       mp->msgflags |= SEQMOD;
+    }
+}
diff --git a/uip/spost.c b/uip/spost.c
new file mode 100644 (file)
index 0000000..7d65ead
--- /dev/null
@@ -0,0 +1,833 @@
+
+/*
+ * spost.c -- feed messages to sendmail
+ *
+ * This is a simpler, faster, replacement for "post" for use
+ * when "sendmail" is the transport system.
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <signal.h>
+#include <h/addrsbr.h>
+#include <h/aliasbr.h>
+#include <h/dropsbr.h>
+#include <zotnet/tws/tws.h>
+
+#define        uptolow(c)      ((isalpha(c) && isupper (c)) ? tolower (c) : c)
+
+#define MAX_SM_FIELD   1476    /* < largest hdr field sendmail will accept */
+#define FCCS           10      /* max number of fccs allowed */
+
+struct swit switches[] = {
+#define        FILTSW               0
+    { "filter filterfile", 0 },
+#define        NFILTSW              1
+    { "nofilter", 0 },
+#define        FRMTSW               2
+    { "format", 0 },
+#define        NFRMTSW              3
+    { "noformat", 0 },
+#define        REMVSW               4
+    { "remove", 0 },
+#define        NREMVSW              5
+    { "noremove", 0 },
+#define        VERBSW               6
+    { "verbose", 0 },
+#define        NVERBSW              7
+    { "noverbose", 0 },
+#define        WATCSW               8
+    { "watch", 0 },
+#define        NWATCSW              9
+    { "nowatch", 0 },
+#define BACKSW              10
+    { "backup", 0 },
+#define NBACKSW             11
+    { "nobackup", 0 },
+#define ALIASW              12
+    { "alias aliasfile", 0 },
+#define NALIASW             13
+    { "noalias", 0 },
+#define WIDTHSW             14
+    { "width columns", 0 },
+#define VERSIONSW           15
+    { "version", 0 },
+#define        HELPSW              16
+    { "help", 4 },
+#define        DEBUGSW             17
+    { "debug", -5 },
+#define        DISTSW              18
+    { "dist", -4 },            /* interface from dist */
+#define CHKSW               19
+    { "check", -5 },           /* interface from whom */
+#define NCHKSW              20
+    { "nocheck", -7 },         /* interface from whom */
+#define WHOMSW              21
+    { "whom", -4 },            /* interface from whom */
+#define PUSHSW              22 /* fork to sendmail then exit */
+    { "push", -4 },
+#define NPUSHSW             23 /* exec sendmail */
+    { "nopush", -6 },
+#define LIBSW               24
+    { "library directory", -7 },
+#define        ANNOSW              25
+    { "idanno number", -6 },
+    { NULL, 0 }
+};
+
+
+/* flags for headers->flags */
+#define        HNOP    0x0000          /* just used to keep .set around */
+#define        HBAD    0x0001          /* bad header - don't let it through */
+#define        HADR    0x0002          /* header has an address field */
+#define        HSUB    0x0004          /* Subject: header */
+#define        HTRY    0x0008          /* try to send to addrs on header */
+#define        HBCC    0x0010          /* don't output this header */
+#define        HMNG    0x0020          /* mung this header */
+#define        HNGR    0x0040          /* no groups allowed in this header */
+#define        HFCC    0x0080          /* FCC: type header */
+#define        HNIL    0x0100          /* okay for this header not to have addrs */
+#define        HIGN    0x0200          /* ignore this header */
+
+/* flags for headers->set */
+#define        MFRM    0x0001          /* we've seen a From: */
+#define        MDAT    0x0002          /* we've seen a Date: */
+#define        MRFM    0x0004          /* we've seen a Resent-From: */
+#define        MVIS    0x0008          /* we've seen sighted addrs */
+#define        MINV    0x0010          /* we've seen blind addrs */
+#define        MRDT    0x0020          /* we've seen a Resent-Date: */
+
+struct headers {
+    char *value;
+    unsigned int flags;
+    unsigned int set;
+};
+
+
+static struct headers NHeaders[] = {
+    { "Return-Path", HBAD,                0 },
+    { "Received",    HBAD,                0 },
+    { "Reply-To",    HADR|HNGR,           0 },
+    { "From",        HADR|HNGR,           MFRM },
+    { "Sender",      HADR|HBAD,           0 },
+    { "Date",        HNOP,                MDAT },
+    { "Subject",     HSUB,                0 },
+    { "To",          HADR|HTRY,           MVIS },
+    { "cc",          HADR|HTRY,           MVIS },
+    { "Bcc",         HADR|HTRY|HBCC|HNIL, MINV },
+    { "Message-Id",  HBAD,                0 },
+    { "Fcc",         HFCC,                0 },
+    { NULL,          0,                   0 }
+};
+
+static struct headers RHeaders[] = {
+    { "Resent-Reply-To",   HADR|HNGR,      0 },
+    { "Resent-From",       HADR|HNGR,      MRFM },
+    { "Resent-Sender",     HADR|HBAD,      0 },
+    { "Resent-Date",       HNOP,           MRDT },
+    { "Resent-Subject",    HSUB,           0 },
+    { "Resent-To",         HADR|HTRY,      MVIS },
+    { "Resent-cc",         HADR|HTRY,      MVIS },
+    { "Resent-Bcc",        HADR|HTRY|HBCC, MINV },
+    { "Resent-Message-Id", HBAD,           0 },
+    { "Resent-Fcc",        HFCC,           0 },
+    { "Reply-To",          HADR,           0 },
+    { "Fcc",               HIGN,           0 },
+    { NULL,                0,              0 }
+};
+
+
+static short fccind = 0;       /* index into fccfold[] */
+
+static int badmsg = 0;         /* message has bad semantics            */
+static int verbose = 0;                /* spell it out                         */
+static int debug = 0;          /* debugging post                       */
+static int rmflg = 1;          /* remove temporary file when done      */
+static int watch = 0;          /* watch the delivery process           */
+static int backflg = 0;                /* rename input file as *.bak when done */
+static int whomflg = 0;                /* if just checking addresses           */
+static int pushflg = 0;                /* if going to fork to sendmail         */
+static int aliasflg = -1;      /* if going to process aliases          */
+static int outputlinelen=72;
+
+static unsigned msgflags = 0;  /* what we've seen */
+
+static enum {
+    normal, resent
+} msgstate = normal;
+
+static char tmpfil[] = "/tmp/pstXXXXXX";
+
+static char from[BUFSIZ];      /* my network address */
+static char signature[BUFSIZ]; /* my signature */
+static char *filter = NULL;    /* the filter for BCC'ing */
+static char *subject = NULL;   /* the subject field for BCC'ing */
+static char *fccfold[FCCS];    /* foldernames for FCC'ing */
+
+static struct headers *hdrtab; /* table for the message we're doing */
+static FILE *out;              /* output (temp) file */
+
+extern char *sendmail;
+
+/*
+ * external prototypes
+ */
+extern char *getfullname (void);
+extern char *getusername (void);
+
+/*
+ * static prototypes
+ */
+static void putfmt (char *, char *, FILE *);
+static void start_headers (void);
+static void finish_headers (FILE *);
+static int get_header (char *, struct headers *);
+static void putadr (char *, struct mailname *);
+static int putone (char *, int, int);
+static void insert_fcc (struct headers *, char *);
+static void file (char *);
+static void fcc (char *, char *);
+
+#if 0
+static void die (char *, char *, ...);
+static void make_bcc_file (void);
+#endif
+
+
+int
+main (int argc, char **argv)
+{
+    int state, i, pid, compnum;
+    char *cp, *msg = NULL, **argp, **arguments;
+    char *sargv[16], buf[BUFSIZ], name[NAMESZ];
+    FILE *in;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* foil search of user profile/context */
+    if (context_foil (NULL) == -1)
+       done (1);
+
+    mts_init (invo_name);
+    arguments = getarguments (invo_name, argc, argv, 0);
+    argp = arguments;
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown", cp);
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf), "%s [switches] file", invo_name);
+                   print_help (buf, switches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case DEBUGSW: 
+                   debug++;
+                   continue;
+
+               case DISTSW:
+                   msgstate = resent;
+                   continue;
+
+               case WHOMSW:
+                   whomflg++;
+                   continue;
+
+               case FILTSW:
+                   if (!(filter = *argp++) || *filter == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+               case NFILTSW:
+                   filter = NULL;
+                   continue;
+               
+               case REMVSW: 
+                   rmflg++;
+                   continue;
+               case NREMVSW: 
+                   rmflg = 0;
+                   continue;
+
+               case BACKSW: 
+                   backflg++;
+                   continue;
+               case NBACKSW: 
+                   backflg = 0;
+                   continue;
+
+               case VERBSW: 
+                   verbose++;
+                   continue;
+               case NVERBSW: 
+                   verbose = 0;
+                   continue;
+
+               case WATCSW: 
+                   watch++;
+                   continue;
+               case NWATCSW: 
+                   watch = 0;
+                   continue;
+               
+               case PUSHSW:
+                   pushflg++;
+                   continue;
+               case NPUSHSW:
+                   pushflg = 0;
+                   continue;
+
+               case ALIASW:
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   if (aliasflg < 0)
+                       alias (AliasFile);/* load default aka's */
+                   aliasflg = 1;
+                   if ((state = alias(cp)) != AK_OK)
+                       adios (NULL, "aliasing error in file %s - %s",
+                              cp, akerror(state) );
+                   continue;
+               case NALIASW:
+                   aliasflg = 0;
+                   continue;
+
+               case WIDTHSW:
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   outputlinelen = atoi (cp);
+                   if (outputlinelen <= 10)
+                       outputlinelen = 72;
+                   continue;
+
+               case LIBSW:
+               case ANNOSW:
+                   /* -library & -idanno switch ignored */
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+           }
+       }
+       if (msg)
+           adios (NULL, "only one message at a time!");
+       else
+           msg = cp;
+    }
+
+    if (aliasflg < 0)
+       alias (AliasFile);      /* load default aka's */
+
+    if (!msg)
+       adios (NULL, "usage: %s [switches] file", invo_name);
+
+    if ((in = fopen (msg, "r")) == NULL)
+       adios (msg, "unable to open");
+
+    start_headers ();
+    if (debug) {
+       verbose++;
+       out = stdout;
+    }
+    else {
+           mktemp (tmpfil);
+           if ((out = fopen (tmpfil, "w")) == NULL)
+               adios (tmpfil, "unable to create");
+           chmod (tmpfil, 0600);
+       }
+
+    hdrtab = (msgstate == normal) ? NHeaders : RHeaders;
+
+    for (compnum = 1, state = FLD;;) {
+       switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
+           case FLD: 
+               compnum++;
+               putfmt (name, buf, out);
+               continue;
+
+           case FLDPLUS: 
+               compnum++;
+               cp = add (buf, cp);
+               while (state == FLDPLUS) {
+                   state = m_getfld (state, name, buf, sizeof(buf), in);
+                   cp = add (buf, cp);
+               }
+               putfmt (name, cp, out);
+               free (cp);
+               continue;
+
+           case BODY: 
+               finish_headers (out);
+               fprintf (out, "\n%s", buf);
+               if(whomflg == 0)
+                   while (state == BODY) {
+                       state = m_getfld (state, name, buf, sizeof(buf), in);
+                       fputs (buf, out);
+                   }
+               break;
+
+           case FILEEOF: 
+               finish_headers (out);
+               break;
+
+           case LENERR: 
+           case FMTERR: 
+               adios (NULL, "message format error in component #%d",
+                       compnum);
+
+           default: 
+               adios (NULL, "getfld() returned %d", state);
+       }
+       break;
+    }
+
+    fclose (in);
+    if (backflg && !whomflg) {
+       strncpy (buf, m_backup (msg), sizeof(buf));
+       if (rename (msg, buf) == NOTOK)
+           advise (buf, "unable to rename %s to", msg);
+    }
+
+    if (debug) {
+       done (0);
+    }
+    else
+       fclose (out);
+
+    file (tmpfil);
+
+    /*
+     * re-open the temp file, unlink it and exec sendmail, giving it
+     * the msg temp file as std in.
+     */
+    if ( freopen( tmpfil, "r", stdin) == NULL)
+       adios (tmpfil, "can't reopen for sendmail");
+    if (rmflg)
+       unlink (tmpfil);
+
+    argp = sargv;
+    *argp++ = "send-mail";
+    *argp++ = "-m";    /* send to me too */
+    *argp++ = "-t";    /* read msg for recipients */
+    *argp++ = "-i";    /* don't stop on "." */
+    if (whomflg)
+       *argp++ = "-bv";
+    if (watch || verbose)
+       *argp++ = "-v";
+    *argp = NULL;
+
+    if (pushflg && !(watch || verbose)) {
+       /* fork to a child to run sendmail */
+       for (i=0; (pid = vfork()) == NOTOK && i < 5; i++)
+           sleep(5);
+       switch (pid) {
+           case NOTOK:
+               fprintf (verbose ? stdout : stderr, "%s: can't fork to %s\n",
+                        invo_name, sendmail);
+               exit(-1);
+           case OK:
+               /* we're the child .. */
+               break;
+           default:
+               exit(0);
+       }
+    }
+    execv ( sendmail, sargv);
+    adios ( sendmail, "can't exec");
+}
+
+/* DRAFT GENERATION */
+
+static void
+putfmt (char *name, char *str, FILE *out)
+{
+    int i;
+    char *cp, *pp;
+    struct headers *hdr;
+
+    while (*str == ' ' || *str == '\t')
+       str++;
+
+    if ((i = get_header (name, hdrtab)) == NOTOK) {
+       fprintf (out, "%s: %s", name, str);
+       return;
+    }
+
+    hdr = &hdrtab[i];
+    if (hdr->flags & HIGN)
+       return;
+    if (hdr->flags & HBAD) {
+       advise (NULL, "illegal header line -- %s:", name);
+       badmsg++;
+       return;
+    }
+    msgflags |= hdr->set;
+
+    if (hdr->flags & HSUB)
+       subject = subject ? add (str, add ("\t", subject)) : getcpy (str);
+
+    if (hdr->flags & HFCC) {
+       if ((cp = strrchr(str, '\n')))
+           *cp = 0;
+       for (cp = pp = str; cp = strchr(pp, ','); pp = cp) {
+           *cp++ = 0;
+           insert_fcc (hdr, pp);
+       }
+       insert_fcc (hdr, pp);
+       return;
+    }
+
+#ifdef notdef
+    if (hdr->flags & HBCC) {
+       insert_bcc(str);
+       return;
+    }
+#endif /* notdef */
+
+    if (*str != '\n' && *str != '\0')
+       if (aliasflg && hdr->flags & HTRY) {
+           /* this header contains address(es) that we have to do
+            * alias expansion on.  Because of the saved state in
+            * getname we have to put all the addresses into a list.
+            * We then let putadr munch on that list, possibly
+            * expanding aliases.
+            */
+           register struct mailname *f = 0;
+           register struct mailname *mp = 0;
+
+           while ((cp = getname(str))) {
+               mp = getm( cp, NULL, 0, AD_HOST, NULL);
+               if (f == 0) {
+                   f = mp;
+                   mp->m_next = mp;
+               } else {
+                   mp->m_next = f->m_next;
+                   f->m_next = mp;
+                   f = mp;
+               }
+           }
+           f = mp->m_next; mp->m_next = 0;
+           putadr( name, f );
+       } else {
+           fprintf (out, "%s: %s", name, str );
+       }
+}
+
+
+static void
+start_headers (void)
+{
+    char *cp;
+    char sigbuf[BUFSIZ];
+
+    strncpy(from, getusername(), sizeof(from));
+
+    if ((cp = getfullname ()) && *cp) {
+       strncpy (sigbuf, cp, sizeof(sigbuf));
+       snprintf (signature, sizeof(signature), "%s <%s>", sigbuf,  from);
+    }
+    else
+       snprintf (signature, sizeof(signature), "%s",  from);
+}
+
+
+static void
+finish_headers (FILE *out)
+{
+    switch (msgstate) {
+       case normal: 
+           if (!(msgflags & MDAT))
+               fprintf (out, "Date: %s\n", dtimenow (0));
+           if (msgflags & MFRM)
+               fprintf (out, "Sender: %s\n", from);
+           else
+               fprintf (out, "From: %s\n", signature);
+#ifdef notdef
+           if (!(msgflags & MVIS))
+               fprintf (out, "Bcc: Blind Distribution List: ;\n");
+#endif /* notdef */
+           break;
+
+       case resent: 
+           if (!(msgflags & MRDT))
+               fprintf (out, "Resent-Date: %s\n", dtimenow(0));
+           if (msgflags & MRFM)
+               fprintf (out, "Resent-Sender: %s\n", from);
+           else
+               fprintf (out, "Resent-From: %s\n", signature);
+#ifdef notdef
+           if (!(msgflags & MVIS))
+               fprintf (out, "Resent-Bcc: Blind Re-Distribution List: ;\n");
+#endif /* notdef */
+           break;
+    }
+
+    if (badmsg)
+       adios (NULL, "re-format message and try again");
+}
+
+
+static int
+get_header (char *header, struct headers *table)
+{
+    struct headers *h;
+
+    for (h = table; h->value; h++)
+       if (!strcasecmp (header, h->value))
+           return (h - table);
+
+    return NOTOK;
+}
+
+
+/*
+ * output the address list for header "name".  The address list
+ * is a linked list of mailname structs.  "nl" points to the head
+ * of the list.  Alias substitution should be done on nl.
+ */
+static void
+putadr (char *name, struct mailname *nl)
+{
+    register struct mailname *mp, *mp2;
+    register int linepos;
+    register char *cp;
+    int namelen;
+
+    fprintf (out, "%s: ", name);
+    namelen = strlen(name) + 2;
+    linepos = namelen;
+
+    for (mp = nl; mp; ) {
+       if (linepos > MAX_SM_FIELD) {
+               fprintf (out, "\n%s: ", name);
+               linepos = namelen;
+       }
+       if (mp->m_nohost) {
+           /* a local name - see if it's an alias */
+           cp = akvalue(mp->m_mbox);
+           if (cp == mp->m_mbox)
+               /* wasn't an alias - use what the user typed */
+               linepos = putone( mp->m_text, linepos, namelen );
+           else
+               /* an alias - expand it */
+               while ((cp = getname(cp))) {
+                   if (linepos > MAX_SM_FIELD) {
+                           fprintf (out, "\n%s: ", name);
+                           linepos = namelen;
+                   }
+                   mp2 = getm( cp, NULL, 0, AD_HOST, NULL);
+                   if (akvisible()) {
+                       mp2->m_pers = getcpy(mp->m_mbox);
+                       linepos = putone( adrformat(mp2), linepos, namelen );
+                   } else {
+                       linepos = putone( mp2->m_text, linepos, namelen );
+                   }
+                   mnfree( mp2 );
+               }
+       } else {
+           /* not a local name - use what the user typed */
+           linepos = putone( mp->m_text, linepos, namelen );
+       }
+       mp2 = mp;
+       mp = mp->m_next;
+       mnfree( mp2 );
+    }
+    putc( '\n', out );
+}
+
+static int
+putone (char *adr, int pos, int indent)
+{
+    register int len;
+    static int linepos;
+
+    len = strlen( adr );
+    if (pos == indent)
+       linepos = pos;
+    else if ( linepos+len > outputlinelen ) {
+       fprintf ( out, ",\n%*s", indent, "");
+       linepos = indent;
+       pos += indent + 2;
+    }
+    else {
+       fputs( ", ", out );
+       linepos += 2;
+       pos += 2;
+    }
+    fputs( adr, out );
+
+    linepos += len;
+    return (pos+len);
+}
+
+
+static void
+insert_fcc (struct headers *hdr, char *pp)
+{
+    char   *cp;
+
+    for (cp = pp; isspace (*cp); cp++)
+       continue;
+    for (pp += strlen (pp) - 1; pp > cp && isspace (*pp); pp--)
+       continue;
+    if (pp >= cp)
+       *++pp = 0;
+    if (*cp == 0)
+       return;
+
+    if (fccind >= FCCS)
+       adios (NULL, "too many %ss", hdr->value);
+    fccfold[fccind++] = getcpy (cp);
+}
+
+#if 0
+/* BCC GENERATION */
+
+static void
+make_bcc_file (void)
+{
+    pid_t child_id;
+    int fd, i, status;
+    char *vec[6];
+    FILE * in, *out;
+
+    mktemp (bccfil);
+    if ((out = fopen (bccfil, "w")) == NULL)
+       adios (bccfil, "unable to create");
+    chmod (bccfil, 0600);
+
+    fprintf (out, "Date: %s\n", dtimenow (0));
+    fprintf (out, "From: %s\n", signature);
+    if (subject)
+       fprintf (out, "Subject: %s", subject);
+    fprintf (out, "BCC:\n\n------- Blind-Carbon-Copy\n\n");
+    fflush (out);
+
+    if (filter == NULL) {
+       if ((fd = open (tmpfil, O_RDONLY)) == NOTOK)
+           adios (NULL, "unable to re-open");
+       cpydgst (fd, fileno (out), tmpfil, bccfil);
+       close (fd);
+    }
+    else {
+       vec[0] = r1bindex (mhlproc, '/');
+
+       for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
+           sleep (5);
+       switch (child_id) {
+           case NOTOK: 
+               adios ("vfork", "unable to");
+
+           case OK: 
+               dup2 (fileno (out), 1);
+
+               i = 1;
+               vec[i++] = "-forward";
+               vec[i++] = "-form";
+               vec[i++] = filter;
+               vec[i++] = tmpfil;
+               vec[i] = NULL;
+
+               execvp (mhlproc, vec);
+               adios (mhlproc, "unable to exec");
+
+           default: 
+               if (status = pidwait(child_id, OK))
+                   admonish (NULL, "%s lost (status=0%o)", vec[0], status);
+               break;
+       }
+    }
+
+    fseek (out, 0L, SEEK_END);
+    fprintf (out, "\n------- End of Blind-Carbon-Copy\n");
+    fclose (out);
+}
+#endif /* if 0 */
+
+/* FCC INTERACTION */
+
+static void
+file (char *path)
+{
+    int i;
+
+    if (fccind == 0)
+       return;
+
+    for (i = 0; i < fccind; i++)
+       if (whomflg)
+           printf ("Fcc: %s\n", fccfold[i]);
+       else
+           fcc (path, fccfold[i]);
+}
+
+
+static void
+fcc (char *file, char *folder)
+{
+    pid_t child_id;
+    int i, status;
+    char fold[BUFSIZ];
+
+    if (verbose)
+       printf ("%sFcc: %s\n", msgstate == resent ? "Resent-" : "", folder);
+    fflush (stdout);
+
+    for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
+       sleep (5);
+    switch (child_id) {
+       case NOTOK: 
+           if (!verbose)
+               fprintf (stderr, "  %sFcc %s: ",
+                       msgstate == resent ? "Resent-" : "", folder);
+           fprintf (verbose ? stdout : stderr, "no forks, so not ok\n");
+           break;
+
+       case OK: 
+           snprintf (fold, sizeof(fold), "%s%s",
+                   *folder == '+' || *folder == '@' ? "" : "+", folder);
+           execlp (fileproc, r1bindex (fileproc, '/'),
+                   "-link", "-file", file, fold, NULL);
+           _exit (-1);
+
+       default: 
+           if ((status = pidwait(child_id, OK))) {
+               if (!verbose)
+                   fprintf (stderr, "  %sFcc %s: ",
+                           msgstate == resent ? "Resent-" : "", folder);
+               fprintf (verbose ? stdout : stderr,
+                       " errored (0%o)\n", status);
+           }
+    }
+
+    fflush (stdout);
+}
+
+
+#if 0
+
+/*
+ * TERMINATION
+ */
+
+static void
+die (char *what, char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    advertise (what, NULL, fmt, ap);
+    va_end(ap);
+
+    done (1);
+}
+#endif
diff --git a/uip/termsbr.c b/uip/termsbr.c
new file mode 100644 (file)
index 0000000..77af468
--- /dev/null
@@ -0,0 +1,227 @@
+
+/*
+ * termsbr.c -- termcap support
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+#ifdef HAVE_TERMIOS_H
+# include <termios.h>
+#else
+# ifdef HAVE_TERMIO_H
+#  include <termio.h>
+# else
+#  include <sgtty.h>
+# endif
+#endif
+
+#ifdef HAVE_TERMCAP_H
+# include <termcap.h>
+#endif
+
+#ifdef GWINSZ_IN_SYS_IOCTL
+# include <sys/ioctl.h>
+#endif
+#ifdef WINSIZE_IN_PTEM
+# include <sys/stream.h>
+# include <sys/ptem.h>
+#endif
+
+#if BUFSIZ<2048
+# define TXTSIZ        2048
+#else
+# define TXTSIZ BUFSIZ
+#endif
+
+/*
+ * These variables are sometimes defined in,
+ * and needed by the termcap library.
+ */
+#ifdef HAVE_OSPEED
+# ifdef MUST_DEFINE_OSPEED
+extern short ospeed;
+extern char PC;
+# endif
+#else
+short ospeed;
+char PC;
+#endif
+
+static long speedcode;
+
+static int initLI = 0;
+static int initCO = 0;
+
+static int HC = 0;       /* are we on a hardcopy terminal?        */
+static int LI = 40;      /* number of lines                       */
+static int CO = 80;      /* number of colums                      */
+static char *CL = NULL;  /* termcap string to clear screen        */
+static char *SE = NULL;  /* termcap string to end standout mode   */
+static char *SO = NULL;  /* termcap string to begin standout mode */
+
+static char termcap[TXTSIZ];
+
+
+static void
+read_termcap(void)
+{
+    char *bp, *cp;
+    char *term;
+
+#ifndef TGETENT_ACCEPTS_NULL
+    char termbuf[TXTSIZ];
+#endif
+
+#ifdef HAVE_TERMIOS_H
+    struct termios tio;
+#else
+# ifdef HAVE_TERMIO_H
+    struct termio tio;
+# else
+    struct sgttyb tio;
+# endif
+#endif
+
+    static int inited = 0;
+
+    if (inited++)
+       return;
+
+    if (!(term = getenv ("TERM")))
+       return;
+
+/*
+ * If possible, we let tgetent allocate its own termcap buffer
+ */
+#ifdef TGETENT_ACCEPTS_NULL
+    if (tgetent (NULL, term) <= 0)
+       return
+#else
+    if (tgetent (termbuf, term) <= 0)
+       return;
+#endif
+
+#ifdef HAVE_TERMIOS_H
+    speedcode = cfgetospeed(&tio);
+#else
+# ifdef HAVE_TERMIO_H
+    speedcode = ioctl(fileno(stdout), TCGETA, &tio) != NOTOK ? tio.c_cflag & CBAUD : 0;
+# else
+    speedcode = ioctl(fileno(stdout), TIOCGETP, (char *) &tio) != NOTOK ? tio.sg_ospeed : 0;
+# endif
+#endif
+
+    HC = tgetflag ("hc");
+
+    if (!initCO && (CO = tgetnum ("co")) <= 0)
+       CO = 80;
+    if (!initLI && (LI = tgetnum ("li")) <= 0)
+       LI = 24;
+
+    cp = termcap;
+    CL = tgetstr ("cl", &cp);
+    if ((bp = tgetstr ("pc", &cp)))
+       PC = *bp;
+    if (tgetnum ("sg") <= 0) {
+       SE = tgetstr ("se", &cp);
+       SO = tgetstr ("so", &cp);
+    }
+}
+
+
+int
+sc_width (void)
+{
+#ifdef TIOCGWINSZ
+    struct winsize win;
+    int width;
+
+    if (ioctl (fileno (stderr), TIOCGWINSZ, &win) != NOTOK
+           && (width = win.ws_col) > 0) {
+       CO = width;
+       initCO++;
+    } else
+#endif /* TIOCGWINSZ */
+       read_termcap();
+
+    return CO;
+}
+
+
+int
+sc_length (void)
+{
+#ifdef TIOCGWINSZ
+    struct winsize win;
+
+    if (ioctl (fileno (stderr), TIOCGWINSZ, &win) != NOTOK
+           && (LI = win.ws_row) > 0)
+       initLI++;
+    else
+#endif /* TIOCGWINSZ */
+       read_termcap();
+
+    return LI;
+}
+
+
+static int
+outc (int c)
+{
+    putchar(c);
+}
+
+
+void
+clear_screen (void)
+{
+    read_termcap ();
+
+    if (CL && speedcode)
+       tputs (CL, LI, outc);
+    else {
+       printf ("\f");
+       if (speedcode)
+           printf ("\200");
+    }
+
+    fflush (stdout);
+}
+
+
+/*
+ * print in standout mode
+ */
+int
+SOprintf (char *fmt, ...)
+{
+    va_list ap;
+
+    read_termcap ();
+    if (!(SO && SE))
+       return NOTOK;
+
+    tputs (SO, 1, outc);
+
+    va_start(ap, fmt);
+    vprintf (fmt, ap);
+    va_end(ap);
+
+    tputs (SE, 1, outc);
+
+    return OK;
+}
+
+/*
+ * Is this a hardcopy terminal?
+ */
+
+int
+sc_hardcopy(void)
+{
+    read_termcap();
+    return HC;
+}
+
diff --git a/uip/viamail.c b/uip/viamail.c
new file mode 100644 (file)
index 0000000..bb5b12c
--- /dev/null
@@ -0,0 +1,249 @@
+
+/*
+ * viamail.c -- send multiple files in a MIME message
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+#include <h/signals.h>
+#include <h/md5.h>
+#include <errno.h>
+#include <signal.h>
+#include <zotnet/mts/mts.h>
+#include <zotnet/tws/tws.h>
+#include <h/mime.h>
+#include <h/mhparse.h>
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+static struct swit switches[] = {
+#define        TOSW                    0
+    { "to mailpath", 0 },
+#define        FROMSW                  1
+    { "from mailpath", 0 },
+#define        SUBJECTSW               2
+    { "subject subject", 0 },
+#define        PARAMSW                 3
+    { "parameters arguments", 0 },
+#define        DESCRIPTSW              4
+    { "description text", 0 },
+#define        COMMENTSW               5
+    { "comment text", 0 },
+#define        DELAYSW                 6
+    { "delay seconds", 0 },
+#define        VERBSW                  7
+    { "verbose", 0 },
+#define        NVERBSW                 8
+    { "noverbose", 0 },
+#define VERSIONSW               9
+    { "version", 0 },
+#define        HELPSW                 10
+    { "help", 4 },
+#define DEBUGSW                11
+    { "debug", -5 },
+    { NULL, 0 }
+};
+
+extern int errno;
+extern int debugsw;
+extern int splitsw;
+extern int verbsw;
+
+int ebcdicsw = 0;      /* hack for linking purposes */
+
+/* mhmisc.c */
+void set_endian (void);
+
+/* mhoutsbr.c */
+int writeBase64aux (FILE *, FILE *);
+
+/*
+ * static prototypes
+ */
+static int via_mail (char *, char *, char *, char *, char *, int, char *);
+
+
+int
+main (int argc, char **argv)
+{
+    int delay = 0;
+    char *f1 = NULL, *f2 = NULL, *f3 = NULL;
+    char *f4 = NULL, *f5 = NULL, *f7 = NULL;
+    char *cp, buf[BUFSIZ];
+    char **argp, **arguments;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* foil search of user profile/context */
+    if (context_foil (NULL) == -1)
+       done (1);
+
+    arguments = getarguments (invo_name, argc, argv, 0);
+    argp = arguments;
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+           case AMBIGSW: 
+               ambigsw (cp, switches);
+               done (1);
+           case UNKWNSW: 
+               adios (NULL, "-%s unknown", cp);
+
+           case HELPSW: 
+               snprintf (buf, sizeof(buf), "%s [switches]", invo_name);
+               print_help (buf, switches, 1);
+               done (1);
+           case VERSIONSW:
+               print_version(invo_name);
+               done (1);
+    
+           case TOSW:
+               if (!(f1 = *argp++))
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               continue;
+           case SUBJECTSW:
+               if (!(f2 = *argp++))
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               continue;
+           case PARAMSW:
+               if (!(f3 = *argp++))
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               continue;
+           case DESCRIPTSW:
+               if (!(f4 = *argp++))
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               continue;
+           case COMMENTSW:
+               if (!(f5 = *argp++))
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               continue;
+           case DELAYSW:
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+
+               /*
+                * If there is an error, just reset the delay parameter
+                * to -1.  We will set a default delay later.
+                */
+               if (sscanf (cp, "%d", &delay) != 1)
+                   delay = -1;
+               continue;
+           case FROMSW:
+               if (!(f7 = *argp++))
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               continue;
+
+           case VERBSW: 
+               verbsw = 1;
+               continue;
+           case NVERBSW: 
+               verbsw = 0;
+               continue;
+
+           case DEBUGSW:
+               debugsw = 1;
+               continue;
+           }
+       }
+    }
+
+    set_endian ();
+
+    if (!f1)
+       adios (NULL, "missing -viamail \"mailpath\" switch");
+
+    via_mail (f1, f2, f3, f4, f5, delay, f7);
+    /* NOTREACHED */
+}
+
+
+/*
+ * VIAMAIL
+ */
+
+static int
+via_mail (char *mailsw, char *subjsw, char *parmsw, char *descsw,
+          char *cmntsw, int delay, char *fromsw)
+{
+    int        status, vecp = 1;
+    char tmpfil[BUFSIZ];
+    char *vec[MAXARGS];
+    struct stat st;
+    FILE *fp;
+
+    umask (~m_gmprot ());
+
+    strncpy (tmpfil, m_tmpfil (invo_name), sizeof(tmpfil));
+    if ((fp = fopen (tmpfil, "w+")) == NULL)
+       adios (tmpfil, "unable to open for writing");
+    chmod (tmpfil, 0600);
+
+    if (!strchr(mailsw, '@'))
+       mailsw = concat (mailsw, "@", LocalName (), NULL);
+    fprintf (fp, "To: %s\n", mailsw);
+
+    if (subjsw)
+       fprintf (fp, "Subject: %s\n", subjsw);
+
+    if (fromsw) {
+       if (!strchr(fromsw, '@'))
+           fromsw = concat (fromsw, "@", LocalName (), NULL);
+       fprintf (fp, "From: %s\n", fromsw);
+    }
+
+    fprintf (fp, "%s: %s\n", VRSN_FIELD, VRSN_VALUE);
+    fprintf (fp, "%s: application/octet-stream", TYPE_FIELD);
+
+    if (parmsw)
+       fprintf (fp, "; %s", parmsw);
+
+    if (cmntsw)
+       fprintf (fp, "\n\t(%s)", cmntsw);
+
+    if (descsw)
+       fprintf (fp, "\n%s: %s", DESCR_FIELD, descsw);
+
+    fprintf (fp, "\n%s: %s\n\n", ENCODING_FIELD, "base64");
+
+    if (fflush (fp))
+       adios (tmpfil, "error writing to");
+
+    writeBase64aux (stdin, fp);
+    if (fflush (fp))
+       adios (tmpfil, "error writing to");
+
+    if (fstat (fileno (fp), &st) == NOTOK)
+       adios ("failed", "fstat of %s", tmpfil);
+
+    if (delay < 0)
+       splitsw = 10;
+    else
+       splitsw = delay;
+
+    status = 0;
+    vec[0] = r1bindex (postproc, '/');
+    if (verbsw)
+       vec[vecp++] = "-verbose";
+
+    switch (sendsbr (vec, vecp, tmpfil, &st, 0)) {
+       case DONE:
+       case NOTOK:
+           status++;
+           break;
+       case OK:
+           break;
+    }
+
+    fclose (fp);
+    if (unlink (tmpfil) == -1)
+       advise (NULL, "unable to remove temp file %s", tmpfil);
+    done (status);
+}
diff --git a/uip/vmh.c b/uip/vmh.c
new file mode 100644 (file)
index 0000000..8b398e6
--- /dev/null
+++ b/uip/vmh.c
@@ -0,0 +1,1513 @@
+
+/*
+ * vmh.c -- visual front-end to nmh
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/signals.h>
+
+#if 0
+#if defined(SYS5) && !defined(TERMINFO)
+/*
+ * Define TERMINFO if you have it.
+ * You get it automatically if you're running SYS5, and you don't get
+ * it if you're not.  (If you're not SYS5, you probably have termcap.)
+ * We distinguish TERMINFO from SYS5 because in this file SYS5 really
+ * means "AT&T line discipline" (termio, not sgttyb), whereas terminfo
+ * is quite a separate issue.
+ */
+#define        TERMINFO 1
+#endif
+#endif
+
+/*
+ * TODO:
+ *       1) Pass signals to client during execution
+ *       2) Figure out a way for the user to say how big the Scan/Display
+ *          windows should be.
+ *       3) If curses ever gets fixed, then XYZ code can be removed
+ */
+
+#include <curses.h>
+
+#ifdef ncr
+# define _SYS_REG_H            /* NCR redefines "ERR" in <sys/reg.h> */
+#endif
+
+#undef OK                      /* tricky */
+
+/* removed for right now */
+#if 0
+#ifdef TERMINFO
+# include <term.h>     /* variables describing terminal capabilities */
+#endif /* TERMINFO */
+#endif
+
+#include <h/vmhsbr.h>
+#include <errno.h>
+#include <setjmp.h>
+#include <signal.h>
+
+#ifndef        sigmask
+# define sigmask(s) (1 << ((s) - 1))
+#endif /* not sigmask */
+
+#ifdef ridge
+# undef SIGTSTP
+#endif /* ridge */
+
+#ifdef HAVE_WRITEV
+# include <sys/uio.h>
+#else
+struct iovec {
+    char *iov_base;
+    int   iov_len;
+};
+#endif
+
+#ifdef hpux
+# include <termio.h>
+# define TCGETATTR             /* tcgetattr() */
+#endif
+
+#ifdef BSD44
+# define USE_OLD_TTY
+# define _maxx maxx            /* curses.h */
+# define _maxy maxy
+# define _curx curx            /* curses.h */
+# define _cury cury
+void __cputchar __P((int));
+# undef        _putchar
+# define _putchar __cputchar
+# include <sys/ioctl.h>                /* sgttyb */
+#endif
+
+#define        ALARM   ((unsigned int) 10)
+#define        PAUSE   ((unsigned int) 2)
+
+#ifndef        abs
+# define abs(a)        ((a) > 0 ? (a) : -(a))
+#endif
+
+#define        SMALLMOVE       1
+#define        LARGEMOVE       10
+
+#define        XYZ                     /* XXX */
+
+static struct swit switches[] = {
+#define        PRMPTSW               0
+    { "prompt string", 6 },
+#define        PROGSW                1
+    { "vmhproc program", 7 },
+#define        NPROGSW               2
+    { "novmhproc", 9 },
+#define VERSIONSW             3
+    { "version", 0 },
+#define        HELPSW                4
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+                                       /* PEERS */
+static int  PEERpid = NOTOK;
+
+static jmp_buf PEERctx;
+
+                                       /* WINDOWS */
+static char *myprompt = "(%s) ";
+
+static WINDOW *Scan;
+static WINDOW *Status;
+static WINDOW *Display;
+static WINDOW *Command;
+
+#define        NWIN    3
+static int numwins;
+WINDOW *windows[NWIN + 1];
+
+
+                                       /* LINES */
+
+struct line {
+    int l_no;
+    char *l_buf;
+    struct line *l_prev;
+    struct line *l_next;
+};
+
+static struct line *lhead = NULL;
+static struct line *ltop = NULL;
+static struct line *ltail = NULL;
+
+static int did_less = 0;
+static int smallmove = SMALLMOVE;
+static int largemove = LARGEMOVE;
+
+
+                                       /* TTYS */
+
+static int  tty_ready = NOTOK;
+
+static int  intrc;
+
+#ifndef        SYS5
+# define ERASE sg.sg_erase
+# define KILL  sg.sg_kill
+static struct sgttyb sg;
+
+#define        EOFC tc.t_eofc
+#define        INTR tc.t_intrc
+static struct tchars tc;
+#else  /* SYS5 */
+# define ERASE sg.c_cc[VERASE]
+# define KILL  sg.c_cc[VKILL]
+# define EOFC  sg.c_cc[VEOF]
+# define INTR  sg.c_cc[VINTR]
+static struct termio sg;
+#endif /* SYS5 */
+
+#ifndef        TIOCGLTC
+# define WERASC        ('W' & 037)
+#else /* TIOCGLTC */
+# ifndef SVR4
+#  define WERASC ltc.t_werasc
+static struct ltchars ltc;
+# else /* SVR4 */
+#  define WERASC sg.c_cc[VWERASE]
+#  undef TIOCGLTC    /* the define exists, but struct ltchars doesn't */
+# endif
+#endif /* TIOCGLTC */
+
+
+#if !defined(SYS5) && !defined(BSD44)
+int _putchar();
+#endif /* not SYS5 */
+
+#ifdef SIGTSTP
+char *tgoto();
+#endif /* SIGTSTP */
+
+                                       /* SIGNALS */
+static RETSIGTYPE ALRMser(int);
+static RETSIGTYPE PIPEser(int);
+static RETSIGTYPE SIGser(int);
+#ifdef SIGTSTP
+static RETSIGTYPE TSTPser(int);
+#endif /* SIGTSTP */
+
+
+                                       /* MISCELLANY */
+extern int errno;
+
+/*
+ * static prototypes
+ */
+static void adorn (char *, char *, ...);
+
+static vmh(), lreset(), linsert(), ladvance(), lretreat(), lgo();
+static TTYon(), TTYoff(), foreground();
+static int PEERinit(), pINI(), pLOOP(), pTTY(), pWIN(), WINinit();
+static int WINgetstr(), WINless(), WINputc(), TTYinit(), pWINaux();
+
+
+int
+main (int argc, char **argv)
+{
+    int vecp = 1, nprog = 0;
+    char *cp, buffer[BUFSIZ];
+    char **argp, **arguments, *vec[MAXARGS];
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    while ((cp = *argp++))
+       if (*cp == '-')
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   vec[vecp++] = --cp;
+                   continue;
+
+               case HELPSW: 
+                   snprintf (buffer, sizeof(buffer), "%s [switches for vmhproc]",
+                       invo_name);
+                   print_help (buffer, switches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case PRMPTSW:
+                   if (!(myprompt = *argp++) || *myprompt == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+
+               case PROGSW: 
+                   if (!(vmhproc = *argp++) || *vmhproc == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+               case NPROGSW:
+                   nprog++;
+                   continue;
+           }
+       else
+           vec[vecp++] = cp;
+
+    if (TTYinit (nprog) == NOTOK || WINinit (nprog) == NOTOK) {
+       vec[vecp] = NULL;
+
+       vec[0] = r1bindex (vmhproc, '/');
+       execvp (vmhproc, vec);
+       adios (vmhproc, "unable to exec");
+    }
+    TTYoff ();
+    PEERinit (vecp, vec);
+    TTYon ();
+
+    vmh ();
+
+    done (0);
+}
+
+
+static void
+vmh (void)
+{
+    char buffer[BUFSIZ];
+
+    for (;;) {
+       pLOOP (RC_QRY, NULL);
+
+       wmove (Command, 0, 0);
+       wprintw (Command, myprompt, invo_name);
+       wclrtoeol (Command);
+       wrefresh (Command);
+
+       switch (WINgetstr (Command, buffer)) {
+           case NOTOK: 
+               break;
+
+           case OK:
+               done (0);       /* NOTREACHED */
+
+           default: 
+               if (*buffer)
+                   pLOOP (RC_CMD, buffer);
+               break;
+       }
+    }
+}
+
+/* PEERS */
+
+static int
+PEERinit (int vecp, char *vec[])
+{
+    int        pfd0[2], pfd1[2];
+    char buf1[BUFSIZ], buf2[BUFSIZ];
+
+    if (pipe (pfd0) == NOTOK || pipe (pfd1) == NOTOK)
+       adios ("pipe", "unable to");
+#ifdef hpux
+    switch (PEERpid = fork ()) {
+    /*
+     * Calling vfork() and then another routine [like close()] before
+     * an exec() messes up the stack frame, causing crib death.
+     * Use fork() instead.
+     */
+#else  /* not hpux */
+    switch (PEERpid = vfork ()) {
+#endif /* not hpux */
+       case NOTOK: 
+           adios ("vfork", "unable to");/* NOTREACHED */
+
+       case OK: 
+           close (pfd0[0]);
+           close (pfd1[1]);
+
+           vec[vecp++] = "-vmhread";
+           snprintf (buf1, sizeof(buf1), "%d", pfd1[0]);
+           vec[vecp++] = buf1;
+           vec[vecp++] = "-vmhwrite";
+           snprintf (buf2, sizeof(buf2), "%d", pfd0[1]);
+           vec[vecp++] = buf2;
+           vec[vecp] = NULL;
+
+           SIGNAL (SIGINT, SIG_DFL);
+           SIGNAL (SIGQUIT, SIG_DFL);
+
+           vec[0] = r1bindex (vmhproc, '/');
+           execvp (vmhproc, vec);
+           perror (vmhproc);
+           _exit (-1);         /* NOTREACHED */
+
+       default: 
+           close (pfd0[1]);
+           close (pfd1[0]);
+
+           rcinit (pfd0[0], pfd1[1]);
+           return pINI ();
+    }
+}
+
+
+static int
+pINI (void)
+{
+    int len, buflen;
+    char *bp, buffer[BUFSIZ];
+    struct record rcs;
+    register struct record *rc = &rcs;
+    register WINDOW **w;
+
+    initrc (rc);
+
+    /* Get buffer ready to go */
+    bp = buffer;
+    buflen = sizeof(buffer);
+
+    snprintf (bp, buflen, "%d %d", RC_VRSN, numwins);
+    len = strlen (bp);
+    bp += len;
+    buflen -= len;
+
+    for (w = windows; *w; w++) {
+       snprintf (bp, buflen, " %d", (*w)->_maxy);
+       len = strlen (bp);
+       bp += len;
+       buflen -= len;
+    }
+
+    switch (str2rc (RC_INI, buffer, rc)) {
+       case RC_ACK: 
+           return OK;
+
+       case RC_ERR: 
+           if (rc->rc_len)
+               adios (NULL, "%s", rc->rc_data);
+           else
+               adios (NULL, "pINI peer error");
+
+       case RC_XXX: 
+           adios (NULL, "%s", rc->rc_data);
+
+       default:
+           adios (NULL, "pINI protocol screw-up");
+    }
+/* NOTREACHED */
+}
+
+
+static int
+pLOOP (char *code, char *str)
+{
+    int        i;
+    struct record rcs;
+    register struct record *rc = &rcs;
+
+    initrc (rc);
+
+    str2peer (code, str);
+    for (;;)
+       switch (peer2rc (rc)) {
+           case RC_TTY:
+               if (pTTY (rc) == NOTOK)
+                   return NOTOK;
+               break;
+
+           case RC_WIN:
+               if (sscanf (rc->rc_data, "%d", &i) != 1
+                       || i <= 0
+                       || i > numwins) {
+                   fmt2peer (RC_ERR, "no such window \"%s\"", rc->rc_data);
+                   return NOTOK;
+               }
+               if (pWIN (windows[i - 1]) == NOTOK)
+                   return NOTOK;
+               break;
+
+           case RC_EOF:
+               return OK;
+
+           case RC_ERR:
+               if (rc->rc_len)
+                   adorn (NULL, "%s", rc->rc_data);
+               else
+                   adorn (NULL, "pLOOP(%s) peer error",
+                           code == RC_QRY ? "QRY" : "CMD");
+               return NOTOK;
+
+           case RC_FIN:
+               if (rc->rc_len)
+                   adorn (NULL, "%s", rc->rc_data);
+               rcdone ();
+               i = pidwait (PEERpid, OK);
+               PEERpid = NOTOK;
+               done (i);
+
+           case RC_XXX: 
+               adios (NULL, "%s", rc->rc_data);
+
+           default:
+               adios (NULL, "pLOOP(%s) protocol screw-up",
+                       code == RC_QRY ? "QRY" : "CMD");
+       }
+}
+
+
+static int
+pTTY (struct record *r)
+{
+    SIGNAL_HANDLER hstat, istat, qstat, tstat;
+    struct record rcs;
+    register struct record *rc = &rcs;
+
+    initrc (rc);
+
+    TTYoff ();
+
+    /* should be changed to block instead of ignore */
+    hstat = SIGNAL (SIGHUP, SIG_IGN);
+    istat = SIGNAL (SIGINT, SIG_IGN);
+    qstat = SIGNAL (SIGQUIT, SIG_IGN);
+    tstat = SIGNAL (SIGTERM, SIG_IGN);
+
+    rc2rc (RC_ACK, 0, NULL, rc);
+
+    SIGNAL (SIGHUP, hstat);
+    SIGNAL (SIGINT, istat);
+    SIGNAL (SIGQUIT, qstat);
+    SIGNAL (SIGTERM, tstat);
+
+    TTYon ();
+
+    if (r->rc_len && strcmp (r->rc_data, "FAST") == 0)
+       goto no_refresh;
+
+#ifdef SIGTSTP
+    SIGNAL (SIGTSTP, SIG_IGN);
+#endif
+
+#ifndef        TERMINFO
+    if (SO)
+       tputs (SO, 0, _putchar);
+#else  /* TERMINFO */
+    putp(enter_standout_mode);
+#endif /* TERMINFO */
+    fprintf (stdout, "Type any key to continue... ");
+    fflush (stdout);
+#ifndef        TERMINFO
+    if (SE)
+       tputs (SE, 0, _putchar);
+#else  /* TERMINFO */
+    putp(exit_standout_mode);
+#endif /* TERMINFO */
+    getc (stdin);
+#ifdef SIGTSTP
+    SIGNAL (SIGTSTP, TSTPser);
+#endif /* SIGTSTP */
+
+    wrefresh (curscr);
+
+no_refresh: ;
+    switch (rc->rc_type) {
+       case RC_EOF: 
+           rc2peer (RC_ACK, 0, NULL);
+           return OK;
+
+       case RC_ERR: 
+           if (rc->rc_len)
+               adorn (NULL, "%s", rc->rc_data);
+           else
+               adorn (NULL, "pTTY peer error");
+           return NOTOK;
+
+       case RC_XXX: 
+           adios (NULL, "%s", rc->rc_data);
+
+       default:
+           adios (NULL, "pTTY protocol screw-up");
+    }
+/* NOTREACHED */
+}
+
+
+static int
+pWIN (WINDOW *w)
+{
+    int i;
+
+    did_less = 0;
+    if ((i = pWINaux (w)) == OK && did_less)
+       WINless (w, 1);
+
+    lreset ();
+
+    return i;
+}
+
+
+static int
+pWINaux (WINDOW *w)
+{
+    register int n;
+    int        eol;
+    register char c, *bp;
+    struct record rcs;
+    register struct record *rc = &rcs;
+
+    initrc (rc);
+
+    werase (w);
+    wmove (w, 0, 0);
+#ifdef XYZ
+    if (w == Status)
+       wstandout (w);
+#endif /* XYZ */
+
+    for (eol = 0;;)
+       switch (rc2rc (RC_ACK, 0, NULL, rc)) {
+           case RC_DATA: 
+               if (eol && WINputc (w, '\n') == ERR && WINless (w, 0))
+                   goto flush;
+               for (bp = rc->rc_data, n = rc->rc_len; n-- > 0; ) {
+                   if ((c = *bp++) == '\n')
+                       linsert (w);
+                   if (WINputc (w, c) == ERR)
+                       if (n == 0 && c == '\n')
+                           eol++;
+                       else
+                           if (WINless (w, 0)) {
+flush: ;
+                               fmt2peer (RC_ERR, "flush window");
+#ifdef XYZ                     /* should NEVER happen... */
+                               if (w == Status)
+                                   wstandend (w);
+#endif /* XYZ */
+                               wrefresh (w);
+                               return NOTOK;
+                           }
+               }
+               break;
+
+           case RC_EOF: 
+               rc2peer (RC_ACK, 0, NULL);
+#ifdef XYZ
+               if (w == Status)
+                   wstandend (w);
+#endif /* XYZ */
+               wrefresh (w);
+               return OK;
+
+           case RC_ERR: 
+               if (rc->rc_len)
+                   adorn (NULL, "%s", rc->rc_data);
+               else
+                   adorn (NULL, "pWIN peer error");
+               return NOTOK;
+
+           case RC_XXX: 
+               adios (NULL, "%s", rc->rc_data);
+
+           default:
+               adios (NULL, "pWIN protocol screw-up");
+       }
+/* NOTREACHED */
+}
+
+
+static int
+pFIN (void)
+{
+    int status;
+
+    if (PEERpid <= OK)
+       return OK;
+
+    rc2peer (RC_FIN, 0, NULL);
+    rcdone ();
+
+    switch (setjmp (PEERctx)) {
+       case OK: 
+           SIGNAL (SIGALRM, ALRMser);
+           alarm (ALARM);
+
+           status = pidwait (PEERpid, OK);
+
+           alarm (0);
+           break;
+
+       default: 
+           kill (PEERpid, SIGKILL);
+           status = NOTOK;
+           break;
+    }
+    PEERpid = NOTOK;
+
+    return status;
+}
+
+/* WINDOWS */
+
+static int
+WINinit (int nprog)
+{
+    register int nlines,       /* not "lines" because terminfo uses that */
+                 top,
+                 bottom;
+
+    foreground ();
+    if (initscr () == (WINDOW *) ERR)
+       if (nprog)
+           return NOTOK;
+       else
+           adios (NULL, "could not initialize terminal");
+#ifdef SIGTSTP
+    SIGNAL (SIGTSTP, SIG_DFL);
+#endif /* SIGTSTP */
+    sideground ();
+
+#ifndef        TERMINFO
+    if (CM == NULL)
+#else  /* TERMINFO */
+    if (cursor_address == NULL)        /* assume mtr wanted "cm", not "CM" */
+#endif /* TERMINFO */
+       if (nprog)
+           return NOTOK;
+       else
+           adios (NULL,
+                   "sorry, your terminal isn't powerful enough to run %s",
+                   invo_name);
+
+#ifndef        TERMINFO
+    if (tgetflag ("xt") || tgetnum ("sg") > 0)
+       SO = SE = US = UE = NULL;
+#else  /* TERMINFO */
+/*
+ * If termcap mapped directly to terminfo, we'd use the following:
+ *  if (teleray_glitch || magic_cookie_glitch > 0)
+ *     enter_standout_mode = exit_standout_mode =
+ *     enter_underline_mode = exit_underline_mode = NULL;
+ * But terminfo does the right thing so we don't have to resort to that.
+ */
+#endif /* TERMINFO */
+
+    if ((nlines = LINES - 1) < 11)
+       adios (NULL, "screen too small");
+    if ((top = nlines / 3 + 1) > LINES / 4 + 2)
+       top--;
+    bottom = nlines - top - 2;
+
+    numwins = 0;
+    Scan = windows[numwins++] = newwin (top, COLS, 0, 0);
+    Status = windows[numwins++] = newwin (1, COLS, top, 0);
+#ifndef        XYZ
+    wstandout (Status);
+#endif /* XYZ */
+    Display = windows[numwins++] = newwin (bottom, COLS, top + 1, 0);
+    Command = newwin (1, COLS - 1, top + 1 + bottom, 0);
+    windows[numwins] = NULL;
+
+    largemove = Display->_maxy / 2 + 2;
+    return OK;
+}
+
+
+static int WINgetstr (WINDOW *w, char *buffer)
+{
+    register int c;
+    register char *bp;
+
+    bp = buffer;
+    *bp = 0;
+
+    for (;;) {
+       switch (c = toascii (wgetch (w))) {
+           case ERR: 
+               adios (NULL, "wgetch lost");
+
+           case '\f':
+               wrefresh (curscr);
+               break;
+
+           case '\r': 
+           case '\n': 
+               *bp = 0;
+               if (bp > buffer) {
+                   leaveok (curscr, FALSE);
+                   wmove (w, 0, w->_curx - (bp - buffer));
+                   wrefresh (w);
+                   leaveok (curscr, TRUE);
+               }
+               return DONE;
+
+           default: 
+               if (c == intrc) {
+                   wprintw (w, " ");
+                   wstandout (w);
+                   wprintw (w, "Interrupt");
+                   wstandend (w);
+                   wrefresh (w);
+                   *buffer = 0;
+                   return NOTOK;
+               }
+               if (c == EOFC) {
+                   if (bp <= buffer)
+                       return OK;
+                   break;
+               }
+               if (c == ERASE) {
+                   if (bp <= buffer)
+                       continue;
+                   bp--, w->_curx--;
+                   wclrtoeol (w);
+                   break;
+               }
+               if (c == KILL) {
+                   if (bp <= buffer)
+                       continue;
+                   w->_curx -= bp - buffer;
+                   bp = buffer;
+                   wclrtoeol (w);
+                   break;
+               }
+               if (c == WERASC) {
+                   if (bp <= buffer)
+                       continue;
+                   do {
+                       bp--, w->_curx--;
+                   } while (isspace (*bp) && bp > buffer);
+
+                   if (bp > buffer) {
+                       do {
+                           bp--, w->_curx--;
+                       } while (!isspace (*bp) && bp > buffer);
+                       if (isspace (*bp))
+                           bp++, w->_curx++;
+                   }
+                   wclrtoeol (w);
+                   break;
+               }
+               
+               if (c >= ' ' && c < '\177')
+                   waddch (w, *bp++ = c);
+               break;
+       }
+
+       wrefresh (w);
+    }
+}
+
+
+static int
+WINwritev (WINDOW *w, struct iovec *iov, int n)
+{
+    register int i;
+
+    werase (w);
+    wmove (w, 0, 0);
+    for (i = 0; i < n; i++, iov++)
+       wprintw (w, "%*.*s", iov->iov_len, iov->iov_len, iov->iov_base);
+    wrefresh (w);
+
+    sleep (PAUSE);
+
+    return OK;
+}
+
+
+static struct {
+    char   *h_msg;
+    int    *h_val;
+}               hlpmsg[] = {
+                    "          forward         backwards", NULL,
+                    "          -------         ---------", NULL,
+                    "next screen       SPACE", NULL,
+                    "next %d line%s    RETURN          y", &smallmove,
+                    "next %d line%s    EOT             u", &largemove,
+                    "go                g               G", NULL,
+                    "", NULL,
+                    "refresh           CTRL-L", NULL,
+                    "quit              q", NULL,
+
+                    NULL, NULL
+};
+
+
+static int
+WINless (WINDOW *w, int fin)
+{
+    register int c, i, n;
+    char *cp;
+    register struct line *lbottom;
+    int nfresh, nwait;
+
+#ifdef notdef
+    int nlatch;
+#endif
+
+    did_less++;
+
+    cp = NULL;
+#ifdef notdef
+    if (fin)
+       ltop = NULL;
+#endif /* notdef */
+    lbottom = NULL;
+    nfresh = 1;
+    nwait = 0;
+    wrefresh (w);
+
+    for (;;) {
+       if (nfresh || nwait) {
+           nfresh = 0;
+#ifdef notdef
+           nlatch = 1;
+
+once_only: ;
+#endif /* notdef */
+           werase (w);
+           wmove (w, 0, 0);
+
+           if (ltop == NULL)
+               if (fin) {
+                   lgo (ltail->l_no - w->_maxy + 1);
+                   if (ltop == NULL)
+                       ltop = lhead;
+               }
+               else
+                   ltop = lbottom && lbottom->l_prev ? lbottom->l_prev
+                           : lbottom;
+
+           for (lbottom = ltop; lbottom; lbottom = lbottom->l_next)
+               if (waddstr (w, lbottom->l_buf) == ERR
+                       || waddch (w, '\n') == ERR)
+                   break;
+           if (lbottom == NULL)
+               if (fin) {
+#ifdef notdef
+                   if (nlatch && (ltail->l_no >= w->_maxy)) {
+                       lgo (ltail->l_no - w->_maxy + 1);
+                       nlatch = 0;
+                       goto once_only;
+                   }
+#endif /* notdef */
+                   lbottom = ltail;
+                   while (waddstr (w, "~\n") != ERR)
+                       continue;
+               }
+               else {
+                   wrefresh (w);
+                   return 0;
+               }
+
+           if (!nwait)
+               wrefresh (w);
+       }
+
+       wmove (Command, 0, 0);
+       if (cp) {
+           wstandout (Command);
+           wprintw (Command, "%s", cp);
+           wstandend (Command);
+           cp = NULL;
+       }
+       else
+           wprintw (Command, fin ? "top:%d bot:%d end:%d" : "top:%d bot:%d",
+                   ltop->l_no, lbottom->l_no, ltail->l_no);
+       wprintw (Command, ">> ");
+       wclrtoeol (Command);
+       wrefresh (Command);
+
+       c = toascii (wgetch (Command));
+
+       werase (Command);
+       wrefresh (Command);
+
+       if (nwait) {
+           nwait = 0;
+           wrefresh (w);
+       }
+
+       n = 0;
+again:         ;
+       switch (c) {
+           case ' ': 
+               ltop = lbottom->l_next;
+               nfresh++;
+               break;
+
+           case '\r': 
+           case '\n': 
+           case 'e': 
+           case 'j': 
+               if (n)
+                   smallmove = n;
+               if (ladvance (smallmove))
+                   nfresh++;
+               break;
+
+           case 'y': 
+           case 'k': 
+               if (n)
+                   smallmove = n;
+               if (lretreat (smallmove))
+                   nfresh++;
+               break;
+
+           case 'd': 
+       eof:    ;
+               if (n)
+                   largemove = n;
+               if (ladvance (largemove))
+                   nfresh++;
+               break;
+
+           case 'u': 
+               if (n)
+                   largemove = n;
+               if (lretreat (largemove))
+                   nfresh++;
+               break;
+
+           case 'g': 
+               if (lgo (n ? n : 1))
+                   nfresh++;
+               break;
+
+           case 'G': 
+               if (lgo (n ? n : ltail->l_no - w->_maxy + 1))
+                   nfresh++;
+               break;
+
+           case '\f': 
+           case 'r': 
+               wrefresh (curscr);
+               break;
+
+           case 'h': 
+           case '?': 
+               werase (w);
+               wmove (w, 0, 0);
+               for (i = 0; hlpmsg[i].h_msg; i++) {
+                   if (hlpmsg[i].h_val)
+                       wprintw (w, hlpmsg[i].h_msg, *hlpmsg[i].h_val,
+                               *hlpmsg[i].h_val != 1 ? "s" : "");
+                   else
+                       waddstr (w, hlpmsg[i].h_msg);
+                   waddch (w, '\n');
+               }
+               wrefresh (w);
+               nwait++;
+               break;
+
+           case 'q': 
+               return 1;
+
+           default: 
+               if (c == EOFC)
+                   goto eof;
+
+               if (isdigit (c)) {
+                   wmove (Command, 0, 0);
+                   i = 0;
+                   while (isdigit (c)) {
+                       wprintw (Command, "%c", c);
+                       wrefresh (Command);
+                       i = i * 10 + c - '0';
+                       c = toascii (wgetch (Command));
+                   }
+                   werase (Command);
+                   wrefresh (Command);
+
+                   if (i > 0) {
+                       n = i;
+                       goto again;
+                   }
+                   cp = "bad number";
+               }
+               else
+                   cp = "not understood";
+               break;
+       }
+    }
+}
+
+
+static int
+WINputc (WINDOW *w, char c)
+{
+    register int x, y;
+
+    switch (c) {
+       default: 
+           if (!isascii (c)) {
+               if (WINputc (w, 'M') == ERR || WINputc (w, '-') == ERR)
+                   return ERR;
+               c = toascii (c);
+           }
+           else
+               if (c < ' ' || c == '\177') {
+                   if (WINputc (w, '^') == ERR)
+                       return ERR;
+                   c ^= 0100;
+               }
+           break;
+
+       case '\t': 
+       case '\n': 
+           break;
+    }
+
+    if (w != Scan)
+       return waddch (w, c);
+
+    if ((x = w->_curx) < 0 || x >= w->_maxx
+           || (y = w->_cury) < 0 || y >= w->_maxy)
+       return DONE;
+
+    switch (c) {
+       case '\t': 
+           for (x = 8 - (x & 0x07); x > 0; x--)
+               if (WINputc (w, ' ') == ERR)
+                   return ERR;
+           break;
+
+       case '\n': 
+           if (++y < w->_maxy) 
+               waddch (w, c);
+           else
+               wclrtoeol (w);
+           break;
+
+       default: 
+           if (++x < w->_maxx) 
+               waddch (w, c);
+           break;
+    }
+
+    return DONE;
+}
+
+/* LINES */
+
+static void
+lreset (void)
+{
+    register struct line *lp, *mp;
+
+    for (lp = lhead; lp; lp = mp) {
+       mp = lp->l_next;
+       free (lp->l_buf);
+       free ((char *) lp);
+    }
+    lhead = ltop = ltail = NULL;
+}
+
+
+static void
+linsert (WINDOW *w)
+{
+    register char *cp;
+    register struct line *lp;
+
+    if ((lp = (struct line *) calloc ((size_t) 1, sizeof *lp)) == NULL)
+       adios (NULL, "unable to allocate line storage");
+
+    lp->l_no = (ltail ? ltail->l_no : 0) + 1;
+#ifndef        BSD44
+    lp->l_buf = getcpy (w->_y[w->_cury]);
+#else
+    lp->l_buf = getcpy (w->lines[w->_cury]->line);
+#endif
+    for (cp = lp->l_buf + strlen (lp->l_buf) - 1; cp >= lp->l_buf; cp--)
+       if (isspace (*cp))
+           *cp = 0;
+       else
+           break;
+
+    if (lhead == NULL)
+       lhead = lp;
+    if (ltop == NULL)
+       ltop = lp;
+    if (ltail)
+       ltail->l_next = lp;
+    lp->l_prev = ltail;
+    ltail = lp;
+}
+
+
+static int
+ladvance (int n)
+{
+    register int i;
+    register struct line *lp;
+
+    for (i = 0, lp = ltop; i < n && lp; i++, lp = lp->l_next)
+       continue;
+
+    if (ltop == lp)
+       return 0;
+
+    ltop = lp;
+    return 1;
+}
+
+
+static int
+lretreat (int n)
+{
+    register int i;
+    register struct line *lp;
+
+    for (i = 0, lp = ltop; i < n && lp; i++, lp = lp->l_prev)
+       if (!lp->l_prev)
+           break;
+
+    if (ltop == lp)
+       return 0;
+
+    ltop = lp;
+    return 1;
+}
+
+
+static int
+lgo (int n)
+{
+    register int i, j;
+    register struct line *lp;
+
+    if ((i = n - (lp = lhead)->l_no)
+           > (j = abs (n - (ltop ? ltop : ltail)->l_no)))
+       i = j, lp = ltop ? ltop : ltail;
+    if (i > (j = abs (ltail->l_no - n)))
+       i = j, lp = ltail;
+
+    if (n >= lp->l_no) {
+       for (; lp; lp = lp->l_next)
+           if (lp->l_no == n)
+               break;
+    }
+    else {
+       for (; lp; lp = lp->l_prev)
+           if (lp->l_no == n)
+               break;
+       if (!lp)
+           lp = lhead;
+    }
+
+    if (ltop == lp)
+       return 0;
+
+    ltop = lp;
+    return 1;
+}
+
+/* TTYS */
+
+static int
+TTYinit (int nprog)
+{
+    if (!isatty (fileno (stdin)) || !isatty (fileno (stdout)))
+       if (nprog)
+           return NOTOK;
+       else
+           adios (NULL, "not a tty");
+
+    foreground ();
+#ifndef        SYS5
+    if (ioctl (fileno (stdin), TIOCGETP, (char *) &sg) == NOTOK)
+       adios ("failed", "ioctl TIOCGETP");
+    if (ioctl (fileno (stdin), TIOCGETC, (char *) &tc) == NOTOK)
+       adios ("failed", "ioctl TIOCGETC");
+#else
+#ifdef TCGETATTR
+    if( tcgetattr( fileno(stdin), &sg) == NOTOK)
+       adios( "failed", "tcgetattr");
+#else  /* SYS5 */
+    if (ioctl (fileno (stdin), TCGETA, &sg) == NOTOK)
+       adios ("failed", "ioctl TCGETA");
+#endif
+#endif
+#ifdef TIOCGLTC
+    if (ioctl (fileno (stdin), TIOCGLTC, (char *) &ltc) == NOTOK)
+       adios ("failed", "ioctl TIOCGLTC");
+#endif /* TIOCGLTC */
+    intrc = INTR;
+    sideground ();
+
+    tty_ready = OK;
+
+    SIGNAL (SIGPIPE, PIPEser);
+
+    return OK;
+}
+
+
+static void
+TTYon (void)
+{
+    if (tty_ready == DONE)
+       return;
+
+    INTR = NOTOK;
+#ifndef        SYS5
+    ioctl (fileno (stdin), TIOCSETC, (char *) &tc);
+#else  /* SYS5 */
+    ioctl (fileno (stdin), TCSETA, &sg);
+#endif /* SYS5 */
+
+    crmode ();
+    noecho ();
+    nonl ();
+    scrollok (curscr, FALSE);
+
+    discard (stdin);
+
+    tty_ready = DONE;
+
+    SIGNAL (SIGHUP, SIGser);
+    SIGNAL (SIGINT, SIGser);
+    SIGNAL (SIGQUIT, SIGser);
+#ifdef SIGTSTP
+    SIGNAL (SIGTSTP, TSTPser);
+#endif /* SIGTSTP */
+}
+
+
+static void
+TTYoff (void)
+{
+    if (tty_ready == NOTOK)
+       return;
+
+    INTR = intrc;
+#ifndef        SYS5
+    ioctl (fileno (stdin), TIOCSETC, (char *) &tc);
+#else  /* SYS5 */
+    ioctl (fileno (stdin), TCSETA, &sg);
+#endif /* SYS5 */
+
+    leaveok (curscr, TRUE);
+    mvcur (0, COLS - 1, LINES - 1, 0);
+    endwin ();
+    if (tty_ready == DONE) {
+#ifndef        TERMINFO
+       if (CE)
+           tputs (CE, 0, _putchar);
+       else
+#else  /* TERMINFO */
+       putp(clr_eol);
+#endif /* TERMINFO */
+           fprintf (stdout, "\r\n");
+    }
+    fflush (stdout);
+
+    tty_ready = NOTOK;
+
+    SIGNAL (SIGHUP, SIG_DFL);
+    SIGNAL (SIGINT, SIG_DFL);
+    SIGNAL (SIGQUIT, SIG_DFL);
+#ifdef SIGTSTP
+    SIGNAL (SIGTSTP, SIG_DFL);
+#endif /* SIGTSTP */
+}
+
+
+static void
+foreground (void)
+{
+#ifdef TIOCGPGRP
+    int pgrp, tpgrp;
+    SIGNAL_HANDLER tstat;
+
+    if ((pgrp = getpgrp()) == NOTOK)
+       adios ("process group", "unable to determine");
+    for (;;) {
+       if (ioctl (fileno (stdin), TIOCGPGRP, (char *) &tpgrp) == NOTOK)
+           adios ("tty's process group", "unable to determine");
+       if (pgrp == tpgrp)
+           break;
+
+       tstat = SIGNAL (SIGTTIN, SIG_DFL);
+       kill (0, SIGTTIN);
+       SIGNAL (SIGTTIN, tstat);
+    }
+    
+    SIGNAL (SIGTTIN, SIG_IGN);
+    SIGNAL (SIGTTOU, SIG_IGN);
+    SIGNAL (SIGTSTP, SIG_IGN);
+#endif /* TIOCGPGRP */
+}
+
+
+void
+sideground (void)
+{
+#ifdef TIOCGPGRP
+    SIGNAL (SIGTTIN, SIG_DFL);
+    SIGNAL (SIGTTOU, SIG_DFL);
+    SIGNAL (SIGTSTP, SIG_DFL);
+#endif /* TIOCGPGRP */
+}
+
+/* SIGNALS */
+
+
+static RETSIGTYPE
+ALRMser (int sig)
+{
+     longjmp (PEERctx, DONE);
+}
+
+
+#ifdef BSD42
+/* ARGSUSED */
+#endif /* BSD42 */
+
+static RETSIGTYPE
+PIPEser (int sig)
+{
+#ifndef RELIABLE_SIGNALS
+    SIGNAL (sig, SIG_IGN);
+#endif
+
+    adios (NULL, "lost peer");
+}
+
+
+static RETSIGTYPE
+SIGser (int sig)
+{
+#ifndef RELIABLE_SIGNALS
+    SIGNAL (sig, SIG_IGN);
+#endif
+
+    done (1);
+}
+
+
+#ifdef SIGTSTP
+static RETSIGTYPE
+TSTPser (int sig)
+{
+#ifndef        TERMINFO
+    tputs (tgoto (CM, 0, LINES - 1), 0, _putchar);
+#else  /* TERMINFO */
+    move(LINES - 1, 0);        /* to lower left corner */
+    clrtoeol();                /* clear bottom line */
+    wrefresh(curscr);  /* flush out everything */
+#endif /* TERMINFO */
+    fflush (stdout);
+
+    TTYoff ();
+#ifdef BSD42
+    sigsetmask (sigblock (0) & ~sigmask (SIGTSTP));
+#endif /* BSD42 */
+
+    kill (getpid (), sig);
+
+#ifdef BSD42
+    sigblock (sigmask (SIGTSTP));
+#endif /* BSD42 */
+    TTYon ();
+
+    wrefresh (curscr);
+}
+#endif /* SIGTSTP */
+
+
+/* MISCELLANY */
+
+void
+done (int status)
+{
+    TTYoff ();
+    pFIN ();
+
+    exit (status);
+}
+
+
+static void
+adorn (char *what, char *fmt, ...)
+{
+    va_list ap;
+    char *cp;
+
+    cp = invo_name;
+    invo_name = NULL;
+
+    va_start(ap, fmt);
+    advertise (what, NULL, fmt, ap);
+    va_end(ap);
+
+    invo_name = cp;
+}
+
+
+void
+advertise (char *what, char *tail, char *fmt, va_list ap)
+{
+    int        eindex = errno;
+    char buffer[BUFSIZ], err[BUFSIZ];
+    struct iovec iob[20];
+    register struct iovec *iov = iob;
+
+    fflush (stdout);
+    fflush (stderr);
+
+    if (invo_name) {
+       iov->iov_len = strlen (iov->iov_base = invo_name);
+       iov++;
+       iov->iov_len = strlen (iov->iov_base = ": ");
+       iov++;
+    }
+    
+    vsnprintf (buffer, sizeof(buffer), fmt, ap);
+    iov->iov_len = strlen (iov->iov_base = buffer);
+    iov++;
+    if (what) {
+       if (*what) {
+           iov->iov_len = strlen (iov->iov_base = " ");
+           iov++;
+           iov->iov_len = strlen (iov->iov_base = what);
+           iov++;
+           iov->iov_len = strlen (iov->iov_base = ": ");
+           iov++;
+       }
+       if (!(iov->iov_base = strerror (eindex))) {
+           snprintf (err, sizeof(err), "Error %d", eindex);
+           iov->iov_base = err;
+       }
+       iov->iov_len = strlen (iov->iov_base);
+       iov++;
+    }
+    if (tail && *tail) {
+       iov->iov_len = strlen (iov->iov_base = ", ");
+       iov++;
+       iov->iov_len = strlen (iov->iov_base = tail);
+       iov++;
+    }
+    iov->iov_len = strlen (iov->iov_base = "\n");
+    iov++;
+
+    if (tty_ready == DONE)
+       WINwritev (Display, iob, iov - iob);
+    else
+       writev (fileno (stderr), iob, iov - iob);
+}
+
diff --git a/uip/vmhsbr.c b/uip/vmhsbr.c
new file mode 100644 (file)
index 0000000..664c388
--- /dev/null
@@ -0,0 +1,224 @@
+
+/*
+ * vmhsbr.c -- routines to help vmh along
+ *
+ * $Id$
+ */
+
+/*
+ * TODO (for vrsn 2):
+ *     INI: include width of windows
+ */
+
+#include <h/mh.h>
+#include <h/vmhsbr.h>
+
+static char *types[] = {
+    "OK",
+    "INI", "ACK", "ERR", "CMD", "QRY", "TTY", "WIN", "DATA", "EOF", "FIN",
+    "XXX", NULL
+};
+
+static FILE *fp = NULL;
+
+static int PEERrfd = NOTOK;
+static int PEERwfd = NOTOK;
+
+extern int  errno;
+
+/*
+ * static prototypes
+ */
+static int rclose (struct record *, char *, ...);
+
+
+int
+rcinit (int rfd, int wfd)
+{
+    char *cp, buffer[BUFSIZ];
+
+    PEERrfd = rfd;
+    PEERwfd = wfd;
+
+    if ((cp = getenv ("MHVDEBUG")) && *cp) {
+       snprintf (buffer, sizeof(buffer), "%s.out", invo_name);
+       if ((fp = fopen (buffer, "w"))) {
+         fseek (fp, 0L, SEEK_END);
+         fprintf (fp, "%d: rcinit (%d, %d)\n", (int) getpid(), rfd, wfd);
+         fflush (fp);
+       }
+    }
+
+    return OK;
+}
+
+
+int
+rcdone (void)
+{
+    if (PEERrfd != NOTOK)
+       close (PEERrfd);
+    if (PEERwfd != NOTOK)
+       close (PEERwfd);
+
+    if (fp) {
+       fclose (fp);
+       fp = NULL;
+    }
+    return OK;
+}
+
+
+int
+rc2rc (char code, int len, char *data, struct record *rc)
+{
+    if (rc2peer (code, len, data) == NOTOK)
+       return NOTOK;
+
+    return peer2rc (rc);
+}
+
+
+int
+str2rc (char code, char *str, struct record *rc)
+{
+    return rc2rc (code, str ? strlen (str) : 0, str, rc);
+}
+
+
+int
+peer2rc (struct record *rc)
+{
+    if (rc->rc_data)
+       free (rc->rc_data);
+
+    if (read (PEERrfd, (char *) rc_head (rc), RHSIZE (rc)) != RHSIZE (rc))
+       return rclose (rc, "read from peer lost(1)");
+    if (rc->rc_len) {
+       if ((rc->rc_data = malloc ((unsigned) rc->rc_len + 1)) == NULL)
+           return rclose (rc, "malloc of %d lost", rc->rc_len + 1);
+       if (read (PEERrfd, rc->rc_data, rc->rc_len) != rc->rc_len)
+           return rclose (rc, "read from peer lost(2)");
+       rc->rc_data[rc->rc_len] = 0;
+    }
+    else
+       rc->rc_data = NULL;
+
+    if (fp) {
+       fseek (fp, 0L, SEEK_END);
+       fprintf (fp, "%d: <--- %s %d: \"%*.*s\"\n", (int) getpid(),
+               types[rc->rc_type], rc->rc_len,
+               rc->rc_len, rc->rc_len, rc->rc_data);
+       fflush (fp);
+    }
+
+    return rc->rc_type;
+}
+
+
+int
+rc2peer (char code, int len, char *data)
+{
+    struct record   rcs;
+    register struct record *rc = &rcs;
+
+    rc->rc_type = code;
+    rc->rc_len = len;
+
+    if (fp) {
+       fseek (fp, 0L, SEEK_END);
+       fprintf (fp, "%d: ---> %s %d: \"%*.*s\"\n", (int) getpid(),
+               types[rc->rc_type], rc->rc_len,
+               rc->rc_len, rc->rc_len, data);
+       fflush (fp);
+    }
+
+    if (write (PEERwfd, (char *) rc_head (rc), RHSIZE (rc)) != RHSIZE (rc))
+       return rclose (rc, "write to peer lost(1)");
+
+    if (rc->rc_len)
+       if (write (PEERwfd, data, rc->rc_len) != rc->rc_len)
+           return rclose (rc, "write to peer lost(2)");
+
+    return OK;
+}
+
+
+int
+str2peer (char code, char *str)
+{
+    return rc2peer (code, str ? strlen (str) : 0, str);
+}
+
+
+int
+fmt2peer (char code, char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    return verr2peer (code, NULL, fmt, ap);
+    va_end(ap);
+}
+
+
+int
+err2peer (char code, char *what, char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    verr2peer(code, what, fmt, ap);
+    va_end(ap);
+}
+
+
+int
+verr2peer (char code, char *what, char *fmt, va_list ap)
+{
+    int eindex = errno;
+    int len, buflen;
+    char *bp, *s, buffer[BUFSIZ * 2];
+
+    /* Get buffer ready to go */
+    bp = buffer;
+    buflen = sizeof(buffer);
+
+    vsnprintf (bp, buflen, fmt, ap);
+    len = strlen (bp);
+    bp += len;
+    buflen -= len;
+
+    if (what) {
+       if (*what) {
+           snprintf (bp, buflen, " %s: ", what);
+           len = strlen (bp);
+           bp += len;
+           buflen -= len;
+       }
+       if ((s = strerror (eindex)))
+           strncpy (bp, s, buflen);
+       else
+           snprintf (bp, buflen, "unknown error %d", eindex);
+       len = strlen (bp);
+       bp += len;
+       buflen -= len;
+    }
+
+    return rc2peer (code, bp - buffer, buffer);
+}
+
+
+static int
+rclose (struct record *rc, char *fmt, ...)
+{
+    va_list ap;
+    static char buffer[BUFSIZ * 2];
+
+    va_start(ap, fmt);
+    vsnprintf (buffer, sizeof(buffer), fmt, ap);
+    va_end(ap);
+
+    rc->rc_len = strlen (rc->rc_data = getcpy (buffer));
+    return (rc->rc_type = RC_XXX);
+}
diff --git a/uip/vmhtest.c b/uip/vmhtest.c
new file mode 100644 (file)
index 0000000..813e6b7
--- /dev/null
@@ -0,0 +1,322 @@
+
+/*
+ * vmhtest.c -- test out vmh protocol
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/vmhsbr.h>
+
+static struct swit switches[] = {
+#define        READSW          0
+    { "vmhread fd", 7 },       
+#define        WRITESW         1
+    { "vmhwrite fd", 8 },      
+#define VERSIONSW       2
+    { "version", 0 },
+#define        HELPSW          3
+    { "help", 4 },
+    { NULL, NULL }
+};
+
+#define        NWIN 20
+static int numwins = 0;
+static int windows[NWIN + 1];
+
+
+static int selcmds = 0;
+#define        selcmd()        (selcmds++ % 2)
+
+static int selwins = 0;
+#define        selwin()        (selwins++ % 2 ? 3 : 1)
+
+
+int
+main (int argc, char **argv)
+{
+    int fd1, fd2;
+    char *cp, buffer[BUFSIZ];
+    char **argp, **arguments;
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* foil search of user profile/context */
+    if (context_foil (NULL) == -1)
+       done (1);
+
+    arguments = getarguments (invo_name, argc, argv, 0);
+    argp = arguments;
+
+    while ((cp = *argp++))
+       if (*cp == '-')
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown", cp);
+
+               case HELPSW: 
+                   snprintf (buffer, sizeof(buffer), "%s [switches]", invo_name);
+                   print_help (buffer, switches, 0);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case READSW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   if ((fd1 = atoi (cp)) < 1)
+                       adios (NULL, "bad argument %s %s", argp[-2], cp);
+                   continue;
+               case WRITESW: 
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   if ((fd2 = atoi (cp)) < 1)
+                       adios (NULL, "bad argument %s %s", argp[-2], cp);
+                   continue;
+           }
+       else
+           adios (NULL, "usage: %s [switches]", invo_name);
+
+    rcinit (fd1, fd2);
+    pINI ();
+    pLOOP ();
+
+    done (0);
+}
+
+
+static int  pINI () {
+    int     i,
+            vrsn;
+    char   *bp;
+    struct record   rcs,
+                   *rc = &rcs;
+
+    initrc (rc);
+
+    switch (peer2rc (rc)) {
+       case RC_INI: 
+           bp = rc->rc_data;
+           while (isspace (*bp))
+               bp++;
+           if (sscanf (bp, "%d", &vrsn) != 1) {
+       bad_init: ;
+               fmt2peer (RC_ERR, "bad init \"%s\"", rc->rc_data);
+               done (1);
+           }
+           if (vrsn != RC_VRSN) {
+               fmt2peer (RC_ERR, "version %d unsupported", vrsn);
+               done (1);
+           }
+
+           while (*bp && !isspace (*bp))
+               bp++;
+           while (isspace (*bp))
+               bp++;
+           if (sscanf (bp, "%d", &numwins) != 1 || numwins <= 0)
+               goto bad_init;
+           if (numwins > NWIN)
+               numwins = NWIN;
+
+           for (i = 1; i <= numwins; i++) {
+               while (*bp && !isspace (*bp))
+                   bp++;
+               while (isspace (*bp))
+                   bp++;
+               if (sscanf (bp, "%d", &windows[i]) != 1 || windows[i] <= 0)
+                   goto bad_init;
+           }
+           rc2peer (RC_ACK, 0, NULL);
+           return OK;
+
+       case RC_XXX: 
+           adios (NULL, "%s", rc->rc_data);
+
+       default: 
+           fmt2peer (RC_ERR, "pINI protocol screw-up");
+           done (1);           /* NOTREACHED */
+    }
+}
+
+
+static int  pLOOP () {
+    struct record   rcs,
+                   *rc = &rcs;
+
+    initrc (rc);
+
+    for (;;)
+       switch (peer2rc (rc)) {
+           case RC_QRY: 
+               pQRY (rc->rc_data);
+               break;
+
+           case RC_CMD: 
+               pCMD (rc->rc_data);
+               break;
+
+           case RC_FIN: 
+               done (0);
+
+           case RC_XXX: 
+               adios (NULL, "%s", rc->rc_data);
+
+           default: 
+               fmt2peer (RC_ERR, "pLOOP protocol screw-up");
+               done (1);
+       }
+}
+
+
+static int  pQRY (str)
+char   *str;
+{
+    rc2peer (RC_EOF, 0, NULL);
+    return OK;
+}
+
+
+static int  pCMD (str)
+char   *str;
+{
+    if ((selcmd () ? pTTY (str) : pWIN (str)) == NOTOK)
+       return NOTOK;
+    rc2peer (RC_EOF, 0, NULL);
+    return OK;
+}
+
+
+static int  pTTY (str)
+char   *str;
+{
+    struct record   rcs,
+                   *rc = &rcs;
+
+    initrc (rc);
+
+    switch (rc2rc (RC_TTY, 0, NULL, rc)) {
+       case RC_ACK: 
+           break;
+
+       case RC_ERR: 
+           return NOTOK;
+
+       case RC_XXX: 
+           adios (NULL, "%s", rc->rc_data);
+
+       default: 
+           fmt2peer (RC_ERR, "pTTY protocol screw-up");
+           done (1);
+    }
+
+    system (str);
+
+    switch (rc2rc (RC_EOF, 0, NULL, rc)) {
+       case RC_ACK: 
+           return OK;
+
+       case RC_XXX: 
+           adios (NULL, "%s", rc->rc_data);/* NOTREACHED */
+
+       default: 
+           fmt2peer (RC_ERR, "pTTY protocol screw-up");
+           done (1);           /* NOTREACHED */
+    }
+}
+
+
+static int  pWIN (str)
+char   *str;
+{
+    int     i,
+            pid,
+            pd[2];
+    char    buffer[BUFSIZ];
+    struct record   rcs,
+                   *rc = &rcs;
+
+    initrc (rc);
+
+    snprintf (buffer, sizeof(buffer), "%d", selwin ());
+    switch (str2rc (RC_WIN, buffer, rc)) {
+       case RC_ACK: 
+           break;
+
+       case RC_ERR: 
+           return NOTOK;
+
+       case RC_XXX: 
+           adios (NULL, "%s", rc->rc_data);
+
+       default: 
+           fmt2peer (RC_ERR, "pWIN protocol screw-up");
+           done (1);
+    }
+
+    if (pipe (pd) == NOTOK) {
+       fmt2peer (RC_ERR, "no pipes");
+       return NOTOK;
+    }
+
+    switch (pid = vfork ()) {
+       case NOTOK: 
+           fmt2peer (RC_ERR, "no forks");
+           return NOTOK;
+
+       case OK: 
+           close (0);
+           open ("/dev/null", O_RDONLY);
+           dup2 (pd[1], 1);
+           dup2 (pd[1], 2);
+           close (pd[0]);
+           close (pd[1]);
+           execlp ("/bin/sh", "sh", "-c", str, NULL);
+           write (2, "no shell\n", strlen ("no shell\n"));
+           _exit (1);
+
+       default: 
+           close (pd[1]);
+           while ((i = read (pd[0], buffer, sizeof buffer)) > 0)
+               switch (rc2rc (RC_DATA, i, buffer, rc)) {
+                   case RC_ACK: 
+                       break;
+
+                   case RC_ERR: 
+                       close (pd[0]);
+                       pidwait (pid, OK);
+                       return NOTOK;
+
+                   case RC_XXX: 
+                       adios (NULL, "%s", rc->rc_data);
+
+                   default: 
+                       fmt2peer (RC_ERR, "pWIN protocol screw-up");
+                       done (1);
+               }
+           if (i == OK)
+               switch (rc2rc (RC_EOF, 0, NULL, rc)) {
+                   case RC_ACK: 
+                       break;
+
+                   case RC_XXX: 
+                       adios (NULL, "%s", rc->rc_data);
+
+                   default: 
+                       fmt2peer (RC_ERR, "pWIN protocol screw-up");
+                       done (1);
+               }
+           if (i == NOTOK)
+               fmt2peer (RC_ERR, "read from pipe lost");
+
+           close (pd[0]);
+           pidwait (pid, OK);
+           return (i != NOTOK ? OK : NOTOK);
+    }
+}
diff --git a/uip/whatnow.c b/uip/whatnow.c
new file mode 100644 (file)
index 0000000..083650e
--- /dev/null
@@ -0,0 +1,18 @@
+
+/*
+ * whatnow.c -- the nmh `WhatNow' shell
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+int
+main (int argc, char **argv)
+{
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    WhatNow (argc, argv);
+}
diff --git a/uip/whatnowproc.c b/uip/whatnowproc.c
new file mode 100644 (file)
index 0000000..caeeec2
--- /dev/null
@@ -0,0 +1,115 @@
+
+/*
+ * whatnowproc.c -- exec the "whatnowproc"
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+
+/*
+ * This routine is used by comp, repl, forw, and dist to exec
+ * the "whatnowproc".  It first sets up all the environment
+ * variables that the "whatnowproc" will need to check, and
+ * then execs the command.  As an optimization, if the
+ * "whatnowproc" is the nmh command "whatnow" (typical case),
+ * it will call this routine directly without exec'ing it.
+ */
+
+int
+what_now (char *ed, int nedit, int use, char *file, char *altmsg, int dist,
+          struct msgs *mp, char *text, int inplace, char *cwd)
+{
+    int found, k, msgnum, vecp;
+    int len, buflen;
+    register char *bp;
+    char buffer[BUFSIZ], *vec[MAXARGS];
+
+    vecp = 0;
+    vec[vecp++] = r1bindex (whatnowproc, '/');
+    vec[vecp] = NULL;
+
+    m_putenv ("mhdraft", file);
+    if (mp)
+       m_putenv ("mhfolder", mp->foldpath);
+    else
+       unputenv ("mhfolder");
+    if (altmsg) {
+       if (mp == NULL || *altmsg == '/' || cwd == NULL)
+           m_putenv ("mhaltmsg", altmsg);
+       else {
+           snprintf (buffer, sizeof(buffer), "%s/%s", mp->foldpath, altmsg);
+           m_putenv ("mhaltmsg", buffer);
+       }
+    } else {
+       unputenv ("mhaltmsg");
+    }
+    if ((bp = getenv ("mhaltmsg")))/* XXX */
+       m_putenv ("editalt", bp);
+    snprintf (buffer, sizeof(buffer), "%d", dist);
+    m_putenv ("mhdist", buffer);
+    if (nedit) {
+       unputenv ("mheditor");
+    } else {
+       m_putenv ("mheditor", ed ? ed : (ed = context_find ("editor"))
+           ? ed : defaulteditor);
+    }
+    snprintf (buffer, sizeof(buffer), "%d", use);
+    m_putenv ("mhuse", buffer);
+
+    unputenv ("mhmessages");
+    unputenv ("mhannotate");
+    unputenv ("mhinplace");
+
+    if (text && mp && !is_readonly(mp)) {
+       found = 0;
+       bp = buffer;
+       buflen = sizeof(buffer);
+       for (msgnum = mp->lowmsg; msgnum <= mp->hghmsg; msgnum++) {
+           if (is_selected(mp, msgnum)) {
+               snprintf (bp, buflen, "%s%s", found ? " " : "", m_name (msgnum));
+               len = strlen (bp);
+               bp += len;
+               buflen -= len;
+               for (k = msgnum + 1; k <= mp->hghmsg && is_selected(mp, k); k++)
+                   continue;
+               if (--k > msgnum) {
+                   snprintf (bp, buflen, "-%s", m_name (k));
+                   len = strlen (bp);
+                   bp += len;
+                   buflen -= len;
+               }
+               msgnum = k + 1;
+               found++;
+           }
+       }
+       if (found) {
+           m_putenv ("mhmessages", buffer);
+           m_putenv ("mhannotate", text);
+           snprintf (buffer, sizeof(buffer), "%d", inplace);
+           m_putenv ("mhinplace", buffer);
+       }
+    }
+
+    context_save ();   /* save the context file */
+    fflush (stdout);
+
+    if (cwd)
+       chdir (cwd);
+
+    /*
+     * If the "whatnowproc" is the nmh command "whatnow",
+     * we run it internally, rather than exec'ing it.
+     */
+    if (strcmp (vec[0], "whatnow") == 0) {
+       WhatNow (vecp, vec);
+       done (0);
+    }
+
+    execvp (whatnowproc, vec);
+    fprintf (stderr, "unable to exec ");
+    perror (whatnowproc);
+
+    return 0;
+}
diff --git a/uip/whatnowsbr.c b/uip/whatnowsbr.c
new file mode 100644 (file)
index 0000000..b12307d
--- /dev/null
@@ -0,0 +1,975 @@
+
+/*
+ * whatnowsbr.c -- the WhatNow shell
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <h/mime.h>
+
+static struct swit whatnowswitches[] = {
+#define        DFOLDSW                 0
+    { "draftfolder +folder", 0 },
+#define        DMSGSW                  1
+    { "draftmessage msg", 0 },
+#define        NDFLDSW                 2
+    { "nodraftfolder", 0 },
+#define        EDITRSW                 3
+    { "editor editor", 0 },
+#define        NEDITSW                 4
+    { "noedit", 0 },
+#define        PRMPTSW                 5
+    { "prompt string", 4 },
+#define VERSIONSW               6
+    { "version", 0 },
+#define        HELPSW                  7
+    { "help", 4 },
+    { NULL, 0 }
+};
+
+/*
+ * Options at the "whatnow" prompt
+ */
+static struct swit aleqs[] = {
+#define        EDITSW                         0
+    { "edit [<editor> <switches>]", 0 },
+#define        REFILEOPT                      1
+    { "refile [<switches>] +folder", 0 },
+#define BUILDMIMESW                    2
+    { "mime [<switches>]", 0 },
+#define        DISPSW                         3
+    { "display [<switches>]", 0 },
+#define        LISTSW                         4
+    { "list [<switches>]", 0 },
+#define        SENDSW                         5
+    { "send [<switches>]", 0 },
+#define        PUSHSW                         6
+    { "push [<switches>]", 0 },
+#define        WHOMSW                         7
+    { "whom [<switches>]", 0 },
+#define        QUITSW                         8
+    { "quit [-delete]", 0 },
+#define DELETESW                       9
+    { "delete", 0 },
+    { NULL, 0 }
+};
+
+static char *myprompt = "\nWhat now? ";
+
+/*
+ * static prototypes
+ */
+static int editfile (char **, char **, char *, int, struct msgs *,
+       char *, char *, int);
+static int sendfile (char **, char *, int);
+static void sendit (char *, char **, char *, int);
+static int buildfile (char **, char *);
+static int check_draft (char *);
+static int whomfile (char **, char *);
+static int removefile (char *);
+
+#ifdef HAVE_LSTAT
+static int copyf (char *, char *);
+#endif
+
+
+int
+WhatNow (int argc, char **argv)
+{
+    int isdf = 0, nedit = 0, use = 0;
+    char *cp, *dfolder = NULL, *dmsg = NULL;
+    char *ed = NULL, *drft = NULL, *msgnam = NULL;
+    char buf[BUFSIZ], prompt[BUFSIZ];
+    char **argp, **arguments;
+    struct stat st;
+
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, whatnowswitches)) {
+           case AMBIGSW: 
+               ambigsw (cp, whatnowswitches);
+               done (1);
+           case UNKWNSW: 
+               adios (NULL, "-%s unknown", cp);
+
+           case HELPSW: 
+               snprintf (buf, sizeof(buf), "%s [switches] [file]", invo_name);
+               print_help (buf, whatnowswitches, 1);
+               done (1);
+           case VERSIONSW:
+               print_version(invo_name);
+               done (1);
+
+           case DFOLDSW: 
+               if (dfolder)
+                   adios (NULL, "only one draft folder at a time!");
+               if (!(cp = *argp++) || *cp == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               dfolder = path (*cp == '+' || *cp == '@' ? cp + 1 : cp,
+                               *cp != '@' ? TFOLDER : TSUBCWF);
+               continue;
+           case DMSGSW: 
+               if (dmsg)
+                   adios (NULL, "only one draft message at a time!");
+               if (!(dmsg = *argp++) || *dmsg == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               continue;
+           case NDFLDSW: 
+               dfolder = NULL;
+               isdf = NOTOK;
+               continue;
+
+           case EDITRSW: 
+               if (!(ed = *argp++) || *ed == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               nedit = 0;
+               continue;
+           case NEDITSW: 
+               nedit++;
+               continue;
+
+           case PRMPTSW:
+               if (!(myprompt = *argp++) || *myprompt == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               continue;
+           }
+       }
+       if (drft)
+           adios (NULL, "only one draft at a time!");
+       else
+           drft = cp;
+    }
+
+    if ((drft == NULL && (drft = getenv ("mhdraft")) == NULL) || *drft == 0)
+       drft = getcpy (m_draft (dfolder, dmsg, 1, &isdf));
+
+    msgnam = (cp = getenv ("mhaltmsg")) && *cp ? getcpy (cp) : NULL;
+
+    if ((cp = getenv ("mhuse")) && *cp)
+       use = atoi (cp);
+
+    if (ed == NULL && ((ed = getenv ("mheditor")) == NULL || *ed == 0)) {
+       ed = NULL;
+       nedit++;
+    }
+
+    /* start editing the draft, unless -noedit was given */
+    if (!nedit && editfile (&ed, NULL, drft, use, NULL, msgnam, NULL, 1) < 0)
+       done (1);
+
+    snprintf (prompt, sizeof(prompt), myprompt, invo_name);
+    for (;;) {
+       if (!(argp = getans (prompt, aleqs))) {
+           unlink (LINK);
+           done (1);
+       }
+       switch (smatch (*argp, aleqs)) {
+       case DISPSW:
+           /* display the message being replied to, or distributed */
+           if (msgnam)
+               showfile (++argp, msgnam);
+           else
+               advise (NULL, "no alternate message to display");
+           break;
+
+       case BUILDMIMESW:
+           /* Translate MIME composition file */
+           buildfile (++argp, drft);
+           break;
+
+       case EDITSW:
+           /* Call an editor on the draft file */
+           if (*++argp)
+               ed = *argp++;
+           if (editfile (&ed, argp, drft, NOUSE, NULL, msgnam, NULL, 1) == NOTOK)
+               done (1);
+           break;
+
+       case LISTSW: 
+           /* display the draft file */
+           showfile (++argp, drft);
+           break;
+
+       case WHOMSW:
+           /* Check to whom the draft would be sent */
+           whomfile (++argp, drft);
+           break;
+
+       case QUITSW:
+           /* Quit, and possibly delete the draft */
+           if (*++argp && (*argp[0] == 'd' ||
+               ((*argp)[0] == '-' && (*argp)[1] == 'd'))) {
+               removefile (drft);
+           } else {
+               if (stat (drft, &st) != NOTOK)
+                   advise (NULL, "draft left on %s", drft);
+           }
+           done (1);
+
+       case DELETESW:
+           /* Delete draft and exit */
+           removefile (drft);
+           done (1);
+
+       case PUSHSW:
+           /* Send draft in background */
+           if (sendfile (++argp, drft, 1))
+               done (1);
+           break;
+
+       case SENDSW: 
+           /* Send draft */
+           sendfile (++argp, drft, 0);
+           break;
+
+       case REFILEOPT: 
+           /* Refile the draft */
+           if (refile (++argp, drft) == 0)
+               done (0);
+           break;
+
+       default: 
+           /* Unknown command */
+           advise (NULL, "say what?");
+           break;
+       }
+    }
+    /*NOTREACHED*/
+}
+
+/*
+ * EDIT
+ */
+
+static int  reedit = 0;                /* have we been here before?     */
+static char *edsave = NULL;    /* the editor we used previously */
+
+
+static int
+editfile (char **ed, char **arg, char *file, int use, struct msgs *mp,
+         char *altmsg, char *cwd, int save_editor)
+{
+    int pid, status, vecp;
+    char altpath[BUFSIZ], linkpath[BUFSIZ];
+    char *cp, *vec[MAXARGS];
+    struct stat st;
+
+#ifdef HAVE_LSTAT
+    int        slinked;
+#if 0
+    int oumask;        /* PJS: for setting permissions on symlinks. */
+#endif
+#endif /* HAVE_LSTAT */
+
+    /* Was there a previous edit session? */
+    if (reedit) {
+       if (!*ed) {             /* no explicit editor      */
+           *ed = edsave;       /* so use the previous one */
+           if ((cp = r1bindex (*ed, '/')) == NULL)
+               cp = *ed;
+
+           /* unless we've specified it via "editor-next" */
+           cp = concat (cp, "-next", NULL);
+           if ((cp = context_find (cp)) != NULL)
+               *ed = cp;
+       }
+    } else {
+       /* set initial editor */
+       if (*ed == NULL && (*ed = context_find ("editor")) == NULL)
+           *ed = defaulteditor;
+    }
+
+    if (altmsg) {
+       if (mp == NULL || *altmsg == '/' || cwd == NULL)
+           strncpy (altpath, altmsg, sizeof(altpath));
+       else
+           snprintf (altpath, sizeof(altpath), "%s/%s", mp->foldpath, altmsg);
+       if (cwd == NULL)
+           strncpy (linkpath, LINK, sizeof(linkpath));
+       else
+           snprintf (linkpath, sizeof(linkpath), "%s/%s", cwd, LINK);
+    }
+
+    if (altmsg) {
+       unlink (linkpath);
+#ifdef HAVE_LSTAT
+       if (link (altpath, linkpath) == NOTOK) {
+#if 0
+           /* I don't think permission on symlinks matters /JLR */
+           oumask = umask(0044);       /* PJS: else symlinks are world 'r' */
+#endif
+           symlink (altpath, linkpath);
+#if 0
+           umask(oumask);              /* PJS: else symlinks are world 'r' */
+#endif
+           slinked = 1;
+       } else {
+           slinked = 0;
+       }
+#else /* not HAVE_LSTAT */
+       link (altpath, linkpath);
+#endif /* not HAVE_LSTAT */
+    }
+
+    context_save ();   /* save the context file */
+    fflush (stdout);
+
+    switch (pid = vfork ()) {
+       case NOTOK: 
+           advise ("fork", "unable to");
+           status = NOTOK;
+           break;
+
+       case OK: 
+           if (cwd)
+               chdir (cwd);
+           if (altmsg) {
+               if (mp)
+                   m_putenv ("mhfolder", mp->foldpath);
+               m_putenv ("editalt", altpath);
+           }
+
+           vecp = 0;
+           vec[vecp++] = r1bindex (*ed, '/');
+           if (arg)
+               while (*arg)
+                   vec[vecp++] = *arg++;
+           vec[vecp++] = file;
+           vec[vecp] = NULL;
+
+           execvp (*ed, vec);
+           fprintf (stderr, "unable to exec ");
+           perror (*ed);
+           _exit (-1);
+
+       default: 
+           if ((status = pidwait (pid, NOTOK))) {
+#ifdef ATTVIBUG
+               if ((cp = r1bindex (*ed, '/'))
+                       && strcmp (cp, "vi") == 0
+                       && (status & 0x00ff) == 0)
+                   status = 0;
+               else {
+#endif
+               if (((status & 0xff00) != 0xff00)
+                       && (!reedit || (status & 0x00ff)))
+                   if (!use && (status & 0xff00) &&
+                           (rename (file, cp = m_backup (file)) != NOTOK)) {
+                       advise (NULL, "problems with edit--draft left in %s", cp);
+                   } else {
+                       advise (NULL, "problems with edit--%s preserved", file);
+                   }
+               status = -2;    /* maybe "reedit ? -2 : -1"? */
+               break;
+#ifdef ATTVIBUG
+               }
+#endif
+           }
+
+           reedit++;
+#ifdef HAVE_LSTAT
+           if (altmsg
+                   && mp
+                   && !is_readonly(mp)
+                   && (slinked
+                          ? lstat (linkpath, &st) != NOTOK
+                               && S_ISREG(st.st_mode)
+                               && copyf (linkpath, altpath) == NOTOK
+                          : stat (linkpath, &st) != NOTOK
+                               && st.st_nlink == 1
+                               && (unlink (altpath) == NOTOK
+                                       || link (linkpath, altpath) == NOTOK)))
+               advise (linkpath, "unable to update %s from", altmsg);
+#else /* HAVE_LSTAT */
+           if (altmsg
+                   && mp
+                   && !is_readonly(mp)
+                   && stat (linkpath, &st) != NOTOK
+                   && st.st_nlink == 1
+                   && (unlink (altpath) == NOTOK
+                       || link (linkpath, altpath) == NOTOK))
+               advise (linkpath, "unable to update %s from", altmsg);
+#endif /* HAVE_LSTAT */
+    }
+
+    /* normally, we remember which editor we used */
+    if (save_editor)
+       edsave = getcpy (*ed);
+
+    *ed = NULL;
+    if (altmsg)
+       unlink (linkpath);
+
+    return status;
+}
+
+
+#ifdef HAVE_LSTAT
+static int
+copyf (char *ifile, char *ofile)
+{
+    int i, in, out;
+    char buffer[BUFSIZ];
+
+    if ((in = open (ifile, O_RDONLY)) == NOTOK)
+       return NOTOK;
+    if ((out = open (ofile, O_WRONLY | O_TRUNC)) == NOTOK) {
+       admonish (ofile, "unable to open and truncate");
+       close (in);
+       return NOTOK;
+    }
+
+    while ((i = read (in, buffer, sizeof(buffer))) > OK)
+       if (write (out, buffer, i) != i) {
+           advise (ofile, "may have damaged");
+           i = NOTOK;
+           break;
+       }
+
+    close (in);
+    close (out);
+    return i;
+}
+#endif /* HAVE_LSTAT */
+
+
+/*
+ * SEND
+ */
+
+static int
+sendfile (char **arg, char *file, int pushsw)
+{
+    pid_t child_id;
+    int i, vecp;
+    char *cp, *sp, *vec[MAXARGS];
+
+    /* Translate MIME composition file, if necessary */
+    if ((cp = context_find ("automimeproc"))
+           && (!strcmp (cp, "1"))
+           && !getenv ("NOMHNPROC")
+           && check_draft (file)
+           && (buildfile (NULL, file) == NOTOK))
+       return 0;
+
+    /* For backwards compatibility */
+    if ((cp = context_find ("automhnproc"))
+           && !getenv ("NOMHNPROC")
+           && check_draft (file)
+           && (i = editfile (&cp, NULL, file, NOUSE, NULL, NULL, NULL, 0)))
+       return 0;
+
+    /*
+     * If the sendproc is the nmh command `send', then we call
+     * those routines directly rather than exec'ing the command.
+     */
+    if (strcmp (sp = r1bindex (sendproc, '/'), "send") == 0) {
+       cp = invo_name;
+       sendit (invo_name = sp, arg, file, pushsw);
+       invo_name = cp;
+       return 1;
+    }
+
+    context_save ();   /* save the context file */
+    fflush (stdout);
+
+    for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
+       sleep (5);
+    switch (child_id) {
+       case NOTOK: 
+           advise (NULL, "unable to fork, so sending directly...");
+       case OK: 
+           vecp = 0;
+           vec[vecp++] = invo_name;
+           if (pushsw)
+               vec[vecp++] = "-push";
+           if (arg)
+               while (*arg)
+                   vec[vecp++] = *arg++;
+           vec[vecp++] = file;
+           vec[vecp] = NULL;
+
+           execvp (sendproc, vec);
+           fprintf (stderr, "unable to exec ");
+           perror (sendproc);
+           _exit (-1);
+
+       default: 
+           if (pidwait(child_id, OK) == 0)
+               done (0);
+           return 1;
+    }
+}
+
+
+/*
+ * Translate MIME composition file (call buildmimeproc)
+ */
+
+static int
+buildfile (char **argp, char *file)
+{
+    int i;
+    char **args, *ed;
+
+    ed = buildmimeproc;
+
+    /* allocate space for arguments */
+    i = 0;
+    if (argp) {
+       while (argp[i])
+           i++;
+    }
+    if ((args = (char **) malloc((i + 2) * sizeof(char *))) == NULL)
+       adios (NULL, "unable to malloc memory");
+
+    /*
+     * For backward compatibility, we need to add -build
+     * if we are using mhn as buildmimeproc
+     */
+    i = 0;
+    if (strcmp (r1bindex (ed, '/'), "mhn") == 0)
+       args[i++] = "-build";
+
+    /* copy any other arguments */
+    while (argp && *argp)
+       args[i++] = *argp++;
+    args[i] = NULL;
+
+    i = editfile (&ed, args, file, NOUSE, NULL, NULL, NULL, 0);
+    free (args);
+
+    return (i ? NOTOK : OK);
+}
+
+
+/*
+ *  Check if draft is a mhbuild composition file
+ */
+
+static int
+check_draft (char *msgnam)
+{
+    int        state;
+    char buf[BUFSIZ], name[NAMESZ];
+    FILE *fp;
+
+    if ((fp = fopen (msgnam, "r")) == NULL)
+       return 0;
+    for (state = FLD;;)
+       switch (state = m_getfld (state, name, buf, sizeof(buf), fp)) {
+           case FLD:
+           case FLDPLUS:
+           case FLDEOF:
+               /*
+                * If draft already contains any of the
+                * Content-XXX fields, then assume it already
+                * been converted.
+                */
+               if (uprf (name, XXX_FIELD_PRF)) {
+                   fclose (fp);
+                   return 0;
+               }
+               while (state == FLDPLUS)
+                   state = m_getfld (state, name, buf, sizeof(buf), fp);
+               break;
+
+           case BODY:
+               do {
+                   char *bp;
+
+                   for (bp = buf; *bp; bp++)
+                       if (*bp != ' ' && *bp != '\t' && *bp != '\n') {
+                           fclose (fp);
+                           return 1;
+                       }
+
+                   state = m_getfld (state, name, buf, sizeof(buf), fp);
+               } while (state == BODY);
+               /* and fall... */
+
+           default:
+               fclose (fp);
+               return 0;
+       }
+}
+
+
+static struct swit  sendswitches[] = {
+#define        ALIASW            0
+    { "alias aliasfile", 0 },
+#define        DEBUGSW           1
+    { "debug", -5 },
+#define        FILTSW            2
+    { "filter filterfile", 0 },
+#define        NFILTSW           3
+    { "nofilter", 0 },
+#define        FRMTSW            4
+    { "format", 0 },
+#define        NFRMTSW           5
+    { "noformat", 0 },
+#define        FORWSW            6
+    { "forward", 0 },
+#define        NFORWSW           7
+    { "noforward", 0 },
+#define MIMESW            8
+    { "mime", 0 },
+#define NMIMESW           9
+    { "nomime", 0 },
+#define MSGDSW           10
+    { "msgid", 0 },
+#define NMSGDSW          11
+    { "nomsgid", 0 },
+#define SPSHSW           12
+    { "push", 0 },
+#define NSPSHSW          13
+    { "nopush", 0 },
+#define SPLITSW          14
+    { "split seconds", 0 },
+#define UNIQSW           15
+    { "unique", -6 },
+#define NUNIQSW          16
+    { "nounique", -8 },
+#define VERBSW           17
+    { "verbose", 0 },
+#define NVERBSW          18
+    { "noverbose", 0 },
+#define        WATCSW           19
+    { "watch", 0 },
+#define        NWATCSW          20
+    { "nowatch", 0 },
+#define        WIDTHSW          21
+    { "width columns", 0 },
+#define SVERSIONSW       22
+    { "version", 0 },
+#define        SHELPSW          23
+    { "help", 4 },
+#define BITSTUFFSW       24
+    { "dashstuffing", -12 },
+#define NBITSTUFFSW      25
+    { "nodashstuffing", -14 },
+#define        MAILSW           26
+    { "mail", -4 },
+#define        SAMLSW           27
+    { "saml", -4 },
+#define        SSNDSW           28
+    { "send", -4 },
+#define        SOMLSW           29
+    { "soml", -4 },
+#define        CLIESW           30
+    { "client host", -6 },
+#define        SERVSW           31
+    { "server host", -6 },
+#define        SNOOPSW          32
+    { "snoop", -5 },
+#define SDRFSW           33
+    { "draftfolder +folder", -6 },
+#define SDRMSW           34
+    { "draftmessage msg", -6 },
+#define SNDRFSW          35
+    { "nodraftfolder", -3 },
+    { NULL, 0 }
+};
+
+
+extern int debugsw;            /* from sendsbr.c */
+extern int forwsw;
+extern int inplace;
+extern int pushsw;
+extern int splitsw;
+extern int unique;
+extern int verbsw;
+
+extern char *altmsg;           /*  .. */
+extern char *annotext;
+extern char *distfile;
+
+
+static void
+sendit (char *sp, char **arg, char *file, int pushed)
+{
+    int        vecp, n = 1;
+    char *cp, buf[BUFSIZ], **argp;
+    char **arguments, *vec[MAXARGS];
+    struct stat st;
+
+#ifndef        lint
+    int        distsw = 0;
+#endif
+#ifdef UCI
+    FILE *fp;
+#endif
+
+    /*
+     * Make sure these are defined.  In particular, we need
+     * vec[1] to be NULL, in case "arg" is NULL below.  It
+     * doesn't matter what is the value of vec[0], but we
+     * set it to NULL, to help catch "off-by-one" errors.
+     */
+    vec[0] = NULL;
+    vec[1] = NULL;
+
+    /*
+     * Temporarily copy arg to vec, since the brkstring() call in
+     * getarguments() will wipe it out before it is merged in.
+     * Also, we skip the first element of vec, since getarguments()
+     * skips it.  Then we count the number of arguments
+     * copied.  The value of "n" will be one greater than
+     * this in order to simulate the standard argc/argv.
+     */
+    if (arg) {
+       char **bp;
+
+       copyip (arg, vec+1, MAXARGS-1);
+       bp = vec+1;
+       while (*bp++)
+           n++;
+    }
+
+    /*
+     * Merge any arguments from command line (now in vec)
+     * and arguments from profile.
+     */
+    arguments = getarguments (sp, n, vec, 1);
+    argp = arguments;
+
+    debugsw = 0;
+    forwsw = 1;
+    inplace = 1;
+    unique = 0;
+
+    altmsg = NULL;
+    annotext = NULL;
+    distfile = NULL;
+
+    vecp = 1;                  /* we'll get the zero'th element later */
+    vec[vecp++] = "-library";
+    vec[vecp++] = getcpy (m_maildir (""));
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, sendswitches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, sendswitches);
+                   return;
+               case UNKWNSW: 
+                   advise (NULL, "-%s unknown\n", cp);
+                   return;
+
+               case SHELPSW: 
+                   snprintf (buf, sizeof(buf), "%s [switches]", sp);
+                   print_help (buf, sendswitches, 1);
+                   return;
+               case SVERSIONSW:
+                   print_version (invo_name);
+                   return;
+
+               case SPSHSW: 
+                   pushed++;
+                   continue;
+               case NSPSHSW: 
+                   pushed = 0;
+                   continue;
+
+               case SPLITSW: 
+                   if (!(cp = *argp++) || sscanf (cp, "%d", &splitsw) != 1) {
+                       advise (NULL, "missing argument to %s", argp[-2]);
+                       return;
+                   }
+                   continue;
+
+               case UNIQSW: 
+                   unique++;
+                   continue;
+               case NUNIQSW: 
+                   unique = 0;
+                   continue;
+               case FORWSW: 
+                   forwsw++;
+                   continue;
+               case NFORWSW: 
+                   forwsw = 0;
+                   continue;
+
+               case VERBSW: 
+                   verbsw++;
+                   vec[vecp++] = --cp;
+                   continue;
+               case NVERBSW:
+                   verbsw = 0;
+                   vec[vecp++] = --cp;
+                   continue;
+
+               case DEBUGSW: 
+                   debugsw++;  /* fall */
+               case NFILTSW: 
+               case FRMTSW: 
+               case NFRMTSW: 
+               case BITSTUFFSW:
+               case NBITSTUFFSW:
+               case MIMESW: 
+               case NMIMESW: 
+               case MSGDSW: 
+               case NMSGDSW: 
+               case WATCSW: 
+               case NWATCSW: 
+               case MAILSW: 
+               case SAMLSW: 
+               case SSNDSW: 
+               case SOMLSW: 
+               case SNOOPSW: 
+                   vec[vecp++] = --cp;
+                   continue;
+
+               case ALIASW: 
+               case FILTSW: 
+               case WIDTHSW: 
+               case CLIESW: 
+               case SERVSW: 
+                   vec[vecp++] = --cp;
+                   if (!(cp = *argp++) || *cp == '-') {
+                       advise (NULL, "missing argument to %s", argp[-2]);
+                       return;
+                   }
+                   vec[vecp++] = cp;
+                   continue;
+
+               case SDRFSW: 
+               case SDRMSW: 
+                   if (!(cp = *argp++) || *cp == '-') {
+                       advise (NULL, "missing argument to %s", argp[-2]);
+                       return;
+                   }
+               case SNDRFSW: 
+                   continue;
+           }
+       }
+       advise (NULL, "usage: %s [switches]", sp);
+       return;
+    }
+
+    /* allow Aliasfile: profile entry */
+    if ((cp = context_find ("Aliasfile"))) {
+       char **ap, *dp;
+
+       dp = getcpy (cp);
+       for (ap = brkstring (dp, " ", "\n"); ap && *ap; ap++) {
+           vec[vecp++] = "-alias";
+           vec[vecp++] = *ap;
+       }
+    }
+
+    if ((cp = getenv ("SIGNATURE")) == NULL || *cp == 0)
+       if ((cp = context_find ("signature")) && *cp)
+           m_putenv ("SIGNATURE", cp);
+#ifdef UCI
+       else {
+           snprintf (buf, sizeof(buf), "%s/.signature", mypath);
+           if ((fp = fopen (buf, "r")) != NULL
+               && fgets (buf, sizeof(buf), fp) != NULL) {
+                   fclose (fp);
+                   if (cp = strchr (buf, '\n'))
+                       *cp = 0;
+                   m_putenv ("SIGNATURE", buf);
+           }
+       }
+#endif /* UCI */
+
+    if ((annotext = getenv ("mhannotate")) == NULL || *annotext == 0)
+       annotext = NULL;
+    if ((altmsg = getenv ("mhaltmsg")) == NULL || *altmsg == 0)
+       altmsg = NULL;
+    if (annotext && ((cp = getenv ("mhinplace")) != NULL && *cp != 0))
+       inplace = atoi (cp);
+
+    if ((cp = getenv ("mhdist"))
+           && *cp
+#ifndef lint
+           && (distsw = atoi (cp))
+#endif /* not lint */
+           && altmsg) {
+       vec[vecp++] = "-dist";
+       distfile = getcpy (m_scratch (altmsg, invo_name));
+       if (link (altmsg, distfile) == NOTOK)
+           adios (distfile, "unable to link %s to", altmsg);
+    } else {
+       distfile = NULL;
+    }
+
+    if (altmsg == NULL || stat (altmsg, &st) == NOTOK) {
+       st.st_mtime = 0;
+       st.st_dev = 0;
+       st.st_ino = 0;
+    }
+    if ((pushsw = pushed))
+       push ();
+
+    vec[0] = r1bindex (postproc, '/');
+    closefds (3);
+
+    if (sendsbr (vec, vecp, file, &st, 1) == OK)
+       done (0);
+}
+
+/*
+ * WHOM
+ */
+
+static int
+whomfile (char **arg, char *file)
+{
+    pid_t pid;
+    int vecp;
+    char *vec[MAXARGS];
+
+    context_save ();   /* save the context file */
+    fflush (stdout);
+
+    switch (pid = vfork ()) {
+       case NOTOK: 
+           advise ("fork", "unable to");
+           return 1;
+
+       case OK: 
+           vecp = 0;
+           vec[vecp++] = r1bindex (whomproc, '/');
+           vec[vecp++] = file;
+           if (arg)
+               while (*arg)
+                   vec[vecp++] = *arg++;
+           vec[vecp] = NULL;
+
+           execvp (whomproc, vec);
+           fprintf (stderr, "unable to exec ");
+           perror (whomproc);
+           _exit (-1);         /* NOTREACHED */
+
+       default: 
+           return (pidwait (pid, NOTOK) & 0377 ? 1 : 0);
+    }
+}
+
+
+/*
+ * Remove the draft file
+ */
+
+static int
+removefile (char *drft)
+{
+    if (unlink (drft) == NOTOK)
+       adios (drft, "unable to unlink");
+
+    return OK;
+}
diff --git a/uip/whom.c b/uip/whom.c
new file mode 100644 (file)
index 0000000..aeb8d3b
--- /dev/null
@@ -0,0 +1,189 @@
+
+/*
+ * whom.c -- report to whom a message would be sent
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <h/signals.h>
+#include <signal.h>
+
+static struct swit switches[] = {
+#define        ALIASW              0
+    { "alias aliasfile", 0 },
+#define        CHKSW               1
+    { "check", 0 },
+#define        NOCHKSW             2
+    { "nocheck", 0 },
+#define        DRAFTSW             3
+    { "draft", 0 },
+#define        DFOLDSW             4
+    { "draftfolder +folder", 6 },
+#define        DMSGSW              5
+    { "draftmessage msg", 6 },
+#define        NDFLDSW             6
+    { "nodraftfolder", 0 },
+#define VERSIONSW           7
+    { "version", 0 },
+#define        HELPSW              8
+    { "help", 4 },
+#define        CLIESW              9
+    { "client host", -6 },
+#define        SERVSW             10
+    { "server host", -6 },
+#define        SNOOPSW            11
+    { "snoop", -5 },
+    { NULL, 0 }
+};
+
+
+int
+main (int argc, char **argv)
+{
+    pid_t child_id;
+    int i, status, isdf = 0;
+    int distsw = 0, vecp = 0;
+    char *cp, *dfolder = NULL, *dmsg = NULL;
+    char *msg = NULL, **ap, **argp, backup[BUFSIZ];
+    char buf[BUFSIZ], **arguments, *vec[MAXARGS];
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc, argv, 1);
+    argp = arguments;
+
+    vec[vecp++] = invo_name;
+    vec[vecp++] = "-whom";
+    vec[vecp++] = "-library";
+    vec[vecp++] = getcpy (m_maildir (""));
+
+    while ((cp = *argp++)) {
+       if (*cp == '-') {
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   adios (NULL, "-%s unknown", cp);
+
+               case HELPSW: 
+                   snprintf (buf, sizeof(buf), "%s [switches] [file]", invo_name);
+                   print_help (buf, switches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case CHKSW: 
+               case NOCHKSW: 
+               case SNOOPSW:
+                   vec[vecp++] = --cp;
+                   continue;
+
+               case DRAFTSW:
+                   msg = draft;
+                   continue;
+
+               case DFOLDSW: 
+                   if (dfolder)
+                       adios (NULL, "only one draft folder at a time!");
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   dfolder = path (*cp == '+' || *cp == '@' ? cp + 1 : cp,
+                           *cp != '@' ? TFOLDER : TSUBCWF);
+                   continue;
+               case DMSGSW: 
+                   if (dmsg)
+                       adios (NULL, "only one draft message at a time!");
+                   if (!(dmsg = *argp++) || *dmsg == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+               case NDFLDSW: 
+                   dfolder = NULL;
+                   isdf = NOTOK;
+                   continue;
+
+               case ALIASW: 
+               case CLIESW: 
+               case SERVSW: 
+                   vec[vecp++] = --cp;
+                   if (!(cp = *argp++) || *cp == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   vec[vecp++] = cp;
+                   continue;
+           }
+       }
+       if (msg)
+           adios (NULL, "only one draft at a time!");
+       else
+           vec[vecp++] = msg = cp;
+    }
+
+    /* allow Aliasfile: profile entry */
+    if ((cp = context_find ("Aliasfile"))) {
+       char *dp = NULL;
+
+       for (ap = brkstring(dp = getcpy(cp), " ", "\n"); ap && *ap; ap++) {
+           vec[vecp++] = "-alias";
+           vec[vecp++] = *ap;
+       }
+    }
+
+    if (msg == NULL) {
+#ifdef WHATNOW
+       if (dfolder || (cp = getenv ("mhdraft")) == NULL || *cp == '\0')
+#endif /* WHATNOW */
+           cp  = getcpy (m_draft (dfolder, dmsg, 1, &isdf));
+       msg = vec[vecp++] = cp;
+    }
+    if ((cp = getenv ("mhdist"))
+           && *cp
+           && (distsw = atoi (cp))
+           && (cp = getenv ("mhaltmsg"))
+           && *cp) {
+       if (distout (msg, cp, backup) == NOTOK)
+           done (1);
+       vec[vecp++] = "-dist";
+       distsw++;
+    }
+    vec[vecp] = NULL;
+
+    closefds (3);
+
+    if (distsw) {
+       for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
+           sleep (5);
+    }
+
+    switch (distsw ? child_id : OK) {
+       case NOTOK:
+           advise (NULL, "unable to fork, so checking directly...");
+       case OK:
+           execvp (postproc, vec);
+           fprintf (stderr, "unable to exec ");
+           perror (postproc);
+           _exit (-1);
+
+       default:
+           SIGNAL (SIGHUP, SIG_IGN);
+           SIGNAL (SIGINT, SIG_IGN);
+           SIGNAL (SIGQUIT, SIG_IGN);
+           SIGNAL (SIGTERM, SIG_IGN);
+
+           status = pidwait(child_id, OK);
+
+           unlink (msg);
+           if (rename (backup, msg) == NOTOK)
+               adios (msg, "unable to rename %s to", backup);
+           done (status);
+    }
+
+    exit (-1); /* NOT REACHED */
+}
diff --git a/uip/wmh.c b/uip/wmh.c
new file mode 100644 (file)
index 0000000..fce1f59
--- /dev/null
+++ b/uip/wmh.c
@@ -0,0 +1,1387 @@
+
+/*
+ * wmh.c -- window front-end to nmh
+ *
+ * $Id$
+ */
+
+/*
+ * TODO:
+ * Pass signals to client during execution
+ *
+ * Figure out a way for the user to say how big the Scan/Display
+ * windows should be, and where all the windows should be.
+ */
+
+#include <h/mh.h>
+#include <h/signals.h>
+#include <h/vmhsbr.h>
+#include <errno.h>
+#include <setjmp.h>
+#include <signal.h>
+
+#include <sys/uio.h>
+#include <vt.h>
+#include <bitmap.h>
+#include <tools.h>
+
+#define        ALARM ((unsigned int) 10)
+#define        PAUSE ((unsigned int) 2)
+
+#define        SZ(a) (sizeof a / sizeof a[0])
+
+static struct swit switches[] = {
+#define        PRMPTSW            0
+    { "prompt string", 6 },
+#define        PROGSW             1
+    { "vmhproc program", 7 },
+#define        NPROGSW            2
+    { "novmhproc", 9 },
+#define VERSIONSW          3
+    { "version", 0 },
+#define        HELPSW             4
+    { "help", 4 },
+    { NULL, NULL }
+};
+                                       /* PEERS */
+static int PEERpid = NOTOK;
+
+static jmp_buf PEERctx;
+
+
+                                       /* WINDOWS */
+static int dfd = NOTOK;
+static int twd = NOTOK;
+static char *myprompt = "(%s) ";
+
+struct line {
+    int l_no;
+    char *l_buf;
+    struct line *l_prev;
+    struct line *l_next;
+};
+
+#define        W_NULL  0x00
+#define        W_CMND  0x01
+#define        W_FAKE  0x02
+#define        W_EBAR  0x04
+
+typedef struct {
+    int        w_fd;
+    int        w_flags;
+    int        w_wd;
+    struct wstate w_ws;
+    char *w_eb;
+    int        w_ebloc;
+    int        w_ebsize;
+    int        w_cbase;
+    int        w_height;
+    int        w_cheight;
+    int        w_width;
+    int        w_cwidth;
+    struct line *w_head;
+    struct line *w_top;
+    struct line *w_bottom;
+    struct line *w_tail;
+    char w_buffer[BUFSIZ];
+    int        w_bufpos;
+} WINDOW;
+
+
+static WINDOW *Scan;
+static WINDOW *Status;
+static WINDOW *Display;
+static WINDOW *Command;
+
+#define        NWIN 4
+static int numwins;
+WINDOW *windows[NWIN + 1];
+
+WINDOW *WINnew ();
+
+
+#ifdef HAVE_TERMIOS_H
+static struct termios tio;
+# define ERASE tio.c_cc[VERASE]
+# define KILL  tio.c_cc[VKILL]
+# define INTR  tio.c_cc[VINTR]
+#else
+# ifdef HAVE_TERMIO_H
+static struct termio tio;
+#  define ERASE tio.c_cc[VERASE]
+#  define KILL  tio.c_cc[VKILL]
+#  define INTR  tio.c_cc[VINTR]
+# else
+static struct sgttyb tio;
+static struct tchars tc;
+#  define ERASE tio.sg_erase
+#  define KILL  tio.sg_kill
+#  define INTR  tc.t_intrc
+#  define EOFC  tc.t_eofc
+# endif
+#endif
+
+#define        WERASC ltc.t_werasc
+static struct ltchars ltc;
+
+extern int errno;
+
+int ALRMser (), PIPEser (), SIGser ();
+int ADJser (), REFser ();
+
+/*
+ * static prototypes
+ */
+static void adorn(char *, char *, ...);
+
+
+int
+main (int argc, char **argv)
+{
+    int vecp = 1, nprog = 0;
+    char *cp, buffer[BUFSIZ], **argp;
+    char **arguments, *vec[MAXARGS];
+
+#ifdef LOCALE
+    setlocale(LC_ALL, "");
+#endif
+    invo_name = r1bindex (argv[0], '/');
+
+    /* read user profile/context */
+    context_read();
+
+    arguments = getarguments (invo_name, argc,argv, 1);
+    argp = arguments;
+
+    while ((cp = *argp++))
+       if (*cp == '-')
+           switch (smatch (++cp, switches)) {
+               case AMBIGSW: 
+                   ambigsw (cp, switches);
+                   done (1);
+               case UNKWNSW: 
+                   vec[vecp++] = --cp;
+                   continue;
+
+               case HELPSW: 
+                   snprintf (buffer, sizeof(buffer), "%s [switches for vmhproc]",
+                       invo_name);
+                   print_help (buffer, switches, 1);
+                   done (1);
+               case VERSIONSW:
+                   print_version(invo_name);
+                   done (1);
+
+               case PRMPTSW:
+                   if (!(myprompt = *argp++) || *myprompt == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+
+               case PROGSW: 
+                   if (!(vmhproc = *argp++) || *vmhproc == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+               case NPROGSW:
+                   nprog++;
+                   continue;
+           }
+       else
+           vec[vecp++] = cp;
+
+    SIGinit ();
+    if (WINinit (nprog) == NOTOK) {
+       vec[vecp] = NULL;
+
+       vec[0] = r1bindex (vmhproc, '/');
+       execvp (vmhproc, vec);
+       adios (vmhproc, "unable to exec");
+    }
+    PEERinit (vecp, vec);
+
+    vmh ();
+
+    done (0);
+}
+
+
+static void
+vmh (void)
+{
+    char buffer[BUFSIZ], prompt[BUFSIZ];
+
+    for (;;) {
+       pLOOP (RC_QRY, NULL);
+
+       snprintf (prompt, sizeof(prompt), myprompt, invo_name);
+
+       switch (WINgetstr (Command, prompt, buffer)) {
+           case NOTOK: 
+               break;
+
+           case OK: 
+               done (0);       /* NOTREACHED */
+
+           default: 
+               if (*buffer)
+                   pLOOP (RC_CMD, buffer);
+               break;
+       }
+    }
+}
+
+/* PEERS */
+
+static int
+PEERinit (int vecp, char *vec[])
+{
+    int        pfd0[2], pfd1[2];
+    char buf1[BUFSIZ], buf2[BUFSIZ];
+    register WINDOW **w;
+
+    SIGNAL (SIGPIPE, PIPEser);
+
+    if (pipe (pfd0) == NOTOK || pipe (pfd1) == NOTOK)
+       adios ("pipe", "unable to");
+    switch (PEERpid = vfork ()) {
+       case NOTOK: 
+           adios ("vfork", "unable to");/* NOTREACHED */
+
+       case OK: 
+           for (w = windows; *w; w++)
+               if ((*w)->w_fd != NOTOK)
+                   close ((*w)->w_fd);
+           close (pfd0[0]);
+           close (pfd1[1]);
+
+           vec[vecp++] = "-vmhread";
+           snprintf (buf1, sizeof(buf1), "%d", pfd1[0]);
+           vec[vecp++] = buf1;
+           vec[vecp++] = "-vmhwrite";
+           snprintf (buf2, sizeof(buf2), "%d", pfd0[1]);
+           vec[vecp++] = buf2;
+           vec[vecp] = NULL;
+
+           SIGNAL (SIGINT, SIG_DFL);
+           SIGNAL (SIGQUIT, SIG_DFL);
+           SIGNAL (SIGTERM, SIG_DFL);
+
+           vec[0] = r1bindex (vmhproc, '/');
+           execvp (vmhproc, vec);
+           perror (vmhproc);
+           _exit (-1);         /* NOTREACHED */
+
+       default: 
+           close (pfd0[1]);
+           close (pfd1[0]);
+
+           rcinit (pfd0[0], pfd1[1]);
+           return pINI ();
+    }
+}
+
+
+static int
+pINI (void)
+{
+    int len, buflen;
+    char *bp, buffer[BUFSIZ];
+    struct record rcs, *rc;
+    WINDOW **w;
+
+    rc = &rcs;
+    initrc (rc);
+
+    /* Get buffer ready to go */
+    bp = buffer;
+    buflen = sizeof(buffer);
+
+    snprintf (bp, buflen, "%d %d", RC_VRSN, numwins);
+    len = strlen (bp);
+    bp += len;
+    buflen -= len;
+
+    for (w = windows; *w; w++) {
+       snprintf (bp, buflen, " %d", (*w)->w_height);
+       len = strlen (bp);
+       bp += len;
+       buflen -= len;
+    }
+
+    switch (str2rc (RC_INI, buffer, rc)) {
+       case RC_ACK: 
+           return OK;
+
+       case RC_ERR: 
+           if (rc->rc_len)
+               adios (NULL, "%s", rc->rc_data);
+           else
+               adios (NULL, "pINI peer error");
+
+       case RC_XXX: 
+           adios (NULL, "%s", rc->rc_data);
+
+       default:
+           adios (NULL, "pINI protocol screw-up");
+    }
+/* NOTREACHED */
+}
+
+
+static int
+pLOOP (char code, char *str)
+{
+    int        i;
+    struct record rcs, *rc;
+    WINDOW *w;
+
+    rc = &rcs;
+    initrc (rc);
+
+    str2peer (code, str);
+    for (;;)
+       switch (peer2rc (rc)) {
+           case RC_TTY:
+               if (pTTY () == NOTOK)
+                   return NOTOK;
+               break;
+
+           case RC_WIN:
+               if (sscanf (rc->rc_data, "%d", &i) != 1
+                       || i <= 0
+                       || i > numwins) {
+                   fmt2peer (RC_ERR, "no such window \"%s\"", rc->rc_data);
+                   return NOTOK;
+               }
+               if ((w = windows[i - 1])->w_flags & W_CMND) {
+                   fmt2peer (RC_ERR, "not a display window \"%s\"", rc->rc_data);
+                   return NOTOK;
+               }
+               if (pWIN (w) == NOTOK)
+                   return NOTOK;
+               break;
+
+           case RC_EOF:
+               return OK;
+
+           case RC_ERR:
+               if (rc->rc_len)
+                   adorn (NULL, "%s", rc->rc_data);
+               else
+                   adorn (NULL, "pLOOP(%s) peer error",
+                           code == RC_QRY ? "QRY" : "CMD");
+               return NOTOK;
+
+           case RC_FIN:
+               if (rc->rc_len)
+                   adorn (NULL, "%s", rc->rc_data);
+               rcdone ();
+               i = pidwait (PEERpid, OK);
+               PEERpid = NOTOK;
+               done (i);
+
+           case RC_XXX: 
+               adios (NULL, "%s", rc->rc_data);
+
+           default:
+               adios (NULL, "pLOOP(%s) protocol screw-up",
+                       code == RC_QRY ? "QRY" : "CMD");
+       }
+}
+
+
+static int
+pTTY (void)
+{
+    SIGNAL_HANDLER hstat, istat, qstat, tstat;
+    struct record rcs, *rc;
+
+    rc = &rcs;
+    initrc (rc);
+
+    if (ChangeWindowDepth (dfd, twd, 0) == NOTOK)
+       adios ("failed", "ChangeWindowDepth");
+
+    /* should block here instead of ignore */
+    hstat = SIGNAL (SIGHUP, SIG_IGN);
+    istat = SIGNAL (SIGINT, SIG_IGN);
+    qstat = SIGNAL (SIGQUIT, SIG_IGN);
+    tstat = SIGNAL (SIGTERM, SIG_IGN);
+
+    rc2rc (RC_ACK, 0, NULL, rc);
+
+    SIGNAL (SIGHUP, hstat);
+    SIGNAL (SIGINT, istat);
+    SIGNAL (SIGQUIT, qstat);
+    SIGNAL (SIGTERM, tstat);
+
+    switch (rc->rc_type) {
+       case RC_EOF: 
+           rc2peer (RC_ACK, 0, NULL);
+           return OK;
+
+       case RC_ERR: 
+           if (rc->rc_len)
+               adorn (NULL, "%s", rc->rc_data);
+           else
+               adorn (NULL, "pTTY peer error");
+           return NOTOK;
+
+       case RC_XXX: 
+           adios (NULL, "%s", rc->rc_data);
+
+       default:
+           adios (NULL, "pTTY protocol screw-up");
+    }
+/* NOTREACHED */
+}
+
+
+static int
+pWIN (WINDOW *w)
+{
+    int i;
+
+    if ((i = pWINaux (w)) == OK)
+       WINless (w);
+
+    return i;
+}
+
+
+static int
+pWINaux (WINDOW *w)
+{
+    register int n;
+    register char *bp;
+    register struct line *lp, *mp;
+    struct record rcs, *rc;
+
+    rc = &rcs;
+    initrc (rc);
+
+    for (lp = w->w_head; lp; lp = mp) {
+       mp = lp->l_next;
+       free (lp->l_buf);
+       free ((char *) lp);
+    }
+    w->w_head = w->w_top = w->w_bottom = w->w_tail = NULL;
+    w->w_bufpos = 0;
+
+    for (;;)
+       switch (rc2rc (RC_ACK, 0, NULL, rc)) {
+           case RC_DATA: 
+               for (bp = rc->rc_data, n = rc->rc_len; n-- > 0; )
+                   WINputc (w, *bp++);
+               break;
+
+           case RC_EOF: 
+               rc2peer (RC_ACK, 0, NULL);
+               if (w->w_bufpos)
+                   WINputc (w, '\n');
+               return OK;
+
+           case RC_ERR: 
+               if (rc->rc_len)
+                   adorn (NULL, "%s", rc->rc_data);
+               else
+                   adorn (NULL, "pWIN peer error");
+               return NOTOK;
+
+           case RC_XXX: 
+               adios (NULL, "%s", rc->rc_data);
+
+           default:
+               adios (NULL, "pWIN protocol screw-up");
+       }
+/* NOTREACHED */
+}
+
+
+static int
+pFIN (void)
+{
+    int status;
+
+    if (PEERpid <= OK)
+       return OK;
+
+    rc2peer (RC_FIN, 0, NULL);
+    rcdone ();
+
+    switch (setjmp (PEERctx)) {
+       case OK: 
+           SIGNAL (SIGALRM, ALRMser);
+           alarm (ALARM);
+
+           status = pidwait (PEERpid, OK);
+
+           alarm (0);
+           break;
+
+       default: 
+           kill (PEERpid, SIGKILL);
+           status = NOTOK;
+           break;
+    }
+    PEERpid = NOTOK;
+
+    return status;
+}
+
+/* WINDOWS */
+
+/* should dynamically determine all this stuff from gconfig... */
+
+#define        MyX     20              /* anchored hpos */
+#define        MyY     40              /*   .. vpos */
+#define        MyW     800             /*   .. width */
+#define        MyH     500             /*   .. height */
+#define        MyS     30              /*   .. height for Status, about one line */
+
+
+#define        MySlop  45              /* slop */
+
+#define        EWIDTH  25              /* Width of vertical EBAR */
+#define        ESLOP   5               /*   .. slop */
+
+
+static intWINinit (nprog) {
+    short wx, wy, wh, sy;
+    struct gconfig gc;
+
+    if (GetGraphicsConfig (fileno (stderr), &gc) == NOTOK)
+       if (nprog)
+           return NOTOK;
+       else
+           adios (NULL, "not a window");
+
+    if ((dfd = open ("/dev/ttyw0", O_RDWR)) == NOTOK)
+       adios ("/dev/ttyw0", "unable to open");
+
+    if ((twd = GetTopWindow (dfd)) == NOTOK)
+       adios ("failed", "GetTopWindow");
+
+    BlockRefreshAdjust (1);
+
+    numwins = 0;
+
+    wx = gc.w - (MyX + MyW + EWIDTH + ESLOP);
+    Scan = WINnew (wx, wy = MyY, MyW, wh = MyH * 2 / 3, "Scan", W_EBAR);
+
+    wy += wh + MySlop;
+    Status = WINnew (wx, sy = wy, MyW, wh = MyS, "Status", W_FAKE);
+
+    wy += wh + MySlop;
+    Display = WINnew (wx, wy, MyW, MyH, "Display", W_EBAR);
+
+    Command = WINnew (wx, sy, MyW, MyS, invo_name, W_CMND);
+
+    windows[numwins] = NULL;
+
+    return OK;
+}
+
+
+WINDOW *
+WINnew (short wx, short wy, short ww, short wh, char *name, int flags)
+{
+    register WINDOW *w;
+
+    if ((w = (WINDOW *) calloc (1, sizeof *w)) == NULL)
+       adios (NULL, "unable to allocate window");
+
+    if ((w->w_flags = flags) & W_FAKE) {
+       w->w_fd = NOTOK;
+       w->w_height = 1;
+
+       goto out;
+    }
+
+    if (w->w_flags & W_EBAR)
+       ww += EWIDTH + ESLOP;
+    else
+       wx += EWIDTH + ESLOP;
+
+    if ((w->w_fd = OpenWindow (wx, wy, ww, wh, name)) == NOTOK)
+       adios ("failed", "OpenWindow");
+    if ((w->w_wd = GetTopWindow (dfd)) == NOTOK)
+       adios ("failed", "GetTopWindow");
+    if (GetWindowState (w->w_fd, &w->w_ws) == NOTOK)
+       adios ("failed", "GetWindowState");
+    if (SetLineDisc (w->w_fd, TWSDISC) == NOTOK)
+       adios ("failed", "SetLineDisc");
+
+    SetBuf (w->w_fd, 1024);
+    SetAdjust (w->w_fd, numwins, ADJser);
+    SetRefresh (w->w_fd, numwins, REFser);
+
+    SetAddressing (w->w_fd, VT_ABSOLUTE);
+
+    if (w->w_flags & W_EBAR) {
+       w->w_eb = CreateElevatorBar (w->w_fd, 0, 0, EWIDTH,
+                       w->w_ws.height, VT_Gray50, 1, EB_VERTICAL,
+                       EB_ARROWS, w->w_ebloc = 0, w->w_ebsize = EB_MAX,
+                       VT_White);
+       if (w->w_eb == NULL)
+           adios (NULL, "CreateElevatorBar failed");
+       RefreshElevatorBar (w->w_eb);
+    }
+
+    if ((w->w_cbase = CharacterBaseline (w->w_ws.font)) <= 0)
+       w->w_cbase = 14;
+
+    if ((w->w_cheight = CharacterHeight (w->w_ws.font)) <= 0)
+       w->w_cheight = 20;
+    w->w_height = w->w_ws.height / w->w_cheight;
+    if (w->w_height < 1)
+       w->w_height = 1;
+
+                                               /* 1 em */
+    if ((w->w_cwidth = CharacterWidth (w->w_ws.font, 'm')) <= 0)
+       w->w_cwidth = 10;
+    w->w_width = (w->w_ws.width - (w->w_eb ? (EWIDTH + ESLOP) : 0))
+                   / w->w_cwidth;
+    if (w->w_width < 1)
+       w->w_width = 1;
+
+out: ;
+    windows[numwins++] = w;
+
+    return w;
+}
+
+
+static int
+WINgetstr (WINDOW *w, char *prompt, char *buffer)
+{
+    register int c;
+    register char *bp, *ip;
+    char image[BUFSIZ];
+    struct vtseq vts;
+    register struct vtseq  *vt = &vts;
+
+    if (w->w_eb != NULL)
+       adios (NULL, "internal error--elevator bar found");
+
+    if (w->w_head == NULL
+           && (w->w_head = (struct line *) calloc (1, sizeof *w->w_head))
+               == NULL)
+       adios (NULL, "unable to allocate line storage");
+    w->w_head->l_buf = image;
+    w->w_top = w->w_bottom = w->w_tail = w->w_head;
+
+    if (ChangeWindowDepth (dfd, w->w_wd, 0) == NOTOK)
+       adios ("failed", "ChangeWindowDepth");
+
+    strncpy (image, prompt, sizeof(image));
+    bp = ip = image + strlen (image);
+
+    Redisplay (w, 0);
+
+    for (;;)
+       switch (getvtseq (w->w_fd, vt)) {
+           case VT_HARDKEY: 
+               DisplayStatus (w->w_fd, "no hardkeys, please");
+               break;
+
+           case VT_ASCII: 
+               switch (c = toascii (vt->u.ascii)) {
+                   case '\f':  /* refresh? */
+                       break;
+
+                   case '\r': 
+                   case '\n': 
+                       strcpy (buffer, ip);
+                       return DONE;
+
+                   default: 
+                       if (c == INTR) {
+                           adorn (NULL, "Interrupt");
+                           return NOTOK;
+                       }
+
+                       if (c == EOFC) {
+                           if (bp <= ip)
+                               return OK;
+                           break;
+                       }
+
+                       if (c == ERASE) {
+                           if (bp <= ip)
+                               continue;
+                           bp--;
+                           break;
+                       }
+
+                       if (c == KILL) {
+                           if (bp <= ip)
+                               continue;
+                           bp = ip;
+                           break;
+                       }
+
+                       if (c == WERASC) {
+                           if (bp <= ip)
+                               continue;
+                           do {
+                               bp--;
+                           } while (isspace (*bp) && bp > ip);
+                           if (bp > ip) {
+                               do {
+                                   bp--;
+                               } while (!isspace (*bp) && bp > buffer);
+                               if (isspace (*bp))
+                                   bp++;
+                           }
+                           break;
+                       }
+
+                       if (c < ' ' || c >= '\177')
+                           continue;
+                       *bp++ = c;
+                       break;
+               }
+               *bp = NULL;
+               Redisplay (w, 0);
+               break;
+
+           case VT_MOUSE: 
+               switch (vt->u.mouse.buttons
+                       & (VT_MOUSE_LEFT | VT_MOUSE_MIDDLE | VT_MOUSE_RIGHT)) {
+                   case VT_MOUSE_LEFT: 
+                       DisplayStatus (w->w_fd, "use middle or right button");
+                       break;
+
+#define        WPOP    "WMH\0Advance\0Burst\0Exit\0EOF\0"
+                   case VT_MOUSE_MIDDLE: 
+                       SetPosition (w->w_fd, vt->u.mouse.x,
+                               vt->u.mouse.y);
+                       switch (DisplayPopUp (w->w_fd, WPOP)) {
+                           case 1: /* Advance */
+                       do_advance: ;
+                               strcpy (buffer, "advance");
+                               return DONE;
+
+                           case 2: /* Burst */
+                               strcpy (buffer, "burst");
+                               return DONE;
+
+                           case 3: /* Exit */
+                               strcpy (buffer, "exit");
+                               return DONE;
+
+                           case 4: /* EOF */
+                               return OK;
+
+                           default: /* failed or none taken */
+                               break;
+                       }
+                       break;
+#undef WPOP
+
+                   case VT_MOUSE_RIGHT: 
+                       goto do_advance;
+               }
+               break;
+
+           case VT_EOF: 
+               adios (NULL, "end-of-file on window");/* NOTREACHED */
+
+           default: 
+               DisplayStatus (w->w_fd, "unknown VT sequence");
+               break;
+       }
+}
+
+
+static int
+WINputc (WINDOW *w, char c)
+{
+    register int i;
+    register char *cp;
+    register struct line *lp;
+
+    switch (c) {
+       default: 
+           if (!isascii (c)) {
+               if (WINputc (w, 'M') == NOTOK || WINputc (w, '-') == NOTOK)
+                   return NOTOK;
+               c = toascii (c);
+           }
+           else
+               if (c < ' ' || c == '\177') {
+                   if (WINputc (w, '^') == NOTOK)
+                       return NOTOK;
+                   c ^= 0100;
+               }
+           break;
+
+       case '\t': 
+           for (i = 8 - (w->w_bufpos & 0x07); i > 0; i--)
+               if (WINputc (w, ' ') == NOTOK)
+                   return NOTOK;
+           return OK;
+
+       case '\b':
+           if (w->w_bufpos > 0)
+               w->w_bufpos--;
+           return OK;
+
+       case '\n': 
+           break;
+    }
+
+    if (c != '\n') {
+       w->w_buffer[w->w_bufpos++] = c;
+       return OK;
+    }
+
+    w->w_buffer[w->w_bufpos] = NULL;
+    w->w_bufpos = 0;
+
+    if ((lp = (struct line *) calloc (1, sizeof *lp)) == NULL)
+       adios (NULL, "unable to allocate line storage");
+
+    lp->l_no = (w->w_tail ? w->w_tail->l_no : 0) + 1;
+    lp->l_buf = getcpy (w->w_buffer);
+    for (cp = lp->l_buf + strlen (lp->l_buf) - 1; cp >= lp->l_buf; cp--)
+       if (isspace (*cp))
+           *cp = NULL;
+       else
+           break;
+
+    if (w->w_head == NULL)
+       w->w_head = lp;
+    if (w->w_top == NULL)
+       w->w_top = lp;
+    if (w->w_bottom == NULL)
+       w->w_bottom = lp;
+    if (w->w_tail)
+       w->w_tail->l_next = lp;
+    lp->l_prev = w->w_tail;
+    w->w_tail = lp;
+
+    return DONE;
+}
+
+#define        PSLOP   2
+
+
+static char mylineno[5];
+
+static bool cancel[] =  { 1 };
+static struct choice mychoices[] = { LABEL, "cancel", VT_White };
+
+static struct question myquestions[] = {
+    STRING, "Line", SZ (mylineno), (struct choice *) 0,
+
+    TOGGLE, "", SZ (mychoices),  mychoices
+};
+
+static struct menu mymenu = { "Goto", SZ (myquestions), myquestions };
+
+static int *myanswers[] = { (int *) mylineno, (int *) cancel };
+
+
+static void
+WINless (WINDOW *w)
+{
+    int clear, pos, forw, refresh;
+    struct vtseq vts;
+    register struct vtseq *vt = &vts;
+
+    if (w->w_fd == NOTOK) {
+       if (w->w_head)
+           DisplayStatus (dfd, w->w_top->l_buf);
+       else
+           RemoveStatus (dfd);
+
+       return;
+    }
+
+    if (ChangeWindowDepth (dfd, w->w_wd, 0) == NOTOK)
+       adios ("failed", "ChangeWindowDepth");
+
+    Redisplay (w, 0);
+
+    if (w->w_bottom == w->w_tail)
+       return;
+
+    if (w->w_eb == NULL)
+       adios (NULL, "internal error--no elevator bar");
+
+    for (clear = refresh = 0, forw = 1;;) {
+       if (clear) {
+           RemoveStatus (w->w_fd);
+           clear = 0;
+       }
+       if (refresh) {
+           Redisplay (w, 0);
+           refresh = 0;
+       }
+
+       switch (getvtseq (w->w_fd, vt)) {
+           case VT_HARDKEY: 
+           case VT_ASCII: 
+               DisplayStatus (w->w_fd, "use the mouse");
+               clear++;
+               break;
+
+           case VT_MOUSE: 
+               switch (vt->u.mouse.buttons
+                       & (VT_MOUSE_LEFT | VT_MOUSE_MIDDLE | VT_MOUSE_RIGHT)) {
+                   case VT_MOUSE_LEFT: 
+                       if ((pos = vt->u.mouse.x) < EWIDTH) {
+                           pos = w->w_ebloc = DoElevatorBar (w->w_eb, pos,
+                                   vt->u.mouse.y);
+                           refresh = WINgoto (w, ((pos * (w->w_tail->l_no
+                                               - w->w_head->l_no))
+                                       / EB_MAX) + w->w_head->l_no);
+                       }
+                       break;
+
+#define        WPOP "Paging\0Next\0Prev\0Left\0Right\0First\0Last\0Goto ...\0Exit\0"
+                   case VT_MOUSE_MIDDLE: 
+                       SetPosition (w->w_fd, vt->u.mouse.x,
+                               vt->u.mouse.y);
+                       switch (DisplayPopUp (w->w_fd, WPOP)) {
+                           case 1: /* Next */
+                       do_next_page: ;
+                               if (w->w_bottom == w->w_tail)
+                                   forw = 0;
+                               refresh = WINgoto (w, w->w_bottom->l_no + 1 - PSLOP);
+                               break;
+
+                           case 2: /* Prev */
+                       do_prev_page: ;
+                               if (w->w_top == w->w_head)
+                                   forw = 1;
+                               refresh = WINgoto (w, w->w_top->l_no
+                                       - w->w_height + PSLOP);
+                               break;
+
+                           case 3: /* Left */
+                           case 4: /* Right */
+                               DisplayStatus (w->w_fd, "not yet");
+                               clear++;
+                               break;
+
+                           case 5: /* First */
+                               forw = 1;
+                               refresh = WINgoto (w, w->w_head->l_no);
+                               break;
+
+                           case 6: /* Last */
+                               forw = 0;
+                               refresh = WINgoto (w, w->w_tail->l_no
+                                       - w->w_height + 1);
+                               break;
+
+                           case 7: /* Goto ... */
+                               snprintf (mylineno, sizeof(mylineno),
+                                       "%d", w->w_top->l_no);
+                               cancel[0] = 0;
+                               if (PresentMenu (&mymenu, myanswers)
+                                       || cancel[0])
+                                   break;
+                               if (sscanf (mylineno, "%d", &pos) != 1) {
+                                   DisplayStatus (w->w_fd, "bad format");
+                                   clear++;
+                                   break;
+                               }
+                               if (pos < w->w_head->l_no
+                                       || pos > w->w_tail->l_no) {
+                                   DisplayStatus (w->w_fd, "no such line");
+                                   clear++;
+                                   break;
+                               }
+                               refresh = WINgoto (w, pos);
+                               break;
+
+                           case 8: /* Exit */
+                               return;
+
+                           default: /* failed or none taken */
+                               break;
+                       }
+                       break;
+#undef WPOP
+
+                   case VT_MOUSE_RIGHT: 
+                       if (forw) {
+                           if (w->w_bottom == w->w_tail)
+                               return;
+                           else
+                               goto do_next_page;
+                       }
+                       else
+                           goto do_prev_page;
+               }
+               break;
+
+           case VT_EOF: 
+               adios (NULL, "end-of-file on window");/* NOTREACHED */
+
+           default: 
+               DisplayStatus (w->w_fd, "unknown VT sequence");
+               clear++;
+               break;
+       }
+    }
+}
+
+
+static int
+WINgoto (WINDOW *w, int n)
+{
+    register int i, j;
+    register struct line *lp;
+
+    if (n > (i = w->w_tail->l_no - w->w_height + 1))
+       n = i;
+    if (n < w->w_head->l_no)
+       n = w->w_head->l_no;
+
+    if ((i = n - (lp = w->w_head)->l_no)
+           > (j = abs (n - w->w_top->l_no)))
+       i = j, lp = w->w_top;
+
+    if (i > (j = abs (w->w_tail->l_no - n)))
+       i = j, lp = w->w_tail;
+
+    if (n >= lp->l_no) {
+       for (; lp; lp = lp->l_next)
+           if (lp->l_no == n)
+               break;
+    }
+    else {
+       for (; lp; lp = lp->l_prev)
+           if (lp->l_no == n)
+               break;
+       if (!lp)
+           lp = w->w_head;
+    }
+
+    if (w->w_top == lp)
+       return 0;
+
+    w->w_top = lp;
+
+    return 1;
+}
+
+
+static int
+ADJser (int id, short ww, short wh)
+{
+    register WINDOW *w;
+
+    if (id < 0 || id >= numwins)
+       adios (NULL, "ADJser on bogus window (%d)", id);
+    w = windows[id];
+    if (w->w_fd == NOTOK)
+       adios (NULL, "ADJser on closed window (%d)", id);
+
+    w->w_ws.width = w->w_ws.tw = ww;
+    w->w_ws.height = w->w_ws.th = wh;
+
+    if (w->w_eb) {
+       DeleteElevatorBar (w->w_eb);
+       w->w_eb = CreateElevatorBar (w->w_fd, 0, 0, EWIDTH,
+                       w->w_ws.height, VT_Gray50, 1, EB_VERTICAL,
+                       EB_ARROWS, w->w_ebloc = 0, w->w_ebsize = EB_MAX,
+                       VT_White);
+       if (w->w_eb == NULL)
+           adios (NULL, "CreateElevatorBar failed");
+    }
+
+    Redisplay (w, 1);
+}
+
+
+static int
+REFser (int id, short wx, short wy, short ww, short wh)
+{
+    short cx, cy, cw, ch;
+    register WINDOW *w;
+
+    if (id < 0 || id >= numwins)
+       adios (NULL, "REFser on bogus window (%d)", id);
+    w = windows[id];
+    if (w->w_fd == NOTOK)
+       adios (NULL, "REFser on closed window (%d)", id);
+
+
+    if (GetWindowState (w->w_fd, &w->w_ws) == NOTOK)
+       adios ("failed", "GetWindowState");
+
+    GetPermanentClipping (w->w_fd, &cx, &cy, &cw, &ch);
+    SetPermanentClipping (w->w_fd, wx, wy, ww, wh);
+    Redisplay (w, 1);
+    SetPermanentClipping (w->w_fd, cx, cy, cw, ch);
+}
+
+
+static void
+Redisplay (WINDOW *w, int doeb)
+{
+    register int y;
+    short sx;
+    register struct line *lp;
+
+    if (w->w_fd == NOTOK)
+       return;
+
+    sx = w->w_eb ? (EWIDTH + ESLOP) : 0;
+    w->w_height = w->w_ws.height / w->w_cheight;
+    if (w->w_height < 1)
+       w->w_height = 1;
+
+    w->w_width = (w->w_ws.width - (w->w_eb ? (EWIDTH + ESLOP) : 0))
+       / w->w_cwidth;
+    if (w->w_width < 1)
+       w->w_width = 1;
+
+    SetPosition (w->w_fd, sx, 0);
+    SetColor (w->w_fd, VT_White);
+    PaintRectangleInterior (w->w_fd, w->w_ws.width, w->w_ws.height);
+
+    if (w->w_head) {
+       SetColor (w->w_fd, VT_Black);
+       for (lp = w->w_top, y = 0;
+               lp && y < w->w_height;
+               w->w_bottom = lp, lp = lp->l_next, y++) {
+           SetPosition (w->w_fd, sx, y * w->w_cheight + w->w_cbase);
+           PaintString (w->w_fd, VT_STREND, lp->l_buf);
+       }
+    }
+
+    if (w->w_eb) {
+       if ((y = EB_LOC (w)) != w->w_ebloc)
+           MoveElevator (w->w_eb, w->w_ebloc = y);
+       if ((y = EB_SIZE (w)) != w->w_ebsize)
+           SizeElevator (w->w_eb, w->w_ebsize = y);
+       if (doeb)
+           RefreshElevatorBar (w->w_eb);
+    }
+
+    Flush (w->w_fd);
+}
+
+
+static int
+EB_SIZE (WINDOW *w)
+{
+    register int i;
+
+    if (w->w_head == NULL)
+       return 0;
+
+    if ((i = w->w_tail->l_no - w->w_head->l_no) <= 0)
+       return EB_MAX;
+
+    return (((w->w_bottom->l_no - w->w_top->l_no) * EB_MAX) / i);
+}
+
+
+static int
+EB_LOC (WINDOW *w)
+{
+    register int i;
+
+    if (w->w_head == NULL)
+       return 0;
+
+    if ((i = w->w_tail->l_no - w->w_head->l_no) <= 0)
+       return EB_MAX;
+
+    return (((w->w_top->l_no - w->w_head->l_no) * EB_MAX) / i);
+}
+
+/* SIGNALS */
+
+static void
+SIGinit (void)
+{
+    foreground ();
+    if (ioctl (fileno (stdin), TIOCGETP, (char *) &tio) == NOTOK)
+       adios ("failed", "ioctl TIOCGETP");
+    if (ioctl (fileno (stdin), TIOCGETC, (char *) &tc) == NOTOK)
+       adios ("failed", "ioctl TIOCGETC");
+    if (ioctl (fileno (stdin), TIOCGLTC, (char *) &ltc) == NOTOK)
+       adios ("failed", "ioctl TIOCGLTC");
+    sideground ();
+
+    SIGNAL (SIGHUP, SIGser);
+    SIGNAL (SIGINT, SIGser);
+    SIGNAL (SIGQUIT, SIGser);
+}
+
+
+static void
+foreground (void)
+{
+#ifdef TIOCGPGRP
+    int pgrp, tpgrp;
+    SIGNAL_HANDLER tstat;
+
+    if ((pgrp = getpgrp (0)) == NOTOK)
+       adios ("process group", "unable to determine");
+    for (;;) {
+       if (ioctl (fileno (stdin), TIOCGPGRP, (char *) &tpgrp) == NOTOK)
+           adios ("tty's process group", "unable to determine");
+       if (pgrp == tpgrp)
+           break;
+
+       tstat = SIGNAL (SIGTTIN, SIG_DFL);
+       kill (0, SIGTTIN);
+       SIGNAL (SIGTTIN, tstat);
+    }
+    
+    SIGNAL (SIGTTIN, SIG_IGN);
+    SIGNAL (SIGTTOU, SIG_IGN);
+    SIGNAL (SIGTSTP, SIG_IGN);
+#endif TIOCGPGRP
+}
+
+
+static void
+sideground (void)
+{
+#ifdef TIOCGPGRP
+    SIGNAL (SIGTTIN, SIG_DFL);
+    SIGNAL (SIGTTOU, SIG_DFL);
+    SIGNAL (SIGTSTP, SIG_DFL);
+#endif TIOCGPGRP
+}
+
+
+static int
+ALRMser (int sig)
+{
+     longjmp (PEERctx, DONE);
+}
+
+
+static int
+PIPEser (int sig)
+{
+#ifndef RELIABLE_SIGNALS
+    SIGNAL (sig, SIG_IGN);
+#endif
+
+    adios (NULL, "lost peer");
+}
+
+
+static int
+SIGser (int sig)
+{
+#ifndef RELIABLE_SIGNALS
+    SIGNAL (sig, SIG_IGN);
+#endif
+
+    done (1);
+}
+
+
+void
+done (int status)
+{
+    if (dfd != NOTOK)
+       RemoveStatus (dfd);
+
+    pFIN ();
+
+    exit (status);
+}
+
+
+static void
+adorn (char *what, char *fmt, ...)
+{
+    va_list ap
+    char *cp;
+
+    cp = invo_name;
+    invo_name = NULL;
+
+    va_start(ap, fmt);
+    advertise (what, NULL, fmt, ap);
+    va_end(ap);
+
+    invo_name = cp;
+}
+
+
+void
+advertise (char *what, char *tail, va_list ap)
+{
+    int eindex = errno;
+    char buffer[BUFSIZ], err[BUFSIZ];
+    struct iovec iob[20];
+    register struct iovec *iov = iob;
+
+    fflush (stdout);
+    fflush (stderr);
+
+    if (invo_name) {
+       iov->iov_len = strlen (iov->iov_base = invo_name);
+       iov++;
+       iov->iov_len = strlen (iov->iov_base = ": ");
+       iov++;
+    }
+    
+    vsnprintf (buffer, sizeof(buffer), fmt, ap);
+    iov->iov_len = strlen (iov->iov_base = buffer);
+    iov++;
+    if (what) {
+       if (*what) {
+           iov->iov_len = strlen (iov->iov_base = " ");
+           iov++;
+           iov->iov_len = strlen (iov->iov_base = what);
+           iov++;
+           iov->iov_len = strlen (iov->iov_base = ": ");
+           iov++;
+       }
+       if (!(iov->iov_base = strerror (eindex))) {
+           snprintf (err, sizeof(err), "unknown error %d", eindex);
+           iov->iov_base = err;
+       }
+       iov->iov_len = strlen (iov->iov_base);
+       iov++;
+    }
+    if (tail && *tail) {
+       iov->iov_len = strlen (iov->iov_base = ", ");
+       iov++;
+       iov->iov_len = strlen (iov->iov_base = tail);
+       iov++;
+    }
+    iov->iov_len = strlen (iov->iov_base = "\n");
+    iov++;
+
+    if (dfd != NOTOK)
+       DisplayVector (iob, iov - iob);
+    else
+       writev (fileno (stderr), iob, iov - iob);
+}
+
+
+static void
+DisplayVector (struct iovec *iov, int n)
+{
+    register int i;
+    register char *cp;
+    char buffer[BUFSIZ];
+
+    for (i = 0, cp = NULL; i < n; i++, iov++) {
+       snprintf (buffer, sizeof(buffer), "%*.*s", iov->iov_len,
+               iov->iov_len, iov->iov_base);
+       cp = add (buffer, cp);
+    }
+
+    DisplayStatus (dfd, cp);
+    free (cp);
+    sleep (PAUSE);
+    RemoveStatus (dfd);
+}
diff --git a/zotnet/Makefile.in b/zotnet/Makefile.in
new file mode 100644 (file)
index 0000000..7391814
--- /dev/null
@@ -0,0 +1,103 @@
+#
+# Makefile for zotnet subdirectory
+#
+# $Id$
+#
+
+SHELL = /bin/sh
+
+srcdir = @srcdir@
+VPATH  = @srcdir@
+
+LORDER = @LORDER@
+TSORT  = @TSORT@
+RANLIB = @RANLIB@
+
+# flags passed to recursive makes in subdirectories
+MAKEDEFS = CC='$(CC)' CPPFLAGS='$(CPPFLAGS)' DEFS='$(DEFS)' \
+CFLAGS='$(CFLAGS)' LDFLAGS='$(LDFLAGS)' LIBS='$(LIBS)' \
+prefix='$(prefix)' exec_prefix='$(exec_prefix)' bindir='$(bindir)' \
+etcdir='$(etcdir)' libdir='$(libdir)' mandir='$(mandir)' \
+mailspool='$(mailspool)' sendmailpath='$(sendmailpath)' \
+default_editor='$(default_editor)' default_pager='$(default_pager)'
+
+# object files in libzot.a
+OBJS = mts/mts.o mts/client.o \
+       tws/dtime.o tws/dtimep.o tws/lexstring.o \
+       mf/mf.o \
+       bboards/getbbent.o
+
+# auxiliary files
+AUX = Makefile.in
+
+# all files in this directory included in the distribution
+DIST = $(AUX)
+
+# subdirectories
+SUBDIRS = mts tws mf bboards
+
+# ========= DEPENDENCIES FOR BUILDING AND INSTALLING ==========
+
+all: all-recursive libzot.a
+
+libzot.a: $(OBJS)
+       rm -f libzot.a
+       ar cr libzot.a `$(LORDER) $(OBJS) | $(TSORT)`
+       $(RANLIB) libzot.a
+
+all-recursive:
+       for subdir in $(SUBDIRS); do \
+         (cd $$subdir && $(MAKE) $(MAKEDEFS) all) || exit 1; \
+       done
+
+install uninstall:
+       for subdir in $(SUBDIRS); do \
+         (cd $$subdir && $(MAKE) $(MAKEDEFS) $@) || exit 1; \
+       done
+
+# ========== DEPENDENCIES FOR CLEANUP ==========
+
+mostlyclean: mostlyclean-recursive mostlyclean-local
+clean:       clean-recursive       clean-local
+distclean:   distclean-recursive   distclean-local
+realclean:   realclean-recursive   realclean-local
+superclean:  superclean-recursive  superclean-local
+
+mostlyclean-local:
+       rm -f *~
+
+clean-local: mostlyclean-local
+       rm -f libzot.a
+
+distclean-local: clean-local
+       rm -f Makefile
+
+realclean-local: distclean-local
+
+superclean-local: realclean-local
+
+mostlyclean-recursive clean-recursive distclean-recursive realclean-recursive superclean-recursive:
+       for subdir in $(SUBDIRS); do \
+         target=`echo $@ | sed 's/-recursive//'`; \
+         (cd $$subdir && $(MAKE) $(MAKEDEFS) $$target) || exit 1; \
+       done
+
+# ========== DEPENDENCIES FOR MAINTENANCE ==========
+
+subdir = zotnet
+
+Makefile: Makefile.in ../config.status
+       cd .. && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+distdir = ../`cat ../distname`/$(subdir)
+nmhdist: $(DIST)
+       @echo "Copying distribution files in $(subdir)"
+       @for file in $(DIST); do \
+         cp -p $(srcdir)/$$file $(distdir); \
+       done
+       @for subdir in $(SUBDIRS); do \
+         mkdir $(distdir)/$$subdir; \
+         chmod 755 $(distdir)/$$subdir; \
+         (cd $$subdir && $(MAKE) $@) || exit 1; \
+       done
+
diff --git a/zotnet/bboards/Makefile.in b/zotnet/bboards/Makefile.in
new file mode 100644 (file)
index 0000000..7dedeea
--- /dev/null
@@ -0,0 +1,76 @@
+#
+# Makefile for zotnet/bboards subdirectory
+#
+# $Id$
+#
+
+SHELL = /bin/sh
+
+top_srcdir = @top_srcdir@
+srcdir     = @srcdir@
+VPATH      = @srcdir@
+
+CC         = @CC@
+CFLAGS     = @CFLAGS@
+DEFS       = @DEFS@
+INCLUDES   = -I../.. -I$(srcdir) -I$(top_srcdir)
+
+COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CFLAGS)
+
+.SUFFIXES:
+.SUFFIXES: .c .o
+
+.c.o:
+       $(COMPILE) $<
+
+# header files
+HDRS = bboards.h
+
+# source files
+SRCS = getbbent.c
+
+# object files
+OBJS = getbbent.o
+
+# auxiliary files
+AUX = Makefile.in
+
+# all files in this directory included in the distribution
+DIST = $(HDRS) $(SRCS) $(AUX)
+
+# ========= DEPENDENCIES FOR BUILDING ==========
+
+all: $(OBJS)
+
+install:
+
+uninstall:
+
+# ========== DEPENDENCIES FOR CLEANUP ==========
+
+mostlyclean:
+       rm -f *.o *~
+
+clean: mostlyclean
+
+distclean: clean
+       rm -f Makefile
+
+realclean: distclean
+
+superclean: realclean
+
+# ========== DEPENDENCIES FOR MAINTENANCE ==========
+
+subdir = zotnet/bboards
+
+Makefile: Makefile.in ../../config.status
+       cd ../.. && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+distdir = ../../`cat ../../distname`/$(subdir)
+nmhdist: $(DIST)
+       @echo "Copying distribution files in $(subdir)"
+       @for file in $(DIST); do \
+         cp -p $(srcdir)/$$file $(distdir); \
+       done
+
diff --git a/zotnet/bboards/bboards.h b/zotnet/bboards/bboards.h
new file mode 100644 (file)
index 0000000..227bee5
--- /dev/null
@@ -0,0 +1,88 @@
+
+/*
+ * bboards.h -- definition of a BBoard structure
+ *
+ * $Id$
+ */
+
+#define        BBOARDS "bboards"       /* name in /etc/passwd               */
+#define        BBDB    "BBoards"       /* file in BBOARDS' home directory   */
+#define        BBMODE  0644            /* default BBoards mode              */
+#define        DISTADR "dist-"         /* prefix for distribution addresses */
+
+#ifdef POP
+# define POPUID        "pop"           /* name in /etc/passwd                  */
+# define POPDB "POP"           /* file in POPUID's home directory      */
+# define POMODE        0600            /* default POP subscriber maildrop mode */
+#endif /* POP */
+
+#define        BB_NULL    0x0000
+#define        BB_ARCH    0x0007       /* archive policy              */
+#define BB_ASAV    0x0001      /* save in archives/ directory */
+#define        BB_AREM    0x0002       /* remove without saving       */
+#define BB_INVIS   0x0010      /* invisible to bbc            */
+#define        BB_REMOTE  0x0020       /* remote to bbc               */
+#define        BB_SEEN    0x0040       /* seen by bbc                 */
+#define        BBITS      "\020\01ARCHIVE\02REMOVE\05INVIS\06REMOTE\07SEEN"
+
+struct bboard {
+    char  *bb_name;            /* name of the bboard      */
+    char **bb_aka;             /* aliases for the bboards */
+
+    char  *bb_file;            /* file it resides in            */
+    char  *bb_archive;         /* file where archives reside    */
+    char  *bb_info;            /* file where maxima resides     */
+    char  *bb_map;             /* file where binary map resides */
+
+    char  *bb_passwd;          /* password for it       */
+    char **bb_leader;          /* list of local leaders */
+
+    char  *bb_addr;            /* network address                      */
+    char  *bb_request;         /* network address for requests         */
+    char  *bb_relay;           /* host acting as relay in local domain */
+    char **bb_dist;            /* distribution list                    */
+
+    unsigned int bb_flags;     /* various flags */
+
+    union {                    /* unassigned    */
+       unsigned int un_count;
+       long         un_mtime;
+    } bb_un;
+    
+    unsigned int bb_maxima;    /* highest BBoard-Id in it      */
+    char *bb_date;             /* date that maxima was written */
+
+    struct bboard *bb_next;    /* unassigned */
+    struct bboard *bb_link;    /* unassigned */
+    struct bboard *bb_chain;   /* unassigned */
+};
+
+#define        bb_count bb_un.un_count
+#define        bb_mtime bb_un.un_mtime
+
+/* flags for setbbent() */
+#define        SB_NULL 0x0000
+#define        SB_STAY 0x0001          /* stay open between calls */
+#define        SB_FAST 0x0002          /* fast parse of file      */
+
+
+/*
+ * prototypes
+ */
+int setbbfile (char *, int);
+int setbbinfo (char *, char *, int);
+int setpwinfo (struct passwd *, char *, int);
+int setbbent (int);
+int endbbent (void);
+long getbbtime (void);
+struct bboard *getbbent (void);
+struct bboard *getbbnam (char *);
+struct bboard *getbbaka (char *);
+static void BBread (void);
+int ldrbb (struct bboard *);
+int ldrchk (struct bboard *);
+struct bboard *getbbcpy (struct bboard *);
+int getbbdist (struct bboard  *, int (*)());
+char *getbberr (void);
+void make_lower (char *, char *);
+
diff --git a/zotnet/bboards/getbbent.c b/zotnet/bboards/getbbent.c
new file mode 100644 (file)
index 0000000..b9904be
--- /dev/null
@@ -0,0 +1,733 @@
+
+/*
+ * getbbent.c -- subroutines for accessing the BBoards file
+ *
+ * $Id$
+ */
+
+#include <h/nmh.h>
+
+#ifdef MMDFONLY
+# include <util.h>
+# include <mmdf.h>
+# include <strings.h>
+#endif /* MMDFONLY */
+
+#include <pwd.h>
+#include <grp.h>
+#include <bboards.h>
+
+#ifdef HAVE_CRYPT_H
+# include <crypt.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifndef        MMDFONLY
+# define NOTOK (-1)
+# define OK    0
+#endif
+
+#define        MaxBBAka  100
+#define        MaxBBLdr  100
+#define        MaxBBDist 100
+
+#define        NCOLON  9               /* currently 10 fields per entry */
+
+#define        COLON   ':'
+#define        COMMA   ','
+#define        NEWLINE '\n'
+
+#define        ARCHIVE "archive"
+#define        CNTFILE ".cnt"
+#define        DSTFILE ".dist"
+#define        MAPFILE ".map"
+
+static int BBuid = -1;
+
+static unsigned int BBflags = SB_NULL;
+
+static char BBName[BUFSIZ] = BBOARDS;
+static char BBDir[BUFSIZ] = "";
+static char BBData[BUFSIZ] = "";
+
+static FILE *BBfile = NULL;
+
+static struct bboard BB;
+static struct bboard *bb = &BB;
+
+static int BBload = 1;
+
+static char BBFile[BUFSIZ];
+static char BBArchive[BUFSIZ];
+static char BBInfo[BUFSIZ];
+static char BBMap[BUFSIZ];
+static char *BBAkas[MaxBBAka];
+static char *BBLeaders[MaxBBLdr];
+static char *BBDists[MaxBBDist];
+static char BBAddr[BUFSIZ];
+static char BBRequest[BUFSIZ];
+static char BBDate[BUFSIZ];
+static char BBErrors[BUFSIZ];
+
+#ifdef MMDFONLY
+extern LLog *logptr;
+#endif
+
+#ifdef UCL
+int called_bbc = 0;
+char *bbs[101];
+#endif
+
+
+/*
+ * static prototypes
+ */
+static int setbbaux (char *, char *);
+static int setpwaux (struct passwd *, char *);
+static void BBread (void);
+static int getbbitem (struct bboard *, char *, int (*)());
+static int  bblose (char *, ...);
+static char *bbskip (char *, char);
+static char *getcpy (char *);
+
+
+int
+setbbfile (char *file, int f)
+{
+    if (BBuid == -1)
+       return setbbinfo (BBOARDS, file, f);
+
+    strncpy (BBData, file, sizeof(BBData));
+
+    BBflags = SB_NULL;
+    endbbent ();
+
+    return setbbent (f);
+}
+
+
+int
+setbbinfo (char *user, char *file, int f)
+{
+    register struct passwd *pw;
+
+    if ((pw = getpwnam (user)) == NULL) {
+       snprintf (BBErrors, sizeof(BBErrors), "unknown user: %s", user);
+       return 0;
+    }
+
+    return setpwinfo (pw, file, f);
+}
+
+
+int
+setpwinfo (struct passwd *pw, char *file, int f)
+{
+    if (!setpwaux (pw, file))
+       return 0;
+
+    BBflags = SB_NULL;
+    endbbent ();
+
+    return setbbent (f);
+}
+
+
+static int
+setbbaux (char *name, char *file)
+{
+    register struct passwd *pw;
+
+    if ((pw = getpwnam (name)) == NULL) {
+       snprintf (BBErrors, sizeof(BBErrors), "unknown user: %s", name);
+       return 0;
+    }
+
+    return setpwaux (pw, file);
+}
+
+
+static int
+setpwaux (struct passwd *pw, char *file)
+{
+    strncpy (BBName, pw->pw_name, sizeof(BBName));
+    BBuid = pw->pw_uid;
+    strncpy (BBDir, pw->pw_dir, sizeof(BBDir));
+    snprintf (BBData, sizeof(BBData), "%s/%s",
+           *file != '/' ? BBDir : "",
+           *file != '/' ? file : file + 1);
+
+    BBflags = SB_NULL;
+
+    return 1;
+}
+
+
+int
+setbbent (int f)
+{
+    if (BBfile == NULL) {
+       if (BBuid == -1 && !setbbaux (BBOARDS, BBDB))
+           return 0;
+
+       if ((BBfile = fopen (BBData, "r")) == NULL) {
+           snprintf (BBErrors, sizeof(BBErrors), "unable to open: %s", BBData);
+           return 0;
+       }
+    }
+    else
+       rewind (BBfile);
+
+    BBflags |= f;
+    return (BBfile != NULL);
+}
+
+
+int
+endbbent (void)
+{
+    if (BBfile != NULL && !(BBflags & SB_STAY)) {
+       fclose (BBfile);
+       BBfile = NULL;
+    }
+
+    return 1;
+}
+
+
+long
+getbbtime (void)
+{
+    struct stat st;
+
+    if (BBfile == NULL) {
+       if (BBuid == -1 && !setbbaux (BBOARDS, BBDB))
+           return 0;
+
+       if (stat (BBData, &st) == NOTOK) {
+           snprintf (BBErrors, sizeof(BBErrors), "unable to stat: %s", BBData);
+           return 0;
+       }
+    } else {
+       if (fstat (fileno (BBfile), &st) == NOTOK) {
+           snprintf (BBErrors, sizeof(BBErrors), "unable to fstat: %s", BBData);
+           return 0;
+       }
+    }
+
+    return ((long) st.st_mtime);
+}
+
+
+struct bboard *
+getbbent (void)
+{
+    register int count;
+    register char *p, *q, *r, *d, *f, **s;
+    static char line[BUFSIZ];
+
+    if (BBfile == NULL && !setbbent (SB_NULL))
+       return NULL;
+
+retry: ;
+    if ((p = fgets (line, sizeof line, BBfile)) == NULL)
+       return NULL;
+
+    for (q = p, count = 0; *q != 0 && *q != NEWLINE; q++)
+       if (*q == COLON)
+           count++;
+
+    if (count != NCOLON) {
+#ifdef MMDFONLY
+       if (q = strchr(p, NEWLINE))
+           *q = 0;
+       ll_log (logptr, LLOGTMP, "bad entry in %s: %s", BBData, p);
+#endif /* MMDFONLY */
+       goto retry;
+    }
+    
+    bb->bb_name = p;
+    p = q = bbskip (p, COLON);
+    p = bb->bb_file = bbskip (p, COLON);
+    bb->bb_archive = bb->bb_info = bb->bb_map = "";
+    p = bb->bb_passwd = bbskip (p, COLON);
+    p = r = bbskip (p, COLON);
+    p = bb->bb_addr = bbskip (p, COLON);
+    p = bb->bb_request = bbskip (p, COLON);
+    p = bb->bb_relay = bbskip (p, COLON);
+    p = d = bbskip (p, COLON);
+    p = f = bbskip (p, COLON);
+    bbskip (p, NEWLINE);
+
+    s = bb->bb_aka = BBAkas;
+    while (*q) {
+       *s++ = q;
+       q = bbskip (q, COMMA);
+    }
+    *s = 0;
+
+    s = bb->bb_leader = BBLeaders;
+    if (*r == 0) {
+       if (!(BBflags & SB_FAST)) {
+           *s++ = BBName;
+           *s = 0;
+       }
+    }
+    else {
+       while (*r) {
+           *s++ = r;
+           r = bbskip (r, COMMA);
+       }
+        *s = 0;
+    }
+
+    s = bb->bb_dist = BBDists;
+    while (*d) {
+       *s++ = d;
+       d = bbskip (d, COMMA);
+    }
+    *s = 0;
+
+    if (*f)
+       sscanf (f, "%o", &bb->bb_flags);
+    else
+       bb->bb_flags = BB_NULL;
+    bb->bb_count = bb->bb_maxima = 0;
+    bb->bb_date = NULL;
+    bb->bb_next = bb->bb_link = bb->bb_chain = NULL;
+
+#ifdef UCL
+       /*
+        * Only do a BBread on bboards that the user has expressed an
+        * interest in, if we were called by bbc.
+        */
+    if (BBload) {
+       register char **ap, *cp;
+       register int bbp;
+
+       if (called_bbc == 0)
+               BBread();
+       else {
+           for (bbp = 0; cp = bbs[bbp]; bbp++) {
+               if (!strcmp(bb->bb_name, cp)) {
+                       BBread();
+                       break;
+                       }
+               for (ap = bb->bb_aka; *ap; ap++)
+                       if (!strcmp(*ap, cp)) {
+                               BBread();
+                               break;
+                               }
+               }
+           }
+       }
+#else
+    if (BBload)
+       BBread ();
+#endif
+
+    return bb;
+}
+
+
+struct bboard *
+getbbnam (char *name)
+{
+    register struct bboard *b = NULL;
+
+    if (!setbbent (SB_NULL))
+       return NULL;
+    BBload = 0;
+    while ((b = getbbent ()) && strcmp (name, b->bb_name))
+       continue;
+    BBload = 1;
+    endbbent ();
+
+    if (b != NULL)
+       BBread ();
+
+    return b;
+}
+
+
+struct bboard *
+getbbaka (char *aka)
+{
+    register char **ap;
+    register struct bboard *b = NULL;
+
+    if (!setbbent (SB_NULL))
+       return NULL;
+    BBload = 0;
+    while ((b = getbbent ()) != NULL)
+       for (ap = b->bb_aka; *ap; ap++)
+           if (strcmp (aka, *ap) == 0)
+               goto hit;
+hit: ;
+    BBload = 1;
+    endbbent ();
+
+    if (b != NULL)
+       BBread ();
+
+    return b;
+}
+
+
+static void
+BBread (void)
+{
+    register int i;
+    register char *cp, *dp, *p, *r;
+    char prf[BUFSIZ];
+    static char line[BUFSIZ];
+    register FILE * info;
+
+    if (BBflags & SB_FAST)
+       return;
+
+    p = strchr(bb->bb_request, '@');
+    r = strchr(bb->bb_addr, '@');
+    BBRequest[0] = 0;
+
+    if (*bb->bb_request == '-')
+       if (p == NULL && r && *r == '@')
+           snprintf (BBRequest, sizeof(BBRequest), "%s%s%s", bb->bb_name, bb->bb_request, r);
+       else
+           snprintf (BBRequest, sizeof(BBRequest), "%s%s", bb->bb_name, bb->bb_request);
+    else
+       if (p == NULL && r && *r == '@' && *bb->bb_request)
+           snprintf (BBRequest, sizeof(BBRequest), "%s%s", bb->bb_request, r);
+
+    if (BBRequest[0])
+       bb->bb_request = BBRequest;
+    else
+       if (*bb->bb_request == 0)
+           bb->bb_request = *bb->bb_addr ? bb->bb_addr
+               : bb->bb_leader[0];
+
+    if (*bb->bb_addr == '@') {
+       snprintf (BBAddr, sizeof(BBAddr), "%s%s", bb->bb_name, bb->bb_addr);
+       bb->bb_addr = BBAddr;
+    }
+    else
+       if (*bb->bb_addr == 0)
+           bb->bb_addr = bb->bb_name;
+
+    if (*bb->bb_file == 0)
+       return;
+    if (*bb->bb_file != '/') {
+       snprintf (BBFile, sizeof(BBFile), "%s/%s", BBDir, bb->bb_file);
+       bb->bb_file = BBFile;
+    }
+
+    if ((cp = strrchr(bb->bb_file, '/')) == NULL || *++cp == 0) {
+       strcpy (prf, "");
+       cp = bb->bb_file;
+    } else {
+       snprintf (prf, sizeof(prf), "%.*s", cp - bb->bb_file, bb->bb_file);
+    }
+    if ((dp = strchr(cp, '.')) == NULL)
+       dp = cp + strlen (cp);
+
+    snprintf (BBArchive, sizeof(BBArchive), "%s%s/%s", prf, ARCHIVE, cp);
+    bb->bb_archive = BBArchive;
+    snprintf (BBInfo, sizeof(BBInfo), "%s.%.*s%s", prf, dp - cp, cp, CNTFILE);
+    bb->bb_info = BBInfo;
+    snprintf (BBMap, sizeof(BBMap), "%s.%.*s%s", prf, dp - cp, cp, MAPFILE);
+    bb->bb_map = BBMap;
+
+    if ((info = fopen (bb->bb_info, "r")) == NULL)
+       return;
+
+    if (fgets (line, sizeof line, info) && (i = atoi (line)) > 0)
+       bb->bb_maxima = (unsigned) i;
+    if (!feof (info) && fgets (line, sizeof line, info)) {
+       strncpy (BBDate, line, sizeof(BBData));
+       if ((cp = strchr(BBDate, NEWLINE)))
+           *cp = 0;
+       bb->bb_date = BBDate;
+    }
+
+    fclose (info);
+}
+
+
+int
+ldrbb (struct bboard *b)
+{
+    register char *p, **q, **r;
+    static uid_t uid = 0;
+    static gid_t gid = 0;
+    static char username[10] = "";
+    register struct passwd *pw;
+    register struct group  *gr;
+
+    if (b == NULL)
+       return 0;
+    if (BBuid == -1 && !setbbaux (BBOARDS, BBDB))
+       return 0;
+
+    if (username[0] == 0) {
+       if ((pw = getpwuid (uid = getuid ())) == NULL)
+           return 0;
+       gid = getgid ();
+       strncpy (username, pw->pw_name, sizeof(username));
+    }
+
+    if (uid == BBuid)
+       return 1;
+
+    q = b->bb_leader;
+    while ((p = *q++))
+       if (*p == '=') {
+           if ((gr = getgrnam (++p)) == NULL)
+               continue;
+           if (gid == gr->gr_gid)
+               return 1;
+           r = gr->gr_mem;
+           while ((p = *r++))
+               if (strcmp (username, p) == 0)
+                   return 1;
+       }
+       else
+           if (strcmp (username, p) == 0)
+               return 1;
+
+    return 0;
+}
+
+
+int
+ldrchk (struct bboard *b)
+{
+    if (b == NULL)
+       return 0;
+
+    if (*b->bb_passwd == 0)
+       return 1;
+
+    if (strcmp (b->bb_passwd,
+               crypt (getpass ("Password: "), b->bb_passwd)) == 0)
+       return 1;
+
+    fprintf (stderr, "Sorry\n");
+    return 0;
+}
+
+
+struct bboard *
+getbbcpy (struct bboard *bp)
+{
+    register char **p, **q;
+    register struct bboard *b;
+
+    if (bp == NULL)
+       return NULL;
+
+    b = (struct bboard *) malloc ((unsigned) sizeof *b);
+    if (b == NULL)
+       return NULL;
+
+    b->bb_name = getcpy (bp->bb_name);
+    b->bb_file = getcpy (bp->bb_file);
+    b->bb_archive = getcpy (bp->bb_archive);
+    b->bb_info = getcpy (bp->bb_info);
+    b->bb_map = getcpy (bp->bb_map);
+    b->bb_passwd = getcpy (bp->bb_passwd);
+    b->bb_flags = bp->bb_flags;
+    b->bb_count = bp->bb_count;
+    b->bb_maxima = bp->bb_maxima;
+    b->bb_date = getcpy (bp->bb_date);
+    b->bb_addr = getcpy (bp->bb_addr);
+    b->bb_request = getcpy (bp->bb_request);
+    b->bb_relay = getcpy (bp->bb_relay);
+
+    for (p = bp->bb_aka; *p; p++)
+       continue;
+    b->bb_aka =
+       q = (char **) calloc ((unsigned) (p - bp->bb_aka + 1), sizeof *q);
+    if (q == NULL)
+       return NULL;
+    for (p = bp->bb_aka; *p; *q++ = getcpy (*p++))
+       continue;
+    *q = NULL;
+
+    for (p = bp->bb_leader; *p; p++)
+       continue;
+    b->bb_leader =
+       q = (char **) calloc ((unsigned) (p - bp->bb_leader + 1), sizeof *q);
+    if (q == NULL)
+       return NULL;
+    for (p = bp->bb_leader; *p; *q++ = getcpy (*p++))
+       continue;
+    *q = NULL;
+
+    for (p = bp->bb_dist; *p; p++)
+       continue;
+    b->bb_dist = 
+       q = (char **) calloc ((unsigned) (p - bp->bb_dist + 1), sizeof *q);
+    if (q == NULL)
+       return NULL;
+    for (p = bp->bb_dist; *p; *q++ = getcpy (*p++))
+       continue;
+    *q = NULL;
+
+    b->bb_next = bp->bb_next;
+    b->bb_link = bp->bb_link;
+    b->bb_chain = bp->bb_chain;
+
+    return b;
+}
+
+
+int
+getbbdist (struct bboard  *bb, int (*action)())
+{
+    register int result;
+    register char **dp;
+
+    BBErrors[0] = 0;
+    for (dp = bb->bb_dist; *dp; dp++)
+       if ((result = getbbitem (bb, *dp, action)))
+           return result;
+
+    return result;
+}
+
+char *
+getbberr (void)
+{
+    return (BBErrors[0] ? BBErrors : NULL);
+}
+
+
+static int
+getbbitem (struct bboard *bb, char *item, int (*action)())
+{
+    register int result;
+    register char *cp, *dp, *hp, *np;
+    char mbox[BUFSIZ],
+         buffer[BUFSIZ],
+         file[BUFSIZ],
+         host[BUFSIZ],
+         prf[BUFSIZ];
+    register FILE *fp;
+
+    switch (*item) {
+       case '*': 
+           switch (*++item) {
+               case '/': 
+                   hp = item;
+                   break;
+
+               case 0: 
+                   if ((cp = strrchr(bb->bb_file, '/')) == NULL || *++cp == 0) {
+                       strcpy (prf, "");
+                       cp = bb->bb_file;
+                   } else {
+                       snprintf (prf, sizeof(prf), "%.*s", cp - bb->bb_file, bb->bb_file);
+                   }
+                   if ((dp = strchr(cp, '.')) == NULL)
+                       dp = cp + strlen (cp);
+                   snprintf (file, sizeof(file), "%s.%.*s%s", prf, dp - cp, cp, DSTFILE);
+                   hp = file;
+                   break;
+
+               default: 
+                   snprintf (file, sizeof(file), "%s/%s", BBDir, item);
+                   hp = file;
+                   break;
+           }
+
+           if ((fp = fopen (hp, "r")) == NULL)
+               return bblose ("unable to read file %s", hp);
+           while (fgets (buffer, sizeof buffer, fp)) {
+               if ((np = strchr(buffer, '\n')))
+                   *np = 0;
+               if ((result = getbbitem (bb, buffer, action))) {
+                   fclose (fp);
+                   bblose ("error with file %s, item %s", hp, buffer);
+                   return result;
+               }
+           }
+           fclose (fp);
+           return OK;
+
+       default: 
+           if ((hp = strrchr(item, '@'))) {
+               *hp++ = 0;
+               strncpy (mbox, item, sizeof(mbox));
+               strncpy (host, hp, sizeof(host));
+               *--hp = '@';
+           }
+           else {
+               snprintf (mbox, sizeof(mbox), "%s%s", DISTADR, bb->bb_name);
+               strncpy (host, item, sizeof(host));
+           }
+           if ((result = (*action) (mbox, host)))
+               bblose ("action (%s, %s) returned 0%o", mbox, host, result);
+           return result;
+    }
+}
+
+
+static int
+bblose (char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    if (BBErrors[0] == 0)
+       vsnprintf (BBErrors, sizeof(BBErrors), fmt, ap);
+
+    va_end(ap);
+    return NOTOK;
+}
+
+
+void
+make_lower (char *s1, char *s2)
+{
+    if (!s1 || !s2)
+       return;
+
+    for (; *s2; s2++)
+       *s1++ = isupper (*s2) ? tolower (*s2) : *s2;
+    *s1 = 0;
+}
+
+
+static char *
+bbskip (char *p, char c)
+{
+    if (p == NULL)
+       return NULL;
+
+    while (*p && *p != c)
+       p++;
+    if (*p)
+       *p++ = 0;
+
+    return p;
+}
+
+
+static char *
+getcpy (char *s)
+{
+    register char *p;
+    size_t len;
+
+    if (s == NULL)
+       return NULL;
+
+    len = strlen (s) + 1;
+    if ((p = malloc (len)))
+       memcpy (p, s, len);
+    return p;
+}
+
diff --git a/zotnet/mf/Makefile.in b/zotnet/mf/Makefile.in
new file mode 100644 (file)
index 0000000..7871ca7
--- /dev/null
@@ -0,0 +1,76 @@
+#
+# Makefile for zotnet/mf subdirectory
+#
+# $Id$
+#
+
+SHELL = /bin/sh
+
+top_srcdir = @top_srcdir@
+srcdir     = @srcdir@
+VPATH      = @srcdir@
+
+CC       = @CC@
+CFLAGS   = @CFLAGS@
+DEFS     = @DEFS@
+INCLUDES = -I../.. -I$(srcdir) -I$(top_srcdir)
+
+COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CFLAGS)
+
+.SUFFIXES:
+.SUFFIXES: .c .o
+
+.c.o:
+       $(COMPILE) $<
+
+# header files
+HDRS = mf.h
+
+# source files
+SRCS = mf.c
+
+# object files
+OBJS = mf.o
+
+# auxiliary files
+AUX = Makefile.in
+
+# all files in this directory included in the distribution
+DIST = $(HDRS) $(SRCS) $(AUX)
+
+# ========= DEPENDENCIES FOR BUILDING ==========
+
+all: $(OBJS)
+
+install:
+
+uninstall:
+
+# ========== DEPENDENCIES FOR CLEANUP ==========
+
+mostlyclean:
+       rm -f *.o *~
+
+clean: mostlyclean
+
+distclean: clean
+       rm -f Makefile
+
+realclean: distclean
+
+superclean: realclean
+
+# ========== DEPENDENCIES FOR MAINTENANCE ==========
+
+subdir = zotnet/mf
+
+Makefile: Makefile.in ../../config.status
+       cd ../.. && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+distdir = ../../`cat ../../distname`/$(subdir)
+nmhdist: $(DIST)
+       @echo "Copying distribution files in $(subdir)"
+       @for file in $(DIST); do \
+         cp -p $(srcdir)/$$file $(distdir); \
+       done
+
diff --git a/zotnet/mf/mf.c b/zotnet/mf/mf.c
new file mode 100644 (file)
index 0000000..b129785
--- /dev/null
@@ -0,0 +1,963 @@
+
+/*
+ * mf.c -- mail filter subroutines
+ *
+ * $Id$
+ */
+
+#include <mf.h>
+#include <ctype.h>
+#include <stdio.h>
+
+/*
+ * static prototypes
+ */
+static char *getcpy (char *);
+static char *add (char *, char *);
+static void compress (char *, char *);
+static int isat (char *);
+static int parse_address (void);
+static int phrase (char *);
+static int route_addr (char *);
+static int local_part (char *);
+static int domain (char *);
+static int route (char *);
+static int my_lex (char *);
+
+
+static char *
+getcpy (char *s)
+{
+    register char *p;
+
+    if (!s) {
+       _cleanup();
+       abort();
+       for(;;)
+           pause();
+    }
+    if ((p = malloc ((size_t) (strlen (s) + 2))))
+       strcpy (p, s);
+    return p;
+}
+
+
+static char *
+add (char *s1, char *s2)
+{
+    register char *p;
+
+    if (!s2)
+       return getcpy (s1);
+
+    if ((p = malloc ((size_t) (strlen (s1) + strlen (s2) + 2))))
+       sprintf (p, "%s%s", s2, s1);
+    free (s2);
+    return p;
+}
+
+int
+isfrom(char *string)
+{
+    return (strncmp (string, "From ", 5) == 0
+           || strncmp (string, ">From ", 6) == 0);
+}
+
+
+int
+lequal (char *a, char *b)
+{
+    for (; *a; a++, b++)
+       if (*b == 0)
+           return FALSE;
+       else {
+           char c1 = islower (*a) ? toupper (*a) : *a;
+           char c2 = islower (*b) ? toupper (*b) : *b;
+           if (c1 != c2)
+               return FALSE;
+       }
+
+    return (*b == 0);
+}
+
+
+/* 
+ * seekadrx() is tricky.  We want to cover both UUCP-style and ARPA-style
+ * addresses, so for each list of addresses we see if we can find some
+ * character to give us a hint.
+ */
+
+
+#define        CHKADR  0               /* undertermined address style */
+#define        UNIXDR  1               /* UNIX-style address */
+#define        ARPADR  2               /* ARPAnet-style address */
+
+
+static char *punctuators = ";<>.()[]";
+static char *vp = NULL;
+static char *tp = NULL;
+
+static struct adrx adrxs1;
+
+
+struct adrx *
+seekadrx (char *addrs)
+{
+    static int state = CHKADR;
+    register char *cp;
+    register struct adrx *adrxp;
+
+    if (state == CHKADR)
+       for (state = UNIXDR, cp = addrs; *cp; cp++)
+           if (strchr(punctuators, *cp)) {
+               state = ARPADR;
+               break;
+           }
+
+    switch (state) {
+       case UNIXDR: 
+           adrxp = uucpadrx (addrs);
+           break;
+
+       case ARPADR: 
+       default:
+           adrxp = getadrx (addrs);
+           break;
+    }
+
+    if (adrxp == NULL)
+       state = CHKADR;
+
+    return adrxp;
+}
+
+
+/*
+ * uucpadrx() implements a partial UUCP-style address parser.  It's based
+ * on the UUCP notion that addresses are separated by spaces or commas.
+ */
+
+
+struct adrx *
+uucpadrx (char *addrs)
+{
+    register char *cp, *wp, *xp, *yp, *zp;
+    register struct adrx *adrxp = &adrxs1;
+
+    if (vp == NULL) {
+       vp = tp = getcpy (addrs);
+       compress (addrs, vp);
+    }
+    else
+       if (tp == NULL) {
+           free (vp);
+           vp = NULL;
+           return NULL;
+       }
+
+    for (cp = tp; isspace (*cp); cp++)
+       continue;
+    if (*cp == 0) {
+       free (vp);
+       vp = tp = NULL;
+       return NULL;
+    }
+
+    if ((wp = strchr(cp, ',')) == NULL)
+       if ((wp = strchr(cp, ' ')) != NULL) {
+           xp = wp;
+           while (isspace (*xp))
+               xp++;
+           if (*xp != 0 && isat (--xp)) {
+               yp = xp + 4;
+               while (isspace (*yp))
+                   yp++;
+               if (*yp != 0)
+                   if ((zp = strchr(yp, ' ')) != NULL)
+                       *zp = 0, tp = ++zp;
+                   else
+                       tp = NULL;
+               else
+                   *wp = 0, tp = ++wp;
+           }
+           else
+               *wp = 0, tp = ++wp;
+       }
+       else
+           tp = NULL;
+    else
+       *wp = 0, tp = ++wp;
+
+    if (adrxp->text)
+       free (adrxp->text);
+    adrxp->text = getcpy (cp);
+    adrxp->mbox = cp;
+    adrxp->host = adrxp->path = NULL;
+    if ((wp = strrchr(cp, '@')) != NULL) {
+       *wp++ = 0;
+       adrxp->host = *wp ? wp : NULL;
+    }
+    else
+       for (wp = cp + strlen (cp) - 4; wp >= cp; wp--)
+           if (isat (wp)) {
+               *wp++ = 0;
+               adrxp->host = wp + 3;
+           }
+
+    adrxp->pers = adrxp->grp = adrxp->note = adrxp->err = NULL;
+    adrxp->ingrp = 0;
+
+    return adrxp;
+}
+
+
+static void
+compress (char *fp, char *tp)
+{
+    register char c, *cp;
+
+    for (c = ' ', cp = tp; (*tp = *fp++) != 0;)
+       if (isspace (*tp)) {
+           if (c != ' ')
+               *tp++ = c = ' ';
+       }
+       else
+           c = *tp++;
+
+    if (c == ' ' && cp < tp)
+       *--tp = 0;
+}
+
+
+static int
+isat (char *p)
+{
+    return (strncmp (p, " AT ", 4)
+           && strncmp (p, " At ", 4)
+           && strncmp (p, " aT ", 4)
+           && strncmp (p, " at ", 4) ? FALSE : TRUE);
+}
+
+
+/*
+ *
+ * getadrx() implements a partial 822-style address parser.  The parser
+ * is neither complete nor correct.  It does however recognize nearly all
+ * of the 822 address syntax.  In addition it handles the majority of the
+ * 733 syntax as well.  Most problems arise from trying to accomodate both.
+ *
+ * In terms of 822, the route-specification in 
+ *
+ *              "<" [route] local-part "@" domain ">"
+ *
+ * is parsed and returned unchanged.  Multiple at-signs are compressed
+ * via source-routing.  Recursive groups are not allowed as per the 
+ * standard.
+ *
+ * In terms of 733, " at " is recognized as equivalent to "@".
+ *
+ * In terms of both the parser will not complain about missing hosts.
+ *
+ * -----
+ *
+ * We should not allow addresses like  
+ *
+ *             Marshall T. Rose <MRose@UCI>
+ *
+ * but should insist on
+ *
+ *             "Marshall T. Rose" <MRose@UCI>
+ *
+ * Unfortunately, a lot of mailers stupidly let people get away with this.
+ *
+ * -----
+ *
+ * We should not allow addresses like
+ *
+ *             <MRose@UCI>
+ *
+ * but should insist on
+ *
+ *             MRose@UCI
+ *
+ * Unfortunately, a lot of mailers stupidly let people's UAs get away with
+ * this.
+ *
+ * -----
+ *
+ * We should not allow addresses like
+ *
+ *             @UCI:MRose@UCI-750a
+ *
+ * but should insist on
+ *
+ *             Marshall Rose <@UCI:MRose@UCI-750a>
+ *
+ * Unfortunately, a lot of mailers stupidly do this.
+ *
+ */
+
+#define        QUOTE   '\\'
+
+#define        LX_END   0
+#define        LX_ERR   1
+#define        LX_ATOM  2
+#define        LX_QSTR  3
+#define        LX_DLIT  4
+#define        LX_SEMI  5
+#define        LX_COMA  6
+#define        LX_LBRK  7
+#define        LX_RBRK  8
+#define        LX_COLN  9
+#define        LX_DOT  10
+#define        LX_AT   11
+
+struct specials {
+    char lx_chr;
+    int  lx_val;
+};
+
+static struct specials special[] = {
+    { ';',   LX_SEMI },
+    { ',',   LX_COMA },
+    { '<',   LX_LBRK },
+    { '>',   LX_RBRK },
+    { ':',   LX_COLN },
+    { '.',   LX_DOT },
+    { '@',   LX_AT },
+    { '(',   LX_ERR },
+    { ')',   LX_ERR },
+    { QUOTE, LX_ERR },
+    { '"',   LX_ERR },
+    { '[',   LX_ERR },
+    { ']',   LX_ERR },
+    { 0,     0 }
+};
+
+static int glevel = 0;
+static int ingrp = 0;
+static int last_lex = LX_END;
+
+static char *dp = NULL;
+static char *cp = NULL;
+static char *ap = NULL;
+static char *pers = NULL;
+static char *mbox = NULL;
+static char *host = NULL;
+static char *path = NULL;
+static char *grp = NULL;
+static char *note = NULL;
+static char err[BUFSIZ];
+static char adr[BUFSIZ];
+
+static struct adrx  adrxs2;
+
+
+struct adrx *
+getadrx (char *addrs)
+{
+    register char *bp;
+    register struct adrx *adrxp = &adrxs2;
+
+    if (pers)
+       free (pers);
+    if (mbox)
+       free (mbox);
+    if (host)
+       free (host);
+    if (path)
+       free (path);
+    if (grp)
+       free (grp);
+    if (note)
+       free (note);
+    pers = mbox = host = path = grp = note = NULL;
+    err[0] = 0;
+
+    if (dp == NULL) {
+       dp = cp = getcpy (addrs ? addrs : "");
+       glevel = 0;
+    }
+    else
+       if (cp == NULL) {
+           free (dp);
+           dp = NULL;
+           return NULL;
+       }
+
+    switch (parse_address ()) {
+       case DONE:
+           free (dp);
+           dp = cp = NULL;
+           return NULL;
+
+       case OK:
+           switch (last_lex) {
+               case LX_COMA:
+               case LX_END:
+                   break;
+
+               default:        /* catch trailing comments */
+                   bp = cp;
+                   my_lex (adr);
+                   cp = bp;
+                   break;
+           }
+           break;
+
+       default:
+           break;
+       }
+
+    if (err[0])
+       for (;;) {
+           switch (last_lex) {
+               case LX_COMA: 
+               case LX_END: 
+                   break;
+
+               default: 
+                   my_lex (adr);
+                   continue;
+           }
+           break;
+       }
+    while (isspace (*ap))
+       ap++;
+    if (cp)
+       sprintf (adr, "%.*s", cp - ap, ap);
+    else
+       strcpy (adr, ap);
+    bp = adr + strlen (adr) - 1;
+    if (*bp == ',' || *bp == ';' || *bp == '\n')
+       *bp = 0;
+
+    adrxp->text = adr;
+    adrxp->pers = pers;
+    adrxp->mbox = mbox;
+    adrxp->host = host;
+    adrxp->path = path;
+    adrxp->grp = grp;
+    adrxp->ingrp = ingrp;
+    adrxp->note = note;
+    adrxp->err = err[0] ? err : NULL;
+
+    return adrxp;
+}
+
+
+static int
+parse_address (void)
+{
+    char buffer[BUFSIZ];
+
+again: ;
+    ap = cp;
+    switch (my_lex (buffer)) {
+       case LX_ATOM: 
+       case LX_QSTR: 
+           pers = getcpy (buffer);
+           break;
+
+       case LX_SEMI: 
+           if (glevel-- <= 0) {
+               strcpy (err, "extraneous semi-colon");
+               return NOTOK;
+           }
+       case LX_COMA: 
+           if (note) {
+               free (note);
+               note = NULL;
+           }
+           goto again;
+
+       case LX_END: 
+           return DONE;
+
+       case LX_LBRK:           /* sigh (2) */
+           goto get_addr;
+
+       case LX_AT:             /* sigh (3) */
+           cp = ap;
+           if (route_addr (buffer) == NOTOK)
+               return NOTOK;
+           return OK;          /* why be choosy? */
+
+       default: 
+           sprintf (err, "illegal address construct (%s)", buffer);
+           return NOTOK;
+    }
+
+    switch (my_lex (buffer)) {
+       case LX_ATOM: 
+       case LX_QSTR: 
+           pers = add (buffer, add (" ", pers));
+    more_phrase: ;             /* sigh (1) */
+           if (phrase (buffer) == NOTOK)
+               return NOTOK;
+
+           switch (last_lex) {
+               case LX_LBRK: 
+           get_addr: ;
+                   if (route_addr (buffer) == NOTOK)
+                       return NOTOK;
+                   if (last_lex == LX_RBRK)
+                       return OK;
+                   sprintf (err, "missing right-bracket (%s)", buffer);
+                   return NOTOK;
+
+               case LX_COLN: 
+           get_group: ;
+                   if (glevel++ > 0) {
+                       sprintf (err, "nested groups not allowed (%s)", pers);
+                       return NOTOK;
+                   }
+                   grp = add (": ", pers);
+                   pers = NULL;
+                   {
+                       char   *pp = cp;
+
+                       for (;;)
+                           switch (my_lex (buffer)) {
+                               case LX_SEMI: 
+                               case LX_END: /* tsk, tsk */
+                                   glevel--;
+                                   return OK;
+
+                               case LX_COMA: 
+                                   continue;
+
+                               default: 
+                                   cp = pp;
+                                   return parse_address ();
+                           }
+                   }
+
+               case LX_DOT:    /* sigh (1) */
+                   pers = add (".", pers);
+                   goto more_phrase;
+
+               default: 
+                   sprintf (err, "no mailbox in address, only a phrase (%s%s)",
+                           pers, buffer);
+                   return NOTOK;
+           }
+
+       case LX_LBRK: 
+           goto get_addr;
+
+       case LX_COLN: 
+           goto get_group;
+
+       case LX_DOT: 
+           mbox = add (buffer, pers);
+           pers = NULL;
+           if (route_addr (buffer) == NOTOK)
+               return NOTOK;
+           goto check_end;
+
+       case LX_AT: 
+           ingrp = glevel;
+           mbox = pers;
+           pers = NULL;
+           if (domain (buffer) == NOTOK)
+               return NOTOK;
+    check_end: ;
+           switch (last_lex) {
+               case LX_SEMI: 
+                   if (glevel-- <= 0) {
+                       strcpy (err, "extraneous semi-colon");
+                       return NOTOK;
+                   }
+               case LX_COMA: 
+               case LX_END: 
+                   return OK;
+
+               default: 
+                   sprintf (err, "junk after local@domain (%s)", buffer);
+                   return NOTOK;
+           }
+
+       case LX_SEMI:           /* no host */
+       case LX_COMA: 
+       case LX_END: 
+           ingrp = glevel;
+           if (last_lex == LX_SEMI && glevel-- <= 0) {
+               strcpy (err, "extraneous semi-colon");
+               return NOTOK;
+           }
+           mbox = pers;
+           pers = NULL;
+           return OK;
+
+       default: 
+           sprintf (err, "missing mailbox (%s)", buffer);
+           return NOTOK;
+    }
+}
+
+
+static int
+phrase (char *buffer)
+{
+    for (;;)
+       switch (my_lex (buffer)) {
+           case LX_ATOM: 
+           case LX_QSTR: 
+               pers = add (buffer, add (" ", pers));
+               continue;
+
+           default: 
+               return OK;
+       }
+}
+
+
+static int
+route_addr (char *buffer)
+{
+    register char *pp = cp;
+
+    if (my_lex (buffer) == LX_AT) {
+       if (route (buffer) == NOTOK)
+           return NOTOK;
+    }
+    else
+       cp = pp;
+
+    if (local_part (buffer) == NOTOK)
+       return NOTOK;
+
+    switch (last_lex) {
+       case LX_AT: 
+           return domain (buffer);
+
+       case LX_SEMI:   /* if in group */
+       case LX_RBRK:           /* no host */
+       case LX_COMA:
+       case LX_END: 
+           return OK;
+
+       default: 
+           sprintf (err, "no at-sign after local-part (%s)", buffer);
+           return NOTOK;
+    }
+}
+
+
+static int
+local_part (char *buffer)
+{
+    ingrp = glevel;
+
+    for (;;) {
+       switch (my_lex (buffer)) {
+           case LX_ATOM: 
+           case LX_QSTR: 
+               mbox = add (buffer, mbox);
+               break;
+
+           default: 
+               sprintf (err, "no mailbox in local-part (%s)", buffer);
+               return NOTOK;
+       }
+
+       switch (my_lex (buffer)) {
+           case LX_DOT: 
+               mbox = add (buffer, mbox);
+               continue;
+
+           default: 
+               return OK;
+       }
+    }
+}
+
+
+static int
+domain (char *buffer)
+{
+    for (;;) {
+       switch (my_lex (buffer)) {
+           case LX_ATOM: 
+           case LX_DLIT: 
+               host = add (buffer, host);
+               break;
+
+           default: 
+               sprintf (err, "no sub-domain in domain-part of address (%s)", buffer);
+               return NOTOK;
+       }
+
+       switch (my_lex (buffer)) {
+           case LX_DOT: 
+               host = add (buffer, host);
+               continue;
+
+           case LX_AT:         /* sigh (0) */
+               mbox = add (host, add ("%", mbox));
+               free (host);
+               host = NULL;
+               continue;
+
+           default: 
+               return OK;
+       }
+    }
+}
+
+
+static int
+route (char *buffer)
+{
+    path = getcpy ("@");
+
+    for (;;) {
+       switch (my_lex (buffer)) {
+           case LX_ATOM: 
+           case LX_DLIT: 
+               path = add (buffer, path);
+               break;
+
+           default: 
+               sprintf (err, "no sub-domain in domain-part of address (%s)", buffer);
+               return NOTOK;
+       }
+       switch (my_lex (buffer)) {
+           case LX_COMA: 
+               path = add (buffer, path);
+               for (;;) {
+                   switch (my_lex (buffer)) {
+                       case LX_COMA: 
+                           continue;
+
+                       case LX_AT: 
+                           path = add (buffer, path);
+                           break;
+
+                       default: 
+                           sprintf (err, "no at-sign found for next domain in route (%s)",
+                                    buffer);
+                   }
+                   break;
+               }
+               continue;
+
+           case LX_AT:         /* XXX */
+           case LX_DOT: 
+               path = add (buffer, path);
+               continue;
+
+           case LX_COLN: 
+               path = add (buffer, path);
+               return OK;
+
+           default: 
+               sprintf (err, "no colon found to terminate route (%s)", buffer);
+               return NOTOK;
+       }
+    }
+}
+
+
+static int
+my_lex (char *buffer)
+{
+    int i, gotat = 0;
+    register char c, *bp;
+
+    bp = buffer;
+    *bp = 0;
+    if (!cp)
+       return (last_lex = LX_END);
+
+    gotat = isat (cp);
+    c = *cp++;
+    while (isspace (c))
+       c = *cp++;
+    if (c == 0) {
+       cp = NULL;
+       return (last_lex = LX_END);
+    }
+
+    if (c == '(')
+       for (*bp++ = c, i = 0;;)
+           switch (c = *cp++) {
+               case 0: 
+                   cp = NULL;
+                   return (last_lex = LX_ERR);
+               case QUOTE: 
+                   *bp++ = c;
+                   if ((c = *cp++) == 0) {
+                       cp = NULL;
+                       return (last_lex = LX_ERR);
+                   }
+                   *bp++ = c;
+                   continue;
+               case '(': 
+                   i++;
+               default: 
+                   *bp++ = c;
+                   continue;
+               case ')': 
+                   *bp++ = c;
+                   if (--i < 0) {
+                       *bp = 0;
+                       note = note ? add (buffer, add (" ", note))
+                           : getcpy (buffer);
+                       return my_lex (buffer);
+                   }
+           }
+
+    if (c == '"')
+       for (*bp++ = c;;)
+           switch (c = *cp++) {
+               case 0: 
+                   cp = NULL;
+                   return (last_lex = LX_ERR);
+               case QUOTE: 
+                   *bp++ = c;
+                   if ((c = *cp++) == 0) {
+                       cp = NULL;
+                       return (last_lex = LX_ERR);
+                   }
+               default: 
+                   *bp++ = c;
+                   continue;
+               case '"': 
+                   *bp++ = c;
+                   *bp = 0;
+                   return (last_lex = LX_QSTR);
+           }
+
+    if (c == '[')
+       for (*bp++ = c;;)
+           switch (c = *cp++) {
+               case 0: 
+                   cp = NULL;
+                   return (last_lex = LX_ERR);
+               case QUOTE: 
+                   *bp++ = c;
+                   if ((c = *cp++) == 0) {
+                       cp = NULL;
+                       return (last_lex = LX_ERR);
+                   }
+               default: 
+                   *bp++ = c;
+                   continue;
+               case ']': 
+                   *bp++ = c;
+                   *bp = 0;
+                   return (last_lex = LX_DLIT);
+           }
+
+    *bp++ = c;
+    *bp = 0;
+    for (i = 0; special[i].lx_chr != 0; i++)
+       if (c == special[i].lx_chr)
+           return (last_lex = special[i].lx_val);
+
+    if (iscntrl (c))
+       return (last_lex = LX_ERR);
+
+    for (;;) {
+       if ((c = *cp++) == 0)
+           break;
+       for (i = 0; special[i].lx_chr != 0; i++)
+           if (c == special[i].lx_chr)
+               goto got_atom;
+       if (iscntrl (c) || isspace (c))
+           break;
+       *bp++ = c;
+    }
+got_atom: ;
+    if (c == 0)
+       cp = NULL;
+    else
+       cp--;
+    *bp = 0;
+    last_lex = !gotat || cp == NULL || strchr(cp, '<') != NULL
+       ? LX_ATOM : LX_AT;
+    return last_lex;
+}
+
+
+char *
+legal_person (char *p)
+{
+    int i;
+    register char *cp;
+    static char buffer[BUFSIZ];
+
+    if (*p == '"')
+       return p;
+    for (cp = p; *cp; cp++)
+       for (i = 0; special[i].lx_chr; i++)
+           if (*cp == special[i].lx_chr) {
+               sprintf (buffer, "\"%s\"", p);
+               return buffer;
+           }
+
+    return p;
+}
+
+
+int
+mfgets (FILE *in, char **bp)
+{
+    int i;
+    register char *cp, *dp, *ep;
+    static int len = 0;
+    static char *pp = NULL;
+
+    if (pp == NULL)
+       if (!(pp = malloc ((size_t) (len = BUFSIZ))))
+           return NOTOK;
+
+    for (ep = (cp = pp) + len - 2;;) {
+       switch (i = getc (in)) {
+           case EOF: 
+       eol:    ;
+               if (cp != pp) {
+                   *cp = 0;
+                   *bp = pp;
+                   return OK;
+               }
+       eoh:    ;
+               *bp = NULL;
+               free (pp);
+               pp = NULL;
+               return DONE;
+
+           case 0: 
+               continue;
+
+           case '\n': 
+               if (cp == pp)   /* end of headers, gobble it */
+                   goto eoh;
+               switch (i = getc (in)) {
+                   default:    /* end of line */
+                   case '\n':  /* end of headers, save for next call */
+                       ungetc (i, in);
+                       goto eol;
+
+                   case ' ':   /* continue headers */
+                   case '\t': 
+                       *cp++ = '\n';
+                       break;
+               }               /* fall into default case */
+
+           default: 
+               *cp++ = i;
+               break;
+       }
+       if (cp >= ep)
+           if (!(dp = realloc (pp, (size_t) (len += BUFSIZ)))) {
+               free (pp);
+               pp = NULL;
+               return NOTOK;
+           }
+           else
+               cp += dp - pp, ep = (pp = cp) + len - 2;
+    }
+}
diff --git a/zotnet/mf/mf.h b/zotnet/mf/mf.h
new file mode 100644 (file)
index 0000000..a4c81e8
--- /dev/null
@@ -0,0 +1,82 @@
+
+/*
+ * mf.h -- include file for mailbox filters
+ *
+ * $Id$
+ */
+
+#include <h/nmh.h>
+
+#ifndef        TRUE
+# define TRUE 1
+#endif
+
+#ifndef        FALSE
+# define FALSE 0
+#endif
+
+#ifndef        NOTOK
+# define NOTOK (-1)
+#endif
+
+#ifndef        OK
+# define OK 0
+#endif
+
+#ifndef        DONE
+# define DONE 1
+#endif
+
+#define        LINESIZ 512
+
+#define        MBXMODE 0600
+#define        TMPMODE 0600
+
+#define        OWIDTH  75              /* length of a header line */
+
+#define        HFROM   1               /* header has From: component    */
+#define        HSNDR   2               /* header has Sender: component  */
+#define        HADDR   3               /* header has address component  */
+#define        HDATE   4               /* header has Date: component    */
+#define        HOTHR   5               /* header is unimportant         */
+
+
+struct adrx {
+    char *text;
+    char *pers;
+    char *mbox;
+    char *host;
+    char *path;
+    char *grp;
+    int ingrp;
+    char *note;
+    char *err;
+};
+
+
+/* 
+ *    Codes returned by uucp2mmdf(), mmdf2uucp()
+ */
+
+#define        MFOK    0               /* all went well                 */
+ /* remaining codes must > DONE         */
+#define        MFPRM   2               /* bad parameter                 */
+#define        MFSIO   3               /* stdio package went screwy     */
+#define        MFROM   4               /* from line was bad             */
+#define        MFHDR   5               /* headers were bad              */
+#define        MFTXT   6               /* text was bad                  */
+#define        MFERR   7               /* I/O or system error           */
+#define        MFDLM   8               /* Bad delimiter in MMDF file    */
+
+
+/*
+ * prototypes
+ */
+int isfrom(char *);
+int lequal (char *, char *);
+int mfgets (FILE *, char **);
+char *legal_person (char *);
+struct adrx *seekadrx (char *);
+struct adrx *getadrx (char *);
+struct adrx *uucpadrx (char *);
+
diff --git a/zotnet/mts/Makefile.in b/zotnet/mts/Makefile.in
new file mode 100644 (file)
index 0000000..ab8ad65
--- /dev/null
@@ -0,0 +1,94 @@
+#
+# Makefile for zotnet/mts subdirectory
+#
+# $Id$
+#
+
+SHELL = /bin/sh
+
+top_srcdir = @top_srcdir@
+srcdir     = @srcdir@
+VPATH      = @srcdir@
+
+prefix      = @prefix@
+exec_prefix = @exec_prefix@
+bindir      = @bindir@
+libdir      = @libdir@
+etcdir      = @sysconfdir@
+
+mailspool    = @mailspool@
+sendmailpath = @sendmailpath@
+
+CC         = @CC@
+CFLAGS     = @CFLAGS@
+DEFS       = @DEFS@
+KRB4_INCLUDES = @KRB4_INCLUDES@
+HESIOD_INCLUDES = @HESIOD_INCLUDES@
+INCLUDES   = -I../.. -I$(srcdir) -I$(top_srcdir) $(KRB4_INCLUDES) $(HESIOD_INCLUDES)
+CONFIGDEFS = -DNMHETCDIR='"$(etcdir)"' -DMAILSPOOL='"$(mailspool)"' -DSENDMAILPATH='"$(sendmailpath)"'
+
+COMPILE  = $(CC) -c $(DEFS) $(INCLUDES) $(CFLAGS)
+COMPILE2 = $(CC) -c $(DEFS) $(CONFIGDEFS) $(INCLUDES) $(CFLAGS)
+
+SED = sed
+
+.SUFFIXES:
+.SUFFIXES: .c .o
+
+.c.o:
+       $(COMPILE) $<
+
+# header files
+HDRS = mts.h
+
+# source files
+SRCS = mts.c client.c
+
+# object files
+OBJS = mts.o client.o
+
+# auxiliary files
+AUX = Makefile.in
+
+# all files in this directory included in the distribution
+DIST = $(HDRS) $(SRCS) $(AUX)
+
+# ========= DEPENDENCIES FOR BUILDING AND INSTALLING ==========
+
+all: $(OBJS)
+
+mts.o: mts.c
+       $(COMPILE2) $<
+
+install:
+
+uninstall:
+
+# ========== DEPENDENCIES FOR CLEANUP ==========
+
+mostlyclean:
+       rm -f *.o *~
+
+clean: mostlyclean
+
+distclean: clean
+       rm -f Makefile
+
+realclean: distclean
+
+superclean: realclean
+
+# ========== DEPENDENCIES FOR MAINTENANCE ==========
+
+subdir = zotnet/mts
+
+Makefile: Makefile.in ../../config.status
+       cd ../.. && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+distdir = ../../`cat ../../distname`/$(subdir)
+nmhdist: $(DIST)
+       @echo "Copying distribution files in $(subdir)"
+       @for file in $(DIST); do \
+         cp -p $(srcdir)/$$file $(distdir); \
+       done
+
diff --git a/zotnet/mts/client.c b/zotnet/mts/client.c
new file mode 100644 (file)
index 0000000..6d86057
--- /dev/null
@@ -0,0 +1,467 @@
+
+/*
+ * client.c -- connect to a server
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+#include <mts.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+
+#ifdef HESIOD
+# include <hesiod.h>
+#endif
+
+#ifdef KPOP
+# include <krb.h>
+# include <ctype.h>
+#endif /* KPOP */
+
+#define        TRUE         1
+#define        FALSE        0
+
+#define        OOPS1      (-2)
+#define        OOPS2      (-3)
+
+#define        MAXARGS   1000
+#define        MAXNETS      5
+#define        MAXHOSTS    25
+
+extern int errno;
+
+struct addrent {
+    int a_addrtype;            /* assumes AF_INET for inet_netof () */
+    union {
+       int un_net;
+       char un_addr[14];
+    } un;
+};
+
+#define        a_net  un.un_net
+#define        a_addr un.un_addr
+
+static struct addrent *n1, *n2;
+static struct addrent nets[MAXNETS];
+static struct addrent *h1, *h2;
+static struct addrent hosts[MAXHOSTS];
+
+#ifdef KPOP
+static CREDENTIALS cred;
+static MSG_DAT msg_data;
+static KTEXT ticket = (KTEXT) NULL;
+static Key_schedule schedule;
+static char *kservice;                 /* "pop" if using kpop */
+char krb_realm[REALM_SZ];
+char *PrincipalHostname();
+#endif /* KPOP */
+
+#if defined(BIND) && !defined(h_addr)
+# define h_addr h_addr_list[0]
+#endif
+
+#define        inaddr_copy(hp,sin) \
+    memcpy(&((sin)->sin_addr), (hp)->h_addr, (hp)->h_length)
+
+/*
+ * static prototypes
+ */
+static int rcaux (struct servent *, struct hostent *, int, char *, int);
+static int getport (int, int, char *, int);
+static int inet (struct hostent *, int);
+struct hostent *gethostbystring (char *s);
+
+/* client's own static version of several nmh subroutines */
+static char **client_brkstring (char *, char *, char *);
+static int client_brkany (char, char *);
+static char **client_copyip (char **, char **, int);
+static char *client_getcpy (char *);
+
+
+int
+client (char *args, char *protocol, char *service, int rproto,
+               char *response, int len_response)
+{
+    int sd;
+    register char **ap;
+    char *arguments[MAXARGS];
+    register struct hostent *hp;
+    register struct servent *sp;
+#ifndef        BIND
+    register struct netent *np;
+#endif
+
+#ifdef KPOP
+    char *cp;
+
+    kservice = service;
+    if (cp = strchr (service, '/')) {  /* "pop/kpop" */
+       *cp++ = '\0';           /* kservice = "pop" */
+       service = cp;           /* service  = "kpop" */
+    } else {
+       kservice = NULL;        /* not using KERBEROS */
+    }
+#endif /* KPOP */
+    
+
+    if ((sp = getservbyname (service, protocol)) == NULL) {
+#ifdef HESIOD
+       if ((sp = hes_getservbyname (service, protocol)) == NULL) {
+           snprintf (response, len_response, "%s/%s: unknown service", protocol, service);
+           return NOTOK;
+       }
+#else
+       snprintf (response, len_response, "%s/%s: unknown service", protocol, service);
+       return NOTOK;
+#endif
+    }
+
+    ap = arguments;
+    if (args != NULL && *args != 0) {
+       ap = client_copyip (client_brkstring (client_getcpy (args), " ", "\n"),
+               ap, MAXARGS);
+    } else {
+       if (servers != NULL && *servers != 0)
+           ap = client_copyip (client_brkstring (client_getcpy (servers), " ", "\n"),
+               ap, MAXARGS);
+    }
+    if (ap == arguments) {
+       *ap++ = client_getcpy ("localhost");
+       *ap = NULL;
+    }
+
+    n1 = nets;
+    n2 = nets + sizeof(nets) / sizeof(nets[0]);
+
+    h1 = hosts;
+    h2 = hosts + sizeof(hosts) / sizeof(hosts[0]);
+
+    for (ap = arguments; *ap; ap++) {
+       if (**ap == '\01') {
+#ifndef        BIND
+           if ((np = getnetbyname (*ap + 1))) {
+               sethostent (1);
+               while ((hp = gethostent()))
+                   if (np->n_addrtype == hp->h_addrtype
+                           && inet (hp, np->n_net)) {
+                       switch (sd = rcaux (sp, hp, rproto, response, len_response)) {
+                           case NOTOK: 
+                               continue;
+                           case OOPS1: 
+                               break;
+                           case OOPS2: 
+                               return NOTOK;
+
+                           default: 
+                               return sd;
+                       }
+                       break;
+                   }
+           }
+#endif
+           continue;
+       }
+
+       if ((hp = gethostbystring (*ap))) {
+           switch (sd = rcaux (sp, hp, rproto, response, len_response)) {
+               case NOTOK: 
+               case OOPS1: 
+                   break;
+               case OOPS2: 
+                   return NOTOK;
+
+               default: 
+                   return sd;
+           }
+           continue;
+       }
+    }
+
+    strncpy (response, "no servers available", len_response);
+    return NOTOK;
+}
+
+
+static int
+rcaux (struct servent *sp, struct hostent *hp, int rproto,
+               char *response, int len_response)
+{
+    int sd;
+    struct in_addr in;
+    register struct addrent *ap;
+    struct sockaddr_in in_socket;
+    register struct sockaddr_in *isock = &in_socket;
+
+#ifdef KPOP
+    int rem;
+#endif /* KPOP */
+
+    for (ap = nets; ap < n1; ap++)
+       if (ap->a_addrtype == hp->h_addrtype && inet (hp, ap->a_net))
+           return NOTOK;
+
+    for (ap = hosts; ap < h1; ap++)
+       if (ap->a_addrtype == hp->h_addrtype
+               && memcmp(ap->a_addr, hp->h_addr, hp->h_length) == 0)
+           return NOTOK;
+
+    if ((sd = getport (rproto, hp->h_addrtype, response, len_response)) == NOTOK)
+       return OOPS2;
+
+    memset (isock, 0, sizeof(*isock));
+    isock->sin_family = hp->h_addrtype;
+    inaddr_copy (hp, isock);
+    isock->sin_port = sp->s_port;
+
+    if (connect (sd, (struct sockaddr *) isock, sizeof(*isock)) == NOTOK)
+       switch (errno) {
+           case ENETDOWN: 
+           case ENETUNREACH: 
+               close (sd);
+               if (n1 < n2) {
+                   n1->a_addrtype = hp->h_addrtype;
+                   memcpy(&in, hp->h_addr, sizeof(in));
+                   n1->a_net = inet_netof (in);
+                   n1++;
+               }
+               return OOPS1;
+
+           case ETIMEDOUT: 
+           case ECONNREFUSED: 
+           default: 
+               close (sd);
+               if (h1 < h2) {
+                   h1->a_addrtype = hp->h_addrtype;
+                   memcpy(h1->a_addr, hp->h_addr, hp->h_length);
+                   h1++;
+               }
+               return NOTOK;
+       }
+
+#ifdef KPOP
+    if (kservice) {    /* "pop" */
+       char *instance;
+
+       if ((instance = strdup (hp->h_name)) == NULL) {
+           close (sd);
+           strncpy (response, "Out of memory.", len_response);
+           return OOPS2;
+       }
+       ticket = (KTEXT) malloc (sizeof(KTEXT_ST));
+       rem = krb_sendauth (0L, sd, ticket, kservice, instance,
+                          (char *) krb_realmofhost (instance),
+                          (unsigned long) 0, &msg_data, &cred, schedule,
+                          (struct sockaddr_in *) NULL,
+                          (struct sockaddr_in *) NULL,
+                          "KPOPV0.1");
+       free (instance);
+       if (rem != KSUCCESS) {
+           close (sd);
+           strncpy (response, "Post office refused connection: ", len_response);
+           strncat (response, krb_err_txt[rem], len_response - strlen(response));
+           return OOPS2;
+       }
+    }
+#endif /* KPOP */
+
+    return sd;
+}
+
+
+static int
+getport (int rproto, int addrtype, char *response, int len_response)
+{
+    int sd, port;
+    struct sockaddr_in in_socket, *isock;
+
+    isock = &in_socket;
+    if (rproto && addrtype != AF_INET) {
+       snprintf (response, len_response, "reserved ports not supported for af=%d", addrtype);
+       errno = ENOPROTOOPT;
+       return NOTOK;
+    }
+
+    if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == NOTOK) {
+       char *s;
+
+       if ((s = strerror (errno)))
+           snprintf (response, len_response, "unable to create socket: %s", s);
+       else
+           snprintf (response, len_response, "unable to create socket: unknown error");
+       return NOTOK;
+    }
+#ifdef KPOP
+    if (kservice)      /* "pop" */
+       return(sd);
+#endif /* KPOP */
+    if (!rproto)
+       return sd;
+
+    memset(isock, 0, sizeof(*isock));
+    isock->sin_family = addrtype;
+    for (port = IPPORT_RESERVED - 1;;) {
+       isock->sin_port = htons ((unsigned short) port);
+       if (bind (sd, (struct sockaddr *) isock, sizeof(*isock)) != NOTOK)
+           return sd;
+
+       switch (errno) {
+           char *s;
+
+           case EADDRINUSE: 
+           case EADDRNOTAVAIL: 
+               if (--port <= IPPORT_RESERVED / 2) {
+                   strncpy (response, "ports available", len_response);
+                   return NOTOK;
+               }
+               break;
+
+           default: 
+               if ((s = strerror (errno)))
+                   snprintf (response, len_response, "unable to bind socket: %s", s);
+               else
+                   snprintf (response, len_response, "unable to bind socket: unknown error");
+               return NOTOK;
+       }
+    }
+}
+
+
+static int
+inet (struct hostent *hp, int net)
+{
+    struct in_addr in;
+
+    memcpy(&in, hp->h_addr, sizeof(in));
+    return (inet_netof (in) == net);
+}
+
+
+/*
+ * taken from ISODE's compat/internet.c
+ */
+
+static char *empty = NULL;
+
+#ifdef h_addr
+static char *addrs[2] = { NULL };
+#endif
+
+struct hostent *
+gethostbystring (char *s)
+{
+    register struct hostent *h;
+    static struct hostent hs;
+#ifdef DG
+    static struct in_addr iaddr;
+#else
+    static unsigned long iaddr;
+#endif
+
+    iaddr = inet_addr (s);
+#ifdef DG
+    if (iaddr.s_addr == NOTOK && strcmp (s, "255.255.255.255"))
+#else
+    if (((int) iaddr == NOTOK) && strcmp (s, "255.255.255.255"))
+#endif
+       return gethostbyname (s);
+
+    h = &hs;
+    h->h_name = s;
+    h->h_aliases = &empty;
+    h->h_addrtype = AF_INET;
+    h->h_length = sizeof(iaddr);
+#ifdef h_addr
+    h->h_addr_list = addrs;
+    memset(addrs, 0, sizeof(addrs));
+#endif
+    h->h_addr = (char *) &iaddr;
+
+    return h;
+}
+
+
+/*
+ * static copies of three nmh subroutines
+ */
+
+static char *broken[MAXARGS + 1];
+
+static char **
+client_brkstring (char *strg, char *brksep, char *brkterm)
+{
+    register int bi;
+    register char c, *sp;
+
+    sp = strg;
+
+    for (bi = 0; bi < MAXARGS; bi++) {
+       while (client_brkany (c = *sp, brksep))
+           *sp++ = 0;
+       if (!c || client_brkany (c, brkterm)) {
+           *sp = 0;
+           broken[bi] = 0;
+           return broken;
+       }
+
+       broken[bi] = sp;
+       while ((c = *++sp) && !client_brkany (c, brksep) && !client_brkany (c, brkterm))
+           continue;
+    }
+    broken[MAXARGS] = 0;
+
+    return broken;
+}
+
+
+/*
+ * returns 1 if chr in strg, 0 otherwise
+ */
+static int
+client_brkany (char chr, char *strg)
+{
+    register char *sp;
+    if (strg)
+       for (sp = strg; *sp; sp++)
+           if (chr == *sp)
+               return 1;
+    return 0;
+}
+
+
+/*
+ * copy a string array and return pointer to end
+ */
+static char **
+client_copyip (char **p, char **q, int len_q)
+{
+    while (*p && --len_q > 0)
+       *q++ = *p++;
+
+    *q = NULL;
+
+    return q;
+}
+
+
+static char *
+client_getcpy (char *str)
+{
+    char *cp;
+    size_t len;
+
+    len = strlen(str) + 1;
+    if (!(cp = malloc(len)))
+       return NULL;
+
+    memcpy (cp, str, len);
+    return cp;
+}
+
diff --git a/zotnet/mts/mts.c b/zotnet/mts/mts.c
new file mode 100644 (file)
index 0000000..31d96bd
--- /dev/null
@@ -0,0 +1,454 @@
+
+/*
+ * mts.c -- definitions for the mail transport system
+ *
+ * $Id$
+ */
+
+#include <h/nmh.h>
+
+#define nmhetcdir(file) NMHETCDIR#file
+
+#include <ctype.h>
+#include <stdio.h>
+#include <mts.h>
+#include <pwd.h>
+#include <netdb.h>
+
+#ifdef HAVE_SYS_UTSNAME_H
+# include <sys/utsname.h>
+#endif
+
+#define        NOTOK   (-1)
+#define        OK        0
+
+extern int errno;
+
+/*
+ * static prototypes
+ */
+static char *tailor_value (char *);
+static void getuserinfo (void);
+
+/*
+ * *mmdfldir and *uucpldir are the maildrop directories.  If maildrops
+ * are kept in the user's home directory, then these should be empty
+ * strings.  In this case, the appropriate ...lfil array should contain
+ * the name of the file in the user's home directory.  Usually, this is
+ * something like ".mail".
+ */
+
+/*
+ * nmh mail transport interface customization file
+ */
+static char *mtsconf = nmhetcdir(/mts.conf);
+
+static char *localname   = "";
+static char *localdomain = "";
+static char *systemname  = "";
+
+char *mmdfldir = MAILSPOOL;
+char *mmdflfil = "";
+char *uucpldir = "/usr/spool/mail";
+char *uucplfil = "";
+
+char *mmdlm1 = "\001\001\001\001\n";
+char *mmdlm2 = "\001\001\001\001\n";
+
+/* Cache the username and fullname of the user */
+static char username[BUFSIZ];
+static char fullname[BUFSIZ];
+
+/* variables for username masquerading */
+static int MMailids = 0;
+static char *mmailid = "0";
+
+
+/*
+ * MTS specific variables
+ */
+#if defined(SENDMTS) || defined(SMTPMTS)
+char *hostable = nmhetcdir(/hosts);
+char *sendmail = SENDMAILPATH;
+#endif
+
+/*
+ * SMTP/POP stuff
+ */
+char *clientname = NULL;
+char *servers    = "localhost \01localnet";
+char *pophost    = "";
+
+/*
+ * BBoards-specific variables
+ */
+char *bb_domain = "";
+
+
+/*
+ * POP BBoards-specific variables
+ */
+#ifdef BPOP
+char *popbbhost = "";
+char *popbbuser = "";
+char *popbblist = nmhetcdir(/hosts.popbb);
+#endif /* BPOP */
+
+/*
+ * Global MailDelivery file
+ */
+char *maildelivery = nmhetcdir(/maildelivery);
+
+
+/*
+ * Aliasing Facility (doesn't belong here)
+ */
+int Everyone = NOTOK;
+static char *everyone = "-1";
+char *NoShell = "";
+
+/*
+ * Customize the MTS settings for nmh by adjusting
+ * the file mts.conf in the nmh etc directory.
+ */
+
+struct bind {
+    char *keyword;
+    char **value;
+};
+
+static struct bind binds[] = {
+    { "localname", &localname },
+    { "localdomain", &localdomain },
+    { "systemname", &systemname },
+    { "mmdfldir", &mmdfldir },
+    { "mmdflfil", &mmdflfil },
+    { "uucpldir", &uucpldir },
+    { "uucplfil", &uucplfil },
+    { "mmdelim1", &mmdlm1 },
+    { "mmdelim2", &mmdlm2 },
+    { "mmailid", &mmailid },
+
+#if defined(SENDMTS) || defined(SMTPMTS)
+    { "hostable", &hostable },
+#endif
+
+#ifdef SENDMTS
+    { "sendmail", &sendmail },
+#endif
+
+    { "clientname",  &clientname },
+    { "servers", &servers },
+    { "pophost", &pophost },
+    { "bbdomain", &bb_domain },
+
+#ifdef BPOP
+    { "popbbhost", &popbbhost },
+    { "popbbuser", &popbbuser },
+    { "popbblist", &popbblist },
+#endif
+
+#ifdef NNTP
+    { "nntphost", &popbbhost },
+#endif
+
+    { "maildelivery", &maildelivery },
+    { "everyone", &everyone },
+    { "noshell", &NoShell },
+    { NULL, NULL }
+};
+
+
+/*
+ * Read the configuration file for the nmh interface
+ * to the mail transport system (MTS).
+ */
+
+void
+mts_init (char *name)
+{
+    char *bp, *cp, buffer[BUFSIZ];
+    struct bind *b;
+    FILE *fp;
+    static int inited = 0;
+
+    if (inited++ || (fp = fopen (mtsconf, "r")) == NULL)
+       return;
+
+    while (fgets (buffer, sizeof(buffer), fp)) {
+       if (!(cp = strchr(buffer, '\n')))
+           break;
+       *cp = 0;
+       if (*buffer == '#' || *buffer == '\0')
+           continue;
+       if (!(bp = strchr(buffer, ':')))
+           break;
+       *bp++ = 0;
+       while (isspace (*bp))
+           *bp++ = 0;
+
+       for (b = binds; b->keyword; b++)
+           if (!strcmp (buffer, b->keyword))
+               break;
+       if (b->keyword && (cp = tailor_value (bp)))
+           *b->value = cp;
+    }
+
+    fclose (fp);
+    MMailids = atoi (mmailid);
+    Everyone = atoi (everyone);
+}
+
+
+#define        QUOTE   '\\'
+
+/*
+ * Convert escaped values, malloc some new space,
+ * and copy string to malloc'ed memory.
+ */
+
+static char *
+tailor_value (char *s)
+{
+    int i, r;
+    char *bp;
+    char buffer[BUFSIZ];
+    size_t len;
+
+    for (bp = buffer; *s; bp++, s++) {
+       if (*s != QUOTE) {
+           *bp = *s;
+       } else {
+           switch (*++s) {
+               case 'b': *bp = '\b'; break;
+               case 'f': *bp = '\f'; break;
+               case 'n': *bp = '\n'; break;
+               case 't': *bp = '\t'; break;
+
+               case 0: s--;
+               case QUOTE: 
+                   *bp = QUOTE;
+                   break;
+
+               default: 
+                   if (!isdigit (*s)) {
+                       *bp++ = QUOTE;
+                       *bp = *s;
+                   }
+                   r = *s != '0' ? 10 : 8;
+                   for (i = 0; isdigit (*s); s++)
+                       i = i * r + *s - '0';
+                   s--;
+                   *bp = toascii (i);
+                   break;
+           }
+       }
+    }
+    *bp = 0;
+
+    len = strlen (buffer) + 1;
+    if ((bp = malloc (len)))
+       memcpy (bp, buffer, len);
+
+    return bp;
+}
+
+/*
+ * Get the fully qualified name of the local host.
+ */
+
+char *
+LocalName (void)
+{
+    static char buffer[BUFSIZ] = "";
+    struct hostent *hp;
+
+#ifdef HAVE_UNAME
+    struct utsname name;
+#endif
+
+    /* check if we have cached the local name */
+    if (buffer[0])
+       return buffer;
+
+    mts_init ("mts");
+
+    /* check if the mts.conf file specifies a "localname" */
+    if (*localname) {
+       strncpy (buffer, localname, sizeof(buffer));
+    } else {
+#ifdef HAVE_UNAME
+       /* first get our local name */
+       uname (&name);
+       strncpy (buffer, name.nodename, sizeof(buffer));
+#else
+       /* first get our local name */
+       gethostname (buffer, sizeof(buffer));
+#endif
+#ifndef BIND
+       sethostent (1);
+#endif
+       /* now fully qualify our name */
+       if ((hp = gethostbyname (buffer)))
+           strncpy (buffer, hp->h_name, sizeof(buffer));
+    }
+
+    /*
+     * If the mts.conf file specifies a "localdomain",
+     * we append that now.  This should rarely be needed.
+     */
+    if (*localdomain) {
+       strcat (buffer, ".");
+       strcat (buffer, localdomain);
+    }
+
+    return buffer;
+}
+
+
+/*
+ * This is only for UUCP mail.  It gets the hostname
+ * as part of the UUCP "domain".
+ */
+
+char *
+SystemName (void)
+{
+    static char buffer[BUFSIZ] = "";
+
+#ifdef HAVE_UNAME
+    struct utsname name;
+#endif
+
+    /* check if we have cached the system name */
+    if (buffer[0])
+       return buffer;
+
+    mts_init ("mts");
+
+    /* check if mts.conf file specifies a "systemname" */
+    if (*systemname) {
+       strncpy (buffer, systemname, sizeof(buffer));
+       return buffer;
+    }
+
+#ifdef HAVE_UNAME
+    uname (&name);
+    strncpy (buffer, name.nodename, sizeof(buffer));
+#else
+    gethostname (buffer, sizeof(buffer));
+#endif
+
+    return buffer;
+}
+
+
+/*
+ * Get the username of current user
+ */
+
+char *
+getusername (void)
+{
+    if (username[0] == '\0')
+       getuserinfo();
+
+    return username;
+}
+
+
+/*
+ * Get full name of current user (typically from GECOS
+ * field of password file).
+ */
+
+char *
+getfullname (void)
+{
+    if (username[0] == '\0')
+       getuserinfo();
+
+    return fullname;
+}
+
+
+/*
+ * Find the user's username and full name, and cache them.
+ * It also handles mmailid processing (username masquerading)
+ */
+
+static void
+getuserinfo (void)
+{
+    register char *cp, *np;
+    register struct passwd *pw;
+
+#ifdef KPOP
+    uid_t uid;
+
+    uid = getuid ();
+    if (uid == geteuid () && (cp = getenv ("USER")) != NULL
+       && (pw = getpwnam (cp)) != NULL)
+      strncpy (username, cp, sizeof(username));
+    else if ((pw = getpwuid (uid)) == NULL
+            || pw->pw_name == NULL
+            || *pw->pw_name == '\0') {
+#else /* KPOP */
+    if ((pw = getpwuid (getuid ())) == NULL
+           || pw->pw_name == NULL
+           || *pw->pw_name == '\0') {
+#endif /* KPOP */
+
+       strncpy (username, "unknown", sizeof(username));
+       snprintf (fullname, sizeof(fullname), "The Unknown User-ID (%d)",
+               (int) getuid ());
+       return;
+    }
+
+    np = pw->pw_gecos;
+
+    /*
+     * Do mmailid (username masquerading) processing.  The GECOS
+     * field should have the form "Full Name <fakeusername>".
+     */
+#ifndef        GCOS_HACK
+    for (cp = fullname; *np && *np != (MMailids ? '<' : ','); *cp++ = *np++)
+       continue;
+#else
+    for (cp = fullname; *np && *np != (MMailids ? '<' : ','); ) {
+       if (*np == '&') {       /* blech! */
+           strcpy (cp, pw->pw_name);
+           *cp = toupper(*cp);
+           while (*cp)
+               cp++;
+           np++;
+       } else {
+           *cp++ = *np++;
+       }
+    }
+#endif
+
+    *cp = '\0';
+    if (MMailids) {
+       if (*np)
+           np++;
+       for (cp = username; *np && *np != '>'; *cp++ = *np++)
+           continue;
+       *cp = '\0';
+    }
+    if (MMailids == 0 || *np == '\0')
+       strncpy (username, pw->pw_name, sizeof(username));
+
+    if ((cp = getenv ("SIGNATURE")) && *cp)
+       strncpy (fullname, cp, sizeof(fullname));
+
+    if (strchr(fullname, '.')) {               /*  quote any .'s */
+       char tmp[BUFSIZ];
+
+       /* should quote "'s too */
+       snprintf (tmp, sizeof(tmp), "\"%s\"", fullname);
+       strncpy (fullname, tmp, sizeof(fullname));
+    }
+
+    return;
+}
diff --git a/zotnet/mts/mts.h b/zotnet/mts/mts.h
new file mode 100644 (file)
index 0000000..66aa590
--- /dev/null
@@ -0,0 +1,82 @@
+
+/*
+ * mts.h -- definitions for the mail system
+ *
+ * $Id$
+ */
+
+/*
+ * Local and UUCP Host Name
+ */
+char *LocalName(void);
+char *SystemName(void);
+
+/*
+ * Mailboxes
+ */
+extern char *mmdfldir;
+extern char *mmdflfil;
+extern char *uucpldir;
+extern char *uucplfil;
+
+#define        MAILDIR (mmdfldir && *mmdfldir ? mmdfldir : getenv ("HOME"))
+#define        MAILFIL (mmdflfil && *mmdflfil ? mmdflfil : getusername ())
+#define        UUCPDIR (uucpldir && *uucpldir ? uucpldir : getenv ("HOME"))
+#define        UUCPFIL (uucplfil && *uucplfil ? uucplfil : getusername ())
+
+char *getusername(void);
+char *getfullname(void);
+
+/*
+ * Separators
+ */
+extern char *mmdlm1;
+extern char *mmdlm2;
+
+#define        isdlm1(s) (strcmp (s, mmdlm1) == 0)
+#define        isdlm2(s) (strcmp (s, mmdlm2) == 0)
+
+/*
+ * Read mts.conf file
+ */
+void mts_init (char *);
+
+/*
+ * MTS specific variables
+ */
+#if defined(SENDMTS) || defined (SMTPMTS)
+extern char *hostable;
+extern char *sendmail;
+#endif
+
+/*
+ * SMTP/POP stuff
+ */
+extern char *clientname;
+extern char *servers;
+extern char *pophost;
+
+/*
+ * BBoards-specific variables
+ */
+extern char *bb_domain;
+
+/*
+ * POP BBoards-specific variables
+ */
+#ifdef BPOP
+extern char *popbbhost;
+extern char *popbbuser;
+extern char *popbblist;
+#endif /* BPOP */
+
+/*
+ * Global MailDelivery File
+ */
+extern char *maildelivery;
+
+/*
+ * Aliasing Facility (doesn't belong here)
+ */
+extern int Everyone;
+extern char *NoShell;
diff --git a/zotnet/tws/Makefile.in b/zotnet/tws/Makefile.in
new file mode 100644 (file)
index 0000000..3f96711
--- /dev/null
@@ -0,0 +1,99 @@
+#
+# Makefile for zotnet/tws subdirectory
+#
+# $Id$
+#
+
+SHELL = /bin/sh
+
+top_srcdir = @top_srcdir@
+srcdir     = @srcdir@
+VPATH      = @srcdir@
+
+CC         = @CC@
+CFLAGS     = @CFLAGS@
+DEFS       = @DEFS@
+INCLUDES   = -I../.. -I$(srcdir) -I$(top_srcdir)
+
+COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CFLAGS)
+
+AWK = @AWK@
+# LEX = @LEX@
+LEX = lex
+SED = sed
+
+.SUFFIXES:
+.SUFFIXES: .c .o
+
+.c.o:
+       $(COMPILE) $<
+
+# header files
+HDRS = tws.h
+
+# source files
+SRCS = dtime.c lexstring.c
+
+# object files
+OBJS = dtimep.o dtime.o lexstring.o
+
+# auxiliary files
+AUX = Makefile.in dtimep.lex lexedit.sed dtimep.c-lexed
+
+# all files in this directory included in the distribution
+DIST = $(HDRS) $(SRCS) $(AUX)
+
+# ========= DEPENDENCIES FOR BUILDING ==========
+
+all: $(OBJS)
+
+# This will bomb if lex is really flex, so check
+# file and use pre-generated version if necessary
+dtimep.c: $(srcdir)/dtimep.lex $(srcdir)/lexedit.sed
+       $(LEX) -nt $(srcdir)/dtimep.lex | $(SED) -f $(srcdir)/lexedit.sed > $@
+       -@len=`wc -l $@ | $(AWK) ' { print $$1 } '`; \
+       if [ $$len -gt 500 ]; \
+         then exit 0; \
+       else \
+         echo "LEX FAILED: using pre-lexed $@"; \
+         cp $(srcdir)/$@-lexed $@; \
+       fi
+
+# This needs to be generated by lex, not flex
+dtimep.c-lexed: $(srcdir)/dtimep.lex $(srcdir)/lexedit.sed
+       $(LEX) -nt $(srcdir)/dtimep.lex | $(SED) -f $(srcdir)/lexedit.sed > $(srcdir)/$@
+
+install:
+
+uninstall:
+
+# ========== DEPENDENCIES FOR CLEANUP ==========
+
+mostlyclean:
+       rm -f *.o *~
+
+clean: mostlyclean
+       rm -f dtimep.c
+
+distclean: clean
+       rm -f Makefile
+
+realclean: distclean
+       rm -f dtimep.c-lexed
+
+superclean: realclean
+
+# ========== DEPENDENCIES FOR MAINTENANCE ==========
+
+subdir = zotnet/tws
+
+Makefile: Makefile.in ../../config.status
+       cd ../.. && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+distdir = ../../`cat ../../distname`/$(subdir)
+nmhdist: $(DIST)
+       @echo "Copying distribution files in $(subdir)"
+       @for file in $(DIST); do \
+         cp -p $(srcdir)/$$file $(distdir); \
+       done
+
diff --git a/zotnet/tws/dtime.c b/zotnet/tws/dtime.c
new file mode 100644 (file)
index 0000000..7e791f3
--- /dev/null
@@ -0,0 +1,508 @@
+
+/*
+ * dtime.c -- time/date routines
+ *
+ * $Id$
+ */
+
+#include <h/nmh.h>
+#include <tws.h>
+
+#if !defined(HAVE_TM_GMTOFF) && !defined(HAVE_TZSET)
+# include <sys/timeb.h>
+#endif
+
+#ifdef TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#if !defined(HAVE_TM_GMTOFF) && defined(HAVE_TZSET)
+extern int daylight;
+extern long timezone;
+extern char *tzname[];
+#endif
+
+#ifndef        abs
+# define abs(a) (a >= 0 ? a : -a)
+#endif
+
+/*
+ * The number of days in the year, accounting for leap years
+ */
+#define        dysize(y)       \
+       (((y) % 4) ? 365 : (((y) % 100) ? 366 : (((y) % 400) ? 365 : 366)))
+
+char *tw_moty[] = {
+    "Jan", "Feb", "Mar", "Apr",
+    "May", "Jun", "Jul", "Aug",
+    "Sep", "Oct", "Nov", "Dec",
+    NULL
+};
+
+char *tw_dotw[] = {
+    "Sun", "Mon", "Tue",
+    "Wed", "Thu", "Fri",
+    "Sat", NULL
+};
+
+char *tw_ldotw[] = {
+    "Sunday",    "Monday",   "Tuesday",
+    "Wednesday", "Thursday", "Friday",
+    "Saturday",  NULL
+};
+
+struct zone {
+    char *std;
+    char *dst;
+    int shift;
+};
+
+static struct zone zones[] = {
+    { "GMT", "BST", 0 },
+    { "EST", "EDT", -5 },
+    { "CST", "CDT", -6 },
+    { "MST", "MDT", -7 },
+    { "PST", "PDT", -8 },
+#if 0
+/* RFC1123 specifies do not use military TZs */
+    { "A", NULL, -1 },
+    { "B", NULL, -2 },
+    { "C", NULL, -3 },
+    { "D", NULL, -4 },
+    { "E", NULL, -5 },
+    { "F", NULL, -6 },
+    { "G", NULL, -7 },
+    { "H", NULL, -8 },
+    { "I", NULL, -9 },
+    { "K", NULL, -10 },
+    { "L", NULL, -11 },
+    { "M", NULL, -12 },
+    { "N", NULL, 1 },
+#ifndef        HUJI
+    { "O", NULL, 2 },
+#else
+    { "JST", "JDT", 2 },
+#endif
+    { "P", NULL, 3 },
+    { "Q", NULL, 4 },
+    { "R", NULL, 5 },
+    { "S", NULL, 6 },
+    { "T", NULL, 7 },
+    { "U", NULL, 8 },
+    { "V", NULL, 9 },
+    { "W", NULL, 10 },
+    { "X", NULL, 11 },
+    { "Y", NULL, 12 },
+#endif
+    { NULL, NULL, 0 }
+};
+
+static int dmsize[] = {
+    31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+
+/*
+ * Get current time (adjusted for local time
+ * zone and daylight savings time) expressed
+ * as nmh "broken-down" time structure.
+ */
+
+struct tws *
+dlocaltimenow (void)
+{
+    time_t clock;
+
+    time (&clock);
+    return dlocaltime (&clock);
+}
+
+
+/*
+ * Take clock value and return pointer to nmh time structure
+ * containing "broken-down" time.  The time is adjusted for
+ * local time zone and daylight savings time.
+ */
+
+struct tws *
+dlocaltime (time_t *clock)
+{
+    static struct tws tw;
+    struct tm *tm;
+
+#if !defined(HAVE_TM_GMTOFF) && !defined(HAVE_TZSET)
+    struct timeb tb;
+#endif
+
+    if (!clock)
+       return NULL;
+
+    tm = localtime (clock);
+
+    tw.tw_sec  = tm->tm_sec;
+    tw.tw_min  = tm->tm_min;
+    tw.tw_hour = tm->tm_hour;
+    tw.tw_mday = tm->tm_mday;
+    tw.tw_mon  = tm->tm_mon;
+
+    /*
+     * tm_year is always "year - 1900".
+     * So we correct for this.
+     */
+    tw.tw_year = tm->tm_year + 1900;
+    tw.tw_wday = tm->tm_wday;
+    tw.tw_yday = tm->tm_yday;
+
+    tw.tw_flags = TW_NULL;
+    if (tm->tm_isdst)
+       tw.tw_flags |= TW_DST;
+
+#ifdef HAVE_TM_GMTOFF
+    tw.tw_zone = tm->tm_gmtoff / 60;
+    if (tm->tm_isdst)                  /* if DST is in effect */
+       tw.tw_zone -= 60;               /* reset to normal offset */
+#else
+# ifdef HAVE_TZSET
+    tzset();
+    tw.tw_zone = -(timezone / 60);
+# else
+    ftime (&tb);
+    tw.tw_zone = -tb.timezone;
+# endif
+#endif
+
+    tw.tw_flags &= ~TW_SDAY;
+    tw.tw_flags |= TW_SEXP;
+    tw.tw_flags &= ~TW_SZONE;
+    tw.tw_flags |= TW_SZEXP;
+
+    tw.tw_clock = *clock;
+
+    return (&tw);
+}
+
+
+/*
+ * Take clock value and return pointer to nmh time
+ * structure containing "broken-down" time.  Time is
+ * expressed in UTC (Coordinated Universal Time).
+ */
+
+struct tws *
+dgmtime (time_t *clock)
+{
+    static struct tws tw;
+    struct tm *tm;
+
+    if (!clock)
+       return NULL;
+
+    tm = gmtime (clock);
+
+    tw.tw_sec  = tm->tm_sec;
+    tw.tw_min  = tm->tm_min;
+    tw.tw_hour = tm->tm_hour;
+    tw.tw_mday = tm->tm_mday;
+    tw.tw_mon  = tm->tm_mon;
+
+    /*
+     * tm_year is always "year - 1900"
+     * So we correct for this.
+     */
+    tw.tw_year = tm->tm_year + 1900;
+    tw.tw_wday = tm->tm_wday;
+    tw.tw_yday = tm->tm_yday;
+
+    tw.tw_flags = TW_NULL;
+    if (tm->tm_isdst)
+       tw.tw_flags |= TW_DST;
+
+    tw.tw_zone = 0;
+
+    tw.tw_flags &= ~TW_SDAY;
+    tw.tw_flags |= TW_SEXP;
+    tw.tw_flags &= ~TW_SZONE;
+    tw.tw_flags |= TW_SZEXP;
+
+    tw.tw_clock = *clock;
+
+    return (&tw);
+}
+
+
+/*
+ * Using a nmh "broken-down" time structure,
+ * produce a 26-byte date/time string, such as
+ *
+ *     Tue Jan 14 17:49:03 1992\n\0
+ */
+
+char *
+dctime (struct tws *tw)
+{
+    static char buffer[25];
+
+    if (!tw)
+       return NULL;
+
+    snprintf (buffer, sizeof(buffer), "%.3s %.3s %02d %02d:%02d:%02d %.4d\n",
+           tw_dotw[tw->tw_wday], tw_moty[tw->tw_mon], tw->tw_mday,
+           tw->tw_hour, tw->tw_min, tw->tw_sec,
+           tw->tw_year < 100 ? tw->tw_year + 1900 : tw->tw_year);
+
+    return buffer;
+}
+
+
+/*
+ * Produce a date/time string of the form
+ *
+ *     Mon, 16 Jun 1992 15:30:48 -700 (or)
+ *     Mon, 16 Jun 1992 15:30:48 EDT
+ *
+ * for the current time, as specified by rfc822.
+ * The first form is required by rfc1123.
+ */
+
+char *
+dtimenow (int alpha_timezone)
+{
+    time_t clock;
+
+    time (&clock);
+    return dtime (&clock, alpha_timezone);
+}
+
+
+/*
+ * Using a local calendar time value, produce
+ * a date/time string of the form
+ *
+ *     Mon, 16 Jun 1992 15:30:48 -700  (or)
+ *     Mon, 16 Jun 1992 15:30:48 EDT
+ *
+ * as specified by rfc822.  The first form is required
+ * by rfc1123 for outgoing messages.
+ */
+
+char *
+dtime (time_t *clock, int alpha_timezone)
+{
+    if (alpha_timezone)
+       /* use alpha-numeric timezones */
+       return dasctime (dlocaltime (clock), TW_NULL);
+    else
+       /* use numeric timezones */
+       return dasctime (dlocaltime (clock), TW_ZONE);
+}
+
+
+/*
+ * Using a nmh "broken-down" time structure, produce
+ * a date/time string of the form
+ *
+ *     Mon, 16 Jun 1992 15:30:48 -0700
+ *
+ * as specified by rfc822 and rfc1123.
+ */
+
+char *
+dasctime (struct tws *tw, int flags)
+{
+    char buffer[80];
+    static char result[80];
+
+    if (!tw)
+       return NULL;
+
+    /* Display timezone if known */
+    if ((tw->tw_flags & TW_SZONE) == TW_SZNIL)
+       result[0] = '\0';
+    else
+       snprintf(result, sizeof(result), " %s", dtimezone(tw->tw_zone, tw->tw_flags | flags));
+
+    snprintf(buffer, sizeof(buffer), "%02d %s %0*d %02d:%02d:%02d%s",
+           tw->tw_mday, tw_moty[tw->tw_mon],
+           tw->tw_year < 100 ? 2 : 4, tw->tw_year,
+           tw->tw_hour, tw->tw_min, tw->tw_sec, result);
+
+    if ((tw->tw_flags & TW_SDAY) == TW_SEXP)
+       snprintf (result, sizeof(result), "%s, %s", tw_dotw[tw->tw_wday], buffer);
+    else
+       if ((tw->tw_flags & TW_SDAY) == TW_SNIL)
+           strncpy (result, buffer, sizeof(result));
+       else
+           snprintf (result, sizeof(result), "%s (%s)", buffer, tw_dotw[tw->tw_wday]);
+
+    return result;
+}
+
+
+/*
+ * Get the timezone for given offset
+ */
+
+char *
+dtimezone (int offset, int flags)
+{
+    int hours, mins;
+    struct zone *z;
+    static char buffer[10];
+
+    if (offset < 0) {
+       mins = -((-offset) % 60);
+       hours = -((-offset) / 60);
+    } else {
+       mins = offset % 60;
+       hours = offset / 60;
+    }
+
+    if (!(flags & TW_ZONE) && mins == 0) {
+#if defined(HAVE_TZSET) && defined(HAVE_TZNAME)
+       tzset();
+       return ((flags & TW_DST) ? tzname[1] : tzname[0]);
+#else
+       for (z = zones; z->std; z++)
+           if (z->shift == hours)
+               return (z->dst && (flags & TW_DST) ? z->dst : z->std);
+#endif
+    }
+
+#if defined(DSTXXX)
+    if (flags & TW_DST)
+       hours += 1;
+#endif /* defined(DSTXXX) */
+    snprintf (buffer, sizeof(buffer), "%s%02d%02d",
+               offset < 0 ? "-" : "+", abs (hours), abs (mins));
+    return buffer;
+}
+
+
+/*
+ * Convert nmh time structure for local "broken-down"
+ * time to calendar time (clock value).  This routine
+ * is based on the gtime() routine written by Steven Shafer
+ * at CMU.  It was forwarded to MTR by Jay Lepreau at Utah-CS.
+ */
+
+time_t
+dmktime (struct tws *tw)
+{
+    int i, sec, min, hour, mday, mon, year;
+    time_t result;
+
+    if (tw->tw_clock != 0)
+       return tw->tw_clock;
+
+    if ((sec = tw->tw_sec) < 0 || sec > 61
+           || (min = tw->tw_min) < 0 || min > 59
+           || (hour = tw->tw_hour) < 0 || hour > 23
+           || (mday = tw->tw_mday) < 1 || mday > 31
+           || (mon = tw->tw_mon + 1) < 1 || mon > 12)
+       return (tw->tw_clock = (time_t) -1);
+
+    year = tw->tw_year;
+
+    result = 0;
+    if (year < 100)
+       year += 1900;
+
+    for (i = 1970; i < year; i++)
+       result += dysize (i);
+    if (dysize (year) == 366 && mon >= 3)
+       result++;
+    while (--mon)
+       result += dmsize[mon - 1];
+    result += mday - 1;
+    result = 24 * result + hour;
+    result = 60 * result + min;
+    result = 60 * result + sec;
+    result -= 60 * tw->tw_zone;
+    if (tw->tw_flags & TW_DST)
+       result -= 60 * 60;
+
+    return (tw->tw_clock = result);
+}
+
+
+/*
+ * Simple calculation of day of the week.  Algorithm
+ * used is Zeller's congruence.  We assume that
+ * if tw->tw_year < 100, then the century = 19.
+ */
+
+void
+set_dotw (struct tws *tw)
+{
+    int month, day, year, century;
+
+    month = tw->tw_mon - 1;
+    day = tw->tw_mday;
+    year = tw->tw_year % 100;
+    century = tw->tw_year < 100 ? 19 : tw->tw_year / 100;
+
+    if (month <= 0) {
+       month += 12;
+       if (--year < 0) {
+           year += 100;
+           century--;
+       }
+    }
+
+    tw->tw_wday =
+       ((26 * month - 2) / 10 + day + year + year / 4
+           - 3 * century / 4 + 1) % 7;
+
+    tw->tw_flags &= ~TW_SDAY, tw->tw_flags |= TW_SIMP;
+}
+
+
+/*
+ * Copy nmh time structure
+ */
+
+void
+twscopy (struct tws *tb, struct tws *tw)
+{
+    *tb = *tw;  /* struct copy */
+
+#if 0
+    tb->tw_sec   = tw->tw_sec;
+    tb->tw_min   = tw->tw_min;
+    tb->tw_hour  = tw->tw_hour;
+    tb->tw_mday  = tw->tw_mday;
+    tb->tw_mon   = tw->tw_mon;
+    tb->tw_year  = tw->tw_year;
+    tb->tw_wday  = tw->tw_wday;
+    tb->tw_yday  = tw->tw_yday;
+    tb->tw_zone  = tw->tw_zone;
+    tb->tw_clock = tw->tw_clock;
+    tb->tw_flags = tw->tw_flags;
+#endif
+}
+
+
+/*
+ * Compare two nmh time structures
+ */
+
+int
+twsort (struct tws *tw1, struct tws *tw2)
+{
+    time_t c1, c2;
+
+    if (tw1->tw_clock == 0)
+       dmktime (tw1);
+    if (tw2->tw_clock == 0)
+       dmktime (tw2);
+
+    return ((c1 = tw1->tw_clock) > (c2 = tw2->tw_clock) ? 1
+           : c1 == c2 ? 0 : -1);
+}
diff --git a/zotnet/tws/dtimep.c-lexed b/zotnet/tws/dtimep.c-lexed
new file mode 100644 (file)
index 0000000..12a209b
--- /dev/null
@@ -0,0 +1,1672 @@
+#include <stdio.h>
+static int start_cond = 0;
+#define BEGIN start_cond =
+struct yysvf { 
+       struct yywork *yystoff;
+       struct yysvf *yyother;
+       int *yystops;};
+# define Z 2
+#include <h/nmh.h>
+#include <tws.h>
+#if !defined(HAVE_TM_GMTOFF) && !defined(HAVE_TZSET)
+# include <sys/timeb.h>
+#endif
+
+#if !defined(HAVE_TM_GMTOFF) && defined(HAVE_TZSET)
+extern int  daylight;
+extern long timezone;
+extern char *tzname[];
+#endif
+
+
+# line 49 "./dtimep.lex"
+/*
+ * Patchable flag that says how to interpret NN/NN/NN dates. When
+ * true, we do it European style: DD/MM/YY. When false, we do it
+ * American style: MM/DD/YY.  Of course, these are all non-RFC822
+ * compliant.
+ */
+int europeandate = 0;
+
+
+# line 57 "./dtimep.lex"
+/*
+ * Table to convert month names to numeric month.  We use the
+ * fact that the low order 5 bits of the sum of the 2nd & 3rd
+ * characters of the name is a hash with no collisions for the 12
+ * valid month names.  (The mask to 5 bits maps any combination of
+ * upper and lower case into the same hash value).
+ */
+static int month_map[] = {
+       0,
+       6,      /* 1 - Jul */
+       3,      /* 2 - Apr */
+       5,      /* 3 - Jun */
+       0,
+       10,     /* 5 - Nov */
+       0,
+       1,      /* 7 - Feb */
+       11,     /* 8 - Dec */
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,      /*15 - Jan */
+       0,
+       0,
+       0,
+       2,      /*19 - Mar */
+       0,
+       8,      /*21 - Sep */
+       0,
+       9,      /*23 - Oct */
+       0,
+       0,
+       4,      /*26 - May */
+       0,
+       7       /*28 - Aug */
+};
+
+# line 95 "./dtimep.lex"
+/*
+ * Same trick for day-of-week using the hash function
+ *  (c1 & 7) + (c2 & 4)
+ */
+static int day_map[] = {
+       0,
+       0,
+       0,
+       6,      /* 3 - Sat */
+       4,      /* 4 - Thu */
+       0,
+       5,      /* 6 - Fri */
+       0,      /* 7 - Sun */
+       2,      /* 8 - Tue */
+       1       /* 9 - Mon */,
+       0,
+       3       /*11 - Wed */
+};
+#define SETDAY { tw.tw_wday= day_map[(cp[0] & 7) + (cp[1] & 4)];\
+               tw.tw_flags &= ~TW_SDAY; tw.tw_flags |= TW_SEXP;\
+               cp += 2; }
+#define SETMONTH { tw.tw_mon = month_map[(cp[0] + cp[1]) & 0x1f]; gotdate++;\
+                cp += 2;\
+                SKIPD;}
+#define        CVT1OR2 (i=(*cp++ - '0'), isdigit(*cp)? i*10 + (*cp++ - '0') : i)
+#define        CVT2      ((cp[0] - '0')*10 + (cp[1] - '0'))
+#define        CVT4    ((((cp[0] - '0')*10 + (cp[1] - '0'))*10 + \
+                                     (cp[2] - '0'))*10 + (cp[3] - '0'))
+#define SKIPD  { while ( !isdigit(*cp++) ) ;  --cp; }
+#define EXPZONE        { tw.tw_flags &= ~TW_SZONE; tw.tw_flags |= TW_SZEXP; }
+#define ZONE(x)        { tw.tw_zone=(x); EXPZONE; }
+#define ZONED(x) { ZONE(x); tw.tw_flags |= TW_DST; }
+#define        LC(c)   (isupper (c) ? tolower (c) : (c))
+
+#ifdef DSTXXX
+# ifdef TIME_WITH_SYS_TIME
+#  include <sys/time.h>
+#  include <time.h>
+# else
+#  ifdef HAVE_SYS_TIME_H
+#   include <sys/time.h>
+#  else
+#   include <time.h>
+#  endif
+# endif
+
+static void
+zonehack (struct tws *tw)
+{
+    register struct tm *tm;
+
+    if (dmktime (tw) == (time_t) -1)
+       return;
+
+    tm = localtime (&tw->tw_clock);
+    if (tm->tm_isdst) {
+       tw->tw_flags |= TW_DST;
+       tw->tw_zone -= 60;
+    }
+}
+#endif /* DSTXXX */
+struct tws *
+dparsetime (char *str)
+{
+       register int i;
+       static struct tws tw;
+       register char *cp;
+       register int gotdate = 0;
+       time_t tclock;
+
+#ifdef HAVE_TM_GMTOFF
+       struct tm *tm;
+       time_t clock;
+#else
+# ifndef HAVE_TZSET
+       struct timeb tb;
+# endif /* not HAVE_TZSET */
+#endif /* HAVE_TM_GMTOFF */
+
+       start_cond = 0;
+
+       /* Zero out the struct. */
+       memset( (char *) &tw, 0, sizeof(tw));
+
+       /* Set default time zone. */
+#ifdef HAVE_TM_GMTOFF
+       time (&clock);
+       tm = localtime(&clock);
+       tw.tw_zone = tm->tm_gmtoff / 60;
+       if (tm->tm_isdst)                       /* if DST is in effect */
+               tw.tw_zone -= 60;               /* reset to normal offset */
+#else
+# ifdef HAVE_TZSET
+       tzset();
+       tw.tw_zone = -(timezone / 60);
+# else
+       ftime(&tb);
+       tw.tw_zone = -tb.timezone;
+# endif /* HAVE_TZSET */
+#endif /* HAVE_TM_GMTOFF */
+
+       while (isspace(*str))
+               str++;
+       while (1)
+               switch (cp = str, *cp ? lex_string( &str, start_cond) : 0) {
+
+               case -1:
+                       if (!gotdate || tw.tw_year == 0)
+                               return (struct tws *)0;
+                       /* fall through */
+               case 0:
+                       if (tw.tw_year == 0) {
+                               /* Set default year. */
+                               time (&tclock);
+                               tw.tw_year = localtime(&tclock)->tm_year + 1900;
+                       } else if (tw.tw_year < 100) {
+                               /* assume no 2-digit years > 1999 */
+                               tw.tw_year += 1900;
+                       }
+                       return &tw;
+
+#ifdef __cplusplus
+/* to avoid CC and lint complaining yyfussy not being used ...*/
+static int __lex_hack = 0;
+if (__lex_hack) goto yyfussy;
+#endif
+case 1:
+
+# line 219 "./dtimep.lex"
+                       SETDAY;
+break;
+case 2:
+
+# line 220 "./dtimep.lex"
+               {
+                                       cp++;
+                                       SETDAY;
+                                       }
+break;
+case 3:
+
+# line 224 "./dtimep.lex"
+{
+                                       if (europeandate) {
+                                               /* European: DD/MM/YY */
+                                               tw.tw_mday = CVT1OR2;
+                                               cp++;
+                                               tw.tw_mon  = CVT1OR2 - 1;
+                                       } else {
+                                               /* American: MM/DD/YY */
+                                               tw.tw_mon  = CVT1OR2 - 1;
+                                               cp++;
+                                               tw.tw_mday = CVT1OR2;
+                                       }
+                                       cp++;
+                                       for (i = 0; isdigit(*cp); )
+                                               i = i*10 + (*cp++ - '0');
+                                       tw.tw_year = i;
+                                       gotdate++;      /* XXX */
+                                       }
+break;
+case 4:
+
+# line 242 "./dtimep.lex"
+               {
+                                       if (europeandate) {
+                                               tw.tw_mday = CVT1OR2; cp++;
+                                               tw.tw_mon  = CVT1OR2 - 1;
+                                       } else {
+                                               tw.tw_mon = CVT1OR2 - 1; cp++;
+                                               tw.tw_mday  = CVT1OR2;
+                                       }
+                                       gotdate++;
+                                       }
+break;
+case 5:
+
+# line 252 "./dtimep.lex"
+{
+                                       tw.tw_mday = CVT1OR2;
+                                       while ( !isalpha(*cp++) )
+                                               ;
+                                       SETMONTH;
+                                       for (i = 0; isdigit(*cp); )
+                                               i = i*10 + (*cp++ - '0');
+                                       tw.tw_year = i;
+                                       }
+break;
+case 6:
+
+# line 261 "./dtimep.lex"
+              {
+                                        tw.tw_mday = CVT1OR2;
+                                        while ( ! isalpha( *cp++ ) )
+                                                ;
+                                        SETMONTH;
+                                        }
+break;
+case 7:
+
+# line 267 "./dtimep.lex"
+{
+                                       cp++;
+                                       SETMONTH;
+                                       tw.tw_mday = CVT1OR2;
+                                       SKIPD;
+                                       for (i = 0; isdigit(*cp); )
+                                               i = i*10 + (*cp++ - '0');
+                                       tw.tw_year = i;
+                                       }
+break;
+case 8:
+
+# line 276 "./dtimep.lex"
+               {
+                                       cp++;
+                                       SETMONTH;
+                                       tw.tw_mday = CVT1OR2;
+                                       }
+break;
+case 9:
+
+# line 282 "./dtimep.lex"
+       {       /* hack: ctime w/o TZ */
+                                       tw.tw_hour = CVT1OR2; cp++;
+                                       tw.tw_min  = CVT1OR2; cp++;
+                                       tw.tw_sec  = CVT1OR2;
+                                       SKIPD;
+                                       tw.tw_year = CVT4; cp+=4;
+                                       }
+break;
+case 10:
+
+# line 289 "./dtimep.lex"
+                       {
+                                       tw.tw_hour = CVT1OR2; cp++;
+                                       tw.tw_min  = CVT1OR2; cp++;
+                                       tw.tw_sec  = CVT1OR2;
+                                       BEGIN Z;
+                                       }
+break;
+case 11:
+
+# line 295 "./dtimep.lex"
+                       {
+                                       tw.tw_hour = CVT1OR2; cp++;
+                                       tw.tw_min = CVT1OR2;
+                                       BEGIN Z;
+                                       }
+break;
+case 12:
+
+# line 300 "./dtimep.lex"
+                       {
+                                       tw.tw_hour = CVT1OR2; cp++;
+                                       if (tw.tw_hour == 12)
+                                               tw.tw_hour = 0;
+                                       tw.tw_min  = CVT1OR2;
+                                       BEGIN Z;
+                                       }
+break;
+case 13:
+
+# line 307 "./dtimep.lex"
+               {
+                                       tw.tw_hour = CVT1OR2; cp++;
+                                       if (tw.tw_hour == 12)
+                                               tw.tw_hour = 0;
+                                       tw.tw_min  = CVT1OR2; cp++;
+                                       tw.tw_sec  = CVT1OR2;
+                                       BEGIN Z;
+                                       }
+break;
+case 14:
+
+# line 315 "./dtimep.lex"
+                       {
+                                       tw.tw_hour = CVT1OR2; cp++;
+                                       if (tw.tw_hour != 12)
+                                               tw.tw_hour += 12;
+                                       tw.tw_min  = CVT1OR2;
+                                       BEGIN Z;
+                                       }
+break;
+case 15:
+
+# line 322 "./dtimep.lex"
+               {
+                                       tw.tw_hour = CVT1OR2; cp++;
+                                       if (tw.tw_hour != 12)
+                                               tw.tw_hour += 12;
+                                       tw.tw_min  = CVT1OR2; cp++;
+                                       tw.tw_sec  = CVT1OR2;
+                                       BEGIN Z;
+                                       }
+break;
+case 16:
+
+# line 330 "./dtimep.lex"
+               {
+                                       tw.tw_hour = CVT2; cp+=2;
+                                       tw.tw_min  = CVT2; cp+=2;
+                                       tw.tw_sec  = CVT2; cp+=2;
+                                       BEGIN Z;
+                                       }
+break;
+case 17:
+
+# line 336 "./dtimep.lex"
+                       {
+                                       /*
+                                        * Luckly, 4 digit times in the range
+                                        * 1960-1999 aren't legal as hour
+                                        * and minutes.
+                                        */
+                                       tw.tw_year = CVT4; cp+=4;
+                                       }
+break;
+case 18:
+
+# line 344 "./dtimep.lex"
+               {
+                                       if (tw.tw_hour || tw.tw_min 
+                                                           || tw.tw_sec) {
+                                           tw.tw_year = CVT4; cp+=4;
+                                           tw.tw_zone = 0;
+                                       } else {
+                                           tw.tw_hour = CVT2; cp+=2;
+                                           tw.tw_min  = CVT2; cp+=2;
+                                           BEGIN Z;
+                                       }
+                                       }
+break;
+case 19:
+
+# line 355 "./dtimep.lex"
+                       ZONE(0 * 60);
+break;
+case 20:
+
+# line 356 "./dtimep.lex"
+                       ZONE(0 * 60);
+break;
+case 21:
+
+# line 357 "./dtimep.lex"
+                       ZONE(2 * 60);
+break;
+case 22:
+
+# line 358 "./dtimep.lex"
+                       ZONED(2 * 60);
+break;
+case 23:
+
+# line 359 "./dtimep.lex"
+                       ZONE(-5 * 60);
+break;
+case 24:
+
+# line 360 "./dtimep.lex"
+                       ZONED(-5 * 60);
+break;
+case 25:
+
+# line 361 "./dtimep.lex"
+                       ZONE(-6 * 60);
+break;
+case 26:
+
+# line 362 "./dtimep.lex"
+                       ZONED(-6 * 60);
+break;
+case 27:
+
+# line 363 "./dtimep.lex"
+                       ZONE(-7 * 60);
+break;
+case 28:
+
+# line 364 "./dtimep.lex"
+                       ZONED(-7 * 60);
+break;
+case 29:
+
+# line 365 "./dtimep.lex"
+                       ZONE(-8 * 60);
+break;
+case 30:
+
+# line 366 "./dtimep.lex"
+                       ZONED(-8 * 60);
+break;
+case 31:
+
+# line 367 "./dtimep.lex"
+                       ZONE(-(3 * 60 + 30));
+break;
+case 32:
+
+# line 368 "./dtimep.lex"
+                       ZONE(-4 * 60);
+break;
+case 33:
+
+# line 369 "./dtimep.lex"
+                       ZONED(-4 * 60);
+break;
+case 34:
+
+# line 370 "./dtimep.lex"
+                       ZONE(-9 * 60);
+break;
+case 35:
+
+# line 371 "./dtimep.lex"
+                       ZONED(-9 * 60);
+break;
+case 36:
+
+# line 372 "./dtimep.lex"
+                       ZONE(-10 * 60);
+break;
+case 37:
+
+# line 373 "./dtimep.lex"
+                       ZONED(-10 * 60);
+break;
+case 38:
+
+# line 374 "./dtimep.lex"
+                       ZONED(-1 * 60);
+break;
+case 39:
+
+# line 375 "./dtimep.lex"
+                       {
+                                       tw.tw_zone = 60 * (('a'-1) - LC(*cp));
+                                       EXPZONE; 
+                                       }
+break;
+case 40:
+
+# line 379 "./dtimep.lex"
+                       {
+                                       tw.tw_zone = 60 * ('a' - LC(*cp));
+                                       EXPZONE; 
+                                       }
+break;
+case 41:
+
+# line 383 "./dtimep.lex"
+                       {
+                                       tw.tw_zone = 60 * (LC(*cp) - 'm');
+                                       EXPZONE; 
+                                       }
+break;
+case 42:
+
+# line 387 "./dtimep.lex"
+               {
+                                       cp++;
+                                       tw.tw_zone = ((cp[0] * 10 + cp[1])
+                                                    -('0' * 10   + '0'))*60
+                                                   +((cp[2] * 10 + cp[3])
+                                                    -('0' * 10   + '0'));
+                                       EXPZONE;
+#ifdef DSTXXX
+                                       zonehack (&tw);
+#endif /* DSTXXX */
+                                       cp += 4;
+                                       }
+break;
+case 43:
+
+# line 399 "./dtimep.lex"
+               {
+                                       cp++;
+                                       tw.tw_zone = (('0' * 10   + '0')
+                                                    -(cp[0] * 10 + cp[1]))*60
+                                                   +(('0' * 10   + '0')
+                                                    -(cp[2] * 10 + cp[3]));
+                                       EXPZONE;
+#ifdef DSTXXX
+                                       zonehack (&tw);
+#endif /* DSTXXX */
+                                       cp += 4;
+                                       }
+break;
+case 44:
+
+# line 411 "./dtimep.lex"
+               {
+                                       SKIPD;
+                                       tw.tw_year = CVT4; cp+=4;
+                                       }
+break;
+case 45:
+
+# line 415 "./dtimep.lex"
+case 46:
+
+# line 416 "./dtimep.lex"
+;
+break;
+       default: return(0);
+} }
+/* end of yylex */
+int yyvstop[] = {
+0,
+
+46,
+0,
+
+45,
+0,
+
+46,
+0,
+
+39,
+0,
+
+39,
+0,
+
+39,
+0,
+
+39,
+0,
+
+39,
+0,
+
+39,
+0,
+
+39,
+0,
+
+39,
+0,
+
+39,
+0,
+
+40,
+0,
+
+40,
+0,
+
+41,
+0,
+
+41,
+0,
+
+41,
+0,
+
+41,
+0,
+
+41,
+0,
+
+41,
+0,
+
+41,
+0,
+
+41,
+0,
+
+41,
+0,
+
+19,
+0,
+
+4,
+0,
+
+4,
+0,
+
+11,
+0,
+
+1,
+0,
+
+1,
+0,
+
+1,
+0,
+
+1,
+0,
+
+1,
+0,
+
+1,
+0,
+
+1,
+0,
+
+33,
+0,
+
+32,
+0,
+
+38,
+0,
+
+26,
+0,
+
+25,
+0,
+
+24,
+0,
+
+23,
+0,
+
+20,
+0,
+
+37,
+0,
+
+36,
+0,
+
+22,
+0,
+
+21,
+0,
+
+28,
+0,
+
+27,
+0,
+
+31,
+0,
+
+30,
+0,
+
+29,
+0,
+
+35,
+0,
+
+34,
+0,
+
+4,
+0,
+
+4,
+0,
+
+4,
+0,
+
+18,
+0,
+
+11,
+0,
+
+11,
+0,
+
+6,
+0,
+
+6,
+0,
+
+6,
+0,
+
+6,
+0,
+
+6,
+0,
+
+6,
+0,
+
+6,
+0,
+
+6,
+0,
+
+6,
+0,
+
+6,
+0,
+
+6,
+0,
+
+6,
+0,
+
+17,
+18,
+0,
+
+1,
+0,
+
+2,
+0,
+
+18,
+0,
+
+10,
+0,
+
+12,
+0,
+
+14,
+0,
+
+6,
+0,
+
+17,
+18,
+0,
+
+8,
+0,
+
+44,
+0,
+
+42,
+0,
+
+43,
+0,
+
+2,
+0,
+
+3,
+0,
+
+16,
+0,
+
+10,
+0,
+
+10,
+0,
+
+5,
+0,
+
+8,
+0,
+
+8,
+0,
+
+1,
+0,
+
+3,
+0,
+
+3,
+0,
+
+13,
+0,
+
+15,
+0,
+
+6,
+0,
+
+5,
+0,
+
+5,
+0,
+
+5,
+0,
+
+5,
+0,
+
+7,
+0,
+
+9,
+0,
+
+7,
+0,
+
+7,
+0,
+0};
+# define YYTYPE int
+struct yywork { YYTYPE verify, advance; } yycrank[] = {
+0,0,   0,0,    0,0,    0,0,    
+0,0,   0,0,    0,0,    0,0,    
+0,0,   0,0,    1,5,    1,6,    
+5,5,   0,0,    0,0,    0,0,    
+0,0,   0,0,    0,0,    0,0,    
+0,0,   0,0,    0,0,    0,0,    
+0,0,   0,0,    0,0,    0,0,    
+0,0,   0,0,    0,0,    0,0,    
+0,0,   1,5,    0,0,    5,5,    
+3,21,  3,6,    0,0,    0,0,    
+0,0,   1,7,    0,0,    0,0,    
+0,0,   0,0,    0,0,    0,0,    
+0,0,   1,8,    1,9,    1,8,    
+1,10,  1,10,   1,10,   1,10,   
+1,10,  1,10,   1,10,   3,21,   
+9,63,  22,83,  22,83,  0,0,    
+0,0,   0,0,    0,0,    3,7,    
+0,0,   0,0,    3,22,   0,0,    
+3,23,  0,0,    0,0,    3,8,    
+3,9,   3,8,    3,10,   3,10,   
+3,10,  3,10,   3,10,   3,10,   
+3,10,  10,64,  10,64,  10,64,  
+10,64, 10,64,  10,64,  10,64,  
+10,64, 10,64,  10,64,  0,0,    
+0,0,   0,0,    1,11,   15,72,  
+59,143,        1,12,   14,70,  1,13,   
+12,67, 13,68,  17,75,  1,14,   
+19,79, 20,81,  1,15,   1,16,   
+1,17,  15,73,  11,65,  16,74,  
+1,18,  1,19,   13,69,  11,66,  
+1,20,  19,80,  14,71,  25,99,  
+3,24,  3,25,   3,26,   3,27,   
+3,28,  3,29,   3,30,   3,31,   
+3,32,  3,33,   3,34,   3,34,   
+3,35,  3,36,   3,37,   3,38,   
+3,39,  3,39,   3,40,   3,41,   
+3,42,  3,39,   3,43,   3,39,   
+3,44,  7,45,   8,50,   18,76,  
+26,100,        28,102, 30,104, 18,77,  
+7,46,  24,97,  42,114, 45,117, 
+31,105,        21,21,  7,47,   7,48,   
+23,84, 23,84,  7,49,   26,101, 
+28,103,        24,65,  38,112, 18,78,  
+24,98, 8,50,   24,66,  31,106, 
+36,74, 46,118, 49,123, 56,139, 
+36,111,        57,140, 55,137, 60,144, 
+21,21, 38,113, 8,51,   55,138, 
+8,52,  8,53,   8,53,   8,53,   
+8,53,  8,53,   8,53,   8,53,   
+8,53,  8,53,   8,53,   8,54,   
+21,82, 21,82,  21,82,  21,82,  
+21,82, 21,82,  21,82,  21,82,  
+21,82, 21,82,  47,119, 61,145, 
+62,146,        23,85,  23,86,  23,87,  
+44,115,        23,88,  35,72,  23,89,  
+23,90, 35,109, 23,91,  50,50,  
+33,70, 23,92,  23,93,  33,107, 
+23,94, 58,141, 47,120, 44,116, 
+35,73, 23,95,  65,148, 48,121, 
+35,110,        23,96,  8,55,   51,124, 
+66,149,        8,56,   33,108, 8,57,   
+33,71, 67,150, 50,50,  8,58,   
+48,122,        58,142, 8,59,   8,60,   
+8,61,  68,151, 69,152, 70,153, 
+8,62,  73,158, 71,154, 50,124, 
+71,155,        74,159, 51,124, 52,134, 
+52,134,        52,134, 52,134, 52,134, 
+52,134,        52,134, 52,134, 52,134, 
+52,134,        75,160, 76,161, 77,162, 
+78,163,        79,164, 51,133, 51,133, 
+51,133,        51,133, 51,133, 51,133, 
+51,133,        51,133, 51,133, 51,133, 
+53,135,        53,135, 53,135, 53,135, 
+53,135,        53,135, 53,135, 53,135, 
+53,135,        53,135, 54,136, 54,136, 
+54,136,        54,136, 54,136, 54,136, 
+54,136,        54,136, 54,136, 54,136, 
+72,156,        80,165, 81,166, 50,125, 
+93,111,        85,97,  50,126, 72,157, 
+50,127,        97,170, 91,107, 92,109, 
+50,128,        64,50,  98,171, 50,129, 
+50,130,        50,131, 99,172, 51,55,  
+85,98, 50,132, 51,56,  100,173,        
+51,57, 91,108, 92,110, 101,174,        
+51,58, 102,175,        103,176,        51,59,  
+51,60, 51,61,  104,177,        105,178,        
+64,50, 51,62,  63,135, 63,135, 
+63,135,        63,135, 63,135, 63,135, 
+63,147,        63,147, 63,147, 63,147, 
+106,179,       64,51,  107,180,        64,52,  
+82,167,        82,167, 82,167, 82,167, 
+82,167,        82,167, 82,167, 82,167, 
+82,167,        82,167, 64,54,  83,168, 
+83,168,        83,168, 83,168, 83,168, 
+83,168,        83,168, 83,168, 83,168, 
+83,168,        84,169, 84,169, 84,169, 
+84,169,        84,169, 84,169, 84,169, 
+84,169,        84,169, 84,169, 108,181,        
+109,182,       110,183,        111,184,        112,185,        
+113,186,       115,187,        116,188,        117,189,        
+118,190,       119,191,        120,192,        121,193,        
+122,194,       123,195,        124,124,        126,198,        
+125,196,       64,55,  127,199,        128,200,        
+64,56, 125,197,        64,57,  129,202,        
+130,203,       131,204,        64,58,  132,205,        
+133,206,       64,59,  64,60,  64,61,  
+137,216,       138,217,        139,218,        64,62,  
+140,219,       124,124,        141,220,        128,201,        
+134,206,       135,210,        135,210,        135,210,        
+135,210,       135,210,        135,210,        135,210,        
+135,210,       135,210,        135,210,        133,206,        
+142,221,       143,223,        142,222,        144,225,        
+145,226,       146,227,        153,236,        155,157,        
+143,224,       158,238,        159,239,        134,206,        
+133,207,       160,240,        162,242,        133,208,        
+133,208,       133,208,        133,208,        133,208,        
+133,208,       133,208,        133,208,        133,208,        
+133,208,       148,229,        134,207,        134,209,        
+134,209,       134,209,        134,209,        134,209,        
+134,209,       134,209,        134,209,        134,209,        
+134,209,       136,211,        147,228,        147,228,        
+147,228,       147,228,        147,228,        147,228,        
+147,228,       147,228,        147,228,        147,228,        
+148,229,       149,229,        124,125,        150,229,        
+154,229,       124,126,        163,243,        124,127,        
+190,252,       192,254,        196,258,        124,128,        
+136,211,       191,250,        124,129,        124,130,        
+124,131,       151,229,        156,229,        152,234,        
+124,132,       157,229,        161,234,        164,234,        
+149,229,       165,234,        150,229,        154,229,        
+136,212,       136,212,        136,212,        136,212,        
+136,212,       136,212,        136,212,        136,212,        
+136,212,       136,212,        136,213,        166,234,        
+151,229,       156,229,        152,234,        194,250,        
+157,229,       161,234,        164,234,        189,250,        
+165,234,       195,250,        193,250,        197,259,        
+198,260,       199,261,        152,234,        200,262,        
+203,267,       161,234,        164,234,        201,263,        
+165,234,       201,264,        166,234,        167,247,        
+167,247,       167,247,        167,247,        167,247,        
+167,247,       167,247,        167,247,        167,247,        
+167,247,       148,230,        166,234,        204,268,        
+205,269,       136,214,        168,248,        168,248,        
+168,248,       168,248,        168,248,        168,248,        
+168,248,       168,248,        168,248,        168,248,        
+206,206,       191,253,        208,207,        209,207,        
+136,215,       212,213,        214,274,        150,232,        
+169,249,       169,249,        169,249,        169,249,        
+169,249,       169,249,        169,249,        169,249,        
+169,249,       169,249,        189,251,        202,265,        
+156,237,       149,231,        152,235,        206,206,        
+210,271,       211,211,        202,266,        215,275,        
+154,157,       194,256,        195,257,        220,283,        
+222,224,       225,285,        151,233,        193,255,        
+226,286,       227,287,        230,157,        231,290,        
+164,244,       232,291,        161,241,        165,245,        
+233,292,       235,293,        236,294,        210,271,        
+211,211,       237,157,        238,295,        239,296,        
+166,246,       207,270,        207,270,        207,270,        
+207,270,       207,270,        207,270,        207,270,        
+207,270,       207,270,        207,270,        210,272,        
+210,272,       210,272,        210,272,        210,272,        
+210,272,       210,272,        210,272,        210,272,        
+210,272,       213,273,        213,273,        213,273,        
+213,273,       213,273,        213,273,        213,273,        
+213,273,       213,273,        213,273,        240,297,        
+228,288,       234,234,        241,298,        242,299,        
+243,300,       244,301,        245,302,        216,276,        
+246,303,       247,304,        247,304,        247,304,        
+247,304,       247,304,        247,304,        247,304,        
+247,304,       247,304,        247,304,        250,307,        
+251,308,       252,309,        217,276,        228,288,        
+234,234,       253,310,        254,311,        255,312,        
+256,313,       211,214,        216,276,        257,314,        
+276,330,       258,266,        260,266,        279,224,        
+218,276,       265,266,        280,332,        281,333,        
+282,334,       283,335,        284,224,        216,277,        
+211,215,       217,276,        216,278,        216,278,        
+216,278,       216,278,        216,278,        216,278,        
+216,278,       216,278,        216,278,        216,278,        
+258,266,       260,266,        217,277,        218,276,        
+265,266,       217,278,        217,278,        217,278,        
+217,278,       217,278,        217,278,        217,278,        
+217,278,       217,278,        217,278,        219,276,        
+218,277,       259,266,        285,336,        218,278,        
+218,278,       218,278,        218,278,        218,278,        
+218,278,       218,278,        218,278,        218,278,        
+218,278,       264,266,        263,266,        286,337,        
+287,338,       290,157,        291,342,        292,343,        
+293,344,       294,345,        219,276,        296,346,        
+259,266,       221,276,        266,266,        271,271,        
+297,347,       274,274,        262,266,        216,279,        
+298,348,       299,349,        301,350,        219,277,        
+264,266,       263,266,        219,278,        219,278,        
+219,278,       219,278,        219,278,        219,278,        
+219,278,       219,278,        219,278,        219,278,        
+221,276,       266,266,        271,271,        223,276,        
+274,274,       262,266,        260,317,        265,320,        
+218,281,       258,315,        217,280,        261,266,        
+268,266,       221,277,        269,266,        275,275,        
+221,278,       221,278,        221,278,        221,278,        
+221,278,       221,278,        221,278,        221,278,        
+221,278,       221,278,        223,276,        302,351,        
+303,352,       224,276,        267,266,        288,288,        
+308,353,       310,354,        261,266,        268,266,        
+312,355,       269,266,        275,275,        223,277,        
+229,229,       313,356,        223,278,        223,278,        
+223,278,       223,278,        223,278,        223,278,        
+223,278,       223,278,        223,278,        223,278,        
+224,276,       267,266,        288,288,        314,357,        
+219,282,       264,266,        315,266,        316,358,        
+317,359,       259,316,        318,360,        229,229,        
+319,361,       224,277,        320,266,        321,362,        
+224,278,       224,278,        224,278,        224,278,        
+224,278,       224,278,        224,278,        224,278,        
+224,278,       224,278,        263,266,        229,289,        
+229,289,       229,289,        229,289,        229,289,        
+229,289,       229,289,        229,289,        229,289,        
+229,289,       221,224,        262,319,        322,363,        
+323,364,       223,284,        248,305,        248,305,        
+248,305,       248,305,        248,305,        248,305,        
+248,305,       248,305,        248,305,        248,305,        
+249,306,       249,306,        249,306,        249,306,        
+249,306,       249,306,        249,306,        249,306,        
+249,306,       249,306,        268,322,        328,368,        
+261,318,       329,369,        330,370,        332,224,        
+273,326,       269,323,        267,321,        270,324,        
+270,324,       270,324,        270,324,        270,324,        
+270,324,       270,324,        270,324,        270,324,        
+270,324,       272,325,        272,325,        272,325,        
+272,325,       272,325,        272,325,        272,325,        
+272,325,       272,325,        272,325,        273,326,        
+333,373,       334,374,        277,277,        278,331,        
+278,331,       278,331,        278,331,        278,331,        
+278,331,       278,331,        278,331,        278,331,        
+278,331,       324,365,        325,325,        273,327,        
+273,327,       273,327,        273,327,        273,327,        
+273,327,       273,327,        273,327,        273,327,        
+273,327,       277,277,        335,375,        336,376,        
+289,339,       337,377,        338,378,        341,340,        
+342,380,       343,381,        344,234,        345,157,        
+324,365,       325,325,        346,382,        347,157,        
+348,383,       277,278,        277,278,        277,278,        
+277,278,       277,278,        277,278,        277,278,        
+277,278,       277,278,        277,278,        289,339,        
+324,366,       324,366,        324,366,        324,366,        
+324,366,       324,366,        324,366,        324,366,        
+324,366,       324,366,        326,326,        289,340,        
+273,328,       327,326,        331,371,        289,341,        
+289,341,       289,341,        289,341,        289,341,        
+289,341,       289,341,        289,341,        289,341,        
+289,341,       339,339,        340,379,        273,329,        
+349,384,       350,385,        352,386,        353,250,        
+354,387,       326,326,        355,388,        357,389,        
+327,326,       331,371,        358,266,        359,390,        
+360,391,       361,392,        362,393,        363,394,        
+364,395,       365,365,        367,396,        373,399,        
+339,339,       340,379,        326,367,        374,400,        
+375,224,       331,372,        331,372,        331,372,        
+331,372,       331,372,        331,372,        331,372,        
+331,372,       331,372,        331,372,        368,368,        
+369,369,       370,370,        371,371,        376,401,        
+365,365,       366,365,        366,365,        366,365,        
+366,365,       366,365,        366,365,        366,365,        
+366,365,       366,365,        366,365,        377,224,        
+378,402,       384,404,        386,405,        389,406,        
+390,407,       391,408,        368,368,        369,369,        
+370,370,       371,371,        392,266,        379,379,        
+393,409,       394,266,        395,410,        397,412,        
+402,413,       410,415,        326,328,        398,371,        
+412,412,       327,328,        372,398,        372,398,        
+372,398,       372,398,        372,398,        372,398,        
+372,398,       372,398,        372,398,        372,398,        
+0,0,   326,329,        379,379,        0,0,    
+327,329,       396,411,        396,411,        396,411,        
+396,411,       0,0,    398,371,        412,412,        
+0,0,   0,0,    0,0,    414,417,        
+417,417,       0,0,    379,403,        379,403,        
+379,403,       379,403,        379,403,        379,403,        
+379,403,       379,403,        379,403,        379,403,        
+403,414,       403,414,        403,414,        403,414,        
+403,414,       403,414,        403,414,        403,414,        
+403,414,       403,414,        414,417,        417,417,        
+0,0,   0,0,    371,397,        411,416,        
+411,416,       411,416,        411,416,        411,416,        
+411,416,       411,416,        411,416,        411,416,        
+411,416,       0,0,    414,418,        414,418,        
+414,418,       414,418,        414,418,        414,418,        
+414,418,       414,418,        414,418,        414,418,        
+418,417,       418,417,        418,417,        418,417,        
+418,417,       418,417,        418,417,        418,417,        
+418,417,       418,417,        0,0,    0,0,    
+0,0};
+struct yysvf yysvec[] = {
+0,     0,      0,
+yycrank+1,     0,              0,      
+yycrank+0,     yysvec+1,       0,      
+yycrank+27,    0,              0,      
+yycrank+0,     yysvec+3,       0,      
+yycrank+3,     0,              yyvstop+1,
+yycrank+0,     0,              yyvstop+3,
+yycrank+47,    0,              0,      
+yycrank+141,   0,              0,      
+yycrank+3,     yysvec+8,       0,      
+yycrank+37,    yysvec+8,       0,      
+yycrank+2,     0,              0,      
+yycrank+3,     0,              0,      
+yycrank+4,     0,              0,      
+yycrank+5,     0,              0,      
+yycrank+2,     0,              0,      
+yycrank+4,     0,              0,      
+yycrank+7,     0,              0,      
+yycrank+54,    0,              0,      
+yycrank+4,     0,              0,      
+yycrank+8,     0,              0,      
+yycrank+152,   0,              yyvstop+5,
+yycrank+13,    0,              0,      
+yycrank+116,   0,              0,      
+yycrank+57,    0,              yyvstop+7,
+yycrank+8,     0,              yyvstop+9,
+yycrank+52,    0,              yyvstop+11,
+yycrank+0,     yysvec+12,      yyvstop+13,
+yycrank+53,    0,              yyvstop+15,
+yycrank+0,     yysvec+13,      yyvstop+17,
+yycrank+45,    0,              yyvstop+19,
+yycrank+60,    0,              yyvstop+21,
+yycrank+0,     0,              yyvstop+23,
+yycrank+127,   0,              0,      
+yycrank+0,     0,              yyvstop+25,
+yycrank+121,   0,              yyvstop+27,
+yycrank+65,    0,              yyvstop+29,
+yycrank+0,     yysvec+17,      yyvstop+31,
+yycrank+70,    0,              yyvstop+33,
+yycrank+0,     0,              yyvstop+35,
+yycrank+0,     yysvec+18,      yyvstop+37,
+yycrank+0,     yysvec+19,      yyvstop+39,
+yycrank+42,    0,              yyvstop+41,
+yycrank+0,     yysvec+20,      yyvstop+43,
+yycrank+116,   0,              yyvstop+45,
+yycrank+45,    0,              0,      
+yycrank+66,    0,              0,      
+yycrank+113,   0,              0,      
+yycrank+131,   0,              0,      
+yycrank+77,    0,              0,      
+yycrank+214,   0,              0,      
+yycrank+230,   0,              0,      
+yycrank+215,   0,              0,      
+yycrank+240,   yysvec+8,       0,      
+yycrank+250,   0,              0,      
+yycrank+70,    0,              0,      
+yycrank+78,    0,              0,      
+yycrank+80,    0,              0,      
+yycrank+132,   0,              0,      
+yycrank+3,     0,              0,      
+yycrank+72,    0,              0,      
+yycrank+112,   0,              0,      
+yycrank+111,   0,              0,      
+yycrank+298,   yysvec+8,       0,      
+yycrank+312,   0,              0,      
+yycrank+120,   0,              0,      
+yycrank+137,   0,              0,      
+yycrank+146,   0,              0,      
+yycrank+155,   0,              0,      
+yycrank+149,   0,              0,      
+yycrank+145,   0,              0,      
+yycrank+150,   0,              0,      
+yycrank+194,   0,              0,      
+yycrank+147,   0,              0,      
+yycrank+143,   0,              0,      
+yycrank+157,   0,              0,      
+yycrank+158,   0,              0,      
+yycrank+163,   0,              0,      
+yycrank+166,   0,              0,      
+yycrank+160,   0,              0,      
+yycrank+208,   0,              0,      
+yycrank+210,   0,              0,      
+yycrank+312,   0,              0,      
+yycrank+323,   0,              0,      
+yycrank+333,   0,              0,      
+yycrank+213,   0,              0,      
+yycrank+0,     yysvec+25,      0,      
+yycrank+0,     yysvec+26,      0,      
+yycrank+0,     yysvec+28,      0,      
+yycrank+0,     yysvec+30,      0,      
+yycrank+0,     yysvec+31,      0,      
+yycrank+218,   0,              0,      
+yycrank+219,   0,              0,      
+yycrank+197,   0,              0,      
+yycrank+0,     yysvec+38,      0,      
+yycrank+0,     yysvec+42,      0,      
+yycrank+0,     yysvec+44,      0,      
+yycrank+201,   0,              0,      
+yycrank+206,   0,              0,      
+yycrank+210,   0,              0,      
+yycrank+215,   0,              0,      
+yycrank+219,   0,              0,      
+yycrank+221,   0,              0,      
+yycrank+222,   0,              0,      
+yycrank+226,   0,              0,      
+yycrank+227,   0,              0,      
+yycrank+240,   0,              0,      
+yycrank+242,   0,              0,      
+yycrank+275,   0,              0,      
+yycrank+276,   0,              0,      
+yycrank+277,   0,              0,      
+yycrank+278,   0,              0,      
+yycrank+279,   0,              0,      
+yycrank+280,   0,              0,      
+yycrank+0,     0,              yyvstop+47,
+yycrank+281,   0,              0,      
+yycrank+282,   0,              0,      
+yycrank+294,   0,              0,      
+yycrank+290,   0,              0,      
+yycrank+285,   0,              0,      
+yycrank+292,   0,              0,      
+yycrank+286,   0,              0,      
+yycrank+303,   0,              0,      
+yycrank+305,   0,              0,      
+yycrank+397,   0,              0,      
+yycrank+296,   0,              0,      
+yycrank+306,   0,              0,      
+yycrank+309,   0,              0,      
+yycrank+314,   0,              0,      
+yycrank+318,   0,              0,      
+yycrank+305,   0,              0,      
+yycrank+318,   0,              0,      
+yycrank+318,   0,              0,      
+yycrank+411,   0,              yyvstop+49,
+yycrank+423,   0,              yyvstop+51,
+yycrank+385,   0,              0,      
+yycrank+472,   0,              yyvstop+53,
+yycrank+310,   0,              0,      
+yycrank+322,   0,              0,      
+yycrank+327,   0,              0,      
+yycrank+330,   0,              0,      
+yycrank+320,   0,              0,      
+yycrank+336,   0,              0,      
+yycrank+331,   0,              0,      
+yycrank+329,   0,              0,      
+yycrank+332,   0,              0,      
+yycrank+337,   0,              0,      
+yycrank+434,   0,              0,      
+yycrank+460,   0,              0,      
+yycrank+484,   0,              0,      
+yycrank+486,   0,              0,      
+yycrank+500,   0,              0,      
+yycrank+502,   0,              yyvstop+55,
+yycrank+333,   yysvec+149,     0,      
+yycrank+487,   0,              0,      
+yycrank+350,   yysvec+150,     0,      
+yycrank+501,   0,              0,      
+yycrank+504,   0,              0,      
+yycrank+353,   yysvec+152,     yyvstop+57,
+yycrank+353,   yysvec+150,     0,      
+yycrank+346,   yysvec+157,     0,      
+yycrank+505,   0,              yyvstop+59,
+yycrank+342,   yysvec+157,     0,      
+yycrank+398,   yysvec+152,     yyvstop+61,
+yycrank+506,   0,              yyvstop+63,
+yycrank+508,   0,              yyvstop+65,
+yycrank+522,   0,              yyvstop+67,
+yycrank+507,   0,              0,      
+yycrank+522,   0,              0,      
+yycrank+540,   0,              0,      
+yycrank+0,     0,              yyvstop+69,
+yycrank+0,     0,              yyvstop+71,
+yycrank+0,     0,              yyvstop+73,
+yycrank+0,     0,              yyvstop+75,
+yycrank+0,     0,              yyvstop+77,
+yycrank+0,     0,              yyvstop+79,
+yycrank+0,     0,              yyvstop+81,
+yycrank+0,     0,              yyvstop+83,
+yycrank+0,     0,              yyvstop+85,
+yycrank+0,     0,              yyvstop+87,
+yycrank+0,     0,              yyvstop+89,
+yycrank+0,     0,              yyvstop+91,
+yycrank+0,     0,              yyvstop+93,
+yycrank+0,     0,              yyvstop+95,
+yycrank+0,     0,              yyvstop+97,
+yycrank+0,     0,              yyvstop+99,
+yycrank+0,     0,              yyvstop+101,
+yycrank+0,     0,              yyvstop+103,
+yycrank+0,     0,              yyvstop+105,
+yycrank+498,   0,              0,      
+yycrank+400,   yysvec+189,     0,      
+yycrank+464,   0,              0,      
+yycrank+401,   yysvec+189,     0,      
+yycrank+501,   0,              0,      
+yycrank+494,   0,              0,      
+yycrank+500,   0,              0,      
+yycrank+388,   0,              0,      
+yycrank+440,   0,              0,      
+yycrank+445,   0,              0,      
+yycrank+447,   0,              0,      
+yycrank+437,   0,              0,      
+yycrank+443,   0,              0,      
+yycrank+485,   0,              0,      
+yycrank+430,   0,              0,      
+yycrank+451,   0,              0,      
+yycrank+456,   0,              0,      
+yycrank+571,   0,              yyvstop+107,
+yycrank+585,   0,              0,      
+yycrank+537,   yysvec+206,     yyvstop+109,
+yycrank+536,   yysvec+206,     yyvstop+111,
+yycrank+595,   0,              yyvstop+113,
+yycrank+596,   0,              yyvstop+115,
+yycrank+527,   yysvec+211,     yyvstop+117,
+yycrank+605,   0,              0,      
+yycrank+477,   0,              0,      
+yycrank+498,   0,              0,      
+yycrank+662,   0,              yyvstop+119,
+yycrank+677,   0,              yyvstop+121,
+yycrank+691,   0,              yyvstop+123,
+yycrank+726,   0,              yyvstop+125,
+yycrank+494,   yysvec+217,     yyvstop+127,
+yycrank+752,   0,              yyvstop+129,
+yycrank+511,   yysvec+218,     yyvstop+131,
+yycrank+778,   0,              yyvstop+133,
+yycrank+804,   0,              yyvstop+135,
+yycrank+512,   yysvec+218,     yyvstop+137,
+yycrank+505,   yysvec+224,     yyvstop+139,
+yycrank+501,   yysvec+224,     yyvstop+141,
+yycrank+655,   yysvec+210,     yyvstop+143,
+yycrank+815,   0,              0,      
+yycrank+510,   0,              0,      
+yycrank+504,   0,              0,      
+yycrank+512,   0,              0,      
+yycrank+507,   0,              0,      
+yycrank+656,   0,              yyvstop+146,
+yycrank+528,   0,              0,      
+yycrank+529,   0,              0,      
+yycrank+525,   0,              0,      
+yycrank+533,   0,              0,      
+yycrank+522,   0,              0,      
+yycrank+565,   0,              0,      
+yycrank+552,   0,              0,      
+yycrank+566,   0,              0,      
+yycrank+571,   0,              0,      
+yycrank+554,   0,              0,      
+yycrank+570,   0,              0,      
+yycrank+571,   0,              0,      
+yycrank+625,   0,              0,      
+yycrank+830,   0,              0,      
+yycrank+840,   0,              0,      
+yycrank+639,   0,              yyvstop+148,
+yycrank+587,   0,              0,      
+yycrank+588,   0,              0,      
+yycrank+575,   0,              0,      
+yycrank+593,   0,              0,      
+yycrank+576,   0,              0,      
+yycrank+592,   0,              0,      
+yycrank+594,   0,              0,      
+yycrank+688,   yysvec+216,     0,      
+yycrank+728,   yysvec+217,     0,      
+yycrank+689,   yysvec+218,     0,      
+yycrank+786,   yysvec+219,     0,      
+yycrank+757,   yysvec+217,     0,      
+yycrank+741,   yysvec+221,     0,      
+yycrank+740,   yysvec+218,     0,      
+yycrank+692,   yysvec+223,     0,      
+yycrank+753,   yysvec+224,     0,      
+yycrank+805,   yysvec+218,     0,      
+yycrank+787,   yysvec+224,     0,      
+yycrank+789,   yysvec+224,     0,      
+yycrank+859,   0,              0,      
+yycrank+754,   0,              yyvstop+150,
+yycrank+869,   0,              0,      
+yycrank+895,   0,              yyvstop+152,
+yycrank+756,   0,              yyvstop+154,
+yycrank+790,   0,              yyvstop+156,
+yycrank+599,   yysvec+224,     yyvstop+158,
+yycrank+921,   0,              0,      
+yycrank+883,   0,              0,      
+yycrank+591,   0,              0,      
+yycrank+587,   0,              0,      
+yycrank+594,   0,              0,      
+yycrank+587,   0,              0,      
+yycrank+608,   0,              0,      
+yycrank+602,   0,              0,      
+yycrank+629,   0,              0,      
+yycrank+653,   0,              0,      
+yycrank+651,   0,              0,      
+yycrank+806,   0,              yyvstop+160,
+yycrank+947,   0,              yyvstop+163,
+yycrank+637,   0,              0,      
+yycrank+656,   0,              0,      
+yycrank+658,   0,              0,      
+yycrank+635,   0,              0,      
+yycrank+643,   0,              0,      
+yycrank+0,     yysvec+293,     0,      
+yycrank+661,   0,              0,      
+yycrank+663,   0,              0,      
+yycrank+668,   0,              0,      
+yycrank+660,   0,              0,      
+yycrank+0,     yysvec+293,     0,      
+yycrank+670,   0,              0,      
+yycrank+714,   0,              0,      
+yycrank+697,   0,              0,      
+yycrank+0,     0,              yyvstop+165,
+yycrank+0,     0,              yyvstop+167,
+yycrank+0,     0,              yyvstop+169,
+yycrank+0,     0,              yyvstop+171,
+yycrank+695,   0,              0,      
+yycrank+0,     yysvec+308,     0,      
+yycrank+717,   0,              0,      
+yycrank+0,     yysvec+308,     0,      
+yycrank+720,   0,              0,      
+yycrank+728,   0,              0,      
+yycrank+724,   0,              0,      
+yycrank+734,   0,              0,      
+yycrank+728,   0,              0,      
+yycrank+735,   0,              0,      
+yycrank+729,   0,              0,      
+yycrank+751,   0,              0,      
+yycrank+746,   0,              0,      
+yycrank+742,   0,              0,      
+yycrank+777,   0,              0,      
+yycrank+775,   0,              0,      
+yycrank+932,   0,              yyvstop+173,
+yycrank+933,   0,              yyvstop+175,
+yycrank+981,   0,              yyvstop+177,
+yycrank+984,   0,              yyvstop+179,
+yycrank+790,   0,              0,      
+yycrank+792,   0,              0,      
+yycrank+786,   0,              0,      
+yycrank+985,   0,              yyvstop+181,
+yycrank+787,   0,              0,      
+yycrank+830,   0,              0,      
+yycrank+832,   0,              0,      
+yycrank+840,   0,              0,      
+yycrank+857,   0,              0,      
+yycrank+856,   0,              0,      
+yycrank+849,   0,              0,      
+yycrank+996,   0,              yyvstop+183,
+yycrank+997,   0,              0,      
+yycrank+915,   yysvec+339,     yyvstop+185,
+yycrank+859,   0,              0,      
+yycrank+847,   0,              0,      
+yycrank+918,   yysvec+234,     yyvstop+187,
+yycrank+842,   0,              0,      
+yycrank+865,   0,              0,      
+yycrank+853,   0,              0,      
+yycrank+871,   0,              0,      
+yycrank+910,   0,              0,      
+yycrank+912,   0,              0,      
+yycrank+0,     yysvec+293,     0,      
+yycrank+910,   0,              0,      
+yycrank+970,   0,              0,      
+yycrank+915,   0,              0,      
+yycrank+917,   0,              0,      
+yycrank+0,     yysvec+308,     0,      
+yycrank+915,   0,              0,      
+yycrank+902,   0,              0,      
+yycrank+921,   0,              0,      
+yycrank+923,   0,              0,      
+yycrank+907,   0,              0,      
+yycrank+924,   0,              0,      
+yycrank+922,   0,              0,      
+yycrank+915,   0,              0,      
+yycrank+1016,  0,              yyvstop+189,
+yycrank+1001,  yysvec+324,     yyvstop+191,
+yycrank+969,   0,              0,      
+yycrank+1034,  0,              yyvstop+193,
+yycrank+1035,  0,              yyvstop+195,
+yycrank+1036,  0,              yyvstop+197,
+yycrank+1037,  0,              yyvstop+199,
+yycrank+1034,  yysvec+331,     yyvstop+201,
+yycrank+926,   0,              0,      
+yycrank+917,   0,              0,      
+yycrank+911,   0,              0,      
+yycrank+946,   0,              0,      
+yycrank+945,   0,              0,      
+yycrank+962,   0,              0,      
+yycrank+1062,  0,              0,      
+yycrank+0,     yysvec+347,     0,      
+yycrank+0,     yysvec+345,     0,      
+yycrank+0,     yysvec+347,     0,      
+yycrank+0,     yysvec+293,     0,      
+yycrank+960,   0,              0,      
+yycrank+0,     yysvec+293,     0,      
+yycrank+965,   0,              0,      
+yycrank+0,     yysvec+308,     0,      
+yycrank+0,     yysvec+308,     0,      
+yycrank+966,   0,              0,      
+yycrank+963,   0,              0,      
+yycrank+951,   0,              0,      
+yycrank+949,   0,              0,      
+yycrank+971,   0,              0,      
+yycrank+959,   0,              0,      
+yycrank+976,   0,              0,      
+yycrank+1043,  0,              0,      
+yycrank+959,   0,              0,      
+yycrank+1070,  0,              yyvstop+203,
+yycrank+0,     yysvec+377,     0,      
+yycrank+0,     yysvec+375,     0,      
+yycrank+0,     yysvec+377,     0,      
+yycrank+975,   0,              0,      
+yycrank+1072,  0,              0,      
+yycrank+0,     yysvec+347,     0,      
+yycrank+0,     yysvec+293,     0,      
+yycrank+0,     yysvec+308,     0,      
+yycrank+0,     yysvec+394,     0,      
+yycrank+0,     yysvec+392,     0,      
+yycrank+0,     yysvec+394,     0,      
+yycrank+976,   0,              0,      
+yycrank+1087,  0,              0,      
+yycrank+1071,  0,              yyvstop+205,
+yycrank+0,     yysvec+377,     0,      
+yycrank+1098,  0,              yyvstop+207,
+yycrank+0,     yysvec+394,     0,      
+yycrank+0,     0,              yyvstop+209,
+yycrank+1099,  0,              yyvstop+211,
+yycrank+1108,  yysvec+414,     yyvstop+213,
+0,     0,      0};
+struct yywork *yytop = yycrank+1165;
+struct yysvf *yybgin = yysvec+1;
+char yymatch[] = {
+  0,   1,   1,   1,   1,   1,   1,   1, 
+  1,   9,   1,   1,   1,   1,   1,   1, 
+  1,   1,   1,   1,   1,   1,   1,   1, 
+  1,   1,   1,   1,   1,   1,   1,   1, 
+  9,   1,   1,   1,   1,   1,   1,   1, 
+  1,   1,   1,   1,   1,   1,   1,   1, 
+ 48,  48,  50,  51,  51,  51,  54,  54, 
+ 54,  54,   1,   1,   1,   1,   1,   1, 
+  1,   1,   1,   1,   1,   1,   1,   1, 
+  1,   1,   1,   1,   1,   1,   1,   1, 
+  1,   1,   1,   1,   1,   1,   1,   1, 
+  1,   1,   1,   1,   1,   1,   1,   1, 
+  1,  97,  97,  97,  97,  97,  97,  97, 
+ 97,  97,   1, 107, 107, 107, 110, 110, 
+110, 110, 110, 110, 110, 110, 110, 110, 
+110, 110,   1,   1,   1,   1,   1,   1, 
+  1,   1,   1,   1,   1,   1,   1,   1, 
+  1,   1,   1,   1,   1,   1,   1,   1, 
+  1,   1,   1,   1,   1,   1,   1,   1, 
+  1,   1,   1,   1,   1,   1,   1,   1, 
+  1,   1,   1,   1,   1,   1,   1,   1, 
+  1,   1,   1,   1,   1,   1,   1,   1, 
+  1,   1,   1,   1,   1,   1,   1,   1, 
+  1,   1,   1,   1,   1,   1,   1,   1, 
+  1,   1,   1,   1,   1,   1,   1,   1, 
+  1,   1,   1,   1,   1,   1,   1,   1, 
+  1,   1,   1,   1,   1,   1,   1,   1, 
+  1,   1,   1,   1,   1,   1,   1,   1, 
+  1,   1,   1,   1,   1,   1,   1,   1, 
+  1,   1,   1,   1,   1,   1,   1,   1, 
+  1,   1,   1,   1,   1,   1,   1,   1, 
+  1,   1,   1,   1,   1,   1,   1,   1, 
+0};
+char yyextra[] = {
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0};
+/*     Copyright (c) 1989 AT&T */
+/*       All Rights Reserved   */
+
+/*     THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T     */
+/*     The copyright notice above does not evidence any        */
+/*     actual or intended publication of such source code.     */
+
+#pragma ident  "@(#)ncform     6.8     95/02/11 SMI"
+
diff --git a/zotnet/tws/dtimep.lex b/zotnet/tws/dtimep.lex
new file mode 100644 (file)
index 0000000..467dc85
--- /dev/null
@@ -0,0 +1,417 @@
+%e 2000
+%p 5000
+%n 1000
+%a 4000
+%START Z
+sun    (sun(day)?)
+mon    (mon(day)?)
+tue    (tue(sday)?)
+wed    (wed(nesday)?)
+thu    (thu(rsday)?)
+fri    (fri(day)?)
+sat    (sat(urday)?)
+
+DAY    ({sun}|{mon}|{tue}|{wed}|{thu}|{fri}|{sat})
+
+jan    (jan(uary)?)
+feb    (feb(ruary)?)
+mar    (mar(ch)?)
+apr    (apr(il)?)
+may    (may)
+jun    (jun(e)?)
+jul    (jul(y)?)
+aug    (aug(ust)?)
+sep    (sep(tember)?)
+oct    (oct(ober)?)
+nov    (nov(ember)?)
+dec    (dec(ember)?)
+
+MONTH  ({jan}|{feb}|{mar}|{apr}|{may}|{jun}|{jul}|{aug}|{sep}|{oct}|{nov}|{dec})
+
+w      ([ \t]*)
+W      ([ \t]+)
+D      ([0-9]?[0-9])
+d      [0-9]
+%{
+#include <h/nmh.h>
+#include <tws.h>
+#if !defined(HAVE_TM_GMTOFF) && !defined(HAVE_TZSET)
+# include <sys/timeb.h>
+#endif
+
+#if !defined(HAVE_TM_GMTOFF) && defined(HAVE_TZSET)
+extern int  daylight;
+extern long timezone;
+extern char *tzname[];
+#endif
+
+/*
+ * Patchable flag that says how to interpret NN/NN/NN dates. When
+ * true, we do it European style: DD/MM/YY. When false, we do it
+ * American style: MM/DD/YY.  Of course, these are all non-RFC822
+ * compliant.
+ */
+int europeandate = 0;
+
+/*
+ * Table to convert month names to numeric month.  We use the
+ * fact that the low order 5 bits of the sum of the 2nd & 3rd
+ * characters of the name is a hash with no collisions for the 12
+ * valid month names.  (The mask to 5 bits maps any combination of
+ * upper and lower case into the same hash value).
+ */
+static int month_map[] = {
+       0,
+       6,      /* 1 - Jul */
+       3,      /* 2 - Apr */
+       5,      /* 3 - Jun */
+       0,
+       10,     /* 5 - Nov */
+       0,
+       1,      /* 7 - Feb */
+       11,     /* 8 - Dec */
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,      /*15 - Jan */
+       0,
+       0,
+       0,
+       2,      /*19 - Mar */
+       0,
+       8,      /*21 - Sep */
+       0,
+       9,      /*23 - Oct */
+       0,
+       0,
+       4,      /*26 - May */
+       0,
+       7       /*28 - Aug */
+};
+/*
+ * Same trick for day-of-week using the hash function
+ *  (c1 & 7) + (c2 & 4)
+ */
+static int day_map[] = {
+       0,
+       0,
+       0,
+       6,      /* 3 - Sat */
+       4,      /* 4 - Thu */
+       0,
+       5,      /* 6 - Fri */
+       0,      /* 7 - Sun */
+       2,      /* 8 - Tue */
+       1       /* 9 - Mon */,
+       0,
+       3       /*11 - Wed */
+};
+#define SETDAY { tw.tw_wday= day_map[(cp[0] & 7) + (cp[1] & 4)];\
+               tw.tw_flags &= ~TW_SDAY; tw.tw_flags |= TW_SEXP;\
+               cp += 2; }
+#define SETMONTH { tw.tw_mon = month_map[(cp[0] + cp[1]) & 0x1f]; gotdate++;\
+                cp += 2;\
+                SKIPD;}
+#define        CVT1OR2 (i=(*cp++ - '0'), isdigit(*cp)? i*10 + (*cp++ - '0') : i)
+#define        CVT2      ((cp[0] - '0')*10 + (cp[1] - '0'))
+#define        CVT4    ((((cp[0] - '0')*10 + (cp[1] - '0'))*10 + \
+                                     (cp[2] - '0'))*10 + (cp[3] - '0'))
+#define SKIPD  { while ( !isdigit(*cp++) ) ;  --cp; }
+#define EXPZONE        { tw.tw_flags &= ~TW_SZONE; tw.tw_flags |= TW_SZEXP; }
+#define ZONE(x)        { tw.tw_zone=(x); EXPZONE; }
+#define ZONED(x) { ZONE(x); tw.tw_flags |= TW_DST; }
+#define        LC(c)   (isupper (c) ? tolower (c) : (c))
+
+#ifdef DSTXXX
+# ifdef TIME_WITH_SYS_TIME
+#  include <sys/time.h>
+#  include <time.h>
+# else
+#  ifdef HAVE_SYS_TIME_H
+#   include <sys/time.h>
+#  else
+#   include <time.h>
+#  endif
+# endif
+
+static void
+zonehack (struct tws *tw)
+{
+    register struct tm *tm;
+
+    if (dmktime (tw) == (time_t) -1)
+       return;
+
+    tm = localtime (&tw->tw_clock);
+    if (tm->tm_isdst) {
+       tw->tw_flags |= TW_DST;
+       tw->tw_zone -= 60;
+    }
+}
+#endif /* DSTXXX */
+%}
+%%
+%{
+struct tws *
+dparsetime (char *str)
+{
+       register int i;
+       static struct tws tw;
+       register char *cp;
+       register int gotdate = 0;
+       time_t tclock;
+
+#ifdef HAVE_TM_GMTOFF
+       struct tm *tm;
+       time_t clock;
+#else
+# ifndef HAVE_TZSET
+       struct timeb tb;
+# endif /* not HAVE_TZSET */
+#endif /* HAVE_TM_GMTOFF */
+
+       start_cond = 0;
+
+       /* Zero out the struct. */
+       memset( (char *) &tw, 0, sizeof(tw));
+
+       /* Set default time zone. */
+#ifdef HAVE_TM_GMTOFF
+       time (&clock);
+       tm = localtime(&clock);
+       tw.tw_zone = tm->tm_gmtoff / 60;
+       if (tm->tm_isdst)                       /* if DST is in effect */
+               tw.tw_zone -= 60;               /* reset to normal offset */
+#else
+# ifdef HAVE_TZSET
+       tzset();
+       tw.tw_zone = -(timezone / 60);
+# else
+       ftime(&tb);
+       tw.tw_zone = -tb.timezone;
+# endif /* HAVE_TZSET */
+#endif /* HAVE_TM_GMTOFF */
+
+       while (isspace(*str))
+               str++;
+       while (1)
+               switch (cp = str, *cp ? lex_string( &str, start_cond) : 0) {
+
+               case -1:
+                       if (!gotdate || tw.tw_year == 0)
+                               return (struct tws *)0;
+                       /* fall through */
+               case 0:
+                       if (tw.tw_year == 0) {
+                               /* Set default year. */
+                               time (&tclock);
+                               tw.tw_year = localtime(&tclock)->tm_year + 1900;
+                       } else if (tw.tw_year < 100) {
+                               /* assume no 2-digit years > 1999 */
+                               tw.tw_year += 1900;
+                       }
+                       return &tw;
+
+%}
+{DAY}","?{w}                           SETDAY;
+"("{DAY}")"(","?)                      {
+                                       cp++;
+                                       SETDAY;
+                                       }
+{D}(("-"{D}"-")|("/"{D}"/")){D}?{d}{d}{w}      {
+                                       if (europeandate) {
+                                               /* European: DD/MM/YY */
+                                               tw.tw_mday = CVT1OR2;
+                                               cp++;
+                                               tw.tw_mon  = CVT1OR2 - 1;
+                                       } else {
+                                               /* American: MM/DD/YY */
+                                               tw.tw_mon  = CVT1OR2 - 1;
+                                               cp++;
+                                               tw.tw_mday = CVT1OR2;
+                                       }
+                                       cp++;
+                                       for (i = 0; isdigit(*cp); )
+                                               i = i*10 + (*cp++ - '0');
+                                       tw.tw_year = i;
+                                       gotdate++;      /* XXX */
+                                       }
+{D}("/"|"-"){D}{w}                     {
+                                       if (europeandate) {
+                                               tw.tw_mday = CVT1OR2; cp++;
+                                               tw.tw_mon  = CVT1OR2 - 1;
+                                       } else {
+                                               tw.tw_mon = CVT1OR2 - 1; cp++;
+                                               tw.tw_mday  = CVT1OR2;
+                                       }
+                                       gotdate++;
+                                       }
+{D}{w}(-)?{w}{MONTH}{w}(-)?{w}{D}?{d}{d}({W}at)?{w}    {
+                                       tw.tw_mday = CVT1OR2;
+                                       while ( !isalpha(*cp++) )
+                                               ;
+                                       SETMONTH;
+                                       for (i = 0; isdigit(*cp); )
+                                               i = i*10 + (*cp++ - '0');
+                                       tw.tw_year = i;
+                                       }
+{D}"-"?{MONTH}({W}at)?{w}               {
+                                        tw.tw_mday = CVT1OR2;
+                                        while ( ! isalpha( *cp++ ) )
+                                                ;
+                                        SETMONTH;
+                                        }
+{MONTH}{W}{D}","{W}{D}?{d}{d}{w}       {
+                                       cp++;
+                                       SETMONTH;
+                                       tw.tw_mday = CVT1OR2;
+                                       SKIPD;
+                                       for (i = 0; isdigit(*cp); )
+                                               i = i*10 + (*cp++ - '0');
+                                       tw.tw_year = i;
+                                       }
+{MONTH}{W}{D}{w}                       {
+                                       cp++;
+                                       SETMONTH;
+                                       tw.tw_mday = CVT1OR2;
+                                       }
+
+{D}:{D}:{D}{W}19[6-9]{d}               {       /* hack: ctime w/o TZ */
+                                       tw.tw_hour = CVT1OR2; cp++;
+                                       tw.tw_min  = CVT1OR2; cp++;
+                                       tw.tw_sec  = CVT1OR2;
+                                       SKIPD;
+                                       tw.tw_year = CVT4; cp+=4;
+                                       }
+{D}:{D}:{D}{w}                         {
+                                       tw.tw_hour = CVT1OR2; cp++;
+                                       tw.tw_min  = CVT1OR2; cp++;
+                                       tw.tw_sec  = CVT1OR2;
+                                       BEGIN Z;
+                                       }
+{D}:{D}{w}                             {
+                                       tw.tw_hour = CVT1OR2; cp++;
+                                       tw.tw_min = CVT1OR2;
+                                       BEGIN Z;
+                                       }
+{D}:{D}{w}am{w}                                {
+                                       tw.tw_hour = CVT1OR2; cp++;
+                                       if (tw.tw_hour == 12)
+                                               tw.tw_hour = 0;
+                                       tw.tw_min  = CVT1OR2;
+                                       BEGIN Z;
+                                       }
+{D}:{D}:{D}{w}am{w}                    {
+                                       tw.tw_hour = CVT1OR2; cp++;
+                                       if (tw.tw_hour == 12)
+                                               tw.tw_hour = 0;
+                                       tw.tw_min  = CVT1OR2; cp++;
+                                       tw.tw_sec  = CVT1OR2;
+                                       BEGIN Z;
+                                       }
+{D}:{D}{w}pm{w}                                {
+                                       tw.tw_hour = CVT1OR2; cp++;
+                                       if (tw.tw_hour != 12)
+                                               tw.tw_hour += 12;
+                                       tw.tw_min  = CVT1OR2;
+                                       BEGIN Z;
+                                       }
+{D}:{D}:{D}{w}pm{w}                    {
+                                       tw.tw_hour = CVT1OR2; cp++;
+                                       if (tw.tw_hour != 12)
+                                               tw.tw_hour += 12;
+                                       tw.tw_min  = CVT1OR2; cp++;
+                                       tw.tw_sec  = CVT1OR2;
+                                       BEGIN Z;
+                                       }
+[0-2]{d}{d}{d}{d}{d}{w}                        {
+                                       tw.tw_hour = CVT2; cp+=2;
+                                       tw.tw_min  = CVT2; cp+=2;
+                                       tw.tw_sec  = CVT2; cp+=2;
+                                       BEGIN Z;
+                                       }
+19[6-9]{d}{w}                          {
+                                       /*
+                                        * Luckly, 4 digit times in the range
+                                        * 1960-1999 aren't legal as hour
+                                        * and minutes.
+                                        */
+                                       tw.tw_year = CVT4; cp+=4;
+                                       }
+[0-2]{d}{d}{d}{w}                      {
+                                       if (tw.tw_hour || tw.tw_min 
+                                                           || tw.tw_sec) {
+                                           tw.tw_year = CVT4; cp+=4;
+                                           tw.tw_zone = 0;
+                                       } else {
+                                           tw.tw_hour = CVT2; cp+=2;
+                                           tw.tw_min  = CVT2; cp+=2;
+                                           BEGIN Z;
+                                       }
+                                       }
+<Z>"-"?ut                              ZONE(0 * 60);
+<Z>"-"?gmt                             ZONE(0 * 60);
+<Z>"-"?jst                             ZONE(2 * 60);
+<Z>"-"?jdt                             ZONED(2 * 60);
+<Z>"-"?est                             ZONE(-5 * 60);
+<Z>"-"?edt                             ZONED(-5 * 60);
+<Z>"-"?cst                             ZONE(-6 * 60);
+<Z>"-"?cdt                             ZONED(-6 * 60);
+<Z>"-"?mst                             ZONE(-7 * 60);
+<Z>"-"?mdt                             ZONED(-7 * 60);
+<Z>"-"?pst                             ZONE(-8 * 60);
+<Z>"-"?pdt                             ZONED(-8 * 60);
+<Z>"-"?nst                             ZONE(-(3 * 60 + 30));
+<Z>"-"?ast                             ZONE(-4 * 60);
+<Z>"-"?adt                             ZONED(-4 * 60);
+<Z>"-"?yst                             ZONE(-9 * 60);
+<Z>"-"?ydt                             ZONED(-9 * 60);
+<Z>"-"?hst                             ZONE(-10 * 60);
+<Z>"-"?hdt                             ZONED(-10 * 60);
+<Z>"-"?bst                             ZONED(-1 * 60);
+<Z>[a-i]                               {
+                                       tw.tw_zone = 60 * (('a'-1) - LC(*cp));
+                                       EXPZONE; 
+                                       }
+<Z>[k-m]                               {
+                                       tw.tw_zone = 60 * ('a' - LC(*cp));
+                                       EXPZONE; 
+                                       }
+<Z>[n-y]                               {
+                                       tw.tw_zone = 60 * (LC(*cp) - 'm');
+                                       EXPZONE; 
+                                       }
+<Z>"+"[0-1]{d}{d}{d}                   {
+                                       cp++;
+                                       tw.tw_zone = ((cp[0] * 10 + cp[1])
+                                                    -('0' * 10   + '0'))*60
+                                                   +((cp[2] * 10 + cp[3])
+                                                    -('0' * 10   + '0'));
+                                       EXPZONE;
+#ifdef DSTXXX
+                                       zonehack (&tw);
+#endif /* DSTXXX */
+                                       cp += 4;
+                                       }
+<Z>"-"[0-1]{d}{d}{d}                   {
+                                       cp++;
+                                       tw.tw_zone = (('0' * 10   + '0')
+                                                    -(cp[0] * 10 + cp[1]))*60
+                                                   +(('0' * 10   + '0')
+                                                    -(cp[2] * 10 + cp[3]));
+                                       EXPZONE;
+#ifdef DSTXXX
+                                       zonehack (&tw);
+#endif /* DSTXXX */
+                                       cp += 4;
+                                       }
+<Z>{W}{d}{d}{d}{d}                     {
+                                       SKIPD;
+                                       tw.tw_year = CVT4; cp+=4;
+                                       }
+\n     |
+{W}    ;
+%%
diff --git a/zotnet/tws/lexedit.sed b/zotnet/tws/lexedit.sed
new file mode 100644 (file)
index 0000000..601df0f
--- /dev/null
@@ -0,0 +1,20 @@
+2,/^extern int yylineno;$/c\
+static int start_cond = 0;\
+#define BEGIN start_cond =
+/^struct yysvf \*yyestate;$/,/^extern struct yysvf yysvec/d
+/^# define YYNEWLINE /,/^[     ]*int nstr;/d
+/^[    ]*while((nstr = yylook()/,/^[   ]*if(yywrap()) /d
+/^case -1:$/,/^} return(0); }/c\
+       default: return(0);\
+} }
+/^struct yysvf *yybgin = yysvec+1;$/d
+/^int yylineno /,$d
+/^# define YYTYPE short/c\
+# define YYTYPE int
+/^unsigned char yymatch\[\] = {/c\
+char yymatch[] = {
+/^unsigned char yyextra\[\] = {/c\
+char yyextra[] = {
+/^# define YYTYPE unsigned short/c\
+# define YYTYPE int
+/^if (__once_yylex) {$/,/if(yymbcurmax<=0) yymbcurmax=MB_CUR_MAX;$/d
diff --git a/zotnet/tws/lexstring.c b/zotnet/tws/lexstring.c
new file mode 100644 (file)
index 0000000..91506e7
--- /dev/null
@@ -0,0 +1,257 @@
+
+/*
+ * lexstring.c
+ *
+ * $Id$
+ */
+
+#define ONECASE 1
+
+#include <stdio.h>
+#include <ctype.h>
+
+#define YYLERR yysvec
+#define YYTYPE int
+#define YYLMAX 256
+
+struct yysvf { 
+#ifndef hpux
+       struct yywork *yystoff;
+#else  /* hpux */
+       int yystoff;
+#endif /* hpux */
+       struct yysvf *yyother;
+       int *yystops;
+};
+
+struct yywork { 
+    YYTYPE verify;
+    YYTYPE advance; 
+}; 
+
+extern int yyvstop[];
+extern struct yywork yycrank[];
+extern struct yysvf yysvec[];
+extern char yymatch[];
+extern char yyextra[];
+
+#ifdef LEXDEBUG
+static int debug = 0;
+#endif /* LEXDEBUG */
+
+#ifdef ONECASE
+static char case_map[] = {
+     0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
+    10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
+    20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
+    30,  31,  32,  33,  34,  35,  36,  37,  38,  39,
+    40,  41,  42,  43,  44,  45,  46,  47,  48,  49,
+    50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
+    60,  61,  62,  63,  64,  97,  98,  99, 100, 101,
+   102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+   112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
+   122,  91,  92,  93,  94,  95,  96,  97,  98,  99,
+   100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+   110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
+   120, 121, 122, 123, 124, 125, 126, 127,
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+   0, 0, 0, 0, 0, 0, 0, 0
+};
+#endif /* ONECASE */
+
+
+int
+lex_string (char **strptr, int start_cond)
+{
+    struct yysvf *state, **lsp;
+    struct yywork *tran;
+    int statenum;
+    int ch;
+    char *cp = *strptr;
+    int *found;
+    struct yysvf *yylstate[YYLMAX];
+
+    /* start off machines */
+    lsp = yylstate;
+    statenum = 1 + start_cond;
+    state = yysvec + statenum;
+    for (;;) {
+#ifdef LEXDEBUG
+               if (debug) {
+                       fprintf(stderr,"%d ",statenum - 1);
+               }
+#endif
+#ifndef hpux
+               tran = state->yystoff;
+#else  /* hpux */
+               tran = &yycrank[state->yystoff];
+#endif /* hpux */
+               if(tran == yycrank)
+                       /* may not be any transitions */
+                       if (state->yyother == 0 ||
+#ifndef hpux
+                           state->yyother->yystoff == yycrank)
+#else  /* hpux */
+                           state->yyother->yystoff == 0)
+#endif /* hpux */
+                               break;
+
+#ifdef ONECASE
+               ch = case_map[(unsigned char) *cp++];
+#else  /*  not ONECASE */
+               ch = *cp++;
+#endif /* ONECASE */
+#ifdef LEXDEBUG
+               if (debug) {
+                       fprintf(stderr,"(");
+                       allprint(ch);
+                       fprintf(stderr, ")");
+               }
+#endif
+tryagain:
+#ifndef hpux
+               if ( tran > yycrank){
+#else  /* hpux */
+               if ( (int)tran > (int)yycrank){
+#endif /* hpux */
+                       tran += ch;
+                       if (tran->verify == statenum){
+                               if ((statenum = tran->advance) == 0){
+                                       /* error transitions */
+                                       --cp;
+                                       break;
+                               }
+                               state = statenum + yysvec;
+                               *lsp++ = state;
+                               goto contin;
+                       }
+
+#ifndef hpux
+               } else if(tran < yycrank) {
+#else  /* hpux */
+               } else if( (int)tran < (int)yycrank) {
+#endif /* hpux */
+                       tran = yycrank+(yycrank-tran) + ch;
+#ifdef LEXDEBUG
+                       if (debug) {
+                               fprintf(stderr," compressed");
+                       }
+#endif
+                       if (tran->verify == statenum){
+                               if ((statenum = tran->advance) == 0)
+                                       /* error transitions */
+                                       break;
+
+                               state = statenum + yysvec;
+                               *lsp++ = state;
+                               goto contin;
+                       }
+                       tran += (yymatch[ch] - ch);
+#ifdef LEXDEBUG
+                       if (debug) {
+                               fprintf(stderr,"(fb ");
+                               allprint(yymatch[ch]);
+                               fprintf(stderr,")");
+                       }
+#endif
+                       if (tran->verify == statenum){
+                               if((statenum = tran->advance) == 0)
+                                       /* error transition */
+                                       break;
+
+                               state = statenum + yysvec;
+                               *lsp++ = state;
+                               goto contin;
+                       }
+               }
+               if ((state = state->yyother) &&
+#ifndef hpux
+                   (tran = state->yystoff) != yycrank){
+#else  /* hpux */
+                   (tran = &yycrank[state->yystoff]) != yycrank){
+#endif /* hpux */
+                       statenum = state - yysvec;
+#ifdef LEXDEBUG
+                       if (debug) {
+                               fprintf(stderr,"fb %d", statenum - 1);
+                       }
+#endif
+                       goto tryagain;
+               } else
+                       break;
+
+contin:
+#ifdef LEXDEBUG
+               if (debug) {
+                       fprintf(stderr,">");
+               }
+#endif
+               ;
+       }
+#ifdef LEXDEBUG
+       if (debug) {
+               fprintf(stderr,"\nStopped in state %d (",*(lsp-1)-yysvec-1);
+               allprint(ch);
+               fprintf(stderr, ") ");
+       }
+#endif
+       while (lsp-- > yylstate){
+               if (*lsp != 0 && (found= (*lsp)->yystops) && *found > 0){
+                       if(yyextra[*found]){
+                               /* must backup */
+                               ch = -*found;
+                               do {
+                                       while (*found && *found++ != ch)
+                                               ;
+                                } while (lsp > yylstate &&
+                                         (found = (*--lsp)->yystops));
+                       }
+#ifdef LEXDEBUG
+                       if (debug) {
+                               fprintf(stderr," Match \"");
+                               for ( cp = *strptr;
+                                     cp <= ((*strptr)+(lsp-yylstate));
+                                     cp++)
+                                       allprint( *cp );
+                               fprintf(stderr,"\" action %d\n",*found);
+                       }
+#endif
+                       *strptr += (lsp - yylstate + 1);
+                       return(*found);
+               }
+       }
+       /* the string didn't match anything - if we're looking at
+        * eos, just return 0.  Otherwise, bump the string pointer
+        * and return -1.
+        */
+#ifdef LEXDEBUG
+       if (debug) {
+               fprintf(stderr," No match\n");
+       }
+#endif /* LEXDEBUG */
+       if ( **strptr ) {
+               (*strptr)++;
+               return (-1);
+       }
+       return (0);
+}
+
+#ifdef LEXDEBUG
+void
+allprint(char c)
+{
+       if ( c < 32 ) {
+           putc( '^', stderr );
+           c += 32;
+       } else if ( c == 127 ) {
+           putc( '^', stderr );
+           c = '?';
+       }
+       putc( c, stderr );
+}
+#endif /* LEXDEBUG */
diff --git a/zotnet/tws/tws.h b/zotnet/tws/tws.h
new file mode 100644 (file)
index 0000000..29702db
--- /dev/null
@@ -0,0 +1,62 @@
+
+/*
+ * tws.h
+ *
+ * $Id$
+ */
+
+/* DST vs. GMT nonsense */
+#define        DSTXXX
+
+struct tws {
+    int tw_sec;                /* seconds after the minute - [0, 61] */
+    int tw_min;                /* minutes after the hour - [0, 59]   */
+    int tw_hour;       /* hour since midnight - [0, 23]      */
+    int tw_mday;       /* day of the month - [1, 31]         */
+    int tw_mon;                /* months since January - [0, 11]     */
+    int tw_year;       /* 4 digit year (ie, 1997)            */
+    int tw_wday;       /* days since Sunday - [0, 6]         */
+    int tw_yday;       /* days since January 1 - [0, 365]    */
+    int tw_zone;
+    time_t tw_clock;   /* if != 0, corresponding calendar value */
+    int tw_flags;
+};
+
+#define        TW_NULL  0x0000
+
+#define        TW_SDAY  0x0003 /* how day-of-week was determined */
+#define        TW_SNIL  0x0000 /*   not given                    */
+#define        TW_SEXP  0x0001 /*   explicitly given             */
+#define        TW_SIMP  0x0002 /*   implicitly given             */
+
+#define        TW_SZONE 0x0004 /* how timezone was determined    */
+#define        TW_SZNIL 0x0000 /*   not given                    */
+#define        TW_SZEXP 0x0004 /*   explicitly given             */
+
+#define        TW_DST   0x0010 /* daylight savings time          */
+#define        TW_ZONE  0x0020 /* use numeric timezones only     */
+
+#define        dtwszone(tw) dtimezone (tw->tw_zone, tw->tw_flags)
+
+extern char *tw_dotw[];
+extern char *tw_ldotw[];
+extern char *tw_moty[];
+
+/*
+ * prototypes
+ */
+char *dtime (time_t *, int);
+char *dtimenow (int);
+char *dctime (struct tws *);
+struct tws *dlocaltimenow (void);
+struct tws *dlocaltime (time_t *);
+struct tws *dgmtime (time_t *);
+char *dasctime (struct tws *, int);
+char *dtimezone (int, int);
+void twscopy (struct tws *, struct tws *);
+int twsort (struct tws *, struct tws *);
+time_t dmktime (struct tws *);
+void set_dotw (struct tws *);
+
+struct tws *dparsetime (char *);
+