--- /dev/null
+
+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.
--- /dev/null
+
+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.
--- /dev/null
+
+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.
--- /dev/null
+#
+# 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
--- /dev/null
+#
+# 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.
+
+--------------------------------------
--- /dev/null
+
+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
+
--- /dev/null
+#
+# 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
+
--- /dev/null
+#
+# 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/
--- /dev/null
+[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.
--- /dev/null
+#
+# 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
+
--- /dev/null
+
+/****** 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
--- /dev/null
+
+# 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
+])
--- /dev/null
+/* 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
--- /dev/null
+#
+# 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
+
--- /dev/null
+
+/*
+ * 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;
+
--- /dev/null
+#!/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\";"
--- /dev/null
+#! /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 ""
--- /dev/null
+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 ""
--- /dev/null
+;
+; 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)
--- /dev/null
+#
+# 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
+
--- /dev/null
+To:
+cc:
+Subject:
+--------
--- /dev/null
+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:
--- /dev/null
+Resent-To:
+Resent-cc:
--- /dev/null
+To:
+cc:
+Subject:
+--------
--- /dev/null
+width=10000
+body:nocomponent,overflowoffset=0
--- /dev/null
+width=80,overflowoffset=10
+leftadjust,compress,compwidth=9
+Date:formatfield="%<(nodate{text})%{text}%|%(tws{text})%>"
+From:
+Subject:
+:
+body:nocomponent,overflowoffset=0,noleftadjust,nocompress
--- /dev/null
+; 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
--- /dev/null
+; 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
--- /dev/null
+; 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
+:
--- /dev/null
+; mhl.reply
+;
+; default message filter for `repl' (repl -format)
+;
+body:component="> ",overflowtext="> ",overflowoffset=0
--- /dev/null
+#! /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
--- /dev/null
+#! /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"
+
--- /dev/null
+#
+# 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
--- /dev/null
+%(lit)%(formataddr{addresses})\
+%<(nonnull)%(void(width))%(putaddr Resent-To: )\n%>\
+Resent-Fcc: outbox
--- /dev/null
+%; 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%>\
+--------
--- /dev/null
+%; 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\
+--------
--- /dev/null
+%; 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}>>%>
--- /dev/null
+%<(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})
--- /dev/null
+%; 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}>>%>
--- /dev/null
+%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}%>
--- /dev/null
+%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}%>
--- /dev/null
+%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}%>
--- /dev/null
+%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}>>%>
--- /dev/null
+#!/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
--- /dev/null
+#
+# 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
+
--- /dev/null
+
+/*
+ * 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 *);
--- /dev/null
+
+/*
+ * 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;
--- /dev/null
+
+/*
+ * 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);
+
--- /dev/null
+
+/*
+ * 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 */
--- /dev/null
+
+/*
+ * 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 **);
--- /dev/null
+/*
+ * 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 *));
+
--- /dev/null
+
+/*
+ * 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>
+
--- /dev/null
+
+/*
+ * 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 }
+};
--- /dev/null
+
+/*
+ * 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
+
--- /dev/null
+
+/*
+ * 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)
+
--- /dev/null
+
+/*
+ * 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 **);
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+
+/*
+ * 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. */
+
--- /dev/null
+/*
+ * 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
--- /dev/null
+
+/*
+ * picksbr.h -- definitions for picksbr.c
+ *
+ * $Id$
+ */
+
+/*
+ * prototypes
+ */
+int pcompile (char **, char *);
+int pmatches (FILE *, int, long, long);
--- /dev/null
+
+/*
+ * 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
--- /dev/null
+
+/*
+ * 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 *);
+
--- /dev/null
+
+/*
+ * 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 */
--- /dev/null
+
+/*
+ * 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);
--- /dev/null
+
+/*
+ * 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);
--- /dev/null
+
+/*
+ * 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);
+
--- /dev/null
+#! /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
--- /dev/null
+#
+# 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
+
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
+.\}
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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.
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" $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
+..
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+.\"
+.\" %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
--- /dev/null
+#! /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
--- /dev/null
+#
+# 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
+
--- /dev/null
+#
+# 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
+
--- /dev/null
+
+/*
+ * 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 */
--- /dev/null
+#
+# 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
+
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
+
--- /dev/null
+#
+# 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
+
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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)
+
--- /dev/null
+#
+# 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
+
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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, "-");
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
+
--- /dev/null
+
+/*
+ * 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);
+ }
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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");
+}
--- /dev/null
+
+/*
+ * 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();
+}
--- /dev/null
+
+/*
+ * 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
+}
+
--- /dev/null
+
+/*
+ * done.c -- terminate the program
+ *
+ * $Id$
+ */
+
+#include <h/mh.h>
+
+void
+done (int status)
+{
+ exit (status);
+}
--- /dev/null
+
+/*
+ * 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
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * fmt_def.c -- some defines for sbr/fmt_scan.c
+ *
+ * $Id$
+ */
+
+#include <h/addrsbr.h>
+
+int fmt_norm = AD_NAME;
--- /dev/null
+
+/*
+ * 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';
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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 */
+}
--- /dev/null
+
+/*
+ * 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;
+ }
+ }
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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 */
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+ }
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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 */
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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);
+}
+
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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 = "";
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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++;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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 */
+ }
+ }
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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);
+ }
+}
+
--- /dev/null
+
+/*
+ * 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 == '=');
+}
--- /dev/null
+
+/*
+ * 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
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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));
+ }
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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));
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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 */
+}
--- /dev/null
+#
+# 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 "};"
+}
--- /dev/null
+
+/*
+ * 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
+}
+
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+ }
+ }
+ }
+}
--- /dev/null
+#
+# 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
+
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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 */
--- /dev/null
+
+/*
+ * 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);
+}
+
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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");
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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 */
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+ }
+}
--- /dev/null
+
+/*
+ * 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 */
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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)));
+ }
+}
--- /dev/null
+
+/*
+ * 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);
+}
+
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+ }
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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);
+}
+
--- /dev/null
+
+/*
+ * 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;
+ }
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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 */
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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;
+ }
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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 */
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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);
+}
+
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+ }
+}
--- /dev/null
+
+/*
+ * 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;
+ }
+}
--- /dev/null
+
+/*
+ * 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;
+ }
+ }
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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);
+}
+
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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;
+ }
+ }
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+ }
+}
--- /dev/null
+
+/*
+ * 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
--- /dev/null
+
+/*
+ * 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;
+}
+
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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 *) <c) == 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);
+}
+
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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);
+ }
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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 */
+}
--- /dev/null
+
+/*
+ * 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 *) <c) == 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);
+}
--- /dev/null
+#
+# 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
+
--- /dev/null
+#
+# 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
+
--- /dev/null
+
+/*
+ * 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 *);
+
--- /dev/null
+
+/*
+ * 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;
+}
+
--- /dev/null
+#
+# 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
+
--- /dev/null
+
+/*
+ * 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;
+ }
+}
--- /dev/null
+
+/*
+ * 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 *);
+
--- /dev/null
+#
+# 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
+
--- /dev/null
+
+/*
+ * 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 = ∅
+ 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;
+}
+
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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;
--- /dev/null
+#
+# 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
+
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+#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"
+
--- /dev/null
+%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} ;
+%%
--- /dev/null
+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
--- /dev/null
+
+/*
+ * 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 */
--- /dev/null
+
+/*
+ * 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 *);
+