v18i033: Mail user's shell version 6.4, Part11/19

Rich Salz rsalz at bbn.com
Fri Mar 17 02:00:21 AEST 1989


Submitted-by: Dan Heller <island!argv at sun.com>
Posting-number: Volume 18, Issue 33
Archive-name: mush6.4/part11



#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 11 (of 19)."
# Contents:  cmd_help msgs.c
# Wrapped by rsalz at papaya.bbn.com on Mon Mar 13 19:25:17 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'cmd_help' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cmd_help'\"
else
echo shar: Extracting \"'cmd_help'\" \(27197 characters\)
sed "s/^X//" >'cmd_help' <<'END_OF_FILE'
X/* @(#)cmd_help	1.4	10/24/88 (Dan heller) */
X
X%?%
XThe `?' command will give you a list of legal commands.  Most
Xcommands accept -? as an option.  This will give you specialized
Xhelp with that particular command.
X%%
X
X%ignore%
X      ignore/unignore [headers]
X
XUse this command to set the message headers you would like not
Xto be printed when you read a message. If no header specified,
Xthen a list of all headers currently being ignored is printed.
XYou must specify a header for unignore.
X
XYou can set the variable "alwaysignore" to force normally
Xignored headers to be ignored while saving messages, forwarding
Xmessages or including messages into message buffers.
X%%
X
X%set%
X      set/unset [variable [= value]]
X
XWith no parameters, set lists all variables and their values.
XTo set a boolean variable (on or off), use:
X	set variable
XTo set a variable's value to a string, use:
X	set variable = value
X
XIf you want double-quotes or white-space embedded in a string,
Xencase the string in single quotes.  If you want single quotes
Xin a string, encase the string in double quotes.
X
XFor a list of all variables with special meanings, use:
X	set ?all
XFor help on a particular one of these variables, use:
X	set ?variable_name
X%%
X
X%readmsg%
XYou can read messages in different ways.  "type" and "print"
Xwill print the current message.  "top" will only print the first
XN lines of the current message where N is the value of the
Xvariable "crt".  "next" will go to the next unread message and
Xprint that.  "previous" will go back and read the first unread
Xmessage previous to the current.  "^" will print the first
Xmessage, "$" will print the last.
X
XAny of these commands can be followed by a message list, and
Xeach message in that list will be printed (or piped to other
Xcommands).
X%%
X
X%alts%
X      alts [hostnames]
X
XThe alts command sets a list of hostnames on which you have an
Xaccount.  Normally, when you respond to all recipients of mail,
Xyour account name will be listed as if you wished to send
Xyourself mail.  If you don't have metoo set, then your name will
Xbe removed from the mailing list if your login name is on the
Xlist and the host specified is in the alternates list.  The
Xspecial parameter `*' instructs alts to match all hostnames; in
Xthat case, only the login name is tested.
X%%
X
X%source%
X      source/saveopts [file]
X
XThe source/saveopts commands will load/save all variable
Xsettings, options, aliases, cmd's, ignored headers ...
Xeverything you can set, it loads or saves.  The file read or
Xwritten follows these rules:
X
X1) If a filename is given, that file is used
X2) The file described by the environment variable MAILRC
X3) In the user's home directory: .mushrc (if it exists)
X4) In the user's home directory: .mailrc (if it exists)
X
XIf saveopts is used and the file exists, confirmation will be
Xrequested before the file is overwritten.
X%%
X
X%general%
XThis is the general help message.  To get help on a specific
Xcommand, try "command -?".  Extended help is given by typing
X"help item" where item is one of:
X    path, msg_list, prompt, hdr_format
XHelp with msg_list is highly advisable!
X
XType "?" to get a list of available commands.  Try "? command"
Xto get help on the particular command that you specify.
X%%
X
X%path%
XWhenever "path" is specified, the following syntax is legal
Xbesides the normal path addressing scheme used by unix:
X ~[user]   -- the home directory of specified user (yours by default)
X %[user]   --/usr/spool/mail/login_name [user_name] (yours by default)
X +file     --the directory described by `set folder'; file is `file'
X%%
X
X%msg_list%
XA "msg_list" references one or more messages.  The user
Xspecifies a group of messages according to a special syntax.
X
X *      All messages.
X ^      The first message.
X $      The last message.
X .      The current message.
X N-M    A range of messages between N and M, inclusive.
X
XIn the last case, N and M may be * ^ $ . or digits referencing
Xexplicit message numbers.  The range must be in ascending order.
X
XYou can also negate messages by placing the message list inside
Xbraces, `{' `}' -- thus, the expression "2-19 {11-14}"
Xreferences messages 2 through 19 except for those from 11
Xthrough 14.
X
XCommands can be "piped" to one another, because the return value
Xof a command is a msg_list, not text.  For example,
X	pick -f fred | lpr
Xwill find all messages "from fred" and send them to the printer.
X
XCommands dealing with more than one message process them in
Xnumeric order -- not necessarily the order specified.  Thus, the
Xcommand "save 1-5 9 7 6 file" will save the messages in
Xascending order, not in the order given.
X%%
X
X%preserve%
X      preserve [msg_list]
X
XThe "preserve" command saves deleted or read messages in your
Xmailbox.  Without explicitely setting preserve, all mail that
Xyou read will be saved in ~/mbox (or set mbox).  Setting the
Xboolean variable "hold" is equivalent to preserving each message
Xas you read it.
X%%
X
X%save%
X      save/write/copy [-s|-S|-a|-A] [!] [msg_list] [filename]
X
XIf no filename is specified, ~/mbox (or the value of the
Xvariable "mbox") is used.  Save and write will append msg if
X`file' already exists.  Specifying the `!' will overwrite the
Xfile (e.g., erasing it first).
X
XTo save messages to a filename beginning with a digit, escape
Xthe filename with a backslash (\).
X
XThe "write" command will write message without the headers (msg
Xbody only).  Save and write both mark messages for deletion
Xunless "keepsave" is set.  The "copy" command is identical to
X"save" except that messages are not marked for deletion
X(identical to having the variable "keepsave" set).
X
XThe -s and -S options save messages to files named by the
Xsubject of the message.  If more than one message is specified,
Xthen the message subject of each message is used.  If -S is
Xspecified, then the subject of the first message is used for all
Xmessages.  Spaces and forward slashes (/) are converted to
Xunderscores (_).
X
XThe -a and -A options save messages by author's login name.
X%%
X
X%lpr%
X      lpr [-n] [-h] [msg_list]
X  -n      print body of message only (not headers)
X  -h      print all headers with message body (default true)
X  -Pxx    print on printer xx
X%%
X
X%respond%
X      replysender/replyall [msg_list] [-r path] [mail_flags] [users]
X
XThe "replysender" command replies only to the sender of a
Xmessage, whereas "replyall" responds to everyone on the To: and
XCc: lines of the message.
X
XThe commands "reply" is identical to "replysender".
X
XIf a message list is indicated, then each message on the list is
Xreplied to in the same manner.  If -r is specified with a host or
Xpath (uucp-style), then each address in the list is routed via
Xthis path.  This overrides the value of auto_route (see man page).
X
XThe address of the author is obtained from certain headers in
Xhis message to you.  Unless you specify otherwise, mush will
Xsearch for the headers Reply-To: Return-Path: and From:.  You
Xcan override these values by setting the variable reply_to_hdr.
X
X    set reply_to_hdr = "sender reply-to return-path from_"
X
XThis example shows that mush will search for (in order), the
Xheaders listed in the reply_to_hdr variable.  If one header isn't
Xfound, then mush looks for the next in the list.  If none of the
Xheaders in the list are found, the default headers (mentioned
Xabove) are searched.  The last header listed in the example is
Xthe special "From " header.  See the man page for more details.
X
XType "mail -?" for information on legal mail flags.
X%%
X
X%sort%
X      sort [-] [a|d|R|s|S]
X  a         by author (alphabetical)
X  d         according to date
X  R         by subject including Re: (alphabetical)
X  s         by subject ignoring Re: (alphabetical)
X  S         by status
X
XThe optional `-' flag will reverse the order of sorting.  By
Xdefault (no parameters), sort sorts messages by status:  New,
Xunread messages are first, followed by preserved messages and
Xfinally the deleted messages are placed at the end.
X
XIf the "date_received" variable is set, sorting by date is
Xdone using the date you received the message.  Otherwise,
Xmessages are sorted by date sent by the original author.
X%%
X
X%pick%
X      pick [-x] [-f|s|t] [-h hdr] [-i] [-r msg_list] [<pat>]
X            [-d [-][date]] [-ago [n days] [n weeks] [n months]]
X
XSearch for patterns within messages.  Entire messages are
Xsearched for <pattern> unless -f, -h, -s, or -t is specified.
XOnly one of -d, -f, -h, -s, -t and -ago can be specified; no
Xpattern is used with -d and -ago.
X
X  -x            return those messages which do NOT match
X  -f            match pattern in the "From:" field (author) only
X  -s            match pattern in the "Subject:" header only
X  -t            match pattern in the "To:" field only
X  -h hdr        match pattern in specified header field only
X  -i            ignore case of letters in when matching
X  -r msg_list   restrict the range of messages search to msg_list
X  -d            select messages sent on [+ after] [- before] date
X        A "date" is of the form:  [+-][month]/[date[/year]]
X        Omitted fields default to today's values.  Examples:
X          pick -d 4/20     messages on Apr 20, this year
X          pick -d -/2/85   on or before the 2nd, this month, 1985
X          pick -d +5/4     on or after May 4, this year
X          pick -d /        finds today's messages only
X        At least one `/' char must be used in a date.  There is
X	no strong date checking; 2/30 would be considered valid.
X  -ago          select messages relative to the current date
X	Date formats for "ago" are more verbose than for -d; see
X	the manual page for details.
X%%
X
X%alias%
XOptions for alias:
X alias                       print all namelists
X alias name                  print namelist associated with name
X alias name namelist         set "name" to the value of namelist
X unalias namelist            unalias names in namelist
X
XA "namelist" consists of one or more addresses.  An address may
Xbe a name already set to another list, a valid user, a file or
Xa program.  Filenames must be full pathnames, i.e., they must
Xbegin with a '/' (or with a ~, which expands to some home dir).
XA "program" must start with a pipe symbol and be encased in
Xquotes:
X
X    "|program_name"
X
XThe command "expand" will print addresses (including sublists)
Xassociated with the given alias.
X%%
X
X%from%
XWith no parameters, "from" will print the current message's
Xheader line.  If given a message list, "from" will print the
Xheaders of the listed messages.
X
XThe special parameters `-' and `+' can be given to move the
Xcurrent message pointer to the previous or next message
Xrespectively, while also printing that message's header.
X
XIf a message list was given in addition to `-' or `+', then
Xthe current message pointer will be set to the first or last
Xmessage, respectively, in the message list given.
X
XExamples:
X
X	from - 10-30 {16}
Xwill print the headers of messages 10 through 30 except for
Xmessage 16 and set the current message pointer to 10.
X
X	pick -f Dan | from +
Xwill print the headers of all messages that contain "Dan" in the
Xauthor's name and set the current message pointer to the last
Xone of that kind in the list.
X
X	from +
Xwill print the header of the message after the current message
Xand increment the current message pointer to that message.
X%%
X
X%own_hdrs%
XThis command is used to set, unset or view your personalized
Xmessage headers.  These headers are included in all your
Xoutgoing mail.
X
XOptions for my_hdr:
X  my_hdr			show all headers
X  my_hdr header			show value of header
X  my_hdr header: string		set header to string
X  un_hdr header			unset header
X%%
X
X%fkey%
XThis command is used to make function key settings in Suntools
X(graphics) mode.  When run as a tool (-t on command line),
Xchoose the Options item, and the "function key" menu option.
X%%
X
X%cmd%
XThis function is used to establish command aliases; cmd's are
Xjust like aliases in the C-shell.  Options are:
X  cmd                       view all commands
X  cmd command               show value of command
X  cmd command "value"       set command to value
X  uncmd command             unset command
X
XIf you want to reference history commands within a cmd,
Xescape the ! with a backslash.  For example:
X
X	cmd r 'replysender \!* ; delete -t'
X
Xwill cmd "r" to reply using whatever parameters you have given on
Xthe command line and then delete that message and print the next
Xmessage (-t parameter to "delete").
X%%
X
X%headers%
X      headers [+|-|N] [[-H]:c]
X  +	print the next screenful (or use the 'z' command).
X  -	print the previous screenful (or use 'z-' ).
X  N	print a screenful starting at message number N.
X  -H:c  where `c' is one of
X     a  all messages (mostly for the command line parameter -H:c)
X     d  deleted messages
X     n  new messages
X     o  old messages
X     p  preserved messages
X     r  replied-to messages
X     s  saved messages
X     u  unread messages
X
XThe "headers" command prints out a screenful of headers.
XDeleted messages are not normally shown; set "show_deleted" to
Xinclude deleted messages.
X
XThe command ":c" is equivalent to "headers -H:c".  The -H can be
Xomitted, i.e., "headers :c" will also work.
X%%
X
X%hdr_format%
XThis variable controls the display of message headers.  Use:
X	set hdr_format="string"
Xto change the header display.  The string uses printf style
Xformatting and follows these conventions:
X    %a  address of the author
X    %c  number of characters (bytes) in the message
X    %d  entire date of the message (see "date_received" variable)
X    %f  "From" field (author name and address)
X    %i  the message-id (may or may not be present)
X    %l  number of lines in the message
X    %M  month name of the message
X    %N  day of the month (number)
X    %n  name of the author
X    %s  subject of the message
X    %t  "To" field (recipients)
X    %T  time of the message (see "mil_time" variable)
X    %W  day of the week (Sun, Mon, etc.)
X    %Y  year of the message 
X    %y  year (last 2 digits only)
X    \n  a newline
X    \t  a tab
X
XA field specifier may be used in any % expansion.  Thus, "%20s"
Xwill print the first 20 characters of the Subject.  No matter
Xwhat the formatting string, the message number, the status of
Xthe message and a '>' (if this is the "current" message) is
Xbefore any user defined format is printed.
X%%
X
X%folder%
X      folder/update [-N] [-r] [!] [%[user]|#|&|file]
X  -N       Do not display the list of headers
X  -r       read only mode (cannot write changes to this folder)
X  %[user]  change to /usr/spool/mail/[user] (you by default)
X  #        change to folder accessed previous to current folder
X  &        change to "mbox" -- default is $mbox or ~/mbox
X
XThe "folder" command changes the current folder; with no parameters,
Xit prints the name of the current folder.  If `!' is specified, the
Xcurrent folder is not updated before changing.
X
XThe "update" command updates the current folder.  In this case, only
Xthe -N and -r options are observed.
X%%
X
X%prompt%
XThis variable controls the prompt that mush will display.
X	set prompt = "string"
XThe "string" follows printf style formatting conventions:
X    %F  full path of the current working folder
X    %f  name (path tail) of the current folder
X    %m  current message number
X    %n  number of new messages
X    %u  number of unread messages
X    %d  number of deleted messages
X    %t  total number of messages
X    %T  current time
X    %N  day of the month (number) (today)
X    %W  weekday name (today)
X    %M  month name (this month)
X    %Y  year (this year)
X    %y  year (last 2 digits only)
X    \n  newline
X    \t  tab
X%%
X
X%quit%
X      quit/exit
X
XThese commands end a mush session.  "quit" will update your
Xmailbox; if new mail has come in, you will be told so and given
Xan option whether to really quit or not.  "exit" will leave mush
Xneither updating your mailbox nor checking for new mail.
X%%
X
X%ls%
XThe "ls" command is exactly like the UNIX command "ls".  All
Xparameters are the same.  The variable "lister" can be set to
Xa list of default parameters, thus avoiding having to specify
Xthem all the time.  The "folders" command is equivalent to
Xdoing "ls $folder" from the Mush prompt.
X%%
X
X%shell%
X      sh [command]
X
XIf a "command" is given, that UNIX command will be executed
Xunder the Bourne shell.  If no command is specified, then an
Xinteractive shell will be started. The environment variable
XSHELL or the local mail shell variable shell  describes the
Xshell to invoke.  If none is set, then the default shell is
Xdefined by the system administrator (currently set to csh).
X
XUsers on systems with job control will probably have little
Xuse for the sh command.
X%%
X
X%stop%
XThe stop command sends a stop signal to the mail shell.  It is
Xequivalent to ^Z as it will stop the process.  Since the shell
Xnever needs to be exited, the command 'q' may be aliased to
X"stop" and the shell may have
X	alias mail %mush
X(assumes csh) which will bring mush into the foreground rather
Xthan having to invoke a new shell.  New mail will be read into
Xthe shell automatically and much time and energy is saved.
X%%
X
X%curses%
XThe curses-based interface for Mush does not require a graphics
Xdisplay, but does requires a terminal which can handle upline
Xcursor movement capabilities.  All commands are one or two
Xkeystroke commands and are executed as soon as the key is typed.
X
XFor a list of current key-to-command bindings, use the "bind"
Xcommand (defaults to 'b' in curses mode).
X%%
X
X%bind%
X      bind <sequence> <curses-command> [ <parameters> ]
X
XBinding is done for the curses interface only.  It allows the
Xuser to bind keystrokes or key sequences to curses-interface
Xcommands.  You cannot bind keystrokes to regular mush commands
Xusing bind.
X
XA bound key-sequence (input by the user) will be converted into
Xthe curses command it is bound to.  For a list of all curses
Xcommands, issue the "bind" command and follow the instructions
Xto get a list.  Currently, parameters are ignored for all curses
Xcommands except the special command "macro".
X
XWhen specifying sequences, you may enter almost anything at the
Xkeyboard that you want to type.  This includes most control
Xcharacters.  A special syntax is provided to specify control
Xcharacters if you wish to set up default key bindings in your
Xinitialization file without using real control characters.
X
XTo bind keystrokes that are control characters in the
Xinitialization file, you must use the notation "\CX" where "X"
Xis an upper-case letter representing the control key you want to
Xuse. "\CN" would be control-N; "\n" is carriage return.  You may
Xnot bind keyboard generated signals; for most users, those key
Xsequences are control-C and control-\.  For users with job
Xcontrol, the suspend characters (usually control-Z and
Xcontrol-Y) are also ignored.
X
XTo specify the escape key, use "\E"; "\C[" will not work.
X
XTrying to bind a key sequence which prefixes another sequence is
Xan error and the user is warned that the longer binding will not
Xwork.  The binding will take place, however, because it is
Xpossible to unbind the shorter sequence, thus validating the
Xlonger sequence.
X
XThe special curses command "macro" allows a string to be
Xexecuted just as if the user typed it directly.  Issue the
X"bind-macro" command for more details.
X%%
X
X%msg_flags%
X      flags [msg_list] [[+|-] [D N O P R S r U]]
X
XThis command displays the status of messages by default.
XIf a list is specified, it will tell which bits of the
Xmessage are set: Delete, New, Old, Preserved, Read, Saved
Xreplied-to, and Unread.  If any (one or more) of the bits
Xare given and no + or - modifier is specified, then the
Xstatus of each message in the list will be set to that
Xstatus absolutely (other status flags are lost).  However,
Xif a + or - is specified, then the status is modified for
Xthat bit to on (+) or off (-).
X
XIf no list is given, then the list of messages is taken
Xfrom a pipe (if piped) or the current message is used.
X%%
X
X%setenv%
X      setenv VARIABLE [value]
X
XVariable names may be any string, but traditionally environment
Xvariables are all upper case.  If no "value" is specified, then
Xthe variable name will be set to an empty string.  If the value
Xcontains spaces, you should enclose the string in quotation
Xmarks.  Use printenv to print a list of all your environment
Xvariables.
X%%
X
X%unsetenv%
X      unsetenv VARIABLE
X
XYou must specify one and only one variable to unset in your
Xenvironment variable settings.  Use printenv to print a list of
Xall your environment variables.
X%%
X
X%edit_msg%
X      edit_msg [msg_list]
X
XThe "edit_msg" command lets you edit messages in your folder.
XWhen editing messages, be careful not to remove certain message
Xheaders such as Date:, From:, or any others that look important.
XIf you remove or change something you shouldn't have, you will
Xbe notified and the temporary file used to edit the message will
Xnot be removed.
X%%
X
X%bind-macro%
X      bind-macro [<sequence> [<expansion>]]
X
XThe "bind-macro" command allows you to set macros in curses
Xmode, so that one keystroke (or a few) will act as though you
Xhad typed a longer sequence.  Using "bind-macro" is equivalent
Xto specifying the "macro" special command as a parameter to
Xthe "bind" command.
X
XGiven no parameters, "bind-macro" will list all current curses
Xmode macros.  Given only a <sequence>, it will show the current
Xbinding for that sequence.  Given both a <sequence> and an
X<expansion>, it will create a macro such that, when the
X<sequence> is typed in curses mode, the effect will be the same
Xas if the <expansion> had been typed instead.
X
XThe same format for control characters that is used for the
X"bind" command may be used in both the <sequence> and the
X<expansion>, i.e.,
X    \Cx     control-x (where x is a capital letter)
X    \E      the escape character (\C[ does NOT work!)
X    \n      a newline (other C-style escapes also work)
X
XExample:
X    bind-macro F [folder]+record\n
X
XIf you are in curses mode and hit the F key, then the curses
Xmode command "folder" will execute and +record (followed by
Xa carriage return) will be entered as if you typed it.
X
XAlso see the "map" and "map!" commands.
X%%
X
X%map%
X      map [<sequence> [<expansion>]]
X
XThe "map" command allows you to use one keystroke (or a few) and
Xhave it act as though you had typed a longer sequence.
X
XGiven no parameters, "map" will list all current line mode
Xmacros.  Given only a <sequence>, it will show the current
Xbinding for that sequence.  Given both a <sequence> and an
X<expansion>, it will create a macro such that, when the
X<sequence> is typed in line mode, the effect will be the same
Xas if the <expansion> had been typed instead.
X
XThe same format for control characters that is used for the
X"bind" command may be used in both the <sequence> and the
X<expansion>, i.e.,
X    \Cx     control-x (where x is a capital letter)
X    \E      the escape character (\C[ does NOT work!)
X    \n      a newline (other C-style escapes also work)
X
XExample:
X    map & print\n
X
XIf you are not in curses mode and hit the & key, then it will
Xbe as if you typed the word "print" and hit carriage return.
X
XTo type a character without having the mapping expanded, precede
Xthe character with a backslash (\).
X
XAlso see the "map!" and "bind" commands.
X%%
X
X%map!%
X      map! [<sequence> [<expansion>]]
X
XThe "map!" command allows you to set macros in message
Xcomposition mode, so that one keystroke (or a few) will act
Xas though you had typed a longer sequence.  map!'s take
Xeffect regardless of whether you started the letter from
Xcurses mode or line mode.
X
XGiven no parameters, "map!" will list all composition mode
Xmacros.  Given only a <sequence>, it will show the current
Xbinding for that sequence.  Given both a <sequence> and an
X<expansion>, it will create a macro such that, when the
X<sequence> is typed in message composition mode, the effect will
Xbe the same as if the <expansion> had been typed instead.
X
XThe same format for control characters that is used for the
X"bind" command may be used in both the <sequence> and the
X<expansion>, i.e.,
X    \Cx     control-x (where x is a capital letter)
X    \E      the escape character (\C[ does NOT work!)
X    \n      a newline (other C-style escapes also work)
X
XExample:
X    map! ! <BANG>
X
XIf you are typing in a letter regardless of which interface you
Xuse and you hit the ! key, then it would be as if you typed the
Xkeys "<BANG>".
X
XTo type a character without having the mapping expanded, precede
Xthe character with a backslash (\).
X
XAlso see the "bind" and "map" commands.
X%%
X
X%eval%
X      eval args ...
X
XThis command causes its arguments to be re-parsed and then
Xexecuted as a mush command.  Example:
X    set initprompt='"$hostname:$cwd "'
X    eval set prompt=$initprompt
X%%
X
X%pipe_msg%
X      pipe [msg-list] unix-command
X
XThe specified unix-command is executed.  The standard input of
Xthe command is the texts of listed messages, including headers
Xthat are not ignored (see "ignore -?" and "set ?show_hdrs").  If
Xthis command is part of a mush pipeline (|) then any list of
Xmessages given is added to those taken from the pipeline.  If no
Xmsg-list is given and there is no pipeline, the current message
Xis used.  The unix-command is executed via "sh", so csh aliases
Xmay not be used.
X
XIf invoked with a capital letter (Pipe), only the bodies of the
Xmessages will be fed to the unix-command -- all headers will be
Xomitted.
X
XExamples:
X    pipe 2 7 more      -- send messages 2 and 7 through "more"
X    pipe patch         -- send the current message to "patch"
X    1 | Pipe nroff     -- send the body of message 1 to "nroff"
X%%
X
X%merge%
X      merge [-N] folder-name
X
XThe contents of the specified folder are read into the current
Xfolder.  If -N is not specified, a header summary is printed
Xfor each message read (see "headers -?").
X
XA list of all the merged messages is returned for use in pipes.
X%%
X
X%echo%
X    echo [-n] [-h | -p] args
X
XEcho simply echoes the parameters to the command back to the user.
X
XIf the -n flag is given, then no newline is appended.
XIf the -h flag is given, then echo looks for formatting parameters
Xas if the "from" command were given on the "current" message.
XIf the -p flag is given, then echo looks for formatting parameters
Xas if your prompt were changed temporarily.
X
XExamples:
X    echo -h This message is from %a and is dated %d
Xmight produce:
X    This message is from island!argv and is dated Dec 14, 1988.
X
X    echo -p There are %n new messages to read in %f.
Xmight produce:
X    There are 5 new messages to read in /usr/spool/mail/argv.
X
XNote that -h and -p cannot be specified together.
X%%
X
X%undigest%
X    undigest [-m] [msg_list] [filename]
X
XA "digest" is a mail message which is a collection of other mail messages
Xmailed to a "moderator" by other users.  The moderator compiles all the
Xmessages into a folder and sends the result to all the subscribers of the
Xmailing list.  The undigest command disassembles the entries into the set
Xof messages which comprises the digest.
X
XThe -m option will merge these messages into the current folder.  Otherwise,
Xif a filename is specified, a new folder is created and the user can change
Xfolders to read the messages separately.
X
XIf a message list is specified, each digest is disassembled to the same
Xfilename (if given).  If no filename is given and the user did not request
Xa merge, then a folder is created based on the subject of the digest.
X%%
END_OF_FILE
if test 27197 -ne `wc -c <'cmd_help'`; then
    echo shar: \"'cmd_help'\" unpacked with wrong size!
fi
# end of 'cmd_help'
fi
if test -f 'msgs.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'msgs.c'\"
else
echo shar: Extracting \"'msgs.c'\" \(24063 characters\)
sed "s/^X//" >'msgs.c' <<'END_OF_FILE'
X/* @(#)msgs.c	(c) copyright 10/18/86 (Dan Heller) */
X
X#include "mush.h"
X
Xvoid
Xdisplay_msg(n, flg)
Xregister int n;
Xlong flg;
X{
X    if (ison(msg[n].m_flags, DELETE) && !do_set(set_options, "show_deleted")) {
X	print("Message %d deleted; ", n+1);
X#ifdef SUNTOOL
X	if (istool)
X	    print_more("Select UNDELETE to read."), do_clear();
X	else
X#endif /* SUNTOOL */
X	if (iscurses)
X	    print_more("Type 'u' to undelete.");
X	else
X	    print("Type 'undelete %d' to undelete\n", n+1);
X	return;
X    }
X    set_isread(n);
X    if (ison(flg, M_TOP)) {
X	turnon(flg, NO_HEADER);
X	print("Top of "), turnon(glob_flags, CONT_PRNT);
X    }
X
X#ifdef MMDF
X    turnon(flg, NO_SEPARATOR);
X#endif /* MMDF */
X    if (!istool && isoff(flg, NO_PAGE) &&
X	    crt < msg[n].m_lines && isoff(flg, M_TOP)) {
X	char buf[32], *pager = do_set(set_options, "pager");
X	if (!pager)
X	    pager = DEF_PAGER;
X	if (!*pager || !strcmp(pager, "internal"))
X	    pager = NULL; /* default to internal pager if pager set to "" */
X	(void) do_pager(pager, TRUE); /* start pager */
X	(void) do_pager(sprintf(buf, "Message #%d (%d lines)\n",
X			 n+1, msg[n].m_lines), FALSE);
X	(void) copy_msg(n, NULL_FILE, flg);
X	(void) do_pager(NULL, FALSE); /* end pager */
X    } else {
X	print("Message #%d (%d lines)\n", n+1, msg[n].m_lines);
X	(void) copy_msg(n, stdout, flg);
X    }
X}
X
X/*
X * copy message 'n' to file "fp" according to various flag arguments
X * return number of lines copied or -1 if system error on fputs.
X * If "fp" is null, send to internal pager.  This can only happen from
X * display_msg above.
X */
Xcopy_msg(n, fp, flags)
Xregister int n;
Xregister FILE *fp;
Xu_long flags;
X{
X    register int  ignoring = 0, lines = 0;
X    register char *indent_str;
X    int  on_hdr = 1, top, squeeze = 0;
X    char 	  line[BUFSIZ], *show_hdrs = NULL;
X
X    still_more = 0;
X    if (ison(flags, M_TOP)) {
X	register char *p = do_set(set_options, "toplines");
X	top = (p)? atoi(p) : crt;
X    }
X    /* When updating to a folder, always write all headers! */
X    if (ison(flags, UPDATE_STATUS))
X	turnon(flags, NO_IGNORE);
X    else if (do_set(set_options, "alwaysignore"))
X	turnoff(flags, NO_IGNORE);
X    if (isoff(flags, NO_IGNORE)) {
X	if (do_set(set_options, "squeeze"))
X	    squeeze = 1;
X	show_hdrs = do_set(set_options, "show_hdrs");
X    }
X
X#ifdef SUNTOOL
X    if (istool && (!fp || fp == stdout)) {
X	register int x = (msg[n].m_lines + 2) * l_height(curfont);
X
X	if (x > 32765) { /* to overcome a bug in pixrects that sun won't fix */
X	    print("message too big to display using this font");
X	    return 0;
X	}
X	if (x < msg_rect.r_height) /* make it at least as big as the window */
X	    x = msg_rect.r_height;
X	/* If the window isn't big enough, an infinite loop occurs in Addstr */
X	if (x < 2 * l_height(curfont) || msg_rect.r_width < 3*l_width(curfont))
X	    return 0;
X	do_clear();
X	lock_cursors();
X	/* msg_pix is for Addstr() */
X	if (!(msg_pix = mem_create(msg_rect.r_width, x, 1))) {
X	    error("mem_create");
X	    return 0;
X	}
X	pr_rop(msg_pix, 0,0, msg_rect.r_width-1, x-1, PIX_CLR, 0,0,0);
X	on_hdr = 1;
X    }
X#endif /* SUNTOOL */
X    if (ison(flags, INDENT)) {
X	if ((indent_str = do_set(set_options, "pre_indent_str"))) {
X	    char *old_fmt = hdr_format;
X	    hdr_format = indent_str;
X	    fprintf(fp, "%s\n", compose_hdr(n) + 9); /* magic number 9 !! */
X	    hdr_format = old_fmt;
X	}
X	if (!(indent_str = do_set(set_options, "indent_str")))
X	    indent_str = DEF_INDENT_STR;
X    }
X    /* "line" used as dummy here, since 0 bytes read */
X    if (!msg_get(n, line, 0)) {
X	error("Unable to find msg %d", n+1);
X	return -1;
X    }
X    while (still_more < msg[n].m_size && fgets(line, sizeof (line), tmpf)) {
X	still_more += strlen(line);
X#ifdef MMDF
X	if (ison(flags, NO_SEPARATOR)) {
X	    if (!strncmp(line, MSG_SEPARATOR, 4))
X		continue;
X	}
X#endif /* MMDF */
X	/*
X	 * If squeeze is one, all blanks lines squeeze down to one blank line.
X	 * If squeeze is two, squeezing is in progress so wait for the next \n.
X	 */
X	if (*line == '\n') {
X	    if (on_hdr)    /* blank line -- end of header */
X		turnoff(flags, NO_HEADER), on_hdr = 0;
X	    if (squeeze > 1)
X		continue;
X	    else if (squeeze)
X		squeeze = 2;
X	} else if (squeeze > 1)
X	    squeeze = 1;
X
X	if (ison(flags, UPDATE_STATUS))
X	    if (!strncmp(line, "Status:", 7))
X		continue; /* ignore this and other "Status" lines */
X	    else if (!on_hdr) {
X		/* preserve NEW/UNREAD status on preserved messages */
X		register char *p = line;
X		p += Strcpy(p, "Status: O");
X		if (isoff(msg[n].m_flags, UNREAD) &&
X		    isoff(msg[n].m_flags, PRESERVE))
X		    *p++ = 'R';
X		if (ison(msg[n].m_flags, SAVED))
X		    *p++ = 'S';
X		if (ison(msg[n].m_flags, REPLIED))
X		    *p++ = 'r';
X		*p++ = '\n', *p = 0;
X		fputs(line, fp);
X		(void) strcpy(line, "\n");
X		turnoff(flags, UPDATE_STATUS);
X	    }
X	if (on_hdr && (isoff(flags, NO_IGNORE) || ison(flags, FORWARD))) {
X	    register char *p = any(line, " \t:");
X	    if (!p)
X		ignoring = 0, on_hdr = 0;
X	    else if (ignoring)
X		if (*p != ':') {
X		    Debug("Ignoring: %s", line);
X		    continue;
X		} else
X		    ignoring = 0;
X	    if (p && *p == ':') {
X		*p = 0;
X		ignoring = 0;
X		if (show_hdrs) {
X		    if (!chk_two_lists(line, show_hdrs, ":, \t"))
X			ignoring = 1;
X		} else if (ison(flags, FORWARD)) {
X		    if (chk_two_lists(line, IGNORE_ON_FWD, ":, \t"))
X			ignoring = 1;
X		} else {
X		    register struct options *opts;
X		    for (opts = ignore_hdr; opts; opts = opts->next)
X			if (!lcase_strncmp(opts->option, line, -1)) {
X			    ignoring = 1;
X			    break;
X			}
X		}
X		*p = ':';
X		if (ignoring) {
X		    Debug("Ignoring: %s", line);
X		    continue;
X		}
X	    }
X	}
X	if (!on_hdr && ison(flags, M_TOP) && !--top)
X	    break;
X	if (isoff(flags, NO_HEADER)) {
X	    /* note that function returns the number of lines */
X	    lines++;
X#ifdef SUNTOOL
X	    if (istool && (!fp || fp == stdout)) {
X		Addstr(line);
X		continue;
X	    }
X#endif /* SUNTOOL */
X	    if (ison(flags, INDENT))
X		fputs(indent_str, fp);
X	    if (!fp) {
X		if (do_pager(line, FALSE) == EOF)
X		    return -1;
X	    } else if (fputs(line, fp) == EOF)
X		/* Pipe broken, out of file space, etc */
X		return -1;
X	}
X    }
X    if (ison(flags, INDENT) &&
X	(indent_str = do_set(set_options, "post_indent_str")) && *indent_str) {
X	char *old_fmt = hdr_format;
X	hdr_format = indent_str;
X	fprintf(fp, "%s\n", compose_hdr(n)+9); /* magic number 9 !! */
X	hdr_format = old_fmt;
X    }
X#ifdef SUNTOOL
X    if (istool && (!fp || fp == stdout)) {
X	unlock_cursors();
X	txt.y = still_more = msg_rect.r_height;
X	scroll_win(0);  /* causes a display */
X    }
X#endif /* SUNTOOL */
X    return lines;
X}
X
X/*
X * copy tempfile back to folder.
X * Return 1 on success, 0 on failure.
X */
Xcopyback(prompt)
Xchar *prompt;
X{
X    register int	i=0, j=0, k=0;
X    register long	flg = 0;
X    register FILE	*mbox = NULL_FILE, *mail_fp = NULL_FILE;
X#ifndef DOT_LOCK
X#ifdef SYSV
X    FILE 		*save_mail_fp = NULL_FILE;
X#endif /* SYSV */
X#endif /* !DOT_LOCK */
X    char		*mbox_file, action = 0;
X    int 		hold = 0, delete_it = 0, dont_unlink = FALSE;
X    int			isspool, keepsave;
X    static int		first = 1;
X
X    /*
X     * if there is new mail in this folder, the user is notified and
X     * prompted if he really wants to update the folder.  He's either
X     * quitting or changing folders, so let him read the new mail first.
X     */
X    if (!first && check_new_mail() > 0) {
X	if (!istool) {
X	    char buf[16];
X	    if (iscurses)
X		putchar('\n'), turnon(glob_flags, CNTD_CMD);
X	    print("%s [n] ", prompt);
X	    buf[0] = 0;
X	    if (!Getstr(buf, sizeof (buf), 0) || lower(*buf) != 'y')
X		return 0;
X	}
X    }
X    first = 0;
X
X    /* If the user hasn't changed anything, just return true */
X    if (isoff(glob_flags, DO_UPDATE))
X	return 1;
X    if (ison(glob_flags, READ_ONLY)) {
X	print("Unable to update %s: read only\n", mailfile);
X	return 0; /* user should use "exit" instead of "quit". */
X    }
X    if (!msg_cnt) /* prevent unnecessary overwrite */
X	return 1;
X
X#ifdef SUNTOOL
X    if (istool) {
X	timerclear(&(mail_timer.it_interval));
X	timerclear(&(mail_timer.it_value));
X    }
X#endif /* SUNTOOL */
X
X    /* open mbox if: "autodelete" AND "hold" are NOT set. */
X    if (!strcmp(mailfile, spoolfile)
X	    && !(delete_it = !!do_set(set_options, "autodelete"))
X	    && !(hold = !!do_set(set_options, "hold"))) {
X	register char *p;
X	int x = 1; /* tell getpath to ignore "ENOENT" if file not found */
X
X	if (!(p = do_set(set_options, "mbox")))
X	    p = DEF_MBOX;
X	mbox_file = getpath(p, &x);
X	if (x) {
X	    if (x > 0)
X		print("%s is a directory.\n", mbox_file);
X	    else
X		print("Unable to open %s: %s\n", p, mbox_file);
X	    mbox = NULL_FILE;
X	} else {
X	    if (Access(mbox_file, F_OK) == -1) /* does it exist? */
X		mbox = mask_fopen(mbox_file, "w");
X	    else
X		mbox = mask_fopen(mbox_file, "a");
X	    if (!mbox)
X		error("Unable to write to %s", mbox_file);
X	}
X    }
X#ifdef DOT_LOCK
X    if ((i = dot_lock(mailfile)) == 0)
X#endif /* DOT_LOCK */
X#ifdef DOT_LOCK
X    mail_fp = mask_fopen(mailfile, "w+");
X#else /* !DOT_LOCK */
X    /* We can't lock a file unless we have an fd, but "w+" will zero
X     * the file.  If the lock later failed for any reason (possible
X     * race condition with an MTA), we would lose all current mail.
X     * So, open read/write (if possible) and truncate later.
X     */
X    mail_fp = mask_fopen(mailfile, "r+");
X#endif /* DOT_LOCK */
X    if (!mail_fp) {
X	error("Unable to rewrite %s", mailfile);
X	if (mbox)
X	    fclose(mbox);
X	return 0;
X    }
X    if (i != 0 || lock_file(mailfile, mail_fp) == -1) {
X#ifndef DOT_LOCK
X	error("WARNING: unable to lock %s", mailfile);
X#endif /* DOT_LOCK */
X	if (mail_fp)
X	    close_lock(mailfile, mail_fp);
X	if (mbox)
X	    fclose(mbox);
X	return 0;
X    }
X#if !defined(DOT_LOCK) && defined(SYSV)
X    /* SysV can't truncate a file in the middle, so we can't just
X     * write to mail_fp and close.  Instead, we save the mail_fp
X     * and reopen for writing, ignoring our own lock.  After updating,
X     * we can safely fclose both file pointers.
X     */
X    save_mail_fp = mail_fp;
X    /* This could fail if we run out of file descriptors */
X    if (!(mail_fp = fopen(mailfile, "w"))) {
X	error("WARNING: unable to reopen %s for update", mailfile);
X	if (save_mail_fp)
X	    close_lock(mailfile, save_mail_fp);
X	if (mbox)
X	    fclose(mbox);
X	return 0;
X    }
X#endif /* SYSV && !DOT_LOCK */
X
X    print("Updating \"%s\"", mailfile);
X
X    turnon(flg, UPDATE_STATUS);
X    turnon(glob_flags, IGN_SIGS);
X
X    keepsave = !!do_set(set_options, "keepsave");
X    isspool = !strcmp(mailfile, spoolfile);
X
X    for (i = 0; i < msg_cnt; i++)
X	/* check to see if message is marked for deletion or, if read and not
X	 * preserved, delete it if autodelete is set. Otherwise, save the
X	 * message in the spool file if hold is set. If all fails, save in mbox.
X	 */
X	if (ison(msg[i].m_flags, DELETE)
X	||  ison(msg[i].m_flags, SAVED) && !keepsave &&
X	    isoff(msg[i].m_flags, PRESERVE) && isspool
X	||  isoff(msg[i].m_flags, UNREAD) && isoff(msg[i].m_flags, PRESERVE) 
X		&& delete_it) {
X	    Debug("%s %d",
X		(action!='d')? "\ndeleting message:" : "", i+1), action = 'd';
X	    continue;
X	} else if (ison(msg[i].m_flags, UNREAD) ||
X		 ison(msg[i].m_flags, PRESERVE) || hold || !mbox) {
X	    j++;
X	    Debug("%s %d",
X		(action!='s')? "\nsaving in spool:" : "", i+1), action = 's';
X	    if (copy_msg(i, mail_fp, flg) == -1) {
X		error("WARNING: unable to write back to spool");
X		print("ALL mail left in %s\n", tempfile);
X		print("Spool mailbox may be corrupted.\n");
X		dont_unlink = TRUE;
X		break;
X	    }
X	} else if (isspool) {   /* copy back to mbox */
X	    k++;
X	    if (copy_msg(i, mbox, flg) == -1) {
X		error("WARNING: unable to write to mbox");
X		print("Unresolved mail left in %s\n", tempfile);
X		dont_unlink = TRUE;
X		break;
X	    }
X	    Debug("%s %d",
X		(action!='m')? "\nsaving in mbox:" : "", i+1), action = 'm';
X	}
X    Debug("\n%s", mailfile);
X
X#ifndef DOT_LOCK
X#ifdef SYSV
X    /* Close the write file pointer first */
X    fclose(mail_fp);
X    mail_fp = save_mail_fp;
X#else /* !SYSV */
X    /* Truncate the file at the end of what we just wrote.
X     * If you aren't SYSV and you still can't ftruncate(),
X     * you're out of luck?
X     */
X    (void) ftruncate(fileno(mail_fp), ftell(mail_fp));
X#endif /* SYSV */
X#endif /* !DOT_LOCK */
X
X    close_lock(mailfile, mail_fp);
X
X#ifdef SUNTOOL
X    if (istool) {
X	mail_timer.it_value.tv_sec = time_out;
X	setitimer(ITIMER_REAL, &mail_timer, NULL);
X    }
X#endif /* SUNTOOL */
X
X    /* some users like to have zero length folders for frequent usage */
X    if (mbox)
X	fclose(mbox);
X    if (j) {
X	long times[2];
X	times[1] = time(&times[0]) - (long)2;
X	if (!strcmp(mailfile, spoolfile) && utime(mailfile, times))
X	    error("utime");
X	print_more(": saved %d message%s\n", j, (j==1)? NO_STRING: "s");
X    } else
X#ifdef HOMEMAIL
X    if (!dont_unlink && !do_set(set_options, "save_empty"))
X#else /* HOMEMAIL */
X    if (strcmp(mailfile, spoolfile) && !dont_unlink &&
X	!do_set(set_options, "save_empty"))
X#endif /* HOMEMAIL */
X	if (unlink(mailfile))
X	    turnon(glob_flags, CONT_PRNT), error(": cannot remove");
X	else
X	    print_more(": removed\n");
X    else
X	print_more(": empty\n");
X    if (k)
X	print("saved %d message%s in %s\n",k,(k==1)? NO_STRING:"s", mbox_file);
X
X    turnoff(glob_flags, IGN_SIGS);
X
X    return 1;
X}
X
X/*
X * check the sizes of the current folder (file) and the spool file.
X * spool_size is the size in bytes of the user's main mailbox.
X * last_size is the size of the _current_ folder the last time we checked.
X * return true if the current folder has new mail.  check_new_mail() checks
X * for new mail in the system mailbox since it checks against last_spool_size.
X */
Xmail_size()
X{
X    struct stat buf;
X    if (strcmp(mailfile, spoolfile) && !stat(spoolfile, &buf))
X	spool_size = buf.st_size;
X    if (!*mailfile)
X	return 0;
X    if (stat(mailfile, &buf)) {
X	if (errno != ENOENT)
X	    error("Unable to stat %s", mailfile);
X	return 0;
X    }
X    if (!strcmp(mailfile, spoolfile))
X	spool_size = buf.st_size;
X    if (buf.st_size != last_size) {
X	last_size = buf.st_size;
X	return 1;
X    }
X    return 0;
X}
X
Xvoid
Xmail_status(as_prompt)
X{
X    static char buf[256];
X    register int cnt = 0, new = 0, unread = 0, deleted = 0;
X
X    for ( ; cnt < msg_cnt; cnt++) {
X	if (ison(msg[cnt].m_flags, UNREAD))
X	    unread++;
X	if (ison(msg[cnt].m_flags, DELETE))
X	    deleted++;
X	if (isoff(msg[cnt].m_flags, OLD))
X	    new++;
X    }
X    if (as_prompt) {
X	register char *p, *b = buf;
X	for (p = prompt; *p; p++)
X	    if (*p == '\\')
X		switch (*++p) {
X		    case 'n': case 'r': *b++ = '\n';
X		    when 't': *b++ = '\t';
X		    otherwise: *b++ = *p;
X		}
X	    else if (*p == '%')
X		switch (*++p) {
X		    case 'm':
X			b += strlen(sprintf(b,"%d",(msg_cnt)? current_msg+1:0));
X		    when 't':
X			b += strlen(sprintf(b, "%d", msg_cnt));
X		    when 'd':
X			b += strlen(sprintf(b, "%d", deleted));
X		    when 'u':
X			b += strlen(sprintf(b, "%d", unread));
X		    when 'n':
X			b += strlen(sprintf(b, "%d", new));
X		    when 'f':
X		    {
X			char *tail = rindex(mailfile, '/'); 
X			if (tail && tail[1])
X			    b += Strcpy(b, tail+1);
X			else
X			    /* Fall through */
X		    case 'F':
X			b += Strcpy(b, mailfile);
X			if (ison(glob_flags, READ_ONLY))
X			    b += Strcpy(b, " [read-only]");
X		    }
X		    when 'T': case 'D': case 'Y': case 'y':
X		    case 'M': case 'N': case 'W':
X			b += Strcpy(b, Time(p, (long)0));
X		    otherwise: *b++ = *p;
X		}
X	    else if (*p == '!')
X		b += strlen(sprintf(b, "%d", hist_no+1));
X	    else
X		*b++ = *p;
X	*b = 0;
X	print("%s", buf); /* use %s in case "buf" has any %'s in it */
X	return;
X    }
X    (void) sprintf(buf,"\"%s\"%s: %d message%s, %d new, %d unread",
X	mailfile, ison(glob_flags, READ_ONLY)? " [read only]" : "",
X	msg_cnt, (msg_cnt != 1)? "s": NO_STRING, new, unread);
X    if (istool || iscurses)
X	(void) sprintf(buf+strlen(buf), ", %d deleted", deleted);
X#ifdef SUNTOOL
X    if (istool) {
X	static char ic_text[4];
X	extern struct pixrect mail_icon_image1, mail_icon_image2;
X	(void) sprintf(ic_text, "%3d", msg_cnt);
X	tool_set_attributes(tool,
X	    WIN_LABEL, buf,
X	    WIN_ICON_LABEL, ic_text,
X	    WIN_ICON_IMAGE, ison(glob_flags, NEW_MAIL)?
X		&mail_icon_image2 : &mail_icon_image1,
X	    0);
X    } else
X#endif /* SUNTOOL */
X#ifdef CURSES
X	if (iscurses) {
X	    move (0, 0);
X	    printw("%-3d %-.*s",
X		((msg_cnt)? current_msg+1 : 0), COLS-5, buf), clrtoeol();
X	} else
X#endif /* CURSES */
X	    puts(buf);
X    return;
X}
X
X/*
X *  For uucp mailers that use >From lines with "remote from <path>":
X * (where "path" is a hostname or pathnames)
X *
X *  a. Set the return_path to the empty string.
X *  b. For each From_ or >From_ line:
X *  c. Save the username (second token).
X *  d. Save the date (3-7 tokens).
X *  e. If it has a "remote from" then append the remote host
X *	(last token) followed by a "!" to the return_path.
X *  f. If the saved username has a '@' but no '!' then convert it
X *	to UUCP path form.
X *  g. Append the saved username to return_path.
X */
Xparse_from(fp, path)
XFILE *fp;
Xchar path[];
X{
X    char user[256], buf[256]; /* max size for each line in a mail file */
X    register char *p;
X    long save_offset = ftell(fp);
X
X    path[0] = '\0';
X    while (fgets(buf, sizeof buf, fp)) {
X	if (strncmp(buf, ">From ", 6))
X	    break;
X	p = buf + 6;
X
X	(void) sscanf(p, "%s", user);
X
X	while (p = index(p+1, 'r')) {
X	    if (!strncmp(p, "remote from ", 12)) {
X		char *p2 = path+strlen(path);
X		skipspaces(12);
X		sscanf(p, "%s", p2); /* add the new machine to current path */
X		(void) strcat(p2, "!");
X		break;
X	    }
X	}
X
X	if (p)
X	    (void) bang_form(path + strlen(path), user);
X	save_offset = ftell(fp);
X    }
X    fseek(fp, save_offset, L_SET);
X}
X
X/*
X * Scan a file and select messages from it and append them to the current folder
X *
X * If "append" is 1, start where we left off (held in msg[cnt].m_offset)
X * and scan for messages.  Append all messages found until EOF.
X *
X * If "append" is 2, we're merging in a new file, so start at the end of
X * the present folder and append all messages found until EOF.
X *
X * If "append" is 0, then the message separator must exist once and
X * only once.  All extra occurrences of the separator is preceded by a '>'.
X * The list argument will be the message number to replace in the current
X * folder with the message read in from other filename.
X */
Xload_folder(file, append, list)
Xchar *file, *list;
Xint append;
X{
X    char	buf[BUFSIZ];
X    int		lines = 0, msg_found = 0, had_error = 1;
X    int		get_status = 1, cnt;
X    long	bytes, ftell();
X    struct msg  old;
X    char	wkday[4], month[4];
X    int		day, year, mins, hour;
X    FILE       *fp;
X#ifdef MMDF
X    int		begin_sep = 0; /* track beginning vs ending separators */
X#endif /* MMDF */
X
X    if (!(fp = fopen(file, "r"))) {
X	error("Unable to open %s", file);
X	return -1;
X    }
X
X    if (append) {
X	cnt = msg_cnt;
X	(void) fseek(fp, append == 1 ? msg[cnt].m_offset : 0L, L_SET);
X    } else {
X	cnt = (int)list;
X	old = msg[cnt];
X    }
X
X    if (isoff(glob_flags, READ_ONLY)) {
X	if (tmpf)
X	    (void) fclose(tmpf);
X	if (!(tmpf = mask_fopen(tempfile, "a"))) {
X	    error("Unable to open %s for appending", tempfile);
X	    (void) fclose(fp);
X	    return -1;
X	}
X	(void) fseek(tmpf, 0L, 2); /* assure we're at the end of the file */
X    } else if (append == 2) {
X	/* you can't merge in a folder to a read-only folder */
X	(void) fclose(fp);
X	return -1;
X    }
X
X#ifdef MMDF
X    if (!append) {
X	strcpy(buf, MSG_SEPARATOR);
X	goto do_headers;
X    }
X#endif /* MMDF */
X    while (fgets(buf, sizeof (buf), fp)) {
X#ifndef MSG_SEPARATOR
X	if (!strncmp(buf, "From ", 5) &&
X	    /* From uucp Wed Jan 11 20:40:00 1989 */
X	    (sscanf(buf+5, "%*s %3s %3s %d %d:%d:%*d %d",
X		wkday, month, &day, &hour, &mins, &year) == 6 ||
X	    /* From uucp Wed Jan 11 20:40:00 PST 1989 */
X	    sscanf(buf+5, "%*s %3s %3s %d %d:%d:%*d %*s %d",
X		    wkday, month, &day, &hour, &mins, &year) == 6 ||
X	    /* From uucp Wed Jan 11 20:40 PST 1989 */
X	    sscanf(buf+5, "%*s %3s %3s %d %d:%d %*s %d",
X		    wkday, month, &day, &hour, &mins, &year) == 6))
X#else /* MSG_SEPARATOR */
X	if (!strncmp(buf, MSG_SEPARATOR, strlen(MSG_SEPARATOR)))
X#endif /* MSG_SEPARATOR */
X	{
X#ifdef MMDF
X	    if (!append)
X		fputc('>', tmpf);
X	    else if (begin_sep = !begin_sep)
Xdo_headers:
X#else /* MMDF */
X	    if (!append && msg_found)
X		fputc('>', tmpf);
X	    else
X#endif /* MMDF */
X	    {
X		msg_found++;
X		had_error = 0;
X		if (append && cnt == MAXMSGS-2) {
X		    wprint("WARNING: exceeded %d messages.\n", MAXMSGS);
X		    wprint("Not all messages have been loaded.\n");
X		    had_error++;
X		    break;
X		}
X		if (ison(glob_flags, READ_ONLY))
X		    bytes = ftell(fp) - strlen(buf);
X		else {
X		    char path[256];
X		    parse_from(fp, path);
X		    if (path[0])
X			(void) sprintf(buf, "From %s %s %s %d %d:%d:00 %d\n",
X			    path, wkday, month, day, hour, mins, year);
X		    bytes = ftell(tmpf);
X		}
X		/* finish up message structure from previous message.
X		 * if this is incorporating new mail, check "lines" to
X		 * see if previous message has already been set!
X		 */
X		if (cnt && lines) {
X		    msg[cnt-1].m_size = bytes - msg[cnt-1].m_offset;
X		    msg[cnt-1].m_lines = lines;
X		}
X		if (isoff(glob_flags, READ_ONLY) && fputs(buf, tmpf) == -1) {
X		    error(tempfile);
X		    had_error++;
X		    break;
X		}
X		msg[cnt].m_offset = bytes;
X		msg[cnt].m_flags = 0L;
X		lines = 0;
X#ifndef MSG_SEPARATOR
X		if (year > 1900)
X		    year -= 1900;
X		(void) sprintf(buf, "%02d%02d%02d%02d%02d%.3s",
X		    year, month_to_n(month), day, hour, mins, wkday);
X		strdup(msg[cnt].m_date_recv, buf);
X#endif /* MSG_SEPARATOR */
X		turnon(msg[cnt].m_flags, UNREAD); /* initialize */
X
X		/* we've read the "From " line(s), now read the rest of
X		 * the message headers till we get to a blank line.
X		 */
X		while (fgets(buf, sizeof (buf), fp) && (*buf != '\n')) {
X		    register char *p = buf;
X		    if (!strncmp(buf, "Date:", 5))
X			strdup(msg[cnt].m_date_sent, parse_date(p+5));
X		    if (get_status &&
X			!(get_status = strncmp(p, "Status:", 7))) {
X			/* new mail should not have a Status: field! */
X			turnon(msg[cnt].m_flags, OLD);
X			for (p += 8 ; *p != '\n'; p++)
X			    switch(*p) {
X				case 'R': turnoff(msg[cnt].m_flags, UNREAD);
X				when 'P': turnon(msg[cnt].m_flags, UNREAD);
X				when 'S': turnon(msg[cnt].m_flags, SAVED);
X				when 'r': turnon(msg[cnt].m_flags, REPLIED);
X				when 'O': ; /* do nothing */
X				otherwise :
X				    if (ison(glob_flags, WARNING))
X					print("unknown msg status flag: %c",*p);
X			    }
X		    }
X		    if (isoff(glob_flags,READ_ONLY) && fputs(buf, tmpf) == -1) {
X			error(tempfile);
X			had_error++;
X			break;
X		    }
X		    lines++;
X		}
X		if (!msg[cnt].m_date_sent || !*msg[cnt].m_date_sent)
X		    if (!msg[cnt].m_date_recv || !*msg[cnt].m_date_recv) {
X			wprint("Message %d has *no* date!?\n", cnt+1);
X			msg[cnt].m_date_sent = msg[cnt].m_date_recv =
X			    "0000000000XXX";
X		    } else
X			strdup(msg[cnt].m_date_sent, msg[cnt].m_date_recv);
X		else if (!msg[cnt].m_date_recv || !*msg[cnt].m_date_recv)
X		    strdup(msg[cnt].m_date_recv, msg[cnt].m_date_sent);
X		if (had_error)
X		    break;
X		if (append && list)
X		    set_msg_bit(list, cnt);
X		if (append)
X		    cnt = ++msg_cnt;
X		get_status = 1;
X	    }
X	}
X	if (msg_found) {
X	    lines++;
X	    if (isoff(glob_flags, READ_ONLY) && fputs(buf, tmpf) == -1) {
X		error(tempfile);
X		had_error++;
X		break;
X	    }
X	}
X    }
X    if (msg_found && append != 1)
X	turnon(glob_flags, DO_UPDATE);
X#ifdef MMDF
X    if (!append)
X	fputs(END_MSG_SEP, tmpf);
X#endif /* MMDF */
X    if (had_error) {
X	if (!append)
X	    msg[cnt] = old;
X	if (!msg_found && !append)
X	    print("File not left in correct message format.\n");
X    } else {
X	if (append)
X	    cnt--;
X	if (isoff(glob_flags, READ_ONLY))
X	    msg[cnt].m_size = ftell(tmpf) - msg[cnt].m_offset;
X	else
X	    msg[cnt].m_size = ftell(fp) - msg[cnt].m_offset;
X	msg[cnt].m_lines = lines;
X	/* remember where we were to seek to for when we append new mail */ 
X	if (append)
X	    cnt++;
X    }
X    if (append)
X	msg[cnt].m_offset = ftell(fp);
X    fclose(fp);
X    if (isoff(glob_flags, READ_ONLY)) {
X	fclose(tmpf);
X	if (!(tmpf = fopen(tempfile, "r"))) {
X	    error("Unable to open %s for reading", tempfile);
X	    return -1;
X	}
X    }
X    return !had_error;
X}
END_OF_FILE
if test 24063 -ne `wc -c <'msgs.c'`; then
    echo shar: \"'msgs.c'\" unpacked with wrong size!
fi
# end of 'msgs.c'
fi
echo shar: End of archive 11 \(of 19\).
cp /dev/null ark11isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 19 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.



More information about the Comp.sources.unix mailing list