commit 0ab5efc5eb55c2ffa8517def6a37818f41fd96b8
parent 2bc9e310c17588513df3399f46155f9ae886dfa2
Author: Jake Bauer <jbauer@paritybit.ca>
Date: Tue, 1 Feb 2022 01:44:08 -0500
Add mail(1) code and LICENSE
Diffstat:
A | LICENSE | | | 15 | +++++++++++++++ |
A | Makefile | | | 17 | +++++++++++++++++ |
M | README.md | | | 7 | +++++-- |
A | USD.doc/Makefile | | | 15 | +++++++++++++++ |
A | USD.doc/mail0.nr | | | 73 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | USD.doc/mail1.nr | | | 109 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | USD.doc/mail2.nr | | | 617 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | USD.doc/mail3.nr | | | 131 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | USD.doc/mail4.nr | | | 445 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | USD.doc/mail5.nr | | | 1105 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | USD.doc/mail6.nr | | | 99 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | USD.doc/mail7.nr | | | 105 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | USD.doc/mail8.nr | | | 72 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | USD.doc/mail9.nr | | | 214 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | USD.doc/maila.nr | | | 31 | +++++++++++++++++++++++++++++++ |
A | cmd1.c | | | 524 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | cmd2.c | | | 442 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | cmd3.c | | | 736 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | cmdtab.c | | | 120 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | collect.c | | | 609 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | def.h | | | 265 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | edit.c | | | 286 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | extern.h | | | 263 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | fio.c | | | 522 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | glob.h | | | 95 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | head.c | | | 272 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | lex.c | | | 714 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | list.c | | | 797 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | mail.1 | | | 1276 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | main.c | | | 345 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | misc/mail.help | | | 38 | ++++++++++++++++++++++++++++++++++++++ |
A | misc/mail.rc | | | 2 | ++ |
A | misc/mail.tildehelp | | | 26 | ++++++++++++++++++++++++++ |
A | names.c | | | 628 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | pathnames.h | | | 49 | +++++++++++++++++++++++++++++++++++++++++++++++++ |
A | popen.c | | | 477 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | quit.c | | | 486 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | rcv.h | | | 44 | ++++++++++++++++++++++++++++++++++++++++++++ |
A | send.c | | | 614 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | strings.c | | | 120 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | temp.c | | | 86 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | tty.c | | | 408 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | util.c | | | 643 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | v7.local.c | | | 102 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | vars.c | | | 184 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | version.c | | | 37 | +++++++++++++++++++++++++++++++++++++ |
46 files changed, 14263 insertions(+), 2 deletions(-)
diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,15 @@
+ISC License
+
+Copyright (c) 2022, Jake Bauer
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/Makefile b/Makefile
@@ -0,0 +1,17 @@
+# $OpenBSD: Makefile,v 1.13 2020/12/15 00:50:01 daniel Exp $
+
+PROG= mail
+SRCS= version.c cmd1.c cmd2.c cmd3.c cmdtab.c collect.c \
+ edit.c fio.c head.c v7.local.c lex.c list.c main.c names.c \
+ popen.c quit.c send.c strings.c temp.c tty.c util.c vars.c
+SFILES= mail.help mail.tildehelp
+EFILES= mail.rc
+LINKS= ${BINDIR}/mail ${BINDIR}/Mail ${BINDIR}/mail ${BINDIR}/mailx
+
+distribution:
+ cd ${.CURDIR}/misc; ${INSTALL} ${INSTALL_COPY} -o root -g wheel \
+ -m 644 ${EFILES} ${DESTDIR}/etc
+ cd ${.CURDIR}/misc; ${INSTALL} ${INSTALL_COPY} -o ${BINOWN} -g ${BINGRP} \
+ -m 444 ${SFILES} ${DESTDIR}/usr/share/misc
+
+.include <bsd.prog.mk>
diff --git a/README.md b/README.md
@@ -1,8 +1,11 @@
# smc
-The simple mail client
+A Simple Mail Client.
-This project is a fork of mail(1) from OpenBSD.
+This project is a fork of mail(1) from OpenBSD 7.0.
+
+My changes to the source code are licensed under the ISC License, but much of
+the code retains its original BSD-3-clause license from UCB.
## Reasoning
diff --git a/USD.doc/Makefile b/USD.doc/Makefile
@@ -0,0 +1,15 @@
+# $OpenBSD: Makefile,v 1.4 2004/02/01 15:19:57 jmc Exp $
+# @(#)Makefile 8.1 (Berkeley) 6/8/93
+
+DIR= usd/07.mail
+SRCS= mail0.nr mail1.nr mail2.nr mail3.nr mail4.nr mail5.nr mail6.nr \
+ mail7.nr mail8.nr mail9.nr maila.nr
+MACROS= -me
+
+paper.ps: ${SRCS}
+ ${TBL} ${SRCS} | ${ROFF} > ${.TARGET}
+
+paper.txt: ${SRCS}
+ ${TBL} ${SRCS} | ${ROFF} -Tascii > ${.TARGET}
+
+.include <bsd.doc.mk>
diff --git a/USD.doc/mail0.nr b/USD.doc/mail0.nr
@@ -0,0 +1,73 @@
+.\" $OpenBSD: mail0.nr,v 1.4 2004/06/04 00:04:54 jmc Exp $
+.\"
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS 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 REGENTS OR 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.
+.\"
+.\" @(#)mail0.nr 8.1 (Berkeley) 6/8/93
+.\"
+.if n \{\
+.po 5n
+.ll 70n
+.\}
+.eh 'USD:7-%''Mail Reference Manual'
+.oh 'Mail Reference Manual''USD:7-%'
+.if n \
+.nr fs .5v
+.\".he 'Mail Reference Manual'\n(mo/\n(dy/\n(yr'%'
+.tp
+.sp 1.0i
+.sz 12
+.rb
+.(l C
+MAIL REFERENCE MANUAL
+.)l
+.sz 10
+.sp 2
+.i
+.(l C
+Kurt Shoens
+.)l
+.r
+.(l C
+Revised by
+.)l
+.(l C
+.i
+Craig Leres\ \c
+.r
+and\ \c
+.i
+Mark Andrews
+.)l
+.r
+.(l C
+Version 5.5
+
+
+\*(td
+.)l
+.pn 2
diff --git a/USD.doc/mail1.nr b/USD.doc/mail1.nr
@@ -0,0 +1,109 @@
+.\" $OpenBSD: mail1.nr,v 1.4 2004/06/04 00:04:54 jmc Exp $
+.\"
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS 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 REGENTS OR 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.
+.\"
+.\" @(#)mail1.nr 8.1 (Berkeley) 6/8/93
+.\"
+.sh 1 Introduction
+.pp
+.i Mail
+provides a simple and friendly environment for sending and receiving mail.
+It divides incoming mail into
+its constituent messages and allows the user to deal with them
+in any order. In addition, it provides a set of
+.i ed -\c
+like commands for manipulating messages and sending mail.
+.i Mail
+offers the user simple editing capabilities to ease the composition
+of outgoing messages, as well as providing the ability to define and send
+to names which address groups of users. Finally,
+.i Mail
+is able to send and receive messages across such networks as the
+ARPANET, UUCP, and Berkeley network.
+.pp
+This document describes how to use the
+.i Mail
+program to send and receive messages. The reader is not assumed to
+be familiar with other message handling systems, but should be
+familiar with the \s-2UNIX\s0\**
+.(f
+\** \s-1UNIX\s0 is a trademark of Bell Laboratories.
+.)f
+shell, the text editor, and some of the common \s-2UNIX\s0 commands.
+.q "The \s-2UNIX\s0 Programmer's Manual,"
+.q "An Introduction to Csh,"
+and
+.q "Text Editing with Ex and Vi"
+can be consulted for more information on these topics.
+.pp
+A word of explanation is in order here concerning the name
+.i Mail :
+the original UNIX mail program was known as
+.i /bin/mail .
+The BSD mail program was called
+.i Mail
+to differentiate it from the older mail program.
+.i /bin/mail
+is not included in OpenBSD so there is no ambiguity and the BSD
+mail program is installed as
+.i /usr/bin/mail ;
+.i /usr/bin/Mail
+is simply a link for backwards compatibility.
+To further confuse the issue, a second link was retained for
+compatibility with SystemV systems,
+.i mailx .
+In this document, we use the original name, `Mail',
+to refer to any of these.
+.pp
+Here is how messages are handled:
+the mail system accepts incoming
+.i messages
+for you from other people
+and collects them in a file, called your
+.i "system mailbox" .
+When you log in, the system notifies you if there are any messages
+waiting in your system mailbox. If you are a
+.i csh
+user, you will be notified when new mail arrives if you inform
+the shell of the location of your mailbox. On OpenBSD,
+your system mailbox is located in the directory /var/mail
+in a file with your login name. If your login name is
+.q sam,
+then you can make
+.i csh
+notify you of new mail by including the following line in your .cshrc
+file:
+.(l
+set mail=/var/mail/sam
+.)l
+When you read your mail using
+.i Mail ,
+it reads your system mailbox and separates that file into the
+individual messages that have been sent to you. You can then
+read, reply to, delete, or save these messages.
+Each message is marked with its author and the date they sent it.
diff --git a/USD.doc/mail2.nr b/USD.doc/mail2.nr
@@ -0,0 +1,617 @@
+.\" $OpenBSD: mail2.nr,v 1.5 2004/06/04 00:04:54 jmc Exp $
+.\"
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS 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 REGENTS OR 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.
+.\"
+.\" @(#)mail2.nr 8.2 (Berkeley) 5/16/94
+.\"
+.sh 1 "Common usage"
+.pp
+The
+.i Mail
+command has two distinct usages, according to whether one
+wants to send or receive mail. Sending mail is simple: to send a
+message to a user whose login name is, say,
+\*(lqroot,\*(rq
+use the shell
+command:
+.(l
+% Mail root
+.)l
+then type your message. When you reach the end of the message, type
+an EOT (Control-D) at the beginning of a line, which will cause
+.i Mail
+to echo \*(lqEOT\*(rq and return you to the Shell. When the user you sent mail
+to next logs in, he will receive the message:
+.(l
+You have mail.
+.)l
+to alert him to the existence of your message.
+.pp
+If, while you are composing the message
+you decide that you do not wish to send it after all, you can
+abort the letter with a <Control-C>. Typing a single <Control-C>
+causes
+.i Mail
+to print
+.(l
+(Interrupt -- one more to kill letter)
+.)l
+Typing a second
+<Control-C> causes
+.i Mail
+to save your partial letter on the file
+.q dead.letter
+in your home directory and abort the letter.
+Once you have
+sent mail to someone, there is no way to undo the act, so be
+careful.
+.pp
+The message your recipient reads will consist of the message you
+typed, preceded by a line telling who sent the message (your login name)
+and the date and time it
+was sent.
+.pp
+If you want to send the same message to several other people, you can list
+their login names on the command line.
+Thus,
+.(l
+% Mail sam bob john
+Tuition fees are due next Friday. Don't forget!!
+<Control-D>
+EOT
+%
+.)l
+will send the reminder to sam, bob, and john.
+.pp
+If, when you log in, you see the message,
+.(l
+You have mail.
+.)l
+you can read the mail by typing simply:
+.(l
+% Mail
+.)l
+.i Mail
+will respond by typing its version number and date and then listing
+the messages you have waiting. Then it will type a prompt and await
+your command. The messages are assigned numbers starting with 1 \*- you
+refer to the messages with these numbers.
+.i Mail
+keeps track of which messages are
+.i new
+(have been sent since you last read your mail) and
+.i read
+(have been read by you). New messages have an
+.b N
+next to them in the header listing and old, but unread messages have
+a
+.b U
+next to them.
+.i Mail
+keeps track of new/old and read/unread messages by putting a
+header field called
+.q Status
+into your messages.
+.pp
+To look at a specific message, use the
+.b type
+command, which may be abbreviated to simply
+.b t .
+For example, if you had the following messages:
+.(l
+N 1 root Wed Sep 21 09:21 "Tuition fees"
+N 2 sam Tue Sep 20 22:55
+.)l
+you could examine the first message by giving the command:
+.(l
+type 1
+.)l
+which might cause
+.i Mail
+to respond with, for example:
+.(l
+Message 1:
+From root Wed Sep 21 09:21:45 1978
+Subject: Tuition fees
+Status: R
+
+Tuition fees are due next Wednesday. Don't forget!!
+
+.)l
+Many
+.i Mail
+commands that operate on messages take a message number as an
+argument like the
+.b type
+command. For these commands, there is a notion of a current
+message. When you enter the
+.i Mail
+program, the current message is initially the first one. Thus,
+you can often omit the message number and use, for example,
+.(l
+t
+.)l
+to type the current message. As a further shorthand, you can type a message
+by simply giving its message number. Hence,
+.(l
+1
+.)l
+would type the first message.
+.pp
+Frequently, it is useful to read the messages in your mailbox in order,
+one after another. You can read the next message in
+.i Mail
+by simply typing a newline. As a special case, you can type a newline
+as your first command to
+.i Mail
+to type the first message.
+.pp
+If, after reading a message, you wish to immediately send a reply,
+you can do so with the
+.b reply
+command.
+.b Reply ,
+like
+.b type ,
+takes a message number as an argument.
+.i Mail
+then begins a message addressed to the user who sent you the message.
+You may then type in your letter in reply, followed by a <Control-D>
+at the beginning of a line, as before.
+.i Mail
+will type EOT, then type the ampersand prompt to indicate its readiness
+to accept another command. In our example, if, after typing the
+first message, you wished to reply to it, you might give the command:
+.(l
+reply
+.)l
+.i Mail
+responds by typing:
+.(l
+To: root
+Subject: Re: Tuition fees
+.)l
+and waiting for you to enter your letter.
+You are now in the message collection mode described at the beginning
+of this section and
+.i Mail
+will gather up your message up to a <Control-D>.
+Note that it copies the subject
+header from the original message. This is useful in that correspondence
+about a particular matter will tend to retain the same subject heading,
+making it easy to recognize. If there are other header fields in
+the message, the information found will also be used.
+For example, if the letter had a
+.q "To:"
+header listing several recipients,
+.i Mail
+would arrange to send your reply to the same people as well.
+Similarly, if the original message contained a
+.q "Cc:"
+(carbon copies to) field,
+.i Mail
+would send your reply to
+.i those
+users, too.
+.i Mail
+is careful, though, not too send the message to
+.i you ,
+even if you appear in the
+.q "To:"
+or
+.q "Cc:"
+field, unless you ask to be included explicitly. See section 4 for more
+details.
+.pp
+After typing in your letter, the dialog with
+.i Mail
+might look like the following:
+.(l
+reply
+To: root
+Subject: Tuition fees
+
+Thanks for the reminder
+EOT
+&
+.)l
+.pp
+The
+.b reply
+command is especially useful for sustaining extended conversations
+over the message system, with other
+.q listening
+users receiving copies of the conversation. The
+.b reply
+command can be abbreviated to
+.b r .
+.pp
+Sometimes you will receive a message that has been sent to
+several people and wish to reply
+.i only
+to the person who sent it.
+.b Reply
+with a capital
+.b R
+replies to a message, but sends a copy to the sender only.
+.pp
+If you wish, while reading your mail, to send a message to someone,
+but not as a reply to one of your messages, you can send the message
+directly with the
+.b mail
+command, which takes as arguments the names of the recipients you wish
+to send to. For example, to send a message to
+.q frank,
+you would do:
+.(l
+mail frank
+This is to confirm our meeting next Friday at 4.
+EOT
+&
+.)l
+The
+.b mail
+command can be abbreviated to
+.b m .
+.pp
+Normally, each message you receive is saved in the file
+.i mbox
+in your login directory at the time you leave
+.i Mail .
+Often,
+however, you will not want to save a particular message you
+have received because it is only of passing interest. To avoid
+saving a message in
+.i mbox
+you can delete it using the
+.b delete
+command. In our example,
+.(l
+delete 1
+.)l
+will prevent
+.i Mail
+from saving message 1 (from root) in
+.i mbox .
+In addition to not saving deleted messages,
+.i Mail
+will not let
+you type them, either. The effect is to make the message disappear
+altogether, along with its number. The
+.b delete
+command can be abbreviated to simply
+.b d .
+.pp
+Many features of
+.i Mail
+can be tailored to your liking with the
+.b set
+command. The
+.b set
+command has two forms, depending on whether you are setting
+a
+.i binary
+option or a
+.i valued
+option.
+Binary options are either on or off. For example, the
+.q ask
+option informs
+.i Mail
+that each time you send a message, you want it to prompt you for
+a subject header, to be included in the message.
+To set the
+.q ask
+option, you would type
+.(l
+set ask
+.)l
+.pp
+Another useful
+.i Mail
+option is
+.q hold.
+Unless told otherwise,
+.i Mail
+moves the messages from your system mailbox to the file
+.i mbox
+in your home directory when you leave
+.i Mail .
+If you want
+.i Mail
+to keep your letters in the system mailbox instead, you can set the
+.q hold
+option.
+.pp
+Valued options are values which
+.i Mail
+uses to adapt to your tastes. For example, the
+.q SHELL
+option tells
+.i Mail
+which shell you like to use, and is specified by
+.(l
+set SHELL=/bin/csh
+.)l
+for example. Note that no spaces are allowed in
+.q "SHELL=/bin/csh."
+A complete list of the
+.i Mail
+options appears in section 5.
+.pp
+Another important valued option is
+.q crt.
+If you use a fast video terminal, you will find that when you
+print long messages, they fly by too quickly for you to read them.
+With the
+.q crt
+option, you can make
+.i Mail
+print any message larger than a given number of lines by sending
+it through a paging program. This program is specified by the
+valued option \fBPAGER\fP.
+If \fBPAGER\fP is not set, a default paginator is used.
+For example, most CRT users with 24-line screens should do:
+.(l
+set crt=24
+.)l
+to paginate messages that will not fit on their screens.
+In the default state, \fImore\fP (default paginator) prints a screenful of
+information, then types ``byte XXX'', where `XXX' represents the number
+of bytes paginated.
+Type a space to see the next screenful.
+.pp
+Another adaptation to user needs that
+.i Mail
+provides is that of
+.i aliases .
+An alias is simply a name which stands for one or more
+real user names.
+.i Mail
+sent to an alias is really sent to the list of real users
+associated with it. For example, an alias can be defined for the
+members of a project, so that you can send mail to the whole project
+by sending mail to just a single name. The
+.b alias
+command in
+.i Mail
+defines an alias. Suppose that the users in a project are
+named Sam, Sally, Steve, and Susan. To define an alias called
+.q project
+for them, you would use the
+.i Mail
+command:
+.(l
+alias project sam sally steve susan
+.)l
+The
+.b alias
+command can also be used to provide a convenient name for someone
+whose user name is inconvenient. For example, if a user named
+.q "Bob Anderson"
+had the login name
+.q anderson,"
+you might want to use:
+.(l
+alias bob anderson
+.)l
+so that you could send mail to the shorter name,
+.q bob.
+.pp
+While the
+.b alias
+and
+.b set
+commands allow you to customize
+.i Mail ,
+they have the drawback that they must be retyped each time you enter
+.i Mail .
+To make them more convenient to use,
+.i Mail
+always looks for two files when it is invoked. It first reads
+a system wide file
+.q /etc/mail.rc,
+then a user specific file,
+.q .mailrc,
+which is found in the user's home directory.
+The system wide file
+is maintained by the system administrator and
+contains
+.b set
+commands that are applicable to all users of the system.
+The
+.q .mailrc
+file is usually used by each user to set options the way he likes
+and define individual aliases.
+For example, my .mailrc file looks like this:
+.(l
+set ask nosave SHELL=/bin/csh
+.)l
+As you can see, it is possible to set many options in the
+same
+.b set
+command. The
+.q nosave
+option is described in section 5.
+.pp
+Mail aliasing is implemented
+at the system-wide level
+by the mail delivery
+system
+.i sendmail .
+These aliases are stored in the file /etc/mail/aliases and are
+accessible to all users of the system.
+The lines in /etc/mail/aliases are of
+the form:
+.(l
+alias: name\*<1\*>, name\*<2\*>, name\*<3\*>
+.)l
+where
+.i alias
+is the mailing list name and the
+.i name\*<i\*>
+are the members of the list. Long lists can be continued onto the next
+line by starting the next line with a space or tab. Remember that you
+must execute the command
+.i newaliases
+(as superuser)
+after editing /etc/mail/aliases since the delivery system
+uses an indexed file created by
+.i newaliases .
+.pp
+We have seen that
+.i Mail
+can be invoked with command line arguments which are people
+to send the message to, or with no arguments to read mail.
+Specifying the
+.rb -f
+flag on the command line causes
+.i Mail
+to read messages from a file other than your system mailbox.
+For example, if you have a collection of messages in
+the file
+.q letters
+you can use
+.i Mail
+to read them with:
+.(l
+% Mail -f letters
+.)l
+You can use all
+the
+.i Mail
+commands described in this document to examine, modify, or delete
+messages from your
+.q letters
+file, which will be rewritten when you leave
+.i Mail
+with the
+.b quit
+command described below.
+.pp
+Since mail that you read is saved in the file
+.i mbox
+in your home directory by default, you can read
+.i mbox
+in your home directory by using simply
+.(l
+% Mail -f
+.)l
+.pp
+Normally, messages that you examine using the
+.b type
+command are saved in the file
+.q mbox
+in your home directory if you leave
+.i Mail
+with the
+.b quit
+command described below.
+If you wish to retain a message in your system mailbox
+you can use the
+.b preserve
+command to tell
+.i Mail
+to leave it there.
+The
+.b preserve
+command accepts a list of message numbers, just like
+.b type
+and may be abbreviated to
+.b pre .
+.pp
+Messages in your system mailbox that you do not examine are
+normally retained in your system mailbox automatically.
+If you wish to have such a message saved in
+.i mbox
+without reading it, you may use the
+.b mbox
+command to have them so saved. For example,
+.(l
+mbox 2
+.)l
+in our example would cause the second message (from sam)
+to be saved in
+.i mbox
+when the
+.b quit
+command is executed.
+.b Mbox
+is also the way to direct messages to your
+.i mbox
+file if you have set the
+.q hold
+option described above.
+.b Mbox
+can be abbreviated to
+.b mb .
+.pp
+When you have perused all the messages of interest, you can leave
+.i Mail
+with the
+.b quit
+command, which saves the messages you have typed but not
+deleted in the file
+.i mbox
+in your login directory. Deleted messages are discarded irretrievably,
+and messages left untouched are preserved in your system mailbox so
+that you will see them the next time you type:
+.(l
+% Mail
+.)l
+The
+.b quit
+command can be abbreviated to simply
+.b q .
+.pp
+If you wish for some reason to leave
+.i Mail
+quickly without altering either your system mailbox or
+.i mbox ,
+you can type the
+.b x
+command (short for
+.b exit ),
+which will immediately return you to the Shell without changing anything.
+.pp
+If, instead, you want to execute a Shell command without leaving
+.i Mail ,
+you
+can type the command preceded by an exclamation point, just as in the
+text editor. Thus, for instance:
+.(l
+!date
+.)l
+will print the current date without leaving
+.i Mail .
+.pp
+Finally, the
+.b help
+command is available to print out a brief summary of the
+.i Mail
+commands, using only the single character command abbreviations.
diff --git a/USD.doc/mail3.nr b/USD.doc/mail3.nr
@@ -0,0 +1,131 @@
+.\" $OpenBSD: mail3.nr,v 1.4 2004/06/04 00:04:54 jmc Exp $
+.\"
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS 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 REGENTS OR 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.
+.\"
+.\" @(#)mail3.nr 8.1 (Berkeley) 6/8/93
+.\"
+.sh 1 "Maintaining folders"
+.pp
+.i Mail
+includes a simple facility for maintaining groups of messages together
+in folders. This section describes this facility.
+.pp
+To use the folder facility, you must tell
+.i Mail
+where you wish to keep your folders. Each folder of messages will
+be a single file. For convenience, all of your folders are kept in
+a single directory of your choosing. To tell
+.i Mail
+where your folder directory is, put a line of the form
+.(l
+set folder=letters
+.)l
+in your
+.i .mailrc
+file. If, as in the example above, your folder directory does not
+begin with a `/,'
+.i Mail
+will assume that your folder directory is to be found starting from
+your home directory. Thus, if your home directory is
+.b /home/person
+the above example told
+.i Mail
+to find your folder directory in
+.b /home/person/letters .
+.pp
+Anywhere a file name is expected, you can use a folder name, preceded
+with `+.' For example, to put a message into a folder with the
+.b save
+command, you can use:
+.(l
+save +classwork
+.)l
+to save the current message in the
+.i classwork
+folder. If the
+.i classwork
+folder does not yet exist, it will be created. Note that messages
+which are saved with the
+.b save
+command are automatically removed from your system mailbox.
+.pp
+In order to make a copy of a message in a folder without causing
+that message to be removed from your system mailbox, use the
+.b copy
+command, which is identical in all other respects to the
+.b save
+command. For example,
+.(l
+copy +classwork
+.)l
+copies the current message into the
+.i classwork
+folder and leaves a copy in your system mailbox.
+.pp
+The
+.b folder
+command
+can be used to direct
+.i Mail
+to the contents of a different folder.
+For example,
+.(l
+folder +classwork
+.)l
+directs
+.i Mail
+to read the contents of the
+.i classwork
+folder. All of the commands that you can use on your system
+mailbox are also applicable to folders, including
+.b type ,
+.b delete ,
+and
+.b reply .
+To inquire which folder you are currently editing, use simply:
+.(l
+folder
+.)l
+.pp
+To list your current set of folders, use the
+.b folders
+command.
+.pp
+To start
+.i Mail
+reading one of your folders, you can use the
+.b \-f
+option described in section 2. For example:
+.(l
+% Mail \-f +classwork
+.)l
+will cause
+.i Mail
+to read your
+.i classwork
+folder without looking at your system mailbox.
diff --git a/USD.doc/mail4.nr b/USD.doc/mail4.nr
@@ -0,0 +1,445 @@
+.\" $OpenBSD: mail4.nr,v 1.6 2008/11/03 18:20:45 jmc Exp $
+.\"
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS 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 REGENTS OR 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.
+.\"
+.\" @(#)mail4.nr 8.2 (Berkeley) 5/16/94
+.\"
+.sh 1 "More about sending mail"
+.sh 2 "Tilde escapes"
+.pp
+While typing in a message to be sent to others, it is often
+useful to be able to invoke the text editor on the partial message,
+print the message, execute a shell command, or do some other
+auxiliary function.
+.i Mail
+provides these capabilities through
+.i "tilde escapes" ,
+which consist of a tilde (~) at the beginning of a line, followed by
+a single character which indicates the function to be performed. For
+example, to print the text of the message so far, use:
+.(l
+~p
+.)l
+which will print a line of dashes, the recipients of your message, and
+the text of the message so far.
+Since
+.i Mail
+requires two consecutive <Control-C>'s to abort a letter, you
+can use a single <Control-C> to abort the output of ~p or any other
+~ escape without killing your letter.
+.pp
+If you are dissatisfied with the message as
+it stands, you can invoke the text editor on it using the escape
+.(l
+~e
+.)l
+which causes the message to be copied into a temporary file and an
+instance of the editor to be spawned. After modifying the message to
+your satisfaction, write it out and quit the editor.
+.i Mail
+will respond
+by typing
+.(l
+(continue)
+.)l
+after which you may continue typing text which will be appended to your
+message, or type <Control-D> to end the message.
+A standard text editor is provided by
+.i Mail .
+You can override this default by setting the valued option
+.q EDITOR
+to something else. For example, you might prefer:
+.(l
+set EDITOR=/bin/ed
+.)l
+.pp
+Many systems offer a screen editor as an alternative to the standard
+text editor, such as the
+.i vi
+editor from UC Berkeley,
+or
+.i mg ,
+an emacs-like editor.
+To use the screen, or
+.i visual
+editor, on your current message, you can use the escape,
+.(l
+~v
+.)l
+~v works like ~e, except that the screen editor is invoked instead.
+A default screen editor is defined by
+.i Mail .
+If it does not suit you, you can set the valued option
+.q VISUAL
+to the path name of a different editor.
+.pp
+It is often useful to be able to include the contents of some
+file in your message; the escape
+.(l
+~r filename
+.)l
+is provided for this purpose, and causes the named file to be appended
+to your current message.
+.i Mail
+complains if the file doesn't exist
+or can't be read. If the read is successful, the number of lines and
+characters appended to your message is printed, after which you may continue
+appending text. The filename may contain shell metacharacters like * and ?
+which are expanded according to the conventions of your shell.
+.pp
+As a special case of ~r, the escape
+.(l
+~d
+.)l
+reads in the file
+.q dead.letter
+in your home directory. This is often useful since
+.i Mail
+copies the text
+of your message there when you abort a message with <Control-C>.
+.pp
+To save the current text of your message on a file you may use the
+.(l
+~w filename
+.)l
+escape.
+.i Mail
+will print out the number of lines and characters written
+to the file, after which you may continue appending text to your message.
+Shell metacharacters may be used in the filename, as in ~r and are expanded
+with the conventions of your shell.
+.pp
+If you are sending mail from within
+.i Mail's
+command mode
+you can read a message sent to you into the message
+you are constructing with the escape:
+.(l
+~m 4
+.)l
+which will read message 4 into the current message, shifted right by
+one tab stop. You can name any non-deleted message, or list of messages.
+Messages can also be forwarded without shifting by a tab stop with ~f.
+This is the usual way to forward a message.
+.pp
+If, in the process of composing a message, you decide to add additional
+people to the list of message recipients, you can do so with the escape
+.(l
+~t name1 name2 ...
+.)l
+You may name as few or many additional recipients as you wish. Note
+that the users originally on the recipient list will still receive
+the message; you cannot remove someone from the recipient
+list with ~t.
+.pp
+If you wish, you can associate a subject with your message by using the
+escape
+.(l
+~s Arbitrary string of text
+.)l
+which replaces any previous subject with
+.q "Arbitrary string of text."
+The subject, if given, is sent near the
+top of the message prefixed with
+.q "Subject:"
+You can see what the message will look like by using ~p.
+.pp
+For political reasons, one occasionally prefers to list certain
+people as recipients of carbon copies of a message rather than
+direct recipients. The escape
+.(l
+~c name1 name2 ...
+.)l
+adds the named people to the
+.q "Cc:"
+list, similar to ~t.
+Again, you can execute ~p to see what the message will look like.
+.pp
+The escape
+.(l
+~b name1 name2 ...
+.)l
+adds the named people to the
+.q "Cc:"
+list, but does not make the names visible in the
+.q "Cc:"
+line ("blind" carbon copy).
+.pp
+The recipients of the message together constitute the
+.q "To:"
+field, the subject the
+.q "Subject:"
+field, and the carbon copies the
+.q "Cc:"
+field. If you wish to edit these in ways impossible with the ~t, ~s, ~c
+and ~b escapes, you can use the escape
+.(l
+~h
+.)l
+which prints
+.q "To:"
+followed by the current list of recipients and leaves the cursor
+(or printhead) at the end of the line. If you type in ordinary
+characters, they are appended to the end of the current list of
+recipients. You can also use your erase character to erase back into
+the list of recipients, or your kill character to erase them altogether.
+Thus, for example, if your erase and kill characters are the standard
+(on printing terminals) <Control-H> and <Control-U> keys,
+.(l
+~h
+To: root kurt^H^H^H^Hbill
+.)l
+would change the initial recipients
+.q "root kurt"
+to
+.q "root bill."
+When you type a newline,
+.i Mail
+advances to the
+.q "Subject:"
+field, where the same rules apply. Another newline brings you to
+the
+.q "Cc:"
+field, which may be edited in the same fashion. Another newline
+brings you to the
+.q "Bcc:"
+("blind" carbon copy) field, which follows the same rules as the "Cc:"
+field. Another newline
+leaves you appending text to the end of your message. You can use
+~p to print the current text of the header fields and the body
+of the message.
+.pp
+To effect a temporary escape to the shell, the escape
+.(l
+~!command
+.)l
+is used, which executes
+.i command
+and returns you to mailing mode without altering the text of
+your message. If you wish, instead, to filter the body of your
+message through a shell command, then you can use
+.(l
+~|command
+.)l
+which pipes your message through the command and uses the output
+as the new text of your message. If the command produces no output,
+.i Mail
+assumes that something is amiss and retains the old version
+of your message. A frequently-used filter is the command
+.i fmt ,
+designed to format outgoing mail.
+.pp
+To effect a temporary escape to
+.i Mail
+command mode instead, you can use the
+.(l
+~:\fIMail command\fP
+.)l
+escape. This is especially useful for retyping the message you are
+replying to, using, for example:
+.(l
+~:t
+.)l
+It is also useful for setting options and modifying aliases.
+.pp
+If you wish abort the current message, you can use the escape
+.(l
+~q
+.)l
+This will terminate the current message and return you to the
+shell (or \fIMail\fP if you were using the \fBmail\fP command).
+If the \fBsave\fP option is set, the message will be copied
+to the file
+.q dead.letter
+in your home directory.
+.pp
+If you wish to abort the current message,
+without saving any copy of it whatsoever,
+even if the \fBsave\fP option is set,
+you can use the escape
+.(l
+~x
+.)l
+.pp
+If you wish (for some reason) to send a message that contains
+a line beginning with a tilde, you must double it. Thus, for example,
+.(l
+~~This line begins with a tilde.
+.)l
+sends the line
+.(l
+~This line begins with a tilde.
+.)l
+.pp
+Finally, the escape
+.(l
+~?
+.)l
+prints out a brief summary of the available tilde escapes.
+.pp
+On some terminals (particularly ones with no lower case)
+tilde's are difficult to type.
+.i Mail
+allows you to change the escape character with the
+.q escape
+option. For example, I set
+.(l
+set escape=]
+.)l
+and use a right bracket instead of a tilde. If I ever need to
+send a line beginning with right bracket, I double it, just as for ~.
+Changing the escape character removes the special meaning of ~.
+.sh 2 "Network access"
+.pp
+This section describes how to send mail to people on other machines.
+Recall that sending to a plain login name sends mail to that person
+on your machine. If your machine is directly (or sometimes, even,
+indirectly) connected to the Internet, you can send messages to people
+on the Internet using a name of the form
+.(l
+name@host.domain
+.)l
+where
+.i name
+is the login name of the person you're trying to reach,
+.i host
+is the name of the machine on the Internet,
+and
+.i domain
+is the higher-level scope within which the hostname is known, e.g. EDU (for educational
+institutions), COM (for commercial entities), GOV (for governmental agencies),
+ARPA for many other things, BITNET or CSNET for those networks.
+.pp
+If your recipient logs in on a machine connected to yours by
+UUCP (the Bell Laboratories supplied network that communicates
+over telephone lines), sending mail can be a bit more complicated.
+You must know the list of machines through which your message must
+travel to arrive at his site. So, if his machine is directly connected
+to yours, you can send mail to him using the syntax:
+.(l
+host!name
+.)l
+where, again,
+.i host
+is the name of the machine and
+.i name
+is the login name.
+If your message must go through an intermediary machine first, you
+must use the syntax:
+.(l
+intermediary!host!name
+.)l
+and so on. It is actually a feature of UUCP that the map of all
+the systems in the network is not known anywhere (except where people
+decide to write it down for convenience). Talk to your system administrator
+about good ways to get places; the
+.i uuname
+command will tell you systems whose names are recognized, but not which
+ones are frequently called or well-connected.
+.pp
+When you use the
+.b reply
+command to respond to a letter, there is a problem of figuring out the
+names of the users in the
+.q "To:"
+and
+.q "Cc:"
+lists
+.i "relative to the current machine" .
+If the original letter was sent to you by someone on the local machine,
+then this problem does not exist, but if the message came from a remote
+machine, the problem must be dealt with.
+.i Mail
+uses a heuristic to build the correct name for each user relative
+to the local machine. So, when you
+.b reply
+to remote mail, the names in the
+.q "To:"
+and
+.q "Cc:"
+lists may change somewhat.
+.sh 2 "Special recipients"
+.pp
+As described previously, you can send mail to either user names or
+.b alias
+names. It is also possible to send messages directly to files or to
+programs, using special conventions. If a recipient name has a
+`/' in it or begins with a `+', it is assumed to be the
+path name of a file into which
+to send the message. If the file already exists, the message is
+appended to the end of the file. If you want to name a file in
+your current directory (ie, one for which a `/' would not usually
+be needed) you can precede the name with `./'
+So, to send mail to the file
+.q memo
+in the current directory, you can give the command:
+.(l
+% Mail ./memo
+.)l
+If the name begins with a `+,' it is expanded into the full path name
+of the folder name in your folder directory.
+This ability to send mail to files can be used for a variety of
+purposes, such as maintaining a journal and keeping a record of
+mail sent to a certain group of users. The second example can be
+done automatically by including the full pathname of the record
+file in the
+.b alias
+command for the group. Using our previous
+.b alias
+example, you might give the command:
+.(l
+alias project sam sally steve susan /usr/project/mail_record
+.)l
+Then, all mail sent to "project" would be saved on the file
+.q /usr/project/mail_record
+as well as being sent to the members of the project. This file
+can be examined using
+.i "Mail \-f" .
+.pp
+It is sometimes useful to send mail directly to a program, for
+example one might write a project billboard program and want to access
+it using
+.i Mail .
+To send messages to the billboard program, one can send mail
+to the special name `|billboard' for example.
+.i Mail
+treats recipient names that begin with a `|' as a program to send
+the mail to. An
+.b alias
+can be set up to reference a `|' prefaced name if desired.
+.i Caveats :
+the shell treats `|' specially, so it must be quoted on the command
+line. Also, the `| program' must be presented as a single argument to
+mail. The safest course is to surround the entire name with double
+quotes. This also applies to usage in the
+.b alias
+command. For example, if we wanted to alias `rmsgs' to `rmsgs \-s'
+we would need to say:
+.(l
+alias rmsgs "| rmsgs -s"
+.)l
diff --git a/USD.doc/mail5.nr b/USD.doc/mail5.nr
@@ -0,0 +1,1105 @@
+.\" $OpenBSD: mail5.nr,v 1.6 2008/11/03 18:20:45 jmc Exp $
+.\"
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS 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 REGENTS OR 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.
+.\"
+.\" @(#)mail5.nr 8.2 (Berkeley) 5/16/94
+.\"
+.sh 1 "Additional features"
+.pp
+This section describes some additional commands useful for
+reading your mail, setting options, and handling lists of messages.
+.sh 2 "Message lists"
+.pp
+Several
+.i Mail
+commands accept a list of messages as an argument.
+Along with
+.b type
+and
+.b delete ,
+described in section 2,
+there is the
+.b from
+command, which prints the message headers associated with the
+message list passed to it.
+The
+.b from
+command is particularly useful in conjunction with some of the
+message list features described below.
+.pp
+A
+.i "message list"
+consists of a list of message numbers, ranges, and names,
+separated by spaces or tabs. Message numbers may be either
+decimal numbers, which directly specify messages, or one of the
+special characters
+.q "^" ,
+.q "." ,
+or
+.q "$"
+to specify the first relevant, current, or last
+relevant message, respectively.
+.i Relevant
+here means, for most commands
+.q "not deleted"
+and
+.q "deleted"
+for the
+.b undelete
+command.
+.pp
+A range of messages consists of two message numbers (of the form
+described in the previous paragraph) separated by a dash.
+Thus, to print the first four messages, use
+.(l
+type 1\-4
+.)l
+and to print all the messages from the current message to the last
+message, use
+.(l
+type .\-$
+.)l
+.pp
+A
+.i name
+is a user name. The user names given in the message list are
+collected together and each message selected by other means
+is checked to make sure it was sent by one of the named users.
+If the message consists entirely of user names, then every
+message sent by one of those users that is
+.i relevant
+(in the sense described earlier)
+is selected. Thus, to print every message sent to you by
+.q root,
+do
+.(l
+type root
+.)l
+.pp
+As a shorthand notation, you can specify simply
+.q *
+to get every
+.i relevant
+(same sense)
+message. Thus,
+.(l
+type *
+.)l
+prints all undeleted messages,
+.(l
+delete *
+.)l
+deletes all undeleted messages, and
+.(l
+undelete *
+.)l
+undeletes all deleted messages.
+.pp
+You can search for the presence of a word in subject lines with
+.b / .
+For example, to print the headers of all messages that contain the
+word
+.q PASCAL,
+do:
+.(l
+from /pascal
+.)l
+Note that subject searching ignores upper/lower case differences.
+.sh 2 "List of commands"
+.pp
+This section describes all the
+.i Mail
+commands available when
+receiving mail.
+.ip "\fB\-\fP\ \ "
+The
+.rb \-
+command goes to the previous message and prints it. The
+.rb \-
+command may be given a decimal number
+.i n
+as an argument, in which case the
+.i n th
+previous message is gone to and printed.
+.ip "\fB?\fP\ \ "
+Prints a brief summary of commands.
+.ip "\fB!\fP\ \ "
+Used to preface a command to be executed by the shell.
+.ip "\fBPrint\fP\ \ "
+Like
+.b print ,
+but also print out ignored header fields. See also
+\fBprint\fP, \fBignore\fP, and \fBretain\fP.
+\fBPrint\fP can be abbreviated to \fBP\fP.
+.ip "\fBReply\fP or \fBRespond\fP\ \ "
+Note the capital \fBR\fP in the name.
+Frame a reply to one or more messages.
+The reply (or replies if you are using this on multiple messages)
+will be sent ONLY to the person who sent you the message
+(respectively, the set of people who sent the messages you are
+replying to).
+You can
+add people using the \fB~t\fP, \fB~c\fP, and \fB~b\fP
+tilde escapes. The subject in your reply is formed by prefacing the
+subject in the original message with
+.q "Re:"
+unless it already began thus.
+If the original message included a
+.q "reply-to"
+header field, the reply will go
+.i only
+to the recipient named by
+.q "reply-to."
+You type in your message using the same conventions available to you
+through the
+.b mail
+command.
+The
+.b Reply
+command is especially useful for replying to messages that were sent
+to enormous distribution groups when you really just want to
+send a message to the originator. Use it often.
+\fBReply\fP (and \fBRespond\fP) can be abbreviated to \fBR\fP.
+.ip "\fBType\fP\ \ "
+Identical to the
+.b Print
+command.
+\fBType\fP can be abbreviated to \fBT\fP.
+.ip "\fBalias\fP\ \ "
+Define a name to stand for a set of other names.
+This is used when you want to send messages to a certain
+group of people and want to avoid retyping their names.
+For example
+.(l
+alias project john sue willie kathryn
+.)l
+creates an alias
+.i project
+which expands to the four people John, Sue, Willie, and Kathryn.
+If no arguments are given, all currently-defined aliases are printed.
+If one argument is given, that alias is printed (if it exists).
+\fBAlias\fP can be abbreviated to \fBa\fP.
+.ip "\fBalternates\fP\ \ "
+If you have accounts on several machines, you may find it convenient
+to use /etc/mail/aliases on all the machines except one to direct
+your mail to a single account.
+The
+.b alternates
+command is used to inform
+.i Mail
+that each of these other addresses is really
+.i you .
+.i Alternates
+takes a list of user names and remembers that they are all actually you.
+When you
+.b reply
+to messages that were sent to one of these alternate names,
+.i Mail
+will not bother to send a copy of the message to this other address (which
+would simply be directed back to you by the alias mechanism).
+If
+.i alternates
+is given no argument, it lists the current set of alternate names.
+.b Alternates
+is usually used in the .mailrc file.
+\fBAlternates\fP can be abbreviated to \fBalt\fP.
+.ip "\fBchdir\fP\ \ "
+The
+.b chdir
+command allows you to change your current directory.
+.b Chdir
+takes a single argument, which is taken to be the pathname of
+the directory to change to. If no argument is given,
+.b chdir
+changes to your home directory.
+\fBChdir\fP can be abbreviated to \fBc\fP.
+.ip "\fBcopy\fP\ \ "
+The
+.b copy
+command does the same thing that
+.b save
+does, except that it does not mark the messages it is used on
+for deletion when you quit.
+\fBCopy\fP can be abbreviated to \fBco\fP.
+.ip "\fBdelete\fP\ \ "
+Deletes a list of messages. Deleted messages can be reclaimed
+with the
+.b undelete
+command.
+\fBDelete\fP can be abbreviated to \fBd\fP.
+.ip "\fBdp\fP or \fBdt\fP\ \ "
+These
+commands delete the current message and print the next message.
+They are useful for quickly reading and disposing of mail.
+If there is no next message, \fIMail\fP says ``No more messages.''
+.ip "\fBedit\fP\ \ "
+To edit individual messages using the text editor, the
+.b edit
+command is provided. The
+.b edit
+command takes a list of messages as described under the
+.b type
+command and processes each by writing it into the file
+Message\c
+.i x
+where
+.i x
+is the message number being edited and executing the text editor on it.
+When you have edited the message to your satisfaction, write the message
+out and quit, upon which
+.i Mail
+will read the message back and remove the file.
+.b Edit
+can be abbreviated to
+.b e .
+.ip "\fBelse\fP\ \ "
+Marks the end of the then-part of an
+.b if
+statement and the beginning of the
+part to take effect if the condition of the
+.b if
+statement is false.
+.ip "\fBendif\fP\ \ "
+Marks the end of an
+.b if
+statement.
+.ip "\fBexit\fP or \fBxit\fP\ \ "
+Leave
+.i Mail
+without updating the system mailbox or the file you were reading.
+Thus, if you accidentally delete several messages, you can use
+.b exit
+to avoid scrambling your mailbox.
+\fBExit\fP can be abbreviated to \fBex\fP or \fBx\fP.
+.ip "\fBfile\fP\ \ "
+The same as
+.b folder .
+\fBFile\fP can be abbreviated to \fBfi\fP.
+.ip "\fBfolders\fP\ \ "
+List the names of the folders in your folder directory.
+.ip "\fBfolder\fP\ \ "
+The
+.b folder
+command switches to a new mail file or folder. With no arguments, it
+tells you which file you are currently reading. If you give
+it an argument, it will write out changes (such as deletions)
+you have made in the current file and read the new file.
+Some special conventions are recognized for the name:
+.(b
+.TS
+center;
+c c
+l a.
+Name Meaning
+_
+# Previous file read
+% Your system mailbox
+%name \fIName\fP's system mailbox
+& Your ~/mbox file
++folder A file in your folder directory
+.TE
+.)b
+\fBFolder\fP can be abbreviated to \fBfo\fP.
+.ip "\fBfrom\fP\ \ "
+The
+.b from
+command takes a list of messages and prints out the header lines for each one;
+hence
+.(l
+from joe
+.)l
+is the easy way to display all the message headers from \*(lqjoe.\*(rq
+\fBFrom\fP can be abbreviated to \fBf\fP.
+.ip "\fBheaders\fP\ \ "
+When you start up
+.i Mail
+to read your mail, it lists the message headers that you have.
+These headers tell you who each message is from, when they were
+received, how many lines and characters each message is, and the
+.q "Subject:"
+header field of each message, if present. In addition,
+.i Mail
+tags the message header of each message that has been the object
+of the
+.b preserve
+command with a
+.q P.
+Messages that have been
+.b saved
+or
+.b written
+are flagged with a
+.q *.
+Finally,
+.b deleted
+messages are not printed at all. If you wish to reprint the current
+list of message headers, you can do so with the
+.b headers
+command. The
+.b headers
+command (and thus the initial header listing)
+only lists the first so many message headers.
+The number of headers listed depends on the speed of your
+terminal.
+.i Mail
+maintains a notion of the current
+.q window
+into your messages for the purposes of printing headers.
+Use the
+.b z
+command to move forward a window,
+and
+.b z-
+to move back a window.
+You can move
+.i Mail's
+notion of the current window directly to a particular message by
+using, for example,
+.(l
+headers 40
+.)l
+to move
+.i Mail's
+attention to the messages around message 40.
+\fBHeaders\fP can be abbreviated to \fBh\fP.
+.ip "\fBhelp\fP\ \ "
+Print a brief and usually out of date help message about the commands
+in
+.i Mail .
+The
+.i man
+page for
+.i mail
+is usually more up-to-date than either the help message or this manual.
+It is also a synonym for \fB?\fP.
+.ip "\fBhold\fP\ \ "
+Arrange to hold a list of messages in the system mailbox, instead
+of moving them to the file
+.i mbox
+in your home directory. If you set the binary option
+.i hold ,
+this will happen by default.
+It does not override the \fBdelete\fP command.
+\fBHold\fP can be abbreviated to \fBho\fP.
+.ip "\fBif\fP\ \ "
+Commands in your
+.q .mailrc
+file can be executed conditionally depending on whether you are
+sending or receiving mail with the
+.b if
+command. For example, you can do:
+.(l
+if receive
+ \fIcommands\fP...
+endif
+.)l
+An
+.b else
+form is also available:
+.(l
+if send
+ \fIcommands\fP...
+else
+ \fIcommands\fP...
+endif
+.)l
+Note that the only allowed conditions are
+.b receive
+and
+.b send .
+.ip "\fBignore\fP \ \ "
+.b N.B.:
+.i Ignore
+has been superseded by
+.i retain .
+.br
+Add the list of header fields named to the
+.i "ignore list" .
+Header fields in the ignore list are not printed on your
+terminal when you print a message. This allows you to suppress
+printing of certain machine-generated header fields, such as
+.i Via
+which are not usually of interest. The
+.b Type
+and
+.b Print
+commands can be used to print a message in its entirety, including
+ignored fields.
+If
+.b ignore
+is executed with no arguments, it lists the current set of ignored fields.
+.ip "\fBinc\fP\ \ "
+Incorporate any new messages that have arrived while mail is being read.
+The new messages are added to the end of the message list,
+and the current message is reset to be the first new mail message.
+This does not renumber the existing message list, nor does it
+cause any changes made so far to be saved.
+.ip "\fBlist\fP\ \ "
+List the valid
+.i Mail
+commands.
+\fBList\fP can be abbreviated to \fBl\fP.
+.. .ip \fBlocal\fP
+.. Define a list of local names for this host. This command is useful
+.. when the host is known by more than one name. Names in the list
+.. may be qualified be the domain of the host. The first name on the local
+.. list is the
+.. .i distinguished
+.. name of the host.
+.. The names on the local list are used by
+.. .i Mail
+.. to decide which addresses are local to the host.
+.. For example:
+.. .(l
+.. local ucbarpa.BERKELEY.ARPA arpa.BERKELEY.ARPA \\
+.. arpavax.BERKELEY.ARPA r.BERKELEY.ARPA \\
+.. ucb-arpa.ARPA
+.. .)l
+.. From this list we see that
+.. .i "fred@ucbarpa.BERKELEY.ARPA",
+.. .i "harold@arpa.BERKELEY",
+.. and
+.. .i "larry@r"
+.. are all addresses of users on the local host.
+.. The
+.. .b local
+.. command is usually not used be general users since it is designed for
+.. local configuration; it is usually found in the file /usr/lib/Mail.rc.
+.ip "\fBmail\fP\ \ "
+Send mail to one or more people. If you have the
+.i ask
+option set,
+.i Mail
+will prompt you for a subject to your message. Then you
+can type in your message, using tilde escapes as described in
+section 4 to edit, print, or modify your message. To signal your
+satisfaction with the message and send it, type <Control-D> at the
+beginning of a line, or a . alone on a line if you set the option
+.i dot .
+To abort the message, type two interrupt characters (Control-C
+by default) in a row or use the
+.b ~q
+or
+.b ~x
+escapes.
+The \fBmail\fP command can be abbreviated to \fBm\fP.
+.ip "\fBmbox\fP\ \ "
+Indicate that a list of messages be sent to
+.i mbox
+in your home directory when you quit. This is the default
+action for messages if you do
+.i not
+have the
+.i hold
+option set.
+.ip "\fBmore\fP\ \ "
+Takes a message list and invokes the pager on that list.
+.ip "\fBnext\fP or \fB+\fP\ \ "
+The
+.b next
+command goes to the next message and types it. If given a message list,
+.b next
+goes to the first such message and types it. Thus,
+.(l
+next root
+.)l
+goes to the next message sent by
+.q root
+and types it. The
+.b next
+command can be abbreviated to simply a newline, which means that one
+can go to and type a message by simply giving its message number or
+one of the magic characters
+.q "^"
+.q "."
+or
+.q "$".
+Thus,
+.(l
+\&.
+.)l
+prints the current message and
+.(l
+4
+.)l
+prints message 4, as described previously.
+\fBNext\fP can be abbreviated to \fBn\fP.
+.ip "\fBpreserve\fP\ \ "
+Same as
+.b hold .
+Cause a list of messages to be held in your system mailbox when you quit.
+\fBPreserve\fP can be abbreviated to \fBpre\fP.
+.ip "\fBprint\fP\ \ "
+Print the specified messages. If the
+.b crt
+variable is set, messages longer than the number of lines it indicates
+are paged through the command specified by the \fBPAGER\fP variable.
+The \fBprint\fP command can be abbreviated to \fBp\fP.
+.ip "\fBquit\fP\ \ "
+Terminates the session, saving all undeleted, unsaved and unwritten messages
+in the user's \fImbox\fP file in their login directory
+(messages marked as having been read), preserving all
+messages marked with \fBhold\fP or \fBpreserve\fP or never referenced
+in their system mailbox.
+Any messages that were deleted, saved, written, or saved to \fImbox\fP are
+removed from their system mailbox.
+If new mail has arrived during the session, the message
+``You have new mail'' is given. If given while editing a mailbox file
+with the \fB\-f\fP flag, then the edit file is rewritten.
+A return to the Shell is effected, unless the rewrite of edit file fails,
+in which case the user can escape with the \fBexit\fP command.
+\fBQuit\fP can be abbreviated to \fBq\fP.
+.ip "\fBreply\fP or \fBrespond\fP\ \ "
+Frame a reply to a single message.
+The reply will be sent to the
+person who sent you the message (to which you are replying), plus all
+the people who received the original message, except you. You can
+add people using the \fB~t\fP, \fB~c\fP, and \fB~b\fP
+tilde escapes. The subject in your reply is formed by prefacing the
+subject in the original message with
+.q "Re:"
+unless it already began thus.
+If the original message included a
+.q "reply-to"
+header field, the reply will go
+.i only
+to the recipient named by
+.q "reply-to."
+You type in your message using the same conventions available to you
+through the
+.b mail
+command.
+The \fBreply\fP (and \fBrespond\fP) command can be abbreviated to \fBr\fP.
+.ip "\fBretain\fP\ \ "
+Add the list of header fields named to the \fIretained list\fP.
+Only the header fields in the retain list
+are shown on your terminal when you print a message.
+All other header fields are suppressed.
+The
+.b Type
+and
+.b Print
+commands can be used to print a message in its entirety.
+If
+.b retain
+is executed with no arguments, it lists the current set of
+retained fields.
+.ip "\fBsave\fP\ \ "
+It is often useful to be able to save messages on related topics
+in a file. The
+.b save
+command gives you the ability to do this. The
+.b save
+command takes as an argument a list of message numbers, followed by
+the name of the file in which to save the messages. The messages
+are appended to the named file, thus allowing one to keep several
+messages in the file, stored in the order they were put there.
+The filename in quotes, followed by the line
+count and character count is echoed on the user's terminal.
+An example of the
+.b save
+command relative to our running example is:
+.(l
+s 1 2 tuitionmail
+.)l
+.b Saved
+messages are not automatically saved in
+.i mbox
+at quit time, nor are they selected by the
+.b next
+command described above, unless explicitly specified.
+\fBSave\fP can be abbreviated to \fBs\fP.
+.ip "\fBsaveignore\fP\ \ "
+\fBsaveignore\fP is to \fBsave\fP what \fBignore\fP is to \fBprint\fP
+and \fBtype\fR.
+Header fields thus marked are filtered out when saving a message
+by \fBsave\fP or when automatically saving to \fImbox\fP.
+.ip "\fBsaveretain\fP\ \ "
+\fBsaveretain\fP is to \fBsave\fP what \fBretain\fP is to \fBprint\fP
+and \fBtype\fR.
+Header fields thus marked are the only ones saved with a message
+when saving by \fBsave\fP or when automatically saving to \fImbox\fP.
+\fBsaveretain\fP overrides \fBsaveignore\fP.
+.ip "\fBset\fP\ \ "
+Set an option or give an option a value. Used to customize
+.i Mail .
+Section 5.3 contains a list of the options. Options can be
+.i binary ,
+in which case they are
+.i on
+or
+.i off ,
+or
+.i valued .
+To set a binary option
+.i option
+.i on ,
+do
+.(l
+set option
+.)l
+To give the valued option
+.i option
+the value
+.i value ,
+do
+.(l
+set option=value
+.)l
+There must be no space before or after the ``='' sign.
+If no arguments are given, all variable values are printed.
+Several options can be specified in a single
+.b set
+command.
+\fBSet\fP can be abbreviated to \fBse\fP.
+.ip "\fBshell\fP\ \ "
+The
+.b shell
+command allows you to
+escape to the shell.
+.b Shell
+invokes an interactive shell and allows you to type commands to it.
+When you leave the shell, you will return to
+.i Mail .
+The shell used is a default assumed by
+.i Mail ;
+you can override this default by setting the valued option
+.q SHELL,
+eg:
+.(l
+set SHELL=/bin/csh
+.)l
+\fBShell\fP can be abbreviated to \fBsh\fP.
+.ip "\fBsize\fP\ \ "
+Takes a message list and prints out the size in characters of each
+message.
+.ip "\fBsource\fP\ \ "
+The
+.b source
+command reads
+.i mail
+commands from a file. It is useful when you are trying to fix your
+.q .mailrc
+file and you need to re-read it.
+\fBSource\fP can be abbreviated to \fBso\fP.
+.ip "\fBtop\fP\ \ "
+The
+.b top
+command takes a message list and prints the first five lines
+of each addressed message.
+If you wish, you can change the number of lines that
+.b top
+prints out by setting the valued option
+.q "toplines."
+On a CRT terminal,
+.(l
+set toplines=10
+.)l
+might be preferred.
+\fBTop\fP can be abbreviated to \fBto\fP.
+.ip "\fBtype\fP\ \ "
+Same as \fBprint\fP.
+Takes a message list and types out each message on the terminal.
+The \fBtype\fP command can be abbreviated to \fBt\fP.
+.ip "\fBunalias\fP \ \"
+Takes a list of names defined by \fBalias\fP commands and
+discards the remembered groups of users.
+The group names no longer have any significance.
+.ip "\fBundelete\fP \ \"
+Takes a message list and marks each message as \fInot\fP
+being deleted.
+\fBUndelete\fP can be abbreviated to \fBu\fP.
+.ip "\fBunread\fP\ \ "
+Takes a message list and marks each message as
+.i not
+having been read.
+\fBUnread\fP can be abbreviated to \fBU\fP.
+.ip "\fBunset\fP\ \ "
+Takes a list of option names and discards their remembered values;
+the inverse of \fBset\fP.
+.ip "\fBvisual\fP\ \ "
+It is often useful to be able to invoke one of two editors,
+based on the type of terminal one is using. To invoke
+a display oriented editor, you can use the
+.b visual
+command. The operation of the
+.b visual
+command is otherwise identical to that of the
+.b edit
+command.
+.ne 2v+\n(psu
+.sp \n(psu
+Both the
+.b edit
+and
+.b visual
+commands assume some default text editors.
+The default for
+.q EDITOR
+is
+.i /usr/bin/ex .
+The default for
+.q VISUAL
+is
+.i /usr/bin/vi .
+These default editors can be overridden by the valued options
+.q EDITOR
+and
+.q VISUAL
+for the standard and screen editors. You might want to do:
+.(l
+set EDITOR=/bin/ed VISUAL=/usr/bin/mg
+.)l
+\fBVisual\fP can be abbreviated to \fBv\fP.
+.ip "\fBwrite\fP\ \ "
+The
+.b save
+command always writes the entire message, including the headers,
+into the file. If you want to write just the message itself, you
+can use the
+.b write
+command. The
+.b write
+command has the same syntax as the
+.b save
+command, and can be abbreviated to simply
+.b w .
+Thus, we could write the second message by doing:
+.(l
+w 2 file.c
+.)l
+As suggested by this example, the
+.b write
+command is useful for such tasks as sending and receiving
+source program text over the message system.
+The filename in quotes, followed by additional file information,
+is echoed on the user's terminal.
+.ip "\fBz\fP\ \ "
+.i Mail
+presents message headers in windowfuls as described under
+the
+.b headers
+command.
+You can move
+.i Mail's
+attention forward to the next window by giving the
+.(l
+z+
+.)l
+command. Analogously, you can move to the previous window with:
+.(l
+z\-
+.)l
+.sh 2 "Custom options"
+.pp
+Throughout this manual, we have seen examples of binary and valued options.
+This section describes each of the options in alphabetical order, including
+some that you have not seen yet.
+To avoid confusion, please note that the options are either
+all lower case letters or all upper case letters. When I start a sentence
+such as:
+.q "Ask"
+causes
+.i Mail
+to prompt you for a subject header,
+I am only capitalizing
+.q ask
+as a courtesy to English.
+.ip "\fBEDITOR\fP\ \ "
+The valued option
+.q EDITOR
+defines the pathname of the text editor to be used in the
+.b edit
+command and ~e escape.
+If not defined,
+.i /usr/bin/ex
+is used.
+.ip "\fBLISTER\fP\ \ "
+Pathname of the directory lister to use in the \fBfolders\fP command.
+Default is \fI/bin/ls\fP.
+.ip "\fBMBOX\fP\ \ "
+The name of the \fImbox\fP file.
+It can be the name of a folder.
+The default is ``mbox'' in the user's home directory.
+.ip "\fBPAGER\fP\ \ "
+Pathname of the program to use for paginating output when
+it exceeds \fIcrt\fP lines.
+A default paginator is used if this option is not defined.
+.ip "\fBSHELL\fP\ \ "
+The valued option
+.q SHELL
+gives the path name of your shell. This shell is used for the
+.b !
+command and ~! escape. In addition, this shell expands
+file names with shell metacharacters like * and ? in them.
+.ip "\fBVISUAL\fP\ \ "
+The valued option
+.q VISUAL
+defines the pathname of the screen editor to be used in the
+.b visual
+command
+and ~v escape.
+If not defined,
+.i /usr/bin/vi
+is used.
+.ip "\fBappend\fP\ \ "
+The
+.q append
+option is binary and
+causes messages saved in
+.i mbox
+to be appended to the end rather than prepended.
+Normally, \fIMail\fP will put messages in \fImbox\fP
+in the same order that the system puts messages in your system mailbox.
+By setting
+.q append,
+you are requesting that
+.i mbox
+be appended to regardless. It is in any event quicker to append.
+.ip "\fBask\fP\ \ "
+.q "Ask"
+is a binary option which
+causes
+.i Mail
+to prompt you for the subject of each message you send.
+If you respond with simply a newline, no subject field will be sent.
+.ip "\fBaskbcc\fP\ \ "
+.q Askbcc
+is a binary option which
+causes you to be prompted for additional blind carbon copy recipients at the
+end of each message. Responding with a newline shows your
+satisfaction with the current list.
+.ip "\fBaskcc\fP\ \ "
+.q Askcc
+is a binary option which
+causes you to be prompted for additional carbon copy recipients at the
+end of each message. Responding with a newline shows your
+satisfaction with the current list.
+.ip "\fBautoinc\fP\ \ "
+Causes new mail to be automatically incorporated when it arrives.
+Setting this is similar to issuing the \fBinc\fP command at each prompt,
+except that the current message is not reset when new mail arrives.
+.ip "\fBautoprint\fP\ \ "
+.q Autoprint
+is a binary option which
+causes the
+.b delete
+command to behave like
+.b dp
+\*- thus, after deleting a message, the next one will be typed
+automatically. This is useful when quickly scanning and deleting
+messages in your mailbox.
+.ip "\fBcrt\fP \ \ "
+The valued option
+.I crt
+is used as a threshold to determine how long a message must
+be before
+.b PAGER
+is used to read it.
+.ip "\fBdebug\fP \ \ "
+The binary option
+.q debug
+causes debugging information to be displayed. Use of this
+option is the same as using the \fB\-d\fP command line flag.
+.ip "\fBdot\fP\ \ "
+.q Dot
+is a binary option which, if set, causes
+.i Mail
+to interpret a period alone on a line as the terminator
+of the message you are sending.
+.ip "\fBescape\fP\ \ "
+To allow you to change the escape character used when sending
+mail, you can set the valued option
+.q escape.
+Only the first character of the
+.q escape
+option is used, and it must be doubled if it is to appear as
+the first character of a line of your message. If you change your escape
+character, then ~ loses all its special meaning, and need no longer be doubled
+at the beginning of a line.
+.ip "\fBfolder\fP\ \ "
+The name of the directory to use for storing folders of messages.
+If this name begins with a `/'
+.i Mail
+considers it to be an absolute pathname; otherwise, the folder directory
+is found relative to your home directory.
+.ip "\fBhold\fP\ \ "
+The binary option
+.q hold
+causes messages that have been read but not manually dealt with
+to be held in the system mailbox. This prevents such messages from
+being automatically swept into your \fImbox\fP file.
+.ip "\fBignore\fP\ \ "
+The binary option
+.q ignore
+causes <Control-C> characters from your terminal to be ignored and echoed
+as @'s while you are sending mail. <Control-C> characters retain their
+original meaning in
+.i Mail
+command mode.
+Setting the
+.q ignore
+option is equivalent to supplying the
+.b \-i
+flag on the command line as described in section 6.
+.ip "\fBignoreeof\fP\ \ "
+An option related to
+.q dot
+is
+.q ignoreeof ,
+which makes
+.i Mail
+refuse to accept a <Control-D> as the end of a message.
+.q Ignoreeof
+also applies to
+.i Mail
+command mode.
+.ip "\fBindentprefix\fP\ \ "
+String used by the \fB~m\fP tilde escape for indenting messages,
+in place of the normal tab character (`^I').
+Be sure to quote the value if it contains spaces or tabs.
+.ip "\fBkeep\fP\ \ "
+The
+.q keep
+option causes
+.i Mail
+to truncate your system mailbox instead of deleting it when it
+is empty. This is useful if you elect to protect your mailbox, which
+you would do with the shell command:
+.(l
+chmod 600 /var/mail/yourname
+.)l
+where
+.i yourname
+is your login name. If you do not do this, anyone can probably read
+your mail, although people usually don't.
+.ip "\fBkeepsave\fP\ \ "
+When you
+.b save
+a message,
+.i Mail
+usually discards it when you
+.b quit .
+To retain all saved messages, set the
+.q keepsave
+option.
+.ip "\fBmetoo\fP\ \ "
+When sending mail to an alias,
+.i Mail
+makes sure that if you are included in the alias, that mail will not
+be sent to you. This is useful if a single alias is being used by
+all members of the group. If however, you wish to receive a copy of
+all the messages you send to the alias, you can set the binary option
+.q metoo.
+.ip "\fBnoheader\fP\ \ "
+The binary option
+.q noheader
+suppresses the printing of the version and headers when
+.i Mail
+is first invoked. Setting this option is the same as using
+.b \-N
+on the command line.
+.ip "\fBnosave\fP\ \ "
+Normally,
+when you abort a message with two <Control-C>'s,
+.i Mail
+copies the partial letter to the file
+.q dead.letter
+in your home directory. Setting the binary option
+.q nosave
+prevents this.
+.ip "\fBReplyall\fP\ \ "
+Reverses the sense of
+.i reply
+and
+.i Reply
+commands.
+.ip "\fBquiet\fP\ \ "
+The binary option
+.q quiet
+suppresses the printing of the version when
+.i Mail
+is first invoked,
+as well as printing the for example
+.q "Message 4:"
+from the
+.b type
+command.
+.ip "\fBrecord\fP\ \ "
+If you love to keep records, then the
+valued option
+.q record
+can be set to the name of a file to save your outgoing mail.
+Each new message you send is appended to the end of the file.
+.ip "\fBscreen\fP\ \ "
+When
+.i Mail
+initially prints the message headers, it determines the number to
+print by looking at the speed of your terminal. The faster your
+terminal, the more it prints.
+The valued option
+.q screen
+overrides this calculation and
+specifies how many message headers you want printed.
+This number is also used for scrolling with the
+.b z
+command.
+.ip "\fBsearchheaders\fP\ \ "
+If this option is set, then a message-list specifier in the form
+``/x:y'' will expand to all messages containing the substring
+`y' in the header field `x'.
+The string search is case insensitive.
+If `x' is omitted, it will default to the ``Subject'' header field.
+The form ``/to:y'' is a special case, and will expand
+to all messages containing the substring `y' in the ``To'', ``Cc'',
+or ``Bcc'' header fields.
+The check for ``to'' is case sensitive, so that ``/To:y''
+can be used to limit the search for `y' to just the ``To:'' field.
+.ip "\fBsendmail\fP\ \ "
+To use an alternate mail delivery system, set the
+.q sendmail
+option to the full pathname of the program to use. Note: this is not
+for everyone! Most people should use the default delivery system.
+.ip "\fBtoplines\fP\ \ "
+The valued option
+.q toplines
+defines the number of lines that the
+.q top
+command will print out instead of the default five lines.
+.ip "\fBverbose\fP\ \ "
+The binary option "verbose" causes
+.i Mail
+to invoke sendmail with the
+.b \-v
+flag, which causes it to go into verbose mode and announce expansion
+of aliases, etc. Setting the "verbose" option is equivalent to
+invoking
+.i Mail
+with the
+.b \-v
+flag as described in section 6.
diff --git a/USD.doc/mail6.nr b/USD.doc/mail6.nr
@@ -0,0 +1,99 @@
+.\" $OpenBSD: mail6.nr,v 1.6 2006/03/04 16:18:06 miod Exp $
+.\"
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS 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 REGENTS OR 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.
+.\"
+.\" @(#)mail6.nr 8.2 (Berkeley) 5/16/94
+.\"
+.sh 1 "Command line options"
+.pp
+This section describes command line options for
+.i Mail
+and what they are used for.
+.ip "\-b list\ \ "
+Send blind carbon copies to list.
+.ip "\-c list\ \ "
+Send carbon copies to list of users.
+List should be a comma separated list of names.
+.ip "\-f file\ \ "
+Show the messages in
+.i file
+instead of your system mailbox. If
+.i file
+is omitted,
+.i Mail
+reads
+.i mbox
+in your home directory.
+.ip \-I
+Forces mail to run in interactive mode,
+even when input is not a terminal.
+In particular, the special \fB~\fP command character,
+used when sending mail, is only available interactively.
+.ip \-i
+Ignore tty interrupt signals.
+This is particularly useful when using mail on noisy phone lines.
+.ip \-N
+Suppress the initial printing of headers.
+.ip \-n
+Inhibit reading of /etc/mail.rc upon startup.
+.ip "\-s string"
+Used for sending mail.
+.i String
+is used as the subject of the message being composed. If
+.i string
+contains blanks, you must surround it with quote marks.
+.ip "\-u name"
+Read
+.i names's
+mail instead of your own. Unwitting others often neglect to protect
+their mailboxes, but discretion is advised. Essentially,
+.b "\-u user"
+is a shorthand way of doing
+.b "\-f /var/mail/user".
+.ip "\-v"
+Use the
+.b \-v
+flag when invoking sendmail. This feature may also be enabled
+by setting the option "verbose".
+.pp
+The following command line flags are also recognized, but are
+intended for use by programs invoking
+.i Mail
+and not for people.
+.ip \-d
+Turn on debugging information. Not of general interest.
+.ip "\-T file"
+Arrange to print on
+.i file
+the contents of the
+.i article-id
+fields of all messages that were either read or deleted.
+.b \-T
+is for the
+.i readnews
+program and should NOT be used for reading your mail.
diff --git a/USD.doc/mail7.nr b/USD.doc/mail7.nr
@@ -0,0 +1,105 @@
+.\" $OpenBSD: mail7.nr,v 1.3 2003/06/03 02:56:11 millert Exp $
+.\"
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS 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 REGENTS OR 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.
+.\"
+.\" @(#)mail7.nr 8.1 (Berkeley) 6/8/93
+.\"
+.sh 1 "Format of messages"
+.pp
+This section describes the format of messages.
+Messages begin with a
+.i from
+line, which consists of the word
+.q From
+followed by a user name, followed by anything, followed by
+a date in the format returned by the
+.i ctime
+library routine described in section 3 of the Unix Programmer's
+Manual. A possible
+.i ctime
+format date is:
+.(l
+Tue Dec 1 10:58:23 1981
+.)l
+The
+.i ctime
+date may be optionally followed by a single space and a
+time zone indication, which
+should be three capital letters, such as PDT.
+.pp
+Following the
+.i from
+line are zero or more
+.i "header field"
+lines.
+Each header field line is of the form:
+.(l
+name: information
+.)l
+.i Name
+can be anything, but only certain header fields are recognized as
+having any meaning. The recognized header fields are:
+.i article-id ,
+.i bcc ,
+.i cc ,
+.i from ,
+.i reply-to ,
+.i sender ,
+.i subject ,
+and
+.i to .
+Other header fields are also significant to other systems; see,
+for example, the current Arpanet message standard for much more
+information on this topic.
+A header field can be continued onto following lines by making the
+first character on the following line a space or tab character.
+.pp
+If any headers are present, they must be followed by a blank line.
+The part that follows is called the
+.i body
+of the message, and must be ASCII text, not containing null characters.
+Each line in the message body must be no longer than 512 characters and
+terminated with an ASCII newline character.
+If binary data must be passed through the mail system, it is suggested
+that this data be encoded in a system which encodes six bits into
+a printable character (i.e.: uuencode).
+For example, one could use the upper and lower case letters, the digits,
+and the characters comma and period to make up the 64 characters.
+Then, one can send a 16-bit binary number
+as three characters. These characters should be packed into lines,
+preferably lines about 70 characters long as long lines are transmitted
+more efficiently.
+.pp
+The message delivery system always adds a blank line to the end of
+each message. This blank line must not be deleted.
+.pp
+The UUCP message delivery system sometimes adds a blank line to
+the end of a message each time it is forwarded through a machine.
+.pp
+It should be noted that some network transport protocols enforce
+limits to the lengths of messages.
diff --git a/USD.doc/mail8.nr b/USD.doc/mail8.nr
@@ -0,0 +1,72 @@
+.\" $OpenBSD: mail8.nr,v 1.5 2004/06/04 00:04:54 jmc Exp $
+.\"
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS 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 REGENTS OR 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.
+.\"
+.\" @(#)mail8.nr 8.2 (Berkeley) 5/16/94
+.\"
+.sh 1 "Glossary"
+.pp
+This section contains the definitions of a few phrases
+peculiar to
+.i Mail .
+.ip "\fIalias\fP"
+An alternative name for a person or list of people.
+.ip "\fIflag\fP"
+An option, given on the command line of
+.i Mail ,
+prefaced with a \-. For example,
+.b \-f
+is a flag.
+.ip "\fIheader field\fP"
+At the beginning of a message, a line which contains information
+that is part of the structure of the message. Popular header fields
+include
+.i to ,
+.i cc ,
+and
+.i subject .
+.ip "\fImail\ \ \fP"
+A collection of messages. Often used in the phrase,
+.q "Have you read your mail?"
+.ip "\fImailbox\fP"
+The place where your mail is stored, typically in the directory
+/var/mail.
+.ip "\fImessage\fP"
+A single letter from someone, initially stored in your
+.i mailbox .
+.ip "\fImessage list\fP"
+A string used in
+.i Mail
+command mode to describe a sequence of messages.
+.ip "\fIoption\fP"
+A piece of special purpose information used to tailor
+.i Mail
+to your taste.
+Options are specified with the
+.b set
+command.
diff --git a/USD.doc/mail9.nr b/USD.doc/mail9.nr
@@ -0,0 +1,214 @@
+.\" $OpenBSD: mail9.nr,v 1.8 2009/07/29 18:15:38 martynas Exp $
+.\"
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS 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 REGENTS OR 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.
+.\"
+.\" @(#)mail9.nr 8.2 (Berkeley) 5/16/94
+.\"
+.sh 1 "Summary of commands, options, and escapes"
+.pp
+This section gives a quick summary of the
+.i Mail
+commands, binary and valued options, and tilde escapes.
+.pp
+The following table describes the commands:
+.TS
+center ;
+c ci
+lb l.
+Command Description
+_
++ Same as \fBnext\fP
+- Back up to previous message
+? Print brief summary of \fIMail\fP commands
+! Single command escape to shell
+Print Type message with ignored fields
+Reply Reply to author of message only
+Respond Same as \fBReply\fP
+Type Type message with ignored fields
+alias Define an alias as a set of user names
+alternates List other names you are known by
+chdir Change working directory, home by default
+copy Copy a message to a file or folder
+delete Delete a list of messages
+dp Same as \fBdt\fP
+dt Delete current message, type next message
+edit Edit a list of messages
+else Start of else part of conditional; see \fBif\fP
+endif End of conditional statement; see \fBif\fP
+exit Leave mail without changing anything
+file Interrogate/change current mail file
+folder Same as \fBfile\fP
+folders List the folders in your folder directory
+from List headers of a list of messages
+headers List current window of messages
+help Same as \fB?\fP
+hold Same as \fBpreserve\fP
+if Conditional execution of \fIMail\fP commands
+ignore Set/examine list of ignored header fields
+inc Incorporate new messages
+list List valid \fIMail\fP commands
+mail Send mail to specified names
+mbox Arrange to save a list of messages in \fImbox\fP
+more Invoke pager on message list
+next Go to next message and type it
+preserve Arrange to leave list of messages in system mailbox
+print Print messages
+quit Leave \fIMail\fP; update system mailbox, \fImbox\fP as appropriate
+reply Compose a reply to a message
+respond Same as \fBreply\fP
+retain Supersedes \fBignore\fP
+save Append messages, headers included, on a file
+saveignore List of headers to ignore when using the \fBsave\fP command
+saveretain List of headers to retain when using the \fBsave\fP command
+set Set binary or valued options
+shell Invoke an interactive shell
+size Prints out size of message list
+source Read \fImail\fP commands from a file
+top Print first so many (5 by default) lines of list of messages
+type Same as \fBprint\fP
+unalias Remove alias
+undelete Undelete list of messages
+unread Marks list of messages as not been read
+unset Undo the operation of a \fBset\fP
+visual Invoke visual editor on a list of messages
+write Append messages to a file, don't include headers
+xit Same as \fBexit\fP
+z Scroll to next/previous screenful of headers
+.TE
+.(b
+.pp
+The following table describes the options. Each option is
+shown as being either a binary or valued option.
+.TS
+center;
+c ci ci
+l ci l.
+Option Type Description
+_
+EDITOR valued Pathname of editor for ~e and \fBedit\fP
+LISTER valued Pathname of directory lister
+MBOX valued Pathname of the \fImbox\fP file
+PAGER valued Pathname of pager for \fBPrint\fP, \fBprint\fP, \fBType\fP and \fBtype\fP
+SHELL valued Pathname of shell for \fBshell\fP, ~! and \fB!\fP
+VISUAL valued Pathname of screen editor for ~v, \fBvisual\fP
+append binary Always append messages to end of \fImbox\fP
+ask binary Prompt user for Subject: field when sending
+askbcc binary Prompt user for additional BCc's at end of message
+askcc binary Prompt user for additional Cc's at end of message
+autoinc binary Automatically incorporate new mail
+autoprint binary Print next message after \fBdelete\fP
+crt valued Minimum number of lines before using \fBPAGER\fP
+debug binary Print out debugging information
+dot binary Accept . alone on line to terminate message input
+escape valued Escape character to be used instead of\ \ ~
+folder valued Directory to store folders in
+hold binary Hold messages in system mailbox by default
+ignore binary Ignore <Control-C> while sending mail
+ignoreeof binary Don't terminate letters/command input with \fB^D\fP
+indentprefix valued String used for indenting messages
+keep binary Don't unlink system mailbox when empty
+keepsave binary Don't delete \fBsave\fPd messages by default
+metoo binary Include sending user in aliases
+noheader binary Suppress initial printing of version and headers
+nosave binary Don't save partial letter in \fIdead.letter\fP
+Replyall binary Reverses the sense of the \fB[Rr]eply\fP commands
+quiet binary Suppress printing of \fIMail\fP version/message numbers
+record valued File to save all outgoing mail in
+screen valued Size of window of message headers for \fBz\fP, etc.
+searchheaders binary Search string for message headers
+sendmail valued Choose alternate mail delivery system
+toplines valued Number of lines to print in \fBtop\fP
+verbose binary Invoke sendmail with the \fB\-v\fP flag
+.TE
+.)b
+.(b
+.pp
+The following table summarizes the tilde escapes available
+while sending mail.
+.TS
+center;
+c ci ci
+l li l.
+Escape Arguments Description
+_
+~b name ... Add names to "blind" Cc: list.
+~c name ... Add names to Cc: field.
+~d Read \fIdead.letter\fP into message.
+~e Invoke text editor on partial message.
+~F messages Same as ~f, but includes all headers.
+~f messages Read in messages.
+~h Edit the header fields.
+~M messages Same as ~m, but includes all headers.
+~m messages Read in messages, right shifted by a tab.
+~p Print (show) the message buffer.
+~q Abort message; optionally save copy to ~/dead.letter.
+~r file | ~< file Read a file into the message buffer.
+~s string Set Subject: field to \fIstring\fP.
+~t name ... Add names to To: field.
+~v Invoke display editor on message.
+~w filename Write message to file.
+~x Abort message; no copy is saved.
+~? Print a brief summary of tilde escapes.
+~!command Execute shell command.
+~|command Pipe message through \fIcommand\fP.
+~:command | ~_command Execute a \fIMail\fP command.
+~~string Quote a single tilde.
+~. Simulate end of file on input.
+.TE
+.)b
+.(b
+.pp
+The following table shows the command line flags that
+.i Mail
+accepts:
+.TS
+center;
+c c
+l a.
+Flag Description
+_
+\-b \fIlist\fP Send blind carbon copies to \fIlist\fP.
+\-c \fIlist\fP Send carbon copies to \fIlist\fP
+\-d Turn on debugging
+\-f [\fIname\fP] Show messages in \fIname\fP or \fI~/mbox\fP
+\-I Force \fIMail\fP to run in interactive mode
+\-i Ignore tty interrupt signals
+\-N Suppress the initial printing of headers
+\-n Inhibit reading of /etc/mail.rc
+\-s \fIsubject\fP Use \fIsubject\fP as subject in outgoing mail
+\-T \fIfile\fP Article-id's of read/deleted messages to \fIfile\fP
+\-u \fIuser\fP Read \fIuser\fP's mail instead of your own
+\-v Invoke sendmail with the \fB\-v\fP flag
+.TE
+.)b
+.lp
+Notes:
+.b \-d
+and
+.b \-T
+are not for human use.
diff --git a/USD.doc/maila.nr b/USD.doc/maila.nr
@@ -0,0 +1,31 @@
+.\" $OpenBSD: maila.nr,v 1.3 2003/06/03 02:56:11 millert Exp $
+.\"
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS 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 REGENTS OR 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.
+.\"
+.\" @(#)maila.nr 8.1 (Berkeley) 6/8/93
+.\"
diff --git a/cmd1.c b/cmd1.c
@@ -0,0 +1,524 @@
+/* $OpenBSD: cmd1.c,v 1.29 2011/04/06 11:36:26 miod Exp $ */
+/* $NetBSD: cmd1.c,v 1.9 1997/07/09 05:29:48 mikel Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS 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 REGENTS OR 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.
+ */
+
+#include "rcv.h"
+#include "extern.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * User commands.
+ */
+
+/*
+ * Print the current active headings.
+ * Don't change dot if invoker didn't give an argument.
+ */
+
+static int screen;
+static volatile sig_atomic_t gothdrint;
+
+int
+headers(void *v)
+{
+ int *msgvec = v;
+ int n, mesg, flag, size;
+ struct message *mp;
+ struct sigaction act, oact;
+ sigset_t oset;
+
+ size = screensize();
+ n = msgvec[0];
+ if (n != 0 && size > 0)
+ screen = (n-1)/size;
+ if (screen < 0)
+ screen = 0;
+ mp = &message[screen * size];
+ if (mp >= &message[msgCount])
+ mp = &message[msgCount - size];
+ if (mp < &message[0])
+ mp = &message[0];
+ flag = 0;
+ mesg = mp - &message[0];
+ if (dot != &message[n-1])
+ dot = mp;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+ act.sa_handler = hdrint;
+ if (sigaction(SIGINT, NULL, &oact) == 0 &&
+ oact.sa_handler != SIG_IGN) {
+ (void)sigaction(SIGINT, &act, &oact);
+ (void)sigprocmask(SIG_UNBLOCK, &intset, &oset);
+ }
+ for (gothdrint = 0; !gothdrint && mp < &message[msgCount]; mp++) {
+ mesg++;
+ if (mp->m_flag & MDELETED)
+ continue;
+ if (flag++ >= size)
+ break;
+ printhead(mesg);
+ }
+ if (gothdrint) {
+ fflush(stdout);
+ fputs("\nInterrupt\n", stderr);
+ }
+ if (oact.sa_handler != SIG_IGN) {
+ (void)sigprocmask(SIG_SETMASK, &oset, NULL);
+ (void)sigaction(SIGINT, &oact, NULL);
+ }
+ if (flag == 0) {
+ puts("No more mail.");
+ return(1);
+ }
+ return(0);
+}
+
+/*
+ * Scroll to the next/previous screen
+ */
+int
+scroll(void *v)
+{
+ char *arg = v;
+ int size, maxscreen;
+ int cur[1];
+
+ cur[0] = 0;
+ size = screensize();
+ maxscreen = 0;
+ if (size > 0)
+ maxscreen = (msgCount - 1) / size;
+ switch (*arg) {
+ case 0:
+ case '+':
+ if (screen >= maxscreen) {
+ puts("On last screenful of messages");
+ return(0);
+ }
+ screen++;
+ break;
+
+ case '-':
+ if (screen <= 0) {
+ puts("On first screenful of messages");
+ return(0);
+ }
+ screen--;
+ break;
+
+ default:
+ printf("Unrecognized scrolling command \"%s\"\n", arg);
+ return(1);
+ }
+ return(headers(cur));
+}
+
+/*
+ * Compute screen size.
+ */
+int
+screensize(void)
+{
+ int s;
+ char *cp;
+
+ if ((cp = value("screen")) != NULL && (s = atoi(cp)) > 0)
+ return(s);
+ return(screenheight - 4);
+}
+
+/*
+ * Print out the headlines for each message
+ * in the passed message list.
+ */
+int
+from(void *v)
+{
+ int *msgvec = v;
+ int *ip;
+
+ for (ip = msgvec; *ip != 0; ip++)
+ printhead(*ip);
+ if (--ip >= msgvec)
+ dot = &message[*ip - 1];
+ return(0);
+}
+
+/*
+ * Print out the header of a specific message.
+ * This is a slight improvement to the standard one.
+ */
+void
+printhead(int mesg)
+{
+ struct message *mp;
+ char headline[LINESIZE], *subjline, dispc, curind;
+ char visname[LINESIZE], vissub[LINESIZE];
+ char pbuf[LINESIZE];
+ char fmtline[LINESIZE];
+ const char *fmt;
+ struct headline hl;
+ char *name;
+ char *to, *from;
+ struct name *np;
+ char **ap;
+
+ mp = &message[mesg-1];
+ (void)readline(setinput(mp), headline, LINESIZE, NULL);
+ if ((subjline = hfield("subject", mp)) == NULL &&
+ (subjline = hfield("subj", mp)) == NULL)
+ subjline = "";
+ /*
+ * Bletch!
+ */
+ curind = dot == mp ? '>' : ' ';
+ dispc = ' ';
+ if (mp->m_flag & MSAVED)
+ dispc = '*';
+ if (mp->m_flag & MPRESERVE)
+ dispc = 'P';
+ if ((mp->m_flag & (MREAD|MNEW)) == MNEW)
+ dispc = 'N';
+ if ((mp->m_flag & (MREAD|MNEW)) == 0)
+ dispc = 'U';
+ if (mp->m_flag & MBOX)
+ dispc = 'M';
+ parse(headline, &hl, pbuf);
+ from = nameof(mp, 0);
+ to = skin(hfield("to", mp));
+ np = extract(from, GTO);
+ np = delname(np, myname);
+ if (altnames)
+ for (ap = altnames; *ap; ap++)
+ np = delname(np, *ap);
+ if (np)
+ /* not from me */
+ name = value("show-rcpt") != NULL && to ? to : from;
+ else
+ /* from me - show TO */
+ name = value("showto") != NULL && to ? to : from;
+ strnvis(visname, name, sizeof(visname), VIS_SAFE|VIS_NOSLASH);
+ if (name == to)
+ fmt = "%c%c%3d TO %-14.14s %16.16s %4d/%-5d %s";
+ else
+ fmt = "%c%c%3d %-17.17s %16.16s %4d/%-5d %s";
+ strnvis(vissub, subjline, sizeof(vissub), VIS_SAFE|VIS_NOSLASH);
+ /* hl.l_date was sanity-checked when read in. */
+ snprintf(fmtline, sizeof(fmtline), fmt, curind, dispc, mesg, visname,
+ hl.l_date, mp->m_lines, mp->m_size, vissub);
+ printf("%.*s\n", screenwidth, fmtline);
+}
+
+/*
+ * Print out the value of dot.
+ */
+int
+pdot(void *v)
+{
+ printf("%d\n", (int)(dot - &message[0] + 1));
+ return(0);
+}
+
+/*
+ * Print out all the possible commands.
+ */
+int
+pcmdlist(void *v)
+{
+ extern const struct cmd cmdtab[];
+ const struct cmd *cp;
+ int cc;
+
+ puts("Commands are:");
+ for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) {
+ cc += strlen(cp->c_name) + 2;
+ if (cc > 72) {
+ putchar('\n');
+ cc = strlen(cp->c_name) + 2;
+ }
+ if ((cp+1)->c_name != NULL)
+ printf("%s, ", cp->c_name);
+ else
+ puts(cp->c_name);
+ }
+ return(0);
+}
+
+/*
+ * Pipe message to command
+ */
+int
+pipeit(void *ml, void *sl)
+{
+ int *msgvec = ml;
+ char *cmd = sl;
+
+ return(type1(msgvec, cmd, 0, 0));
+}
+
+/*
+ * Paginate messages, honor ignored fields.
+ */
+int
+more(void *v)
+{
+ int *msgvec = v;
+ return(type1(msgvec, NULL, 1, 1));
+}
+
+/*
+ * Paginate messages, even printing ignored fields.
+ */
+int
+More(void *v)
+{
+ int *msgvec = v;
+
+ return(type1(msgvec, NULL, 0, 1));
+}
+
+/*
+ * Type out messages, honor ignored fields.
+ */
+int
+type(void *v)
+{
+ int *msgvec = v;
+
+ return(type1(msgvec, NULL, 1, 0));
+}
+
+/*
+ * Type out messages, even printing ignored fields.
+ */
+int
+Type(void *v)
+{
+ int *msgvec = v;
+
+ return(type1(msgvec, NULL, 0, 0));
+}
+
+/*
+ * Type out the messages requested.
+ */
+int
+type1(int *msgvec, char *cmd, int doign, int page)
+{
+ int nlines, *ip, restoreterm;
+ struct message *mp;
+ struct termios tbuf;
+ char *cp;
+ FILE *obuf;
+
+ obuf = stdout;
+ restoreterm = 0;
+
+ /*
+ * start a pipe if needed.
+ */
+ if (cmd) {
+ restoreterm = (tcgetattr(fileno(stdin), &tbuf) == 0);
+ obuf = Popen(cmd, "w");
+ if (obuf == NULL) {
+ warn("%s", cmd);
+ obuf = stdout;
+ }
+ } else if (value("interactive") != NULL &&
+ (page || (cp = value("crt")) != NULL)) {
+ nlines = 0;
+ if (!page) {
+ for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++)
+ nlines += message[*ip - 1].m_lines;
+ }
+ if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) {
+ restoreterm = (tcgetattr(fileno(stdin), &tbuf) == 0);
+ cp = value("PAGER");
+ obuf = Popen(cp, "w");
+ if (obuf == NULL) {
+ warn("%s", cp);
+ obuf = stdout;
+ }
+ }
+ }
+
+ /*
+ * Send messages to the output.
+ */
+ for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
+ mp = &message[*ip - 1];
+ touch(mp);
+ dot = mp;
+ if (cmd == NULL && value("quiet") == NULL)
+ fprintf(obuf, "Message %d:\n", *ip);
+ if (sendmessage(mp, obuf, doign ? ignore : 0, NULL) == -1)
+ break;
+ }
+
+ if (obuf != stdout) {
+ (void)Pclose(obuf);
+ if (restoreterm)
+ (void)tcsetattr(fileno(stdin), TCSADRAIN, &tbuf);
+ }
+ return(0);
+}
+
+/*
+ * Print the top so many lines of each desired message.
+ * The number of lines is taken from the variable "toplines"
+ * and defaults to 5.
+ */
+int
+top(void * v)
+{
+ int *msgvec = v;
+ int *ip;
+ struct message *mp;
+ int c, topl, lines, lineb;
+ char *valtop, linebuf[LINESIZE];
+ FILE *ibuf;
+
+ topl = 5;
+ valtop = value("toplines");
+ if (valtop != NULL) {
+ topl = atoi(valtop);
+ if (topl < 0 || topl > 10000)
+ topl = 5;
+ }
+ lineb = 1;
+ for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
+ mp = &message[*ip - 1];
+ touch(mp);
+ dot = mp;
+ if (value("quiet") == NULL)
+ printf("Message %d:\n", *ip);
+ ibuf = setinput(mp);
+ c = mp->m_lines;
+ if (!lineb)
+ putchar('\n');
+ for (lines = 0; lines < c && lines <= topl; lines++) {
+ if (readline(ibuf, linebuf, sizeof(linebuf), NULL) < 0)
+ break;
+ puts(linebuf);
+ lineb = blankline(linebuf);
+ }
+ }
+ return(0);
+}
+
+/*
+ * Touch all the given messages so that they will
+ * get mboxed.
+ */
+int
+stouch(void *v)
+{
+ int *msgvec = v;
+ int *ip;
+
+ for (ip = msgvec; *ip != 0; ip++) {
+ dot = &message[*ip-1];
+ dot->m_flag |= MTOUCH;
+ dot->m_flag &= ~MPRESERVE;
+ }
+ return(0);
+}
+
+/*
+ * Make sure all passed messages get mboxed.
+ */
+int
+mboxit(void *v)
+{
+ int *msgvec = v;
+ int *ip;
+
+ for (ip = msgvec; *ip != 0; ip++) {
+ dot = &message[*ip-1];
+ dot->m_flag |= MTOUCH|MBOX;
+ dot->m_flag &= ~MPRESERVE;
+ }
+ return(0);
+}
+
+/*
+ * List the folders the user currently has.
+ */
+int
+folders(void *v)
+{
+ char *files = (char *)v;
+ char dirname[PATHSIZE];
+ char cmd[BUFSIZ];
+
+ if (getfold(dirname, sizeof(dirname)) < 0)
+ strlcpy(dirname, "$HOME", sizeof(dirname));
+
+ snprintf(cmd, sizeof(cmd), "cd %s; %s %s", dirname, value("LISTER"),
+ files && *files ? files : "");
+
+ (void)run_command(value("SHELL"), 0, -1, -1, "-c", cmd, NULL);
+ return(0);
+}
+
+/*
+ * Update the mail file with any new messages that have
+ * come in since we started reading mail.
+ */
+int
+inc(void *v)
+{
+ int nmsg, mdot;
+
+ nmsg = incfile();
+
+ if (nmsg == 0) {
+ puts("No new mail.");
+ } else if (nmsg > 0) {
+ mdot = newfileinfo(msgCount - nmsg);
+ dot = &message[mdot - 1];
+ } else {
+ puts("\"inc\" command failed...");
+ }
+
+ return(0);
+}
+
+/*
+ * User hit ^C while printing the headers.
+ */
+void
+hdrint(int s)
+{
+
+ gothdrint = 1;
+}
diff --git a/cmd2.c b/cmd2.c
@@ -0,0 +1,442 @@
+/* $OpenBSD: cmd2.c,v 1.22 2015/10/16 17:56:07 mmcc Exp $ */
+/* $NetBSD: cmd2.c,v 1.7 1997/05/17 19:55:10 pk Exp $ */
+
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS 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 REGENTS OR 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.
+ */
+
+#include "rcv.h"
+#include <sys/wait.h>
+#include "extern.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * More user commands.
+ */
+static int igcomp(const void *, const void *);
+
+/*
+ * If any arguments were given, go to the next applicable argument
+ * following dot, otherwise, go to the next applicable message.
+ * If given as first command with no arguments, print first message.
+ */
+int
+next(void *v)
+{
+ struct message *mp;
+ int *msgvec = v;
+ int *ip, *ip2, list[2], mdot;
+
+ if (*msgvec != 0) {
+ /*
+ * If some messages were supplied, find the
+ * first applicable one following dot using
+ * wrap around.
+ */
+ mdot = dot - &message[0] + 1;
+
+ /*
+ * Find the first message in the supplied
+ * message list which follows dot.
+ */
+ for (ip = msgvec; *ip != 0; ip++)
+ if (*ip > mdot)
+ break;
+ if (*ip == 0)
+ ip = msgvec;
+ ip2 = ip;
+ do {
+ mp = &message[*ip2 - 1];
+ if ((mp->m_flag & MDELETED) == 0) {
+ dot = mp;
+ goto hitit;
+ }
+ if (*ip2 != 0)
+ ip2++;
+ if (*ip2 == 0)
+ ip2 = msgvec;
+ } while (ip2 != ip);
+ puts("No messages applicable");
+ return(1);
+ }
+
+ /*
+ * If this is the first command, select message 1.
+ * Note that this must exist for us to get here at all.
+ */
+ if (!sawcom)
+ goto hitit;
+
+ /*
+ * Just find the next good message after dot, no
+ * wraparound.
+ */
+ for (mp = dot+1; mp < &message[msgCount]; mp++)
+ if ((mp->m_flag & (MDELETED|MSAVED)) == 0)
+ break;
+ if (mp >= &message[msgCount]) {
+ puts("At EOF");
+ return(0);
+ }
+ dot = mp;
+hitit:
+ /*
+ * Print dot.
+ */
+ list[0] = dot - &message[0] + 1;
+ list[1] = 0;
+ return(type(list));
+}
+
+/*
+ * Save a message in a file. Mark the message as saved
+ * so we can discard when the user quits.
+ */
+int
+save(void *v)
+{
+ char *str = v;
+
+ return(save1(str, 1, "save", saveignore));
+}
+
+/*
+ * Copy a message to a file without affected its saved-ness
+ */
+int
+copycmd(void *v)
+{
+ char *str = v;
+
+ return(save1(str, 0, "copy", saveignore));
+}
+
+/*
+ * Save/copy the indicated messages at the end of the passed file name.
+ * If mark is true, mark the message "saved."
+ */
+int
+save1(char *str, int mark, char *cmd, struct ignoretab *ignore)
+{
+ struct message *mp;
+ char *file, *disp;
+ int f, *msgvec, *ip;
+ FILE *obuf;
+
+ msgvec = (int *)salloc((msgCount + 2) * sizeof(*msgvec));
+ if ((file = snarf(str, &f)) == NULL)
+ return(1);
+ if (!f) {
+ *msgvec = first(0, MMNORM);
+ if (*msgvec == 0) {
+ printf("No messages to %s.\n", cmd);
+ return(1);
+ }
+ msgvec[1] = 0;
+ }
+ if (f && getmsglist(str, msgvec, 0) < 0)
+ return(1);
+ if ((file = expand(file)) == NULL)
+ return(1);
+ printf("\"%s\" ", file);
+ fflush(stdout);
+ if (access(file, F_OK) >= 0)
+ disp = "[Appended]";
+ else
+ disp = "[New file]";
+ if ((obuf = Fopen(file, "a")) == NULL) {
+ warn(NULL);
+ return(1);
+ }
+ for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
+ mp = &message[*ip - 1];
+ touch(mp);
+ if (sendmessage(mp, obuf, ignore, NULL) < 0) {
+ warn("%s", file);
+ (void)Fclose(obuf);
+ return(1);
+ }
+ if (mark)
+ mp->m_flag |= MSAVED;
+ }
+ fflush(obuf);
+ if (ferror(obuf))
+ warn("%s", file);
+ (void)Fclose(obuf);
+ printf("%s\n", disp);
+ return(0);
+}
+
+/*
+ * Write the indicated messages at the end of the passed
+ * file name, minus header and trailing blank line.
+ */
+int
+swrite(void *v)
+{
+ char *str = v;
+
+ return(save1(str, 1, "write", ignoreall));
+}
+
+/*
+ * Snarf the file from the end of the command line and
+ * return a pointer to it. If there is no file attached,
+ * just return NULL. Put a null in front of the file
+ * name so that the message list processing won't see it,
+ * unless the file name is the only thing on the line, in
+ * which case, return 0 in the reference flag variable.
+ */
+char *
+snarf(char *linebuf, int *flag)
+{
+ char *cp;
+
+ *flag = 1;
+ cp = strlen(linebuf) + linebuf - 1;
+
+ /*
+ * Strip away trailing blanks.
+ */
+ while (cp > linebuf && isspace((unsigned char)*cp))
+ cp--;
+ *++cp = 0;
+
+ /*
+ * Now search for the beginning of the file name.
+ */
+ while (cp > linebuf && !isspace((unsigned char)*cp))
+ cp--;
+ if (*cp == '\0') {
+ puts("No file specified.");
+ return(NULL);
+ }
+ if (isspace((unsigned char)*cp))
+ *cp++ = 0;
+ else
+ *flag = 0;
+ return(cp);
+}
+
+/*
+ * Delete messages.
+ */
+int
+deletecmd(void *v)
+{
+ int *msgvec = v;
+
+ delm(msgvec);
+ return(0);
+}
+
+/*
+ * Delete messages, then type the new dot.
+ */
+int
+deltype(void *v)
+{
+ int *msgvec = v;
+ int list[2];
+ int lastdot;
+
+ lastdot = dot - &message[0] + 1;
+ if (delm(msgvec) >= 0) {
+ list[0] = dot - &message[0] + 1;
+ if (list[0] > lastdot) {
+ touch(dot);
+ list[1] = 0;
+ return(type(list));
+ }
+ puts("At EOF");
+ } else
+ puts("No more messages");
+ return(0);
+}
+
+/*
+ * Delete the indicated messages.
+ * Set dot to some nice place afterwards.
+ * Internal interface.
+ */
+int
+delm(int *msgvec)
+{
+ struct message *mp;
+ int *ip, last;
+
+ last = 0;
+ for (ip = msgvec; *ip != 0; ip++) {
+ mp = &message[*ip - 1];
+ touch(mp);
+ mp->m_flag |= MDELETED|MTOUCH;
+ mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX);
+ last = *ip;
+ }
+ if (last != 0) {
+ dot = &message[last-1];
+ last = first(0, MDELETED);
+ if (last != 0) {
+ dot = &message[last-1];
+ return(0);
+ }
+ else {
+ dot = &message[0];
+ return(-1);
+ }
+ }
+
+ /*
+ * Following can't happen
+ */
+ return(-1);
+}
+
+/*
+ * Undelete the indicated messages.
+ */
+int
+undeletecmd(void *v)
+{
+ int *msgvec = v;
+ int *ip;
+ struct message *mp;
+
+ for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
+ mp = &message[*ip - 1];
+ touch(mp);
+ dot = mp;
+ mp->m_flag &= ~MDELETED;
+ }
+ return(0);
+}
+
+/*
+ * Add the given header fields to the retained list.
+ * If no arguments, print the current list of retained fields.
+ */
+int
+retfield(void *v)
+{
+ char **list = v;
+
+ return(ignore1(list, ignore + 1, "retained"));
+}
+
+/*
+ * Add the given header fields to the ignored list.
+ * If no arguments, print the current list of ignored fields.
+ */
+int
+igfield(void *v)
+{
+ char **list = v;
+
+ return(ignore1(list, ignore, "ignored"));
+}
+
+int
+saveretfield(void *v)
+{
+ char **list = v;
+
+ return(ignore1(list, saveignore + 1, "retained"));
+}
+
+int
+saveigfield(void *v)
+{
+ char **list = v;
+
+ return(ignore1(list, saveignore, "ignored"));
+}
+
+int
+ignore1(char **list, struct ignoretab *tab, char *which)
+{
+ char field[LINESIZE];
+ char **ap;
+ struct ignore *igp;
+ int h;
+
+ if (*list == NULL)
+ return(igshow(tab, which));
+ for (ap = list; *ap != 0; ap++) {
+ istrlcpy(field, *ap, sizeof(field));
+ if (member(field, tab))
+ continue;
+ h = hash(field);
+ igp = calloc(1, sizeof(struct ignore));
+ if (igp == NULL)
+ err(1, "calloc");
+ igp->i_field = strdup(field);
+ if (igp->i_field == NULL)
+ err(1, "strdup");
+ igp->i_link = tab->i_head[h];
+ tab->i_head[h] = igp;
+ tab->i_count++;
+ }
+ return(0);
+}
+
+/*
+ * Print out all currently retained fields.
+ */
+int
+igshow(struct ignoretab *tab, char *which)
+{
+ int h;
+ struct ignore *igp;
+ char **ap, **ring;
+
+ if (tab->i_count == 0) {
+ printf("No fields currently being %s.\n", which);
+ return(0);
+ }
+ ring = (char **)salloc((tab->i_count + 1) * sizeof(char *));
+ ap = ring;
+ for (h = 0; h < HSHSIZE; h++)
+ for (igp = tab->i_head[h]; igp != 0; igp = igp->i_link)
+ *ap++ = igp->i_field;
+ *ap = 0;
+ qsort(ring, tab->i_count, sizeof(char *), igcomp);
+ for (ap = ring; *ap != 0; ap++)
+ puts(*ap);
+ return(0);
+}
+
+/*
+ * Compare two names for sorting ignored field list.
+ */
+static int
+igcomp(const void *l, const void *r)
+{
+
+ return(strcmp(*(char **)l, *(char **)r));
+}
diff --git a/cmd3.c b/cmd3.c
@@ -0,0 +1,736 @@
+/* $OpenBSD: cmd3.c,v 1.28 2019/06/28 13:35:01 deraadt Exp $ */
+/* $NetBSD: cmd3.c,v 1.8 1997/07/09 05:29:49 mikel Exp $ */
+
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS 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 REGENTS OR 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.
+ */
+
+#include "rcv.h"
+#include "extern.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * Still more user commands.
+ */
+static int diction(const void *, const void *);
+
+/*
+ * Process a shell escape by saving signals, ignoring signals,
+ * and forking a sh -c
+ */
+int
+shell(void *v)
+{
+ char *str = v;
+ char *shell;
+ char cmd[BUFSIZ];
+ struct sigaction oact;
+ sigset_t oset;
+
+ (void)ignoresig(SIGINT, &oact, &oset);
+ (void)strlcpy(cmd, str, sizeof(cmd));
+ if (bangexp(cmd, sizeof(cmd)) < 0)
+ return(1);
+ shell = value("SHELL");
+ (void)run_command(shell, 0, 0, -1, "-c", cmd, NULL);
+ (void)sigprocmask(SIG_SETMASK, &oset, NULL);
+ (void)sigaction(SIGINT, &oact, NULL);
+ puts("!");
+ return(0);
+}
+
+/*
+ * Fork an interactive shell.
+ */
+/*ARGSUSED*/
+int
+dosh(void *v)
+{
+ char *shell;
+ struct sigaction oact;
+ sigset_t oset;
+
+ shell = value("SHELL");
+ (void)ignoresig(SIGINT, &oact, &oset);
+ (void)run_command(shell, 0, 0, -1, NULL, NULL, NULL);
+ (void)sigprocmask(SIG_SETMASK, &oset, NULL);
+ (void)sigaction(SIGINT, &oact, NULL);
+ putchar('\n');
+ return(0);
+}
+
+/*
+ * Expand the shell escape by expanding unescaped !'s into the
+ * last issued command where possible.
+ */
+int
+bangexp(char *str, size_t strsize)
+{
+ char bangbuf[BUFSIZ];
+ static char lastbang[BUFSIZ];
+ char *cp, *cp2;
+ int n, changed = 0;
+
+ cp = str;
+ cp2 = bangbuf;
+ n = BUFSIZ;
+ while (*cp) {
+ if (*cp == '!') {
+ if (n < strlen(lastbang)) {
+overf:
+ puts("Command buffer overflow");
+ return(-1);
+ }
+ changed++;
+ strlcpy(cp2, lastbang, sizeof(bangbuf) - (cp2 - bangbuf));
+ cp2 += strlen(lastbang);
+ n -= strlen(lastbang);
+ cp++;
+ continue;
+ }
+ if (*cp == '\\' && cp[1] == '!') {
+ if (--n <= 1)
+ goto overf;
+ *cp2++ = '!';
+ cp += 2;
+ changed++;
+ }
+ if (--n <= 1)
+ goto overf;
+ *cp2++ = *cp++;
+ }
+ *cp2 = 0;
+ if (changed) {
+ (void)printf("!%s\n", bangbuf);
+ (void)fflush(stdout);
+ }
+ (void)strlcpy(str, bangbuf, strsize);
+ (void)strlcpy(lastbang, bangbuf, sizeof(lastbang));
+ return(0);
+}
+
+/*
+ * Print out a nice help message from some file or another.
+ */
+int
+help(void *v)
+{
+
+ (void)run_command(value("PAGER"), 0, -1, -1, _PATH_HELP, NULL);
+ return(0);
+}
+
+/*
+ * Change user's working directory.
+ */
+int
+schdir(void *v)
+{
+ char **arglist = v;
+ char *cp;
+
+ if (*arglist == NULL) {
+ if (homedir == NULL)
+ return(1);
+ cp = homedir;
+ } else {
+ if ((cp = expand(*arglist)) == NULL)
+ return(1);
+ }
+ if (chdir(cp) == -1) {
+ warn("%s", cp);
+ return(1);
+ }
+ return(0);
+}
+
+int
+respond(void *v)
+{
+ int *msgvec = v;
+
+ if (value("Replyall") == NULL)
+ return(_respond(msgvec));
+ else
+ return(_Respond(msgvec));
+}
+
+/*
+ * Reply to a list of messages. Extract each name from the
+ * message header and send them off to mail1()
+ */
+int
+_respond(msgvec)
+ int *msgvec;
+{
+ struct message *mp;
+ char *cp, *rcv, *replyto;
+ char **ap;
+ struct name *np;
+ struct header head;
+
+ if (msgvec[1] != 0) {
+ puts("Sorry, can't reply to multiple messages at once");
+ return(1);
+ }
+ mp = &message[msgvec[0] - 1];
+ touch(mp);
+ dot = mp;
+ if ((rcv = skin(hfield("from", mp))) == NULL)
+ rcv = skin(nameof(mp, 1));
+ if ((replyto = skin(hfield("reply-to", mp))) != NULL)
+ np = extract(replyto, GTO);
+ else if ((cp = skin(hfield("to", mp))) != NULL)
+ np = extract(cp, GTO);
+ else
+ np = NULL;
+ /*
+ * Delete my name from the reply list,
+ * and with it, all my alternate names.
+ */
+ np = delname(np, myname);
+ if (altnames)
+ for (ap = altnames; *ap; ap++)
+ np = delname(np, *ap);
+ if (np != NULL && replyto == NULL)
+ np = cat(np, extract(rcv, GTO));
+ else if (np == NULL) {
+ if (replyto != NULL)
+ puts("Empty reply-to field -- replying to author");
+ np = extract(rcv, GTO);
+ }
+ np = elide(np);
+ head.h_to = np;
+ head.h_from = NULL;
+ if ((head.h_subject = hfield("subject", mp)) == NULL)
+ head.h_subject = hfield("subj", mp);
+ head.h_subject = reedit(head.h_subject);
+ if (replyto == NULL && (cp = skin(hfield("cc", mp))) != NULL) {
+ np = elide(extract(cp, GCC));
+ np = delname(np, myname);
+ if (altnames != 0)
+ for (ap = altnames; *ap; ap++)
+ np = delname(np, *ap);
+ head.h_cc = np;
+ } else
+ head.h_cc = NULL;
+ head.h_bcc = NULL;
+ head.h_smopts = NULL;
+ mail1(&head, 1);
+ return(0);
+}
+
+/*
+ * Modify the subject we are replying to to begin with Re: if
+ * it does not already.
+ */
+char *
+reedit(char *subj)
+{
+ char *newsubj;
+ size_t len;
+
+ if (subj == NULL)
+ return(NULL);
+ if (strncasecmp(subj, "re:", 3) == 0)
+ return(subj);
+ len = strlen(subj) + 5;
+ newsubj = salloc(len);
+ strlcpy(newsubj, "Re: ", len);
+ strlcat(newsubj, subj, len);
+ return(newsubj);
+}
+
+/*
+ * Mark new the named messages, so that they will be left in the system
+ * mailbox as unread.
+ */
+int
+marknew(void *v)
+{
+ int *msgvec = v;
+ int *ip;
+
+ for (ip = msgvec; *ip != 0; ip++) {
+ dot = &message[*ip-1];
+ dot->m_flag &= ~(MBOX|MREAD|MTOUCH);
+ dot->m_flag |= MNEW|MSTATUS;
+ }
+ return(0);
+}
+
+/*
+ * Preserve the named messages, so that they will be sent
+ * back to the system mailbox.
+ */
+int
+preserve(void *v)
+{
+ int *msgvec = v;
+ int *ip, mesg;
+ struct message *mp;
+
+ if (edit) {
+ puts("Cannot \"preserve\" in edit mode");
+ return(1);
+ }
+ for (ip = msgvec; *ip != 0; ip++) {
+ mesg = *ip;
+ mp = &message[mesg-1];
+ mp->m_flag |= MPRESERVE;
+ mp->m_flag &= ~MBOX;
+ dot = mp;
+ }
+ return(0);
+}
+
+/*
+ * Mark all given messages as unread.
+ */
+int
+unread(void *v)
+{
+ int *msgvec = v;
+ int *ip;
+
+ for (ip = msgvec; *ip != 0; ip++) {
+ dot = &message[*ip-1];
+ dot->m_flag &= ~(MREAD|MTOUCH);
+ dot->m_flag |= MSTATUS;
+ }
+ return(0);
+}
+
+/*
+ * Print the size of each message.
+ */
+int
+messize(void *v)
+{
+ int *msgvec = v;
+ struct message *mp;
+ int *ip, mesg;
+
+ for (ip = msgvec; *ip != 0; ip++) {
+ mesg = *ip;
+ mp = &message[mesg-1];
+ printf("%d: %d/%d\n", mesg, mp->m_lines, mp->m_size);
+ }
+ return(0);
+}
+
+/*
+ * Quit quickly. If we are sourcing, just pop the input level
+ * by returning an error.
+ */
+int
+rexit(void *v)
+{
+
+ if (sourcing)
+ return(1);
+ exit(0);
+ /*NOTREACHED*/
+}
+
+/*
+ * Set or display a variable value. Syntax is similar to that
+ * of csh.
+ */
+int
+set(void *v)
+{
+ char **arglist = v;
+ struct var *vp;
+ char *cp, *cp2;
+ char varbuf[BUFSIZ], **ap, **p;
+ int errs, h, s;
+
+ if (*arglist == NULL) {
+ for (h = 0, s = 1; h < HSHSIZE; h++)
+ for (vp = variables[h]; vp != NULL; vp = vp->v_link)
+ s++;
+ ap = (char **)salloc(s * sizeof(*ap));
+ for (h = 0, p = ap; h < HSHSIZE; h++)
+ for (vp = variables[h]; vp != NULL; vp = vp->v_link)
+ *p++ = vp->v_name;
+ *p = NULL;
+ sort(ap);
+ for (p = ap; *p != NULL; p++)
+ printf("%s\t%s\n", *p, value(*p));
+ return(0);
+ }
+ errs = 0;
+ for (ap = arglist; *ap != NULL; ap++) {
+ cp = *ap;
+ cp2 = varbuf;
+ while (*cp != '=' && *cp != '\0')
+ *cp2++ = *cp++;
+ *cp2 = '\0';
+ if (*cp == '\0')
+ cp = "";
+ else
+ cp++;
+ if (equal(varbuf, "")) {
+ puts("Non-null variable name required");
+ errs++;
+ continue;
+ }
+ assign(varbuf, cp);
+ }
+ return(errs);
+}
+
+/*
+ * Unset a bunch of variable values.
+ */
+int
+unset(void *v)
+{
+ char **arglist = v;
+ struct var *vp, *vp2;
+ int errs, h;
+ char **ap;
+
+ errs = 0;
+ for (ap = arglist; *ap != NULL; ap++) {
+ if ((vp2 = lookup(*ap)) == NULL) {
+ if (!sourcing) {
+ printf("\"%s\": undefined variable\n", *ap);
+ errs++;
+ }
+ continue;
+ }
+ h = hash(*ap);
+ if (vp2 == variables[h]) {
+ variables[h] = variables[h]->v_link;
+ vfree(vp2->v_name);
+ vfree(vp2->v_value);
+ (void)free(vp2);
+ continue;
+ }
+ for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
+ ;
+ vp->v_link = vp2->v_link;
+ vfree(vp2->v_name);
+ vfree(vp2->v_value);
+ (void)free(vp2);
+ }
+ return(errs);
+}
+
+/*
+ * Put add users to a group.
+ */
+int
+group(void *v)
+{
+ char **argv = v;
+ struct grouphead *gh;
+ struct group *gp;
+ char **ap, *gname, **p;
+ int h, s;
+
+ if (*argv == NULL) {
+ for (h = 0, s = 1; h < HSHSIZE; h++)
+ for (gh = groups[h]; gh != NULL; gh = gh->g_link)
+ s++;
+ ap = (char **)salloc(s * sizeof(*ap));
+ for (h = 0, p = ap; h < HSHSIZE; h++)
+ for (gh = groups[h]; gh != NULL; gh = gh->g_link)
+ *p++ = gh->g_name;
+ *p = NULL;
+ sort(ap);
+ for (p = ap; *p != NULL; p++)
+ printgroup(*p);
+ return(0);
+ }
+ if (argv[1] == NULL) {
+ printgroup(*argv);
+ return(0);
+ }
+ gname = *argv;
+ h = hash(gname);
+ if ((gh = findgroup(gname)) == NULL) {
+ if ((gh = calloc(1, sizeof(*gh))) == NULL)
+ err(1, "calloc");
+ gh->g_name = vcopy(gname);
+ gh->g_list = NULL;
+ gh->g_link = groups[h];
+ groups[h] = gh;
+ }
+
+ /*
+ * Insert names from the command list into the group.
+ * Who cares if there are duplicates? They get tossed
+ * later anyway.
+ */
+
+ for (ap = argv+1; *ap != NULL; ap++) {
+ if ((gp = calloc(1, sizeof(*gp))) == NULL)
+ err(1, "calloc");
+ gp->ge_name = vcopy(*ap);
+ gp->ge_link = gh->g_list;
+ gh->g_list = gp;
+ }
+ return(0);
+}
+
+/*
+ * Sort the passed string vector into ascending dictionary
+ * order.
+ */
+void
+sort(char **list)
+{
+ char **ap;
+
+ for (ap = list; *ap != NULL; ap++)
+ ;
+ if (ap-list < 2)
+ return;
+ qsort(list, ap-list, sizeof(*list), diction);
+}
+
+/*
+ * Do a dictionary order comparison of the arguments from
+ * qsort.
+ */
+static int
+diction(const void *a, const void *b)
+{
+
+ return(strcmp(*(char **)a, *(char **)b));
+}
+
+/*
+ * The do nothing command for comments.
+ */
+/*ARGSUSED*/
+int
+null(void *v)
+{
+
+ return(0);
+}
+
+/*
+ * Change to another file. With no argument, print information about
+ * the current file.
+ */
+int
+file(void *v)
+{
+ char **argv = v;
+
+ if (argv[0] == NULL) {
+ newfileinfo(0);
+ clearnew();
+ return(0);
+ }
+ if (setfile(*argv) < 0)
+ return(1);
+ announce();
+ return(0);
+}
+
+/*
+ * Expand file names like echo
+ */
+int
+echo(void *v)
+{
+ char **argv = v;
+ char **ap, *cp;
+
+ for (ap = argv; *ap != NULL; ap++) {
+ cp = *ap;
+ if ((cp = expand(cp)) != NULL) {
+ if (ap != argv)
+ putchar(' ');
+ fputs(cp, stdout);
+ }
+ }
+ putchar('\n');
+ return(0);
+}
+
+int
+Respond(void *v)
+{
+ int *msgvec = v;
+
+ if (value("Replyall") == NULL)
+ return(_Respond(msgvec));
+ else
+ return(_respond(msgvec));
+}
+
+/*
+ * Reply to a series of messages by simply mailing to the senders
+ * and not messing around with the To: and Cc: lists as in normal
+ * reply.
+ */
+int
+_Respond(int *msgvec)
+{
+ struct header head;
+ struct message *mp;
+ int *ap;
+ char *cp;
+
+ head.h_to = NULL;
+ for (ap = msgvec; *ap != 0; ap++) {
+ mp = &message[*ap - 1];
+ touch(mp);
+ dot = mp;
+ if ((cp = skin(hfield("from", mp))) == NULL)
+ cp = skin(nameof(mp, 2));
+ head.h_to = cat(head.h_to, extract(cp, GTO));
+ }
+ if (head.h_to == NULL)
+ return(0);
+ mp = &message[msgvec[0] - 1];
+ if ((head.h_subject = hfield("subject", mp)) == NULL)
+ head.h_subject = hfield("subj", mp);
+ head.h_subject = reedit(head.h_subject);
+ head.h_from = NULL;
+ head.h_cc = NULL;
+ head.h_bcc = NULL;
+ head.h_smopts = NULL;
+ mail1(&head, 1);
+ return(0);
+}
+
+/*
+ * Conditional commands. These allow one to parameterize one's
+ * .mailrc and do some things if sending, others if receiving.
+ */
+int
+ifcmd(void *v)
+{
+ char **argv = v;
+ char *cp;
+
+ if (cond != CANY) {
+ puts("Illegal nested \"if\"");
+ return(1);
+ }
+ cond = CANY;
+ cp = argv[0];
+ switch (*cp) {
+ case 'r': case 'R':
+ cond = CRCV;
+ break;
+
+ case 's': case 'S':
+ cond = CSEND;
+ break;
+
+ default:
+ printf("Unrecognized if-keyword: \"%s\"\n", cp);
+ return(1);
+ }
+ return(0);
+}
+
+/*
+ * Implement 'else'. This is pretty simple -- we just
+ * flip over the conditional flag.
+ */
+int
+elsecmd(void *v)
+{
+
+ switch (cond) {
+ case CANY:
+ puts("\"Else\" without matching \"if\"");
+ return(1);
+
+ case CSEND:
+ cond = CRCV;
+ break;
+
+ case CRCV:
+ cond = CSEND;
+ break;
+
+ default:
+ puts("mail's idea of conditions is screwed up");
+ cond = CANY;
+ break;
+ }
+ return(0);
+}
+
+/*
+ * End of if statement. Just set cond back to anything.
+ */
+int
+endifcmd(void *v)
+{
+
+ if (cond == CANY) {
+ puts("\"Endif\" without matching \"if\"");
+ return(1);
+ }
+ cond = CANY;
+ return(0);
+}
+
+/*
+ * Set the list of alternate names.
+ */
+int
+alternates(void *v)
+{
+ char **namelist = v;
+ char **ap, **ap2;
+ int c;
+
+ c = argcount(namelist) + 1;
+ if (c == 1) {
+ if (altnames == 0)
+ return(0);
+ for (ap = altnames; *ap; ap++)
+ printf("%s ", *ap);
+ putchar('\n');
+ return(0);
+ }
+ if (altnames != 0)
+ (void)free(altnames);
+ if ((altnames = calloc(c, sizeof(char *))) == NULL)
+ err(1, "calloc");
+ for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
+ if ((*ap2 = strdup(*ap)) == NULL)
+ err(1, "strdup");
+ }
+ *ap2 = 0;
+ return(0);
+}
diff --git a/cmdtab.c b/cmdtab.c
@@ -0,0 +1,120 @@
+/* $OpenBSD: cmdtab.c,v 1.13 2009/10/27 23:59:40 deraadt Exp $ */
+/* $NetBSD: cmdtab.c,v 1.7 1996/12/28 07:10:59 tls Exp $ */
+
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS 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 REGENTS OR 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.
+ */
+
+#include "def.h"
+#include "extern.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * Define all of the command names and bindings.
+ */
+typedef int (*cfunc_t)(void *);
+const struct cmd cmdtab[] = {
+ /* msgmask msgflag */
+ /* command function argtype result & mask */
+ /* ------- -------- ------- ------- -------- */
+ { "next", { next }, NDMLIST, 0, MMNDEL },
+ { "alias", { group }, M|RAWLIST, 0, 1000 },
+ { "print", { type }, MSGLIST, 0, MMNDEL },
+ { "type", { type }, MSGLIST, 0, MMNDEL },
+ { "Type", { Type }, MSGLIST, 0, MMNDEL },
+ { "Print", { Type }, MSGLIST, 0, MMNDEL },
+ { "visual", { visual }, I|MSGLIST, 0, MMNORM },
+ { "top", { top }, MSGLIST, 0, MMNDEL },
+ { "touch", { stouch }, W|MSGLIST, 0, MMNDEL },
+ { "preserve", { preserve }, W|MSGLIST, 0, MMNDEL },
+ { "delete", { deletecmd }, W|P|MSGLIST, 0, MMNDEL },
+ { "dp", { deltype }, W|MSGLIST, 0, MMNDEL },
+ { "dt", { deltype }, W|MSGLIST, 0, MMNDEL },
+ { "undelete", { undeletecmd }, P|MSGLIST, MDELETED,MMNDEL },
+ { "unset", { unset }, M|RAWLIST, 1, 1000 },
+ { "mail", { sendmail }, R|M|I|STRLIST, 0, 0 },
+ { "mbox", { mboxit }, W|MSGLIST, 0, 0 },
+ { "pipe", { (cfunc_t)pipeit }, MSGLIST|STRLIST,0, MMNDEL },
+ { "|", { (cfunc_t)pipeit }, MSGLIST|STRLIST,0, MMNDEL },
+ { "more", { more }, MSGLIST, 0, MMNDEL },
+ { "page", { more }, MSGLIST, 0, MMNDEL },
+ { "More", { More }, MSGLIST, 0, MMNDEL },
+ { "Page", { More }, MSGLIST, 0, MMNDEL },
+ { "unread", { unread }, MSGLIST, 0, MMNDEL },
+ { "Unread", { unread }, MSGLIST, 0, MMNDEL },
+ { "!", { shell }, I|STRLIST, 0, 0 },
+ { "copy", { copycmd }, M|STRLIST, 0, 0 },
+ { "chdir", { schdir }, M|RAWLIST, 0, 1 },
+ { "cd", { schdir }, M|RAWLIST, 0, 1 },
+ { "save", { save }, STRLIST, 0, 0 },
+ { "source", { source }, M|RAWLIST, 1, 1 },
+ { "set", { set }, M|RAWLIST, 0, 1000 },
+ { "shell", { dosh }, I|NOLIST, 0, 0 },
+ { "version", { pversion }, M|NOLIST, 0, 0 },
+ { "group", { group }, M|RAWLIST, 0, 1000 },
+ { "write", { swrite }, STRLIST, 0, 0 },
+ { "from", { from }, MSGLIST, 0, MMNORM },
+ { "file", { file }, T|M|RAWLIST, 0, 1 },
+ { "folder", { file }, T|M|RAWLIST, 0, 1 },
+ { "folders", { folders }, T|M|STRLIST, 0, 0 },
+ { "?", { help }, M|NOLIST, 0, 0 },
+ { "z", { scroll }, M|STRLIST, 0, 0 },
+ { "headers", { headers }, MSGLIST, 0, MMNDEL },
+ { "help", { help }, M|NOLIST, 0, 0 },
+ { "=", { pdot }, NOLIST, 0, 0 },
+ { "Reply", { Respond }, R|I|MSGLIST, 0, MMNDEL },
+ { "Respond", { Respond }, R|I|MSGLIST, 0, MMNDEL },
+ { "reply", { respond }, R|I|MSGLIST, 0, MMNDEL },
+ { "respond", { respond }, R|I|MSGLIST, 0, MMNDEL },
+ { "edit", { editor }, I|MSGLIST, 0, MMNORM },
+ { "echo", { echo }, M|RAWLIST, 0, 1000 },
+ { "quit", { quitcmd }, NOLIST, 0, 0 },
+ { "list", { pcmdlist }, M|NOLIST, 0, 0 },
+ { "xit", { rexit }, M|NOLIST, 0, 0 },
+ { "exit", { rexit }, M|NOLIST, 0, 0 },
+ { "size", { messize }, MSGLIST, 0, MMNDEL },
+ { "hold", { preserve }, W|MSGLIST, 0, MMNDEL },
+ { "if", { ifcmd }, F|M|RAWLIST, 1, 1 },
+ { "else", { elsecmd }, F|M|RAWLIST, 0, 0 },
+ { "endif", { endifcmd }, F|M|RAWLIST, 0, 0 },
+ { "alternates", { alternates }, M|RAWLIST, 0, 1000 },
+ { "ignore", { igfield }, M|RAWLIST, 0, 1000 },
+ { "discard", { igfield }, M|RAWLIST, 0, 1000 },
+ { "retain", { retfield }, M|RAWLIST, 0, 1000 },
+ { "saveignore", { saveigfield }, M|RAWLIST, 0, 1000 },
+ { "savediscard",{ saveigfield }, M|RAWLIST, 0, 1000 },
+ { "saveretain", { saveretfield }, M|RAWLIST, 0, 1000 },
+#if 0
+ { "Header", { Header }, STRLIST, 0, 1000 },
+#endif
+ { "#", { null }, M|NOLIST, 0, 0 },
+ { "inc", { inc }, T|NOLIST, 0, 0 },
+ { "new", { marknew }, MSGLIST, 0, MMNDEL },
+ { 0, { 0 }, 0, 0, 0 }
+};
diff --git a/collect.c b/collect.c
@@ -0,0 +1,609 @@
+/* $OpenBSD: collect.c,v 1.34 2014/01/17 18:42:30 okan Exp $ */
+/* $NetBSD: collect.c,v 1.9 1997/07/09 05:25:45 mikel Exp $ */
+
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS 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 REGENTS OR 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.
+ */
+
+/*
+ * Mail -- a mail program
+ *
+ * Collect input from standard input, handling
+ * ~ escapes.
+ */
+
+#include "rcv.h"
+#include "extern.h"
+
+/*
+ * Read a message from standard output and return a read file to it
+ * or NULL on error.
+ */
+
+/*
+ * The following hokiness with global variables is so that on
+ * receipt of an interrupt signal, the partial message can be salted
+ * away on dead.letter.
+ */
+static FILE *collf; /* File for saving away */
+static int hadintr; /* Have seen one SIGINT so far */
+
+FILE *
+collect(struct header *hp, int printheaders)
+{
+ FILE *fbuf;
+ int lc, cc, fd, c, t, lastlong, rc, sig;
+ int escape, eofcount, longline;
+ char getsub;
+ char linebuf[LINESIZE], tempname[PATHSIZE], *cp;
+
+ collf = NULL;
+ eofcount = 0;
+ hadintr = 0;
+ lastlong = 0;
+ longline = 0;
+ if ((cp = value("escape")) != NULL)
+ escape = *cp;
+ else
+ escape = ESCAPE;
+ noreset++;
+
+ (void)snprintf(tempname, sizeof(tempname),
+ "%s/mail.RsXXXXXXXXXX", tmpdir);
+ if ((fd = mkstemp(tempname)) == -1 ||
+ (collf = Fdopen(fd, "w+")) == NULL) {
+ warn("%s", tempname);
+ goto err;
+ }
+ (void)rm(tempname);
+
+ /*
+ * If we are going to prompt for a subject,
+ * refrain from printing a newline after
+ * the headers (since some people mind).
+ */
+ t = GTO|GSUBJECT|GCC|GNL;
+ getsub = 0;
+ if (hp->h_subject == NULL && value("interactive") != NULL &&
+ (value("ask") != NULL || value("asksub") != NULL))
+ t &= ~GNL, getsub++;
+ if (printheaders) {
+ puthead(hp, stdout, t);
+ fflush(stdout);
+ }
+ if (getsub && gethfromtty(hp, GSUBJECT) == -1)
+ goto err;
+
+ if (0) {
+cont:
+ /* Come here for printing the after-suspend message. */
+ if (isatty(0)) {
+ puts("(continue)");
+ fflush(stdout);
+ }
+ }
+ for (;;) {
+ c = readline(stdin, linebuf, LINESIZE, &sig);
+
+ /* Act on any signal caught during readline() ignoring 'c' */
+ switch (sig) {
+ case 0:
+ break;
+ case SIGINT:
+ if (collabort())
+ goto err;
+ continue;
+ case SIGHUP:
+ rewind(collf);
+ savedeadletter(collf);
+ /*
+ * Let's pretend nobody else wants to clean up,
+ * a true statement at this time.
+ */
+ exit(1);
+ default:
+ /* Stopped due to job control */
+ (void)kill(0, sig);
+ goto cont;
+ }
+
+ /* No signal, check for error */
+ if (c < 0) {
+ if (value("interactive") != NULL &&
+ value("ignoreeof") != NULL && ++eofcount < 25) {
+ puts("Use \".\" to terminate letter");
+ continue;
+ }
+ break;
+ }
+ lastlong = longline;
+ longline = (c == LINESIZE - 1);
+ eofcount = 0;
+ hadintr = 0;
+ if (linebuf[0] == '.' && linebuf[1] == '\0' &&
+ value("interactive") != NULL && !lastlong &&
+ (value("dot") != NULL || value("ignoreeof") != NULL))
+ break;
+ if (linebuf[0] != escape || value("interactive") == NULL ||
+ lastlong) {
+ if (putline(collf, linebuf, !longline) < 0)
+ goto err;
+ continue;
+ }
+ c = (unsigned char)linebuf[1];
+ switch (c) {
+ default:
+ /*
+ * On double escape, just send the single one.
+ * Otherwise, it's an error.
+ */
+ if (c == escape) {
+ if (putline(collf, &linebuf[1], !longline) < 0)
+ goto err;
+ else
+ break;
+ }
+ puts("Unknown tilde escape.");
+ break;
+ case '!':
+ /*
+ * Shell escape, send the balance of the
+ * line to sh -c.
+ */
+ shell(&linebuf[2]);
+ break;
+ case ':':
+ case '_':
+ /*
+ * Escape to command mode, but be nice!
+ */
+ execute(&linebuf[2], 1);
+ goto cont;
+ case '.':
+ /*
+ * Simulate end of file on input.
+ */
+ goto out;
+ case 'q':
+ /*
+ * Force a quit of sending mail.
+ * Act like an interrupt happened.
+ */
+ hadintr++;
+ collabort();
+ fputs("Interrupt\n", stderr);
+ goto err;
+ case 'x':
+ /*
+ * Force a quit of sending mail.
+ * Do not save the message.
+ */
+ goto err;
+ case 'h':
+ /*
+ * Grab a bunch of headers.
+ */
+ grabh(hp, GTO|GSUBJECT|GCC|GBCC);
+ goto cont;
+ case 't':
+ /*
+ * Add to the To list.
+ */
+ hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO));
+ break;
+ case 's':
+ /*
+ * Set the Subject list.
+ */
+ cp = &linebuf[2];
+ while (isspace((unsigned char)*cp))
+ cp++;
+ hp->h_subject = savestr(cp);
+ break;
+ case 'c':
+ /*
+ * Add to the CC list.
+ */
+ hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC));
+ break;
+ case 'b':
+ /*
+ * Add stuff to blind carbon copies list.
+ */
+ hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC));
+ break;
+ case 'd':
+ linebuf[2] = '\0';
+ strlcat(linebuf, getdeadletter(), sizeof(linebuf));
+ /* fall into . . . */
+ case 'r':
+ case '<':
+ /*
+ * Invoke a file:
+ * Search for the file name,
+ * then open it and copy the contents to collf.
+ */
+ cp = &linebuf[2];
+ while (isspace((unsigned char)*cp))
+ cp++;
+ if (*cp == '\0') {
+ puts("Interpolate what file?");
+ break;
+ }
+ cp = expand(cp);
+ if (cp == NULL)
+ break;
+ if (isdir(cp)) {
+ printf("%s: Directory\n", cp);
+ break;
+ }
+ if ((fbuf = Fopen(cp, "r")) == NULL) {
+ warn("%s", cp);
+ break;
+ }
+ printf("\"%s\" ", cp);
+ fflush(stdout);
+ lc = 0;
+ cc = 0;
+ while ((rc = readline(fbuf, linebuf, LINESIZE, NULL)) >= 0) {
+ if (rc != LINESIZE - 1)
+ lc++;
+ if ((t = putline(collf, linebuf,
+ rc != LINESIZE-1)) < 0) {
+ (void)Fclose(fbuf);
+ goto err;
+ }
+ cc += t;
+ }
+ (void)Fclose(fbuf);
+ printf("%d/%d\n", lc, cc);
+ break;
+ case 'w':
+ /*
+ * Write the message on a file.
+ */
+ cp = &linebuf[2];
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ if (*cp == '\0') {
+ fputs("Write what file!?\n", stderr);
+ break;
+ }
+ if ((cp = expand(cp)) == NULL)
+ break;
+ rewind(collf);
+ exwrite(cp, collf, 1);
+ break;
+ case 'm':
+ case 'M':
+ case 'f':
+ case 'F':
+ /*
+ * Interpolate the named messages, if we
+ * are in receiving mail mode. Does the
+ * standard list processing garbage.
+ * If ~f is given, we don't shift over.
+ */
+ if (forward(linebuf + 2, collf, tempname, c) < 0)
+ goto err;
+ goto cont;
+ case '?':
+ if ((fbuf = Fopen(_PATH_TILDE, "r")) == NULL) {
+ warn(_PATH_TILDE);
+ break;
+ }
+ while ((t = getc(fbuf)) != EOF)
+ (void)putchar(t);
+ (void)Fclose(fbuf);
+ break;
+ case 'p':
+ /*
+ * Print out the current state of the
+ * message without altering anything.
+ */
+ rewind(collf);
+ puts("-------\nMessage contains:");
+ puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL);
+ while ((t = getc(collf)) != EOF)
+ (void)putchar(t);
+ goto cont;
+ case '|':
+ /*
+ * Pipe message through command.
+ * Collect output as new message.
+ */
+ rewind(collf);
+ mespipe(collf, &linebuf[2]);
+ goto cont;
+ case 'v':
+ case 'e':
+ /*
+ * Edit the current message.
+ * 'e' means to use EDITOR
+ * 'v' means to use VISUAL
+ */
+ rewind(collf);
+ mesedit(collf, c);
+ goto cont;
+ }
+ }
+
+ if (value("interactive") != NULL) {
+ if (value("askcc") != NULL || value("askbcc") != NULL) {
+ if (value("askcc") != NULL) {
+ if (gethfromtty(hp, GCC) == -1)
+ goto err;
+ }
+ if (value("askbcc") != NULL) {
+ if (gethfromtty(hp, GBCC) == -1)
+ goto err;
+ }
+ } else {
+ puts("EOT");
+ (void)fflush(stdout);
+ }
+ }
+ goto out;
+err:
+ if (collf != NULL) {
+ (void)Fclose(collf);
+ collf = NULL;
+ }
+out:
+ if (collf != NULL)
+ rewind(collf);
+ noreset--;
+ return(collf);
+}
+
+/*
+ * Write a file, ex-like if f set.
+ */
+int
+exwrite(char *name, FILE *fp, int f)
+{
+ FILE *of;
+ int c;
+ ssize_t cc, lc;
+ struct stat junk;
+
+ if (f) {
+ printf("\"%s\" ", name);
+ fflush(stdout);
+ }
+ if (stat(name, &junk) >= 0 && S_ISREG(junk.st_mode)) {
+ if (!f)
+ fprintf(stderr, "%s: ", name);
+ fputs("File exists\n", stderr);
+ return(-1);
+ }
+ if ((of = Fopen(name, "w")) == NULL) {
+ warn(NULL);
+ return(-1);
+ }
+ lc = 0;
+ cc = 0;
+ while ((c = getc(fp)) != EOF) {
+ cc++;
+ if (c == '\n')
+ lc++;
+ (void)putc(c, of);
+ if (ferror(of)) {
+ warn("%s", name);
+ (void)Fclose(of);
+ return(-1);
+ }
+ }
+ (void)Fclose(of);
+ printf("%lld/%lld\n", (long long)lc, (long long)cc);
+ fflush(stdout);
+ return(0);
+}
+
+/*
+ * Edit the message being collected on fp.
+ * On return, make the edit file the new temp file.
+ */
+void
+mesedit(FILE *fp, int c)
+{
+ FILE *nf;
+ struct sigaction oact;
+ sigset_t oset;
+
+ (void)ignoresig(SIGINT, &oact, &oset);
+ nf = run_editor(fp, (off_t)-1, c, 0);
+ if (nf != NULL) {
+ fseek(nf, 0L, SEEK_END);
+ collf = nf;
+ (void)Fclose(fp);
+ }
+ (void)sigprocmask(SIG_SETMASK, &oset, NULL);
+ (void)sigaction(SIGINT, &oact, NULL);
+}
+
+/*
+ * Pipe the message through the command.
+ * Old message is on stdin of command;
+ * New message collected from stdout.
+ * Sh -c must return 0 to accept the new message.
+ */
+void
+mespipe(FILE *fp, char *cmd)
+{
+ FILE *nf;
+ int fd;
+ char *shell, tempname[PATHSIZE];
+ struct sigaction oact;
+ sigset_t oset;
+
+ (void)ignoresig(SIGINT, &oact, &oset);
+ (void)snprintf(tempname, sizeof(tempname),
+ "%s/mail.ReXXXXXXXXXX", tmpdir);
+ if ((fd = mkstemp(tempname)) == -1 ||
+ (nf = Fdopen(fd, "w+")) == NULL) {
+ warn("%s", tempname);
+ goto out;
+ }
+ (void)rm(tempname);
+ /*
+ * stdin = current message.
+ * stdout = new message.
+ */
+ shell = value("SHELL");
+ if (run_command(shell,
+ 0, fileno(fp), fileno(nf), "-c", cmd, NULL) < 0) {
+ (void)Fclose(nf);
+ goto out;
+ }
+ if (fsize(nf) == 0) {
+ fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
+ (void)Fclose(nf);
+ goto out;
+ }
+ /*
+ * Take new files.
+ */
+ (void)fseek(nf, 0L, SEEK_END);
+ collf = nf;
+ (void)Fclose(fp);
+out:
+ (void)sigprocmask(SIG_SETMASK, &oset, NULL);
+ (void)sigaction(SIGINT, &oact, NULL);
+}
+
+/*
+ * Interpolate the named messages into the current
+ * message, preceding each line with a tab.
+ * Return a count of the number of characters now in
+ * the message, or -1 if an error is encountered writing
+ * the message temporary. The flag argument is 'm' if we
+ * should shift over and 'f' if not.
+ */
+int
+forward(char *ms, FILE *fp, char *fn, int f)
+{
+ int *msgvec;
+ struct ignoretab *ig;
+ char *tabst;
+
+ msgvec = (int *)salloc((msgCount+1) * sizeof(*msgvec));
+ if (msgvec == NULL)
+ return(0);
+ if (getmsglist(ms, msgvec, 0) < 0)
+ return(0);
+ if (*msgvec == 0) {
+ *msgvec = first(0, MMNORM);
+ if (*msgvec == 0) {
+ puts("No appropriate messages");
+ return(0);
+ }
+ msgvec[1] = 0;
+ }
+ if (tolower(f) == 'f')
+ tabst = NULL;
+ else if ((tabst = value("indentprefix")) == NULL)
+ tabst = "\t";
+ ig = isupper(f) ? NULL : ignore;
+ fputs("Interpolating:", stdout);
+ for (; *msgvec != 0; msgvec++) {
+ struct message *mp = message + *msgvec - 1;
+
+ touch(mp);
+ printf(" %d", *msgvec);
+ if (sendmessage(mp, fp, ig, tabst) < 0) {
+ warn("%s", fn);
+ return(-1);
+ }
+ }
+ putchar('\n');
+ return(0);
+}
+
+/*
+ * User aborted during message composition.
+ * Save the partial message in ~/dead.letter.
+ */
+int
+collabort(void)
+{
+ /*
+ * the control flow is subtle, because we can be called from ~q.
+ */
+ if (hadintr == 0 && isatty(0)) {
+ if (value("ignore") != NULL) {
+ puts("@");
+ fflush(stdout);
+ clearerr(stdin);
+ } else {
+ fflush(stdout);
+ fputs("\n(Interrupt -- one more to kill letter)\n",
+ stderr);
+ hadintr++;
+ }
+ return(0);
+ }
+ fflush(stdout);
+ rewind(collf);
+ if (value("nosave") == NULL)
+ savedeadletter(collf);
+ return(1);
+}
+
+void
+savedeadletter(FILE *fp)
+{
+ FILE *dbuf;
+ int c;
+ char *cp;
+
+ if (fsize(fp) == 0)
+ return;
+ cp = getdeadletter();
+ c = umask(077);
+ dbuf = Fopen(cp, "a");
+ (void)umask(c);
+ if (dbuf == NULL)
+ return;
+ while ((c = getc(fp)) != EOF)
+ (void)putc(c, dbuf);
+ (void)Fclose(dbuf);
+ rewind(fp);
+}
+
+int
+gethfromtty(struct header *hp, int gflags)
+{
+
+ hadintr = 0;
+ while (grabh(hp, gflags) != 0) {
+ if (collabort())
+ return(-1);
+ }
+ return(0);
+}
diff --git a/def.h b/def.h
@@ -0,0 +1,265 @@
+/* $OpenBSD: def.h,v 1.16 2015/10/13 08:49:51 guenther Exp $ */
+/* $NetBSD: def.h,v 1.9 1996/12/28 07:11:00 tls Exp $ */
+
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS 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 REGENTS OR 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.
+ *
+ * @(#)def.h 8.4 (Berkeley) 4/20/95
+ * $OpenBSD: def.h,v 1.16 2015/10/13 08:49:51 guenther Exp $
+ */
+
+/*
+ * Mail -- a mail program
+ *
+ * Author: Kurt Shoens (UCB) March 25, 1978
+ */
+
+#ifndef MAIL_DEF_H
+#define MAIL_DEF_H
+
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <limits.h>
+#include <vis.h>
+#include "pathnames.h"
+
+#define APPEND /* New mail goes to end of mailbox */
+
+#define ESCAPE '~' /* Default escape for sending */
+#define NMLSIZE 1024 /* max names in a message list */
+#define PATHSIZE PATH_MAX /* Size of pathnames throughout */
+#define HSHSIZE 59 /* Hash size for aliases and vars */
+#define LINESIZE BUFSIZ /* max readable line width */
+#define STRINGSIZE ((unsigned) 128)/* Dynamic allocation units */
+#define MAXARGC 1024 /* Maximum list of raw strings */
+#define MAXEXP 25 /* Maximum expansion of aliases */
+
+#define equal(a, b) (strcmp(a,b)==0)/* A nice function to string compare */
+
+struct message {
+ short m_flag; /* flags, see below */
+ int m_offset; /* offset in block of message */
+ int m_block; /* block number of this message */
+ int m_size; /* Bytes in the message */
+ int m_lines; /* Lines in the message */
+};
+
+/*
+ * flag bits.
+ */
+#define MUSED (1<<0) /* entry is used, but this bit isn't */
+#define MDELETED (1<<1) /* entry has been deleted */
+#define MSAVED (1<<2) /* entry has been saved */
+#define MTOUCH (1<<3) /* entry has been noticed */
+#define MPRESERVE (1<<4) /* keep entry in sys mailbox */
+#define MMARK (1<<5) /* message is marked! */
+#define MODIFY (1<<6) /* message has been modified */
+#define MNEW (1<<7) /* message has never been seen */
+#define MREAD (1<<8) /* message has been read sometime. */
+#define MSTATUS (1<<9) /* message status has changed */
+#define MBOX (1<<10) /* Send this to mbox, regardless */
+
+/*
+ * Given a file address, determine the block number it represents.
+ */
+#define blockof(off) ((int) ((off) / 4096))
+#define offsetof(off) ((int) ((off) % 4096))
+#define positionof(block, offset) ((off_t)(block) * 4096 + (offset))
+
+/*
+ * Format of the command description table.
+ * The actual table is declared and initialized
+ * in lex.c
+ */
+struct cmd {
+ char *c_name; /* Name of command */
+ union {
+ int (*c_func1)(void *);
+ int (*c_func2)(void *, void *);
+ } cfunc; /* Implementor of the command */
+#define c_func cfunc.c_func1
+#define c_func2 cfunc.c_func2
+ short c_argtype; /* Type of arglist (see below) */
+ short c_msgflag; /* Required flags of messages */
+ short c_msgmask; /* Relevant flags of messages */
+};
+
+/* Yechh, can't initialize unions */
+#define c_minargs c_msgflag /* Minimum argcount for RAWLIST */
+#define c_maxargs c_msgmask /* Max argcount for RAWLIST */
+
+/*
+ * Argument types.
+ */
+#define MSGLIST 0x0001 /* Message list type */
+#define STRLIST 0x0002 /* A pure string */
+#define RAWLIST 0x0004 /* Shell string list */
+#define NOLIST 0x0008 /* Just plain 0 */
+#define NDMLIST 0x0010 /* Message list, no defaults */
+
+#define P 0x0020 /* Autoprint dot after command */
+#define I 0x0040 /* Interactive command bit */
+#define M 0x0080 /* Legal from send mode bit */
+#define W 0x0100 /* Illegal when read only bit */
+#define F 0x0200 /* Is a conditional command */
+#define T 0x0400 /* Is a transparent command */
+#define R 0x0800 /* Cannot be called from collect */
+
+/*
+ * Oft-used mask values
+ */
+#define MMNORM (MDELETED|MSAVED)/* Look at both save and delete bits */
+#define MMNDEL MDELETED /* Look only at deleted bit */
+
+/*
+ * Structure used to return a break down of a head
+ * line (hats off to Bill Joy!)
+ */
+struct headline {
+ char *l_from; /* The name of the sender */
+ char *l_tty; /* His tty string (if any) */
+ char *l_date; /* The entire date string */
+};
+
+#define GTO 1 /* Grab To: line */
+#define GSUBJECT 2 /* Likewise, Subject: line */
+#define GCC 4 /* And the Cc: line */
+#define GBCC 8 /* And also the Bcc: line */
+#define GMASK (GTO|GSUBJECT|GCC|GBCC)
+ /* Mask of places from whence */
+
+#define GNL 16 /* Print blank line after */
+#define GDEL 32 /* Entity removed from list */
+#define GCOMMA 64 /* detract puts in commas */
+
+/*
+ * Structure used to pass about the current
+ * state of the user-typed message header.
+ */
+struct header {
+ struct name *h_to; /* Dynamic "To:" string */
+ char *h_from; /* User-specified "From:" string */
+ char *h_subject; /* Subject string */
+ struct name *h_cc; /* Carbon copies string */
+ struct name *h_bcc; /* Blind carbon copies */
+ struct name *h_smopts; /* Sendmail options */
+};
+
+/*
+ * Structure of namelist nodes used in processing
+ * the recipients of mail and aliases and all that
+ * kind of stuff.
+ */
+struct name {
+ struct name *n_flink; /* Forward link in list. */
+ struct name *n_blink; /* Backward list link */
+ short n_type; /* From which list it came */
+ char *n_name; /* This fella's name */
+};
+
+/*
+ * Structure of a variable node. All variables are
+ * kept on a singly-linked list of these, rooted by
+ * "variables"
+ */
+
+struct var {
+ struct var *v_link; /* Forward link to next variable */
+ char *v_name; /* The variable's name */
+ char *v_value; /* And it's current value */
+};
+
+struct group {
+ struct group *ge_link; /* Next person in this group */
+ char *ge_name; /* This person's user name */
+};
+
+struct grouphead {
+ struct grouphead *g_link; /* Next grouphead in list */
+ char *g_name; /* Name of this group */
+ struct group *g_list; /* Users in group. */
+};
+
+/*
+ * Structure of the hash table of ignored header fields
+ */
+struct ignoretab {
+ int i_count; /* Number of entries */
+ struct ignore {
+ struct ignore *i_link; /* Next ignored field in bucket */
+ char *i_field; /* This ignored field */
+ } *i_head[HSHSIZE];
+};
+
+/*
+ * Token values returned by the scanner used for argument lists.
+ * Also, sizes of scanner-related things.
+ */
+#define TEOL 0 /* End of the command line */
+#define TNUMBER 1 /* A message number */
+#define TDASH 2 /* A simple dash */
+#define TSTRING 3 /* A string (possibly containing -) */
+#define TDOT 4 /* A "." */
+#define TUP 5 /* An "^" */
+#define TDOLLAR 6 /* A "$" */
+#define TSTAR 7 /* A "*" */
+#define TOPEN 8 /* An '(' */
+#define TCLOSE 9 /* A ')' */
+#define TPLUS 10 /* A '+' */
+#define TERROR 11 /* A lexical error */
+
+#define REGDEP 2 /* Maximum regret depth. */
+#define STRINGLEN 1024 /* Maximum length of string token */
+
+/*
+ * Constants for conditional commands.
+ * These describe whether we should be executing stuff or not.
+ */
+#define CANY 0 /* Execute in send or receive mode */
+#define CRCV 1 /* Execute in receive mode only */
+#define CSEND 2 /* Execute in send mode only */
+
+/*
+ * Truncate a file to the last character written. This is
+ * useful just before closing an old file that was opened
+ * for read/write.
+ */
+#define trunc(stream) do { \
+ (void)fflush(stream); \
+ (void)ftruncate(fileno(stream), (off_t)ftell(stream)); \
+} while(0)
+
+#endif /* MAIL_DEF_H */
diff --git a/edit.c b/edit.c
@@ -0,0 +1,286 @@
+/* $OpenBSD: edit.c,v 1.21 2019/06/28 13:35:01 deraadt Exp $ */
+/* $NetBSD: edit.c,v 1.5 1996/06/08 19:48:20 christos Exp $ */
+
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS 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 REGENTS OR 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.
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "rcv.h"
+#include <errno.h>
+#include <fcntl.h>
+#include "extern.h"
+
+int editit(const char *, const char *);
+
+/*
+ * Mail -- a mail program
+ *
+ * Perform message editing functions.
+ */
+
+/*
+ * Edit a message list.
+ */
+int
+editor(void *v)
+{
+ int *msgvec = v;
+
+ return(edit1(msgvec, 'e'));
+}
+
+/*
+ * Invoke the visual editor on a message list.
+ */
+int
+visual(void *v)
+{
+ int *msgvec = v;
+
+ return(edit1(msgvec, 'v'));
+}
+
+/*
+ * Edit a message by writing the message into a funnily-named file
+ * (which should not exist) and forking an editor on it.
+ * We get the editor from the stuff above.
+ */
+int
+edit1(int *msgvec, int type)
+{
+ int nl = 0, c, i;
+ FILE *fp;
+ struct sigaction oact;
+ sigset_t oset;
+ struct message *mp;
+ off_t size;
+
+ /*
+ * Deal with each message to be edited . . .
+ */
+ for (i = 0; msgvec[i] && i < msgCount; i++) {
+ if (i > 0) {
+ char buf[100];
+ char *p;
+
+ printf("Edit message %d [ynq]? ", msgvec[i]);
+ if (fgets(buf, sizeof(buf), stdin) == NULL)
+ break;
+ for (p = buf; *p == ' ' || *p == '\t'; p++)
+ ;
+ if (*p == 'q')
+ break;
+ if (*p == 'n')
+ continue;
+ }
+ dot = mp = &message[msgvec[i] - 1];
+ touch(mp);
+ (void)ignoresig(SIGINT, &oact, &oset);
+ fp = run_editor(setinput(mp), (off_t)mp->m_size, type, readonly);
+ if (fp != NULL) {
+ (void)fseek(otf, 0L, SEEK_END);
+ size = ftell(otf);
+ mp->m_block = blockof(size);
+ mp->m_offset = offsetof(size);
+ mp->m_size = fsize(fp);
+ mp->m_lines = 0;
+ mp->m_flag |= MODIFY;
+ rewind(fp);
+ while ((c = getc(fp)) != EOF) {
+ if (c == '\n') {
+ mp->m_lines++;
+ nl++;
+ } else
+ nl = 0;
+ if (putc(c, otf) == EOF)
+ break;
+ }
+ for (; nl < 2; nl++) {
+ mp->m_lines++;
+ mp->m_size++;
+ putc('\n', otf);
+ }
+ if (ferror(otf))
+ warn("%s", tmpdir);
+ (void)Fclose(fp);
+ }
+ (void)sigprocmask(SIG_SETMASK, &oset, NULL);
+ (void)sigaction(SIGINT, &oact, NULL);
+ }
+ return(0);
+}
+
+/*
+ * Run an editor on the file at "fpp" of "size" bytes,
+ * and return a new file pointer.
+ * Signals must be handled by the caller.
+ * "Type" is 'e' for _PATH_EX, 'v' for _PATH_VI.
+ */
+FILE *
+run_editor(FILE *fp, off_t size, int type, int readonly)
+{
+ FILE *nf = NULL;
+ int t;
+ time_t modtime;
+ char *edit, tempname[PATHSIZE];
+ struct stat statb;
+
+ (void)snprintf(tempname, sizeof(tempname),
+ "%s/mail.ReXXXXXXXXXX", tmpdir);
+ if ((t = mkstemp(tempname)) == -1 ||
+ (nf = Fdopen(t, "w")) == NULL) {
+ warn("%s", tempname);
+ goto out;
+ }
+ if (readonly && fchmod(t, 0400) == -1) {
+ warn("%s", tempname);
+ (void)rm(tempname);
+ goto out;
+ }
+ if (size >= 0)
+ while (--size >= 0 && (t = getc(fp)) != EOF)
+ (void)putc(t, nf);
+ else
+ while ((t = getc(fp)) != EOF)
+ (void)putc(t, nf);
+ (void)fflush(nf);
+ if (fstat(fileno(nf), &statb) == -1)
+ modtime = 0;
+ else
+ modtime = statb.st_mtime;
+ if (ferror(nf)) {
+ (void)Fclose(nf);
+ warn("%s", tempname);
+ (void)rm(tempname);
+ nf = NULL;
+ goto out;
+ }
+ if (Fclose(nf) < 0) {
+ warn("%s", tempname);
+ (void)rm(tempname);
+ nf = NULL;
+ goto out;
+ }
+ nf = NULL;
+ if (type == 'e') {
+ edit = value("EDITOR");
+ if (edit == NULL || edit[0] == '\0')
+ edit = _PATH_EX;
+ } else {
+ edit = value("VISUAL");
+ if (edit == NULL || edit[0] == '\0')
+ edit = _PATH_VI;
+ }
+ if (editit(edit, tempname) == -1) {
+ (void)rm(tempname);
+ goto out;
+ }
+ /*
+ * If in read only mode or file unchanged, just remove the editor
+ * temporary and return.
+ */
+ if (readonly) {
+ (void)rm(tempname);
+ goto out;
+ }
+ if (stat(tempname, &statb) == -1) {
+ warn("%s", tempname);
+ goto out;
+ }
+ if (modtime == statb.st_mtime) {
+ (void)rm(tempname);
+ goto out;
+ }
+ /*
+ * Now switch to new file.
+ */
+ if ((nf = Fopen(tempname, "a+")) == NULL) {
+ warn("%s", tempname);
+ (void)rm(tempname);
+ goto out;
+ }
+ (void)rm(tempname);
+out:
+ return(nf);
+}
+
+/*
+ * Execute an editor on the specified pathname, which is interpreted
+ * from the shell. This means flags may be included.
+ *
+ * Returns -1 on error, or the exit value on success.
+ */
+int
+editit(const char *ed, const char *pathname)
+{
+ char *argp[] = {"sh", "-c", NULL, NULL}, *p;
+ sig_t sighup, sigint, sigquit, sigchld;
+ pid_t pid;
+ int saved_errno, st, ret = -1;
+
+ if (ed == NULL)
+ ed = getenv("VISUAL");
+ if (ed == NULL || ed[0] == '\0')
+ ed = getenv("EDITOR");
+ if (ed == NULL || ed[0] == '\0')
+ ed = _PATH_VI;
+ if (asprintf(&p, "%s %s", ed, pathname) == -1)
+ return (-1);
+ argp[2] = p;
+
+ sighup = signal(SIGHUP, SIG_IGN);
+ sigint = signal(SIGINT, SIG_IGN);
+ sigquit = signal(SIGQUIT, SIG_IGN);
+ sigchld = signal(SIGCHLD, SIG_DFL);
+ if ((pid = fork()) == -1)
+ goto fail;
+ if (pid == 0) {
+ execv(_PATH_BSHELL, argp);
+ _exit(127);
+ }
+ while (waitpid(pid, &st, 0) == -1)
+ if (errno != EINTR)
+ goto fail;
+ if (!WIFEXITED(st))
+ errno = EINTR;
+ else
+ ret = WEXITSTATUS(st);
+
+ fail:
+ saved_errno = errno;
+ (void)signal(SIGHUP, sighup);
+ (void)signal(SIGINT, sigint);
+ (void)signal(SIGQUIT, sigquit);
+ (void)signal(SIGCHLD, sigchld);
+ free(p);
+ errno = saved_errno;
+ return (ret);
+}
diff --git a/extern.h b/extern.h
@@ -0,0 +1,263 @@
+/* $OpenBSD: extern.h,v 1.29 2018/09/16 02:38:57 millert Exp $ */
+/* $NetBSD: extern.h,v 1.7 1997/07/09 05:22:00 mikel Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS 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 REGENTS OR 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.
+ *
+ * @(#)extern.h 8.2 (Berkeley) 4/20/95
+ * $OpenBSD: extern.h,v 1.29 2018/09/16 02:38:57 millert Exp $
+ */
+
+struct name;
+struct name *cat(struct name *, struct name *);
+struct name *delname(struct name *, const char *);
+struct name *elide(struct name *);
+struct name *extract(char *, int);
+struct grouphead;
+struct name *gexpand(struct name *, struct grouphead *, int, int);
+struct name *nalloc(char *, int);
+struct header;
+struct name *outof(struct name *, FILE *, struct header *);
+struct name *put(struct name *, struct name *);
+struct name *tailof(struct name *);
+struct name *usermap(struct name *);
+FILE *Fdopen(int, char *);
+FILE *Fopen(char *, char *);
+FILE *Popen(char *, char *);
+FILE *collect(struct header *, int);
+char *copy(char *, char *);
+char *copyin(char *, char **);
+char *detract(struct name *, int);
+char *expand(char *);
+char *getdeadletter(void);
+struct message;
+char *hfield(char *, struct message *);
+FILE *infix(struct header *, FILE *);
+char *ishfield(char *, char *, char *);
+char *name1(struct message *, int);
+char *nameof(struct message *, int);
+char *nextword(char *, char *);
+char *readtty(char *, char *);
+char *reedit(char *);
+FILE *run_editor(FILE *, off_t, int, int);
+char *salloc(int);
+char *savestr(const char *);
+FILE *setinput(struct message *);
+char *skin(char *);
+char *skip_comment(char *);
+char *snarf(char *, int *);
+const char
+ *username(void);
+char *value(char *);
+char *vcopy(char *);
+char *yankword(char *, char *);
+int Fclose(FILE *);
+int More(void *);
+int Pclose(FILE *);
+int Respond(void *);
+int Type(void *);
+int _Respond(int *);
+int _respond(int *);
+void alter(char *);
+int alternates(void *);
+void announce(void);
+int append(struct message *, FILE *);
+int argcount(char **);
+void assign(char *, char *);
+int bangexp(char *, size_t);
+int blankline(char *);
+int charcount(char *, int);
+int check(int, int);
+void clearnew(void);
+void close_all_files(void);
+int cmatch(char *, char *);
+int collabort(void);
+void commands(void);
+int copycmd(void *);
+int count(struct name *);
+int deletecmd(void *);
+int delm(int *);
+int deltype(void *);
+void demail(void);
+void dointr(void);
+int dosh(void *);
+int echo(void *);
+int edit1(int *, int);
+int editor(void *);
+int edstop(void);
+int elsecmd(void *);
+int endifcmd(void *);
+int evalcol(int);
+int execute(char *, int);
+int exwrite(char *, FILE *, int);
+void fail(char *, char *);
+int file(void *);
+struct grouphead *
+ findgroup(char *);
+void findmail(const char *, char *, int);
+void fioint(int);
+int first(int, int);
+void fixhead(struct header *, struct name *);
+void fmt(char *, struct name *, FILE *, int);
+int folders(void *);
+int forward(char *, FILE *, char *, int);
+void free_child(pid_t);
+int from(void *);
+off_t fsize(FILE *);
+int getfold(char *, int);
+int gethfield(FILE *, char *, int, char **);
+int gethfromtty(struct header *, int);
+int getmsglist(char *, int *, int);
+int getrawlist(char *, char **, int);
+int grabh(struct header *, int);
+int group(void *);
+int hash(char *);
+void hdrint(int);
+int headers(void *);
+int help(void *);
+void holdsigs(void);
+int ifcmd(void *);
+int igfield(void *);
+struct ignoretab;
+int ignore1(char **, struct ignoretab *, char *);
+int ignoresig(int, struct sigaction *, sigset_t *);
+int igshow(struct ignoretab *, char *);
+void intr(int);
+int inc(void *);
+int incfile(void);
+int isdate(char *);
+int isdir(char *);
+int isfileaddr(char *);
+int ishead(char *);
+int isign(char *, struct ignoretab *);
+int isprefix(char *, char *);
+size_t istrlcpy(char *, const char *, size_t);
+const struct cmd *
+ lex(char *);
+void load(char *);
+struct var *
+ lookup(char *);
+int mail(struct name *, struct name *, struct name *, struct name *,
+ char *, char *);
+void mail1(struct header *, int);
+void makemessage(FILE *, int);
+void mark(int);
+int markall(char *, int);
+int marknew(void *);
+int matchsender(char *, int);
+int matchsubj(char *, int);
+int mboxit(void *);
+int member(char *, struct ignoretab *);
+void mesedit(FILE *, int);
+void mespipe(FILE *, char *);
+int messize(void *);
+int metamess(int, int);
+int more(void *);
+int newfileinfo(int);
+int next(void *);
+int null(void *);
+struct headline;
+void parse(char *, struct headline *, char *);
+int pcmdlist(void *);
+int pdot(void *);
+int pipeit(void *, void *);
+void prepare_child(sigset_t *, int, int);
+int preserve(void *);
+void prettyprint(struct name *);
+void printgroup(char *);
+void printhead(int);
+int puthead(struct header *, FILE *, int);
+int putline(FILE *, char *, int);
+int pversion(void *);
+int quit(void);
+int quitcmd(void *);
+int readline(FILE *, char *, int, int *);
+void register_file(FILE *, int, pid_t);
+void regret(int);
+void relsesigs(void);
+int respond(void *);
+int retfield(void *);
+int rexit(void *);
+int rm(char *);
+int run_command(char *cmd, sigset_t *nset, int infd, int outfd, ...);
+int save(void *);
+int save1(char *, int, char *, struct ignoretab *);
+void savedeadletter(FILE *);
+int saveigfield(void *);
+int savemail(char *, FILE *);
+int saveretfield(void *);
+int scan(char **);
+void scaninit(void);
+int schdir(void *);
+int screensize(void);
+int scroll(void *);
+void sendint(int);
+int sendmessage(struct message *, FILE *, struct ignoretab *, char *);
+int sendmail(void *);
+int set(void *);
+int setfile(char *);
+void setmsize(int);
+void setptr(FILE *, off_t);
+void setscreensize(void);
+int shell(void *);
+void sigchild(int);
+void sort(char **);
+int source(void *);
+int spool_lock(void);
+int spool_unlock(void);
+void spreserve(void);
+void sreset(void);
+pid_t start_command(char *cmd, sigset_t *nset, int infd, int outfd, ...);
+pid_t start_commandv(char *, sigset_t *, int, int, __va_list);
+int statusput(struct message *, FILE *, char *);
+void stop(int);
+int stouch(void *);
+int swrite(void *);
+void tinit(void);
+int top(void *);
+void touch(struct message *);
+void ttyint(int);
+void ttystop(int);
+int type(void *);
+int type1(int *, char *, int, int);
+int undeletecmd(void *);
+void unmark(int);
+char **unpack(struct name *, struct name *);
+int unread(void *);
+void unregister_file(FILE *);
+int unset(void *);
+int unstack(void);
+void vfree(char *);
+int visual(void *);
+int wait_child(pid_t);
+int wait_command(int);
+int writeback(FILE *);
+
+extern char *__progname;
+extern char *tmpdir;
+extern const struct cmd *com; /* command we are running */
diff --git a/fio.c b/fio.c
@@ -0,0 +1,522 @@
+/* $OpenBSD: fio.c,v 1.38 2019/06/28 13:35:01 deraadt Exp $ */
+/* $NetBSD: fio.c,v 1.8 1997/07/07 22:57:55 phil Exp $ */
+
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS 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 REGENTS OR 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.
+ */
+
+#include "rcv.h"
+
+#include <unistd.h>
+#include <paths.h>
+#include <errno.h>
+#include <glob.h>
+#include "extern.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * File I/O.
+ */
+
+static volatile sig_atomic_t fiosignal;
+
+/*
+ * Wrapper for read() to catch EINTR.
+ */
+static ssize_t
+myread(int fd, char *buf, int len)
+{
+ ssize_t nread;
+
+ while ((nread = read(fd, buf, len)) == -1 && errno == EINTR)
+ ;
+ return(nread);
+}
+
+/*
+ * Set up the input pointers while copying the mail file into /tmp.
+ */
+void
+setptr(FILE *ibuf, off_t offset)
+{
+ int c, count;
+ char *cp, *cp2;
+ struct message this;
+ FILE *mestmp;
+ int maybe, inhead, omsgCount;
+ char linebuf[LINESIZE], pathbuf[PATHSIZE];
+
+ /* Get temporary file. */
+ (void)snprintf(pathbuf, sizeof(pathbuf), "%s/mail.XXXXXXXXXX", tmpdir);
+ if ((c = mkstemp(pathbuf)) == -1 || (mestmp = Fdopen(c, "r+")) == NULL)
+ err(1, "can't open %s", pathbuf);
+ (void)rm(pathbuf);
+
+ if (offset == 0) {
+ msgCount = 0;
+ } else {
+ /* Seek into the file to get to the new messages */
+ (void)fseeko(ibuf, offset, SEEK_SET);
+ /*
+ * We need to make "offset" a pointer to the end of
+ * the temp file that has the copy of the mail file.
+ * If any messages have been edited, this will be
+ * different from the offset into the mail file.
+ */
+ (void)fseeko(otf, (off_t)0, SEEK_END);
+ offset = ftell(otf);
+ }
+ omsgCount = msgCount;
+ maybe = 1;
+ inhead = 0;
+ this.m_flag = MUSED|MNEW;
+ this.m_size = 0;
+ this.m_lines = 0;
+ this.m_block = 0;
+ this.m_offset = 0;
+ for (;;) {
+ if (fgets(linebuf, sizeof(linebuf), ibuf) == NULL) {
+ if (append(&this, mestmp))
+ err(1, "temporary file");
+ makemessage(mestmp, omsgCount);
+ return;
+ }
+ count = strlen(linebuf);
+ /*
+ * Transforms lines ending in <CR><LF> to just <LF>.
+ * This allows mail to be able to read Eudora mailboxes
+ * that reside on a DOS partition.
+ */
+ if (count >= 2 && linebuf[count-1] == '\n' &&
+ linebuf[count - 2] == '\r') {
+ linebuf[count - 2] = '\n';
+ linebuf[count - 1] = '\0';
+ count--;
+ }
+
+ (void)fwrite(linebuf, sizeof(*linebuf), count, otf);
+ if (ferror(otf))
+ err(1, "%s", pathbuf);
+ if (count && linebuf[count - 1] == '\n')
+ linebuf[count - 1] = '\0';
+ if (maybe && linebuf[0] == 'F' && ishead(linebuf)) {
+ msgCount++;
+ if (append(&this, mestmp))
+ err(1, "temporary file");
+ this.m_flag = MUSED|MNEW;
+ this.m_size = 0;
+ this.m_lines = 0;
+ this.m_block = blockof(offset);
+ this.m_offset = offsetof(offset);
+ inhead = 1;
+ } else if (linebuf[0] == 0) {
+ inhead = 0;
+ } else if (inhead) {
+ for (cp = linebuf, cp2 = "status";; cp++) {
+ if ((c = (unsigned char)*cp2++) == 0) {
+ while (isspace((unsigned char)*cp++))
+ ;
+ if (cp[-1] != ':')
+ break;
+ while ((c = (unsigned char)*cp++) != '\0')
+ if (c == 'R')
+ this.m_flag |= MREAD;
+ else if (c == 'O')
+ this.m_flag &= ~MNEW;
+ inhead = 0;
+ break;
+ }
+ if (*cp != c && *cp != toupper(c))
+ break;
+ }
+ }
+ offset += count;
+ this.m_size += count;
+ this.m_lines++;
+ maybe = linebuf[0] == 0;
+ }
+}
+
+/*
+ * Drop the passed line onto the passed output buffer.
+ * If a write error occurs, return -1, else the count of
+ * characters written, including the newline if requested.
+ */
+int
+putline(FILE *obuf, char *linebuf, int outlf)
+{
+ int c;
+
+ c = strlen(linebuf);
+ (void)fwrite(linebuf, sizeof(*linebuf), c, obuf);
+ if (outlf) {
+ (void)putc('\n', obuf);
+ c++;
+ }
+ if (ferror(obuf))
+ return(-1);
+ return(c);
+}
+
+/*
+ * Read up a line from the specified input into the line
+ * buffer. Return the number of characters read. Do not
+ * include the newline (or carriage return) at the end.
+ */
+int
+readline(FILE *ibuf, char *linebuf, int linesize, int *signo)
+{
+ struct sigaction act;
+ struct sigaction savetstp;
+ struct sigaction savettou;
+ struct sigaction savettin;
+ struct sigaction saveint;
+ struct sigaction savehup;
+ sigset_t oset;
+ int n;
+
+ /*
+ * Setup signal handlers if the caller asked us to catch signals.
+ * Note that we do not restart system calls since we need the
+ * read to be interruptible.
+ */
+ if (signo) {
+ fiosignal = 0;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ act.sa_handler = fioint;
+ if (sigaction(SIGINT, NULL, &saveint) == 0 &&
+ saveint.sa_handler != SIG_IGN) {
+ (void)sigaction(SIGINT, &act, &saveint);
+ (void)sigprocmask(SIG_UNBLOCK, &intset, &oset);
+ }
+ if (sigaction(SIGHUP, NULL, &savehup) == 0 &&
+ savehup.sa_handler != SIG_IGN)
+ (void)sigaction(SIGHUP, &act, &savehup);
+ (void)sigaction(SIGTSTP, &act, &savetstp);
+ (void)sigaction(SIGTTOU, &act, &savettou);
+ (void)sigaction(SIGTTIN, &act, &savettin);
+ }
+
+ clearerr(ibuf);
+ if (fgets(linebuf, linesize, ibuf) == NULL) {
+ if (ferror(ibuf))
+ clearerr(ibuf);
+ n = -1;
+ } else {
+ n = strlen(linebuf);
+ if (n > 0 && linebuf[n - 1] == '\n')
+ linebuf[--n] = '\0';
+ if (n > 0 && linebuf[n - 1] == '\r')
+ linebuf[--n] = '\0';
+ }
+
+ if (signo) {
+ (void)sigprocmask(SIG_SETMASK, &oset, NULL);
+ (void)sigaction(SIGINT, &saveint, NULL);
+ (void)sigaction(SIGHUP, &savehup, NULL);
+ (void)sigaction(SIGTSTP, &savetstp, NULL);
+ (void)sigaction(SIGTTOU, &savettou, NULL);
+ (void)sigaction(SIGTTIN, &savettin, NULL);
+ *signo = fiosignal;
+ }
+
+ return(n);
+}
+
+/*
+ * Return a file buffer all ready to read up the
+ * passed message pointer.
+ */
+FILE *
+setinput(struct message *mp)
+{
+
+ fflush(otf);
+ if (fseek(itf, (long)positionof(mp->m_block, mp->m_offset), SEEK_SET)
+ == -1)
+ err(1, "fseek");
+ return(itf);
+}
+
+/*
+ * Take the data out of the passed ghost file and toss it into
+ * a dynamically allocated message structure.
+ */
+void
+makemessage(FILE *f, int omsgCount)
+{
+ size_t size;
+ struct message *nmessage;
+
+ size = (msgCount + 1) * sizeof(struct message);
+ nmessage = realloc(message, size);
+ if (nmessage == 0)
+ err(1, "realloc");
+ if (omsgCount == 0 || message == NULL)
+ dot = nmessage;
+ else
+ dot = nmessage + (dot - message);
+ message = nmessage;
+ size -= (omsgCount + 1) * sizeof(struct message);
+ fflush(f);
+ (void)lseek(fileno(f), (off_t)sizeof(*message), SEEK_SET);
+ if (myread(fileno(f), (void *) &message[omsgCount], size) != size)
+ errx(1, "Message temporary file corrupted");
+ message[msgCount].m_size = 0;
+ message[msgCount].m_lines = 0;
+ (void)Fclose(f);
+}
+
+/*
+ * Append the passed message descriptor onto the temp file.
+ * If the write fails, return 1, else 0
+ */
+int
+append(struct message *mp, FILE *f)
+{
+
+ return(fwrite((char *) mp, sizeof(*mp), 1, f) != 1);
+}
+
+/*
+ * Delete or truncate a file, but only if the file is a plain file.
+ */
+int
+rm(char *name)
+{
+ struct stat sb;
+
+ if (stat(name, &sb) == -1)
+ return(-1);
+ if (!S_ISREG(sb.st_mode)) {
+ errno = EISDIR;
+ return(-1);
+ }
+ if (unlink(name) == -1) {
+ if (errno == EPERM)
+ return(truncate(name, (off_t)0));
+ else
+ return(-1);
+ }
+ return(0);
+}
+
+static int sigdepth; /* depth of holdsigs() */
+static sigset_t nset, oset;
+/*
+ * Hold signals SIGHUP, SIGINT, and SIGQUIT.
+ */
+void
+holdsigs(void)
+{
+
+ if (sigdepth++ == 0) {
+ sigemptyset(&nset);
+ sigaddset(&nset, SIGHUP);
+ sigaddset(&nset, SIGINT);
+ sigaddset(&nset, SIGQUIT);
+ sigprocmask(SIG_BLOCK, &nset, &oset);
+ }
+}
+
+/*
+ * Release signals SIGHUP, SIGINT, and SIGQUIT.
+ */
+void
+relsesigs(void)
+{
+
+ if (--sigdepth == 0)
+ sigprocmask(SIG_SETMASK, &oset, NULL);
+}
+
+/*
+ * Unblock and ignore a signal
+ */
+int
+ignoresig(int sig, struct sigaction *oact, sigset_t *oset)
+{
+ struct sigaction act;
+ sigset_t nset;
+ int error;
+
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+ act.sa_handler = SIG_IGN;
+ error = sigaction(sig, &act, oact);
+
+ if (error == 0) {
+ sigemptyset(&nset);
+ sigaddset(&nset, sig);
+ (void)sigprocmask(SIG_UNBLOCK, &nset, oset);
+ } else if (oset != NULL)
+ (void)sigprocmask(SIG_BLOCK, NULL, oset);
+
+ return(error);
+}
+
+/*
+ * Determine the size of the file possessed by
+ * the passed buffer.
+ */
+off_t
+fsize(FILE *iob)
+{
+ struct stat sbuf;
+
+ if (fstat(fileno(iob), &sbuf) == -1)
+ return(0);
+ return(sbuf.st_size);
+}
+
+/*
+ * Evaluate the string given as a new mailbox name.
+ * Supported meta characters:
+ * % for my system mail box
+ * %user for user's system mail box
+ * # for previous file
+ * & invoker's mbox file
+ * +file file in folder directory
+ * any shell meta character
+ * Return the file name as a dynamic string.
+ */
+char *
+expand(char *name)
+{
+ const int flags = GLOB_BRACE|GLOB_TILDE|GLOB_NOSORT;
+ char xname[PATHSIZE];
+ char cmdbuf[PATHSIZE]; /* also used for file names */
+ char *match = NULL;
+ glob_t names;
+
+ /*
+ * The order of evaluation is "%" and "#" expand into constants.
+ * "&" can expand into "+". "+" can expand into shell meta characters.
+ * Shell meta characters expand into constants.
+ * This way, we make no recursive expansion.
+ */
+ switch (*name) {
+ case '%':
+ findmail(name[1] ? name + 1 : myname, xname, sizeof(xname));
+ return(savestr(xname));
+ case '#':
+ if (name[1] != 0)
+ break;
+ if (prevfile[0] == 0) {
+ puts("No previous file");
+ return(NULL);
+ }
+ return(savestr(prevfile));
+ case '&':
+ if (name[1] == 0 && (name = value("MBOX")) == NULL)
+ name = "~/mbox";
+ /* fall through */
+ }
+ if (name[0] == '+' && getfold(cmdbuf, sizeof(cmdbuf)) >= 0) {
+ (void)snprintf(xname, sizeof(xname), "%s/%s", cmdbuf, name + 1);
+ name = savestr(xname);
+ }
+ /* catch the most common shell meta character */
+ if (name[0] == '~' && homedir && (name[1] == '/' || name[1] == '\0')) {
+ (void)snprintf(xname, sizeof(xname), "%s%s", homedir, name + 1);
+ name = savestr(xname);
+ }
+ if (strpbrk(name, "~{[*?\\") == NULL)
+ return(savestr(name));
+
+ /* XXX - does not expand enviroment variables. */
+ switch (glob(name, flags, NULL, &names)) {
+ case 0:
+ if (names.gl_pathc == 1)
+ match = savestr(names.gl_pathv[0]);
+ else
+ fprintf(stderr, "\"%s\": Ambiguous.\n", name);
+ break;
+ case GLOB_NOSPACE:
+ fprintf(stderr, "\"%s\": Out of memory.\n", name);
+ break;
+ case GLOB_NOMATCH:
+ fprintf(stderr, "\"%s\": No match.\n", name);
+ break;
+ default:
+ fprintf(stderr, "\"%s\": Expansion failed.\n", name);
+ break;
+ }
+ globfree(&names);
+ return(match);
+}
+
+/*
+ * Determine the current folder directory name.
+ */
+int
+getfold(char *name, int namelen)
+{
+ char *folder;
+
+ if ((folder = value("folder")) == NULL)
+ return(-1);
+ if (*folder == '/')
+ strlcpy(name, folder, namelen);
+ else
+ (void)snprintf(name, namelen, "%s/%s", homedir ? homedir : ".",
+ folder);
+ return(0);
+}
+
+/*
+ * Return the name of the dead.letter file.
+ */
+char *
+getdeadletter(void)
+{
+ char *cp;
+
+ if ((cp = value("DEAD")) == NULL || (cp = expand(cp)) == NULL)
+ cp = expand("~/dead.letter");
+ else if (*cp != '/') {
+ char buf[PATHSIZE];
+
+ (void)snprintf(buf, sizeof(buf), "~/%s", cp);
+ cp = expand(buf);
+ }
+ return(cp);
+}
+
+/*
+ * Signal handler used by readline() to catch SIGINT, SIGHUP, SIGTSTP,
+ * SIGTTOU, SIGTTIN.
+ */
+void
+fioint(int s)
+{
+
+ fiosignal = s;
+}
diff --git a/glob.h b/glob.h
@@ -0,0 +1,95 @@
+/* $OpenBSD: glob.h,v 1.10 2021/01/26 18:21:47 deraadt Exp $ */
+/* $NetBSD: glob.h,v 1.4 1996/06/08 19:48:25 christos Exp $ */
+
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS 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 REGENTS OR 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.
+ *
+ * from: @(#)glob.h 8.1 (Berkeley) 6/6/93
+ * $NetBSD: glob.h,v 1.4 1996/06/08 19:48:25 christos Exp $
+ */
+
+/*
+ * A bunch of global variable declarations lie herein.
+ * def.h must be included first.
+ */
+extern int msgCount; /* Count of messages read in */
+extern int rcvmode; /* True if receiving mail */
+extern int sawcom; /* Set after first command */
+extern int senderr; /* An error while checking */
+extern int edit; /* Indicates editing a file */
+extern int readonly; /* Will be unable to rewrite file */
+extern int noreset; /* String resets suspended */
+extern int sourcing; /* Currently reading variant file */
+extern int loading; /* Loading user definitions */
+extern int cond; /* Current state of conditional exc. */
+extern FILE *itf; /* Input temp file buffer */
+extern FILE *otf; /* Output temp file buffer */
+extern int image; /* File descriptor for image of msg */
+extern FILE *input; /* Current command input file */
+extern char mailname[PATHSIZE]; /* Name of current file */
+extern char prevfile[PATHSIZE]; /* Name of previous file */
+extern char *homedir; /* Path name of home directory */
+extern const char
+ *myname; /* My login name */
+extern off_t mailsize; /* Size of system mailbox */
+extern int lexnumber; /* Number of TNUMBER from scan() */
+extern char lexstring[STRINGLEN]; /* String from TSTRING, scan() */
+extern int regretp; /* Pointer to TOS of regret tokens */
+extern int regretstack[REGDEP]; /* Stack of regretted tokens */
+extern char *string_stack[REGDEP]; /* Stack of regretted strings */
+extern int numberstack[REGDEP]; /* Stack of regretted numbers */
+extern struct message *dot; /* Pointer to current message */
+extern struct message *message; /* The actual message structure */
+extern struct var *variables[HSHSIZE]; /* Pointer to active var list */
+extern struct grouphead *groups[HSHSIZE];/* Pointer to active groups */
+extern struct ignoretab ignore[2]; /* ignored and retained fields
+ 0 is ignore, 1 is retain */
+extern struct ignoretab saveignore[2]; /* ignored and retained fields
+ on save to folder */
+extern struct ignoretab ignoreall[2]; /* special, ignore all headers */
+extern char **altnames; /* List of alternate names for user */
+extern int debug; /* Debug flag set */
+extern int screenwidth; /* Screen width, or best guess */
+extern int screenheight; /* Screen height, or best guess,
+ for "header" command */
+extern int realscreenheight; /* the real screen height */
+extern int uflag; /* Are we in -u mode? */
+extern sigset_t intset; /* Signal set that is just SIGINT */
+
+/*
+ * The pointers for the string allocation routines,
+ * there are NSPACE independent areas.
+ * The first holds STRINGSIZE bytes, the next
+ * twice as much, and so on.
+ */
+#define NSPACE 25 /* Total number of string spaces */
+extern struct strings {
+ char *s_topFree; /* Beginning of this area */
+ char *s_nextFree; /* Next alloctable place here */
+ unsigned s_nleft; /* Number of bytes left here */
+} stringdope[NSPACE];
diff --git a/head.c b/head.c
@@ -0,0 +1,272 @@
+/* $OpenBSD: head.c,v 1.12 2014/01/17 18:42:30 okan Exp $ */
+/* $NetBSD: head.c,v 1.6 1996/12/28 07:11:03 tls Exp $ */
+
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS 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 REGENTS OR 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.
+ */
+
+#include "rcv.h"
+#include "extern.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * Routines for processing and detecting headlines.
+ */
+
+/*
+ * See if the passed line buffer is a mail header.
+ * Return true if yes. Note the extreme pains to
+ * accommodate all funny formats.
+ */
+int
+ishead(char *linebuf)
+{
+ char *cp;
+ struct headline hl;
+ char parbuf[BUFSIZ];
+
+ cp = linebuf;
+ if (*cp++ != 'F' || *cp++ != 'r' || *cp++ != 'o' || *cp++ != 'm' ||
+ *cp++ != ' ')
+ return(0);
+ parse(linebuf, &hl, parbuf);
+ if (hl.l_from == NULL || hl.l_date == NULL) {
+ fail(linebuf, "No from or date field");
+ return(0);
+ }
+ if (!isdate(hl.l_date)) {
+ fail(linebuf, "Date field not legal date");
+ return(0);
+ }
+ /*
+ * I guess we got it!
+ */
+ return(1);
+}
+
+/*ARGSUSED*/
+void
+fail(char *linebuf, char *reason)
+{
+
+ /*
+ if (value("debug") == NULL)
+ return;
+ fprintf(stderr, "\"%s\"\nnot a header because %s\n", linebuf, reason);
+ */
+}
+
+/*
+ * Split a headline into its useful components.
+ * Copy the line into dynamic string space, then set
+ * pointers into the copied line in the passed headline
+ * structure. Actually, it scans.
+ */
+void
+parse(char *line, struct headline *hl, char *pbuf)
+{
+ char *cp, *sp;
+ char word[LINESIZE];
+
+ hl->l_from = NULL;
+ hl->l_tty = NULL;
+ hl->l_date = NULL;
+ cp = line;
+ sp = pbuf;
+ /*
+ * Skip over "From" first.
+ */
+ cp = nextword(cp, word);
+ cp = nextword(cp, word);
+ if (*word)
+ hl->l_from = copyin(word, &sp);
+ if (cp != NULL && strncmp(cp, "tty", 3) == 0) {
+ cp = nextword(cp, word);
+ hl->l_tty = copyin(word, &sp);
+ }
+ if (cp != NULL)
+ hl->l_date = copyin(cp, &sp);
+}
+
+/*
+ * Copy the string on the left into the string on the right
+ * and bump the right (reference) string pointer by the length.
+ * Thus, dynamically allocate space in the right string, copying
+ * the left string into it.
+ */
+char *
+copyin(char *src, char **space)
+{
+ char *cp, *top;
+
+ top = cp = *space;
+ while ((*cp++ = *src++) != '\0')
+ ;
+ *space = cp;
+ return(top);
+}
+
+/*
+ * Test to see if the passed string is a ctime(3) generated
+ * date string as documented in the manual. The template
+ * below is used as the criterion of correctness.
+ * Also, we check for a possible trailing time zone using
+ * the tmztype template.
+ */
+
+/*
+ * 'A' An upper case char
+ * 'a' A lower case char
+ * ' ' A space
+ * '0' A digit
+ * 'O' A digit or space
+ * 'p' A punctuation char
+ * 'P' A punctuation char or space
+ * ':' A colon
+ * 'N' A new line
+ */
+
+/*
+ * Yuck. If the mail file is created by Sys V (Solaris),
+ * there are no seconds in the time...
+ */
+
+/*
+ * If the mail is created by another program such as imapd, it might
+ * have timezone as <-|+>nnnn (-0800 for instance) at the end.
+ */
+
+static char *date_formats[] = {
+ "Aaa Aaa O0 00:00:00 0000", /* Mon Jan 01 23:59:59 2001 */
+ "Aaa Aaa O0 00:00:00 AAA 0000", /* Mon Jan 01 23:59:59 PST 2001 */
+ "Aaa Aaa O0 00:00:00 0000 p0000", /* Mon Jan 01 23:59:59 2001 -0800 */
+ "Aaa Aaa O0 00:00 0000", /* Mon Jan 01 23:59 2001 */
+ "Aaa Aaa O0 00:00 AAA 0000", /* Mon Jan 01 23:59 PST 2001 */
+ "Aaa Aaa O0 00:00 0000 p0000", /* Mon Jan 01 23:59 2001 -0800 */
+ ""
+};
+
+int
+isdate(char *date)
+{
+ int i;
+
+ for(i = 0; *date_formats[i]; i++) {
+ if (cmatch(date, date_formats[i]))
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Match the given string (cp) against the given template (tp).
+ * Return 1 if they match, 0 if they don't
+ */
+int
+cmatch(char *cp, char *tp)
+{
+
+ while (*cp && *tp)
+ switch (*tp++) {
+ case 'a':
+ if (!islower((unsigned char)*cp++))
+ return(0);
+ break;
+ case 'A':
+ if (!isupper((unsigned char)*cp++))
+ return(0);
+ break;
+ case ' ':
+ if (*cp++ != ' ')
+ return(0);
+ break;
+ case '0':
+ if (!isdigit((unsigned char)*cp++))
+ return(0);
+ break;
+ case 'O':
+ if (*cp != ' ' && !isdigit((unsigned char)*cp))
+ return(0);
+ cp++;
+ break;
+ case 'p':
+ if (!ispunct((unsigned char)*cp++))
+ return(0);
+ break;
+ case 'P':
+ if (*cp != ' ' && !ispunct((unsigned char)*cp))
+ return(0);
+ cp++;
+ break;
+ case ':':
+ if (*cp++ != ':')
+ return(0);
+ break;
+ case 'N':
+ if (*cp++ != '\n')
+ return(0);
+ break;
+ }
+ if (*cp || *tp)
+ return(0);
+ return(1);
+}
+
+/*
+ * Collect a liberal (space, tab delimited) word into the word buffer
+ * passed. Also, return a pointer to the next word following that,
+ * or NULL if none follow.
+ */
+char *
+nextword(char *wp, char *wbuf)
+{
+ int c;
+
+ if (wp == NULL) {
+ *wbuf = 0;
+ return(NULL);
+ }
+ while ((c = (unsigned char)*wp++) && c != ' ' && c != '\t') {
+ *wbuf++ = c;
+ if (c == '"') {
+ while ((c = (unsigned char)*wp++) && c != '"')
+ *wbuf++ = c;
+ if (c == '"')
+ *wbuf++ = c;
+ else
+ wp--;
+ }
+ }
+ *wbuf = '\0';
+ for (; c == ' ' || c == '\t'; c = (unsigned char)*wp++)
+ ;
+ if (c == 0)
+ return(NULL);
+ return(wp - 1);
+}
diff --git a/lex.c b/lex.c
@@ -0,0 +1,714 @@
+/* $OpenBSD: lex.c,v 1.42 2021/10/24 21:24:16 deraadt Exp $ */
+/* $NetBSD: lex.c,v 1.10 1997/05/17 19:55:13 pk Exp $ */
+
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS 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 REGENTS OR 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.
+ */
+
+#include "rcv.h"
+#include <errno.h>
+#include <fcntl.h>
+#include "extern.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * Lexical processing of commands.
+ */
+
+char *prompt = "& ";
+
+const struct cmd *com; /* command we are running */
+
+/*
+ * Set up editing on the given file name.
+ * If the first character of name is %, we are considered to be
+ * editing the file, otherwise we are reading our mail which has
+ * signficance for mbox and so forth.
+ */
+int
+setfile(char *name)
+{
+ FILE *ibuf;
+ int i, fd;
+ struct stat stb;
+ char isedit = *name != '%';
+ const char *who = name[1] ? name + 1 : myname;
+ char tempname[PATHSIZE];
+ static int shudclob;
+
+ if ((name = expand(name)) == NULL)
+ return(-1);
+
+ if ((ibuf = Fopen(name, "r")) == NULL) {
+ if (!isedit && errno == ENOENT)
+ goto nomail;
+ warn("%s", name);
+ return(-1);
+ }
+
+ if (fstat(fileno(ibuf), &stb) == -1) {
+ warn("fstat");
+ (void)Fclose(ibuf);
+ return(-1);
+ }
+
+ switch (stb.st_mode & S_IFMT) {
+ case S_IFDIR:
+ (void)Fclose(ibuf);
+ warnc(EISDIR, "%s", name);
+ return(-1);
+
+ case S_IFREG:
+ break;
+
+ default:
+ (void)Fclose(ibuf);
+ warnc(EINVAL, "%s", name);
+ return(-1);
+ }
+
+ /*
+ * Looks like all will be well. We must now relinquish our
+ * hold on the current set of stuff. Must hold signals
+ * while we are reading the new file, else we will ruin
+ * the message[] data structure.
+ */
+ holdsigs();
+ if (shudclob)
+ quit();
+
+ /*
+ * Copy the messages into /tmp
+ * and set pointers.
+ */
+ readonly = 0;
+ if ((i = open(name, O_WRONLY)) == -1)
+ readonly++;
+ else
+ (void)close(i);
+ if (shudclob) {
+ (void)fclose(itf);
+ (void)fclose(otf);
+ }
+ shudclob = 1;
+ edit = isedit;
+ strlcpy(prevfile, mailname, PATHSIZE);
+ if (name != mailname)
+ strlcpy(mailname, name, sizeof(mailname));
+ mailsize = fsize(ibuf);
+ (void)snprintf(tempname, sizeof(tempname),
+ "%s/mail.RxXXXXXXXXXX", tmpdir);
+ if ((fd = mkostemp(tempname, O_CLOEXEC)) == -1 ||
+ (otf = fdopen(fd, "w")) == NULL)
+ err(1, "%s", tempname);
+ if ((itf = fopen(tempname, "re")) == NULL)
+ err(1, "%s", tempname);
+ (void)rm(tempname);
+ setptr(ibuf, (off_t)0);
+ setmsize(msgCount);
+ /*
+ * New mail may have arrived while we were reading
+ * the mail file, so reset mailsize to be where
+ * we really are in the file...
+ */
+ mailsize = ftell(ibuf);
+ (void)Fclose(ibuf);
+ relsesigs();
+ sawcom = 0;
+ if (!edit && msgCount == 0) {
+nomail:
+ fprintf(stderr, "No mail for %s\n", who);
+ return(-1);
+ }
+ return(0);
+}
+
+/*
+ * Incorporate any new mail that has arrived since we first
+ * started reading mail.
+ */
+int
+incfile(void)
+{
+ int newsize;
+ int omsgCount = msgCount;
+ FILE *ibuf;
+
+ ibuf = Fopen(mailname, "r");
+ if (ibuf == NULL)
+ return(-1);
+ holdsigs();
+ if (!spool_lock()) {
+ (void)Fclose(ibuf);
+ relsesigs();
+ return(-1);
+ }
+ newsize = fsize(ibuf);
+ /* make sure mail box has grown and is non-empty */
+ if (newsize == 0 || newsize <= mailsize) {
+ (void)Fclose(ibuf);
+ spool_unlock();
+ relsesigs();
+ return(newsize == mailsize ? 0 : -1);
+ }
+ setptr(ibuf, mailsize);
+ setmsize(msgCount);
+ mailsize = ftell(ibuf);
+ (void)Fclose(ibuf);
+ spool_unlock();
+ relsesigs();
+ return(msgCount - omsgCount);
+}
+
+
+int *msgvec;
+int reset_on_stop; /* reset prompt if stopped */
+
+/*
+ * Interpret user commands one by one. If standard input is not a tty,
+ * print no prompt.
+ */
+void
+commands(void)
+{
+ int n, sig, *sigp;
+ int eofloop = 0;
+ char linebuf[LINESIZE];
+
+ prompt:
+ for (;;) {
+ /*
+ * Print the prompt, if needed. Clear out
+ * string space, and flush the output.
+ */
+ if (!sourcing && value("interactive") != NULL) {
+ if ((value("autoinc") != NULL) && (incfile() > 0))
+ puts("New mail has arrived.");
+ reset_on_stop = 1;
+ printf("%s", prompt);
+ }
+ fflush(stdout);
+ sreset();
+ /*
+ * Read a line of commands from the current input
+ * and handle end of file specially.
+ */
+ n = 0;
+ sig = 0;
+ sigp = sourcing ? NULL : &sig;
+ for (;;) {
+ if (readline(input, &linebuf[n], LINESIZE - n, sigp) < 0) {
+ if (sig) {
+ if (sig == SIGINT)
+ dointr();
+ else if (sig == SIGHUP)
+ /* nothing to do? */
+ exit(1);
+ else {
+ /* Stopped by job control */
+ (void)kill(0, sig);
+ if (reset_on_stop)
+ reset_on_stop = 0;
+ }
+ goto prompt;
+ }
+ if (n == 0)
+ n = -1;
+ break;
+ }
+ if ((n = strlen(linebuf)) == 0)
+ break;
+ n--;
+ if (linebuf[n] != '\\')
+ break;
+ linebuf[n++] = ' ';
+ }
+ reset_on_stop = 0;
+ if (n < 0) {
+ /* eof */
+ if (loading)
+ break;
+ if (sourcing) {
+ unstack();
+ continue;
+ }
+ if (value("interactive") != NULL &&
+ value("ignoreeof") != NULL &&
+ ++eofloop < 25) {
+ puts("Use \"quit\" to quit.");
+ continue;
+ }
+ break;
+ }
+ eofloop = 0;
+ if (execute(linebuf, 0))
+ break;
+ }
+}
+
+/*
+ * Execute a single command.
+ * Command functions return 0 for success, 1 for error, and -1
+ * for abort. A 1 or -1 aborts a load or source. A -1 aborts
+ * the interactive command loop.
+ * Contxt is non-zero if called while composing mail.
+ */
+int
+execute(char *linebuf, int contxt)
+{
+ char word[LINESIZE];
+ char *arglist[MAXARGC];
+ char *cp, *cp2;
+ int c, muvec[2];
+ int e = 1;
+
+ com = NULL;
+
+ /*
+ * Strip the white space away from the beginning
+ * of the command, then scan out a word, which
+ * consists of anything except digits and white space.
+ *
+ * Handle ! escapes differently to get the correct
+ * lexical conventions.
+ */
+ for (cp = linebuf; isspace((unsigned char)*cp); cp++)
+ ;
+ if (*cp == '!') {
+ if (sourcing) {
+ puts("Can't \"!\" while sourcing");
+ goto out;
+ }
+ shell(cp+1);
+ return(0);
+ }
+ cp2 = word;
+ while (*cp &&
+ strchr(" \t0123456789$^.:/-+*'\"", (unsigned char)*cp) == NULL)
+ *cp2++ = *cp++;
+ *cp2 = '\0';
+
+ /*
+ * Look up the command; if not found, bitch.
+ * Normally, a blank command would map to the
+ * first command in the table; while sourcing,
+ * however, we ignore blank lines to eliminate
+ * confusion.
+ */
+ if (sourcing && *word == '\0')
+ return(0);
+ com = lex(word);
+ if (com == NULL) {
+ printf("Unknown command: \"%s\"\n", word);
+ goto out;
+ }
+
+ /*
+ * See if we should execute the command -- if a conditional
+ * we always execute it, otherwise, check the state of cond.
+ */
+ if ((com->c_argtype & F) == 0)
+ if ((cond == CRCV && !rcvmode) || (cond == CSEND && rcvmode))
+ return(0);
+
+ /*
+ * Process the arguments to the command, depending
+ * on the type he expects. Default to an error.
+ * If we are sourcing an interactive command, it's
+ * an error.
+ */
+ if (!rcvmode && (com->c_argtype & M) == 0) {
+ printf("May not execute \"%s\" while sending\n",
+ com->c_name);
+ goto out;
+ }
+ if (sourcing && com->c_argtype & I) {
+ printf("May not execute \"%s\" while sourcing\n",
+ com->c_name);
+ goto out;
+ }
+ if (readonly && com->c_argtype & W) {
+ printf("May not execute \"%s\" -- message file is read only\n",
+ com->c_name);
+ goto out;
+ }
+ if (contxt && com->c_argtype & R) {
+ printf("Cannot recursively invoke \"%s\"\n", com->c_name);
+ goto out;
+ }
+ switch (com->c_argtype & ~(F|P|I|M|T|W|R)) {
+ case MSGLIST|STRLIST:
+ /*
+ * A message list defaulting to nearest forward
+ * legal message.
+ */
+ if (msgvec == 0) {
+ puts("Illegal use of \"message list\"");
+ break;
+ }
+ /*
+ * remove leading blanks.
+ */
+ while (isspace((unsigned char)*cp))
+ cp++;
+
+ if (isdigit((unsigned char)*cp) || *cp == ':') {
+ if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
+ break;
+ /* position to next space - past the message list */
+ while (!isspace((unsigned char)*cp))
+ cp++;
+ /* position to next non-space */
+ while (isspace((unsigned char)*cp))
+ cp++;
+ } else {
+ c = 0; /* no message list */
+ }
+
+ if (c == 0) {
+ *msgvec = first(com->c_msgflag,
+ com->c_msgmask);
+ msgvec[1] = 0;
+ }
+ if (*msgvec == 0) {
+ puts("No applicable messages");
+ break;
+ }
+ /*
+ * Just the straight string, with
+ * leading blanks removed.
+ */
+ while (isspace((unsigned char)*cp))
+ cp++;
+
+ e = (*com->c_func2)(msgvec, cp);
+ break;
+
+ case MSGLIST:
+ /*
+ * A message list defaulting to nearest forward
+ * legal message.
+ */
+ if (msgvec == NULL) {
+ puts("Illegal use of \"message list\"");
+ break;
+ }
+ if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
+ break;
+ if (c == 0) {
+ *msgvec = first(com->c_msgflag,
+ com->c_msgmask);
+ msgvec[1] = 0;
+ }
+ if (*msgvec == 0) {
+ puts("No applicable messages");
+ break;
+ }
+ e = (*com->c_func)(msgvec);
+ break;
+
+ case NDMLIST:
+ /*
+ * A message list with no defaults, but no error
+ * if none exist.
+ */
+ if (msgvec == 0) {
+ puts("Illegal use of \"message list\"");
+ break;
+ }
+ if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
+ break;
+ e = (*com->c_func)(msgvec);
+ break;
+
+ case STRLIST:
+ /*
+ * Just the straight string, with
+ * leading blanks removed.
+ */
+ while (isspace((unsigned char)*cp))
+ cp++;
+ e = (*com->c_func)(cp);
+ break;
+
+ case RAWLIST:
+ /*
+ * A vector of strings, in shell style.
+ */
+ if ((c = getrawlist(cp, arglist,
+ sizeof(arglist) / sizeof(*arglist))) < 0)
+ break;
+ if (c < com->c_minargs) {
+ printf("%s requires at least %d arg(s)\n",
+ com->c_name, com->c_minargs);
+ break;
+ }
+ if (c > com->c_maxargs) {
+ printf("%s takes no more than %d arg(s)\n",
+ com->c_name, com->c_maxargs);
+ break;
+ }
+ e = (*com->c_func)(arglist);
+ break;
+
+ case NOLIST:
+ /*
+ * Just the constant zero, for exiting,
+ * eg.
+ */
+ e = (*com->c_func)(0);
+ break;
+
+ default:
+ errx(1, "Unknown argtype");
+ }
+
+out:
+ /*
+ * Exit the current source file on
+ * error.
+ */
+ if (e) {
+ if (e < 0)
+ return(1);
+ if (loading)
+ return(1);
+ if (sourcing)
+ unstack();
+ return(0);
+ }
+ if (com == NULL)
+ return(0);
+ if (value("autoprint") != NULL && com->c_argtype & P)
+ if ((dot->m_flag & MDELETED) == 0) {
+ muvec[0] = dot - &message[0] + 1;
+ muvec[1] = 0;
+ type(muvec);
+ }
+ if (!sourcing && (com->c_argtype & T) == 0)
+ sawcom = 1;
+ return(0);
+}
+
+/*
+ * Set the size of the message vector used to construct argument
+ * lists to message list functions.
+ */
+void
+setmsize(int n)
+{
+ int *msgvec2;
+ size_t msize;
+
+ msize = (n + 1) * sizeof(*msgvec);
+ if ((msgvec2 = realloc(msgvec, msize)) == NULL)
+ err(1, "realloc");
+ msgvec = msgvec2;
+ memset(msgvec, 0, msize);
+}
+
+/*
+ * Find the correct command in the command table corresponding
+ * to the passed command "word"
+ */
+
+const struct cmd *
+lex(char *word)
+{
+ extern const struct cmd cmdtab[];
+ const struct cmd *cp;
+
+ if (word[0] == '#')
+ word = "#";
+ for (cp = &cmdtab[0]; cp->c_name != NULL; cp++)
+ if (isprefix(word, cp->c_name))
+ return(cp);
+ return(NULL);
+}
+
+/*
+ * Determine if as1 is a valid prefix of as2.
+ * Return true if yep.
+ */
+int
+isprefix(char *as1, char *as2)
+{
+ char *s1, *s2;
+
+ s1 = as1;
+ s2 = as2;
+ while (*s1++ == *s2)
+ if (*s2++ == '\0')
+ return(1);
+ return(*--s1 == '\0');
+}
+
+/*
+ * The following gets called on receipt of an interrupt. This is
+ * to abort printout of a command, mainly.
+ * Dispatching here when command() is inactive crashes rcv.
+ * Close all open files except 0, 1, 2, and the temporary.
+ * Also, unstack all source files.
+ */
+int inithdr; /* am printing startup headers */
+
+void
+dointr(void)
+{
+
+ noreset = 0;
+ if (!inithdr)
+ sawcom++;
+ inithdr = 0;
+ while (sourcing)
+ unstack();
+
+ close_all_files();
+
+ if (image >= 0) {
+ (void)close(image);
+ image = -1;
+ }
+ fputs("Interrupt\n", stderr);
+}
+
+/*
+ * Announce the presence of the current Mail version,
+ * give the message count, and print a header listing.
+ */
+void
+announce(void)
+{
+ int vec[2], mdot;
+
+ mdot = newfileinfo(0);
+ vec[0] = mdot;
+ vec[1] = 0;
+ dot = &message[mdot - 1];
+ if (msgCount > 0 && value("noheader") == NULL) {
+ inithdr++;
+ headers(vec);
+ inithdr = 0;
+ }
+}
+
+/*
+ * Announce information about the file we are editing.
+ * Return a likely place to set dot.
+ */
+int
+newfileinfo(int omsgCount)
+{
+ struct message *mp;
+ int u, n, mdot, d, s;
+ char fname[PATHSIZE], zname[PATHSIZE], *ename;
+
+ for (mp = &message[omsgCount]; mp < &message[msgCount]; mp++)
+ if (mp->m_flag & MNEW)
+ break;
+ if (mp >= &message[msgCount])
+ for (mp = &message[omsgCount]; mp < &message[msgCount]; mp++)
+ if ((mp->m_flag & MREAD) == 0)
+ break;
+ if (mp < &message[msgCount])
+ mdot = mp - &message[0] + 1;
+ else
+ mdot = omsgCount + 1;
+ s = d = 0;
+ for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) {
+ if (mp->m_flag & MNEW)
+ n++;
+ if ((mp->m_flag & MREAD) == 0)
+ u++;
+ if (mp->m_flag & MDELETED)
+ d++;
+ if (mp->m_flag & MSAVED)
+ s++;
+ }
+ ename = mailname;
+ if (getfold(fname, sizeof(fname)) >= 0) {
+ strlcat(fname, "/", sizeof(fname));
+ if (strncmp(fname, mailname, strlen(fname)) == 0) {
+ (void)snprintf(zname, sizeof(zname), "+%s",
+ mailname + strlen(fname));
+ ename = zname;
+ }
+ }
+ printf("\"%s\": ", ename);
+ if (msgCount == 1)
+ fputs("1 message", stdout);
+ else
+ printf("%d messages", msgCount);
+ if (n > 0)
+ printf(" %d new", n);
+ if (u-n > 0)
+ printf(" %d unread", u);
+ if (d > 0)
+ printf(" %d deleted", d);
+ if (s > 0)
+ printf(" %d saved", s);
+ if (readonly)
+ fputs(" [Read only]", stdout);
+ putchar('\n');
+ return(mdot);
+}
+
+/*
+ * Print the current version number.
+ */
+/*ARGSUSED*/
+int
+pversion(void *v)
+{
+ extern const char version[];
+
+ printf("Version %s\n", version);
+ return(0);
+}
+
+/*
+ * Load a file of user definitions.
+ */
+void
+load(char *name)
+{
+ FILE *in, *oldin;
+
+ if ((in = Fopen(name, "r")) == NULL)
+ return;
+ oldin = input;
+ input = in;
+ loading = 1;
+ sourcing = 1;
+ commands();
+ loading = 0;
+ sourcing = 0;
+ input = oldin;
+ (void)Fclose(in);
+}
diff --git a/list.c b/list.c
@@ -0,0 +1,797 @@
+/* $OpenBSD: list.c,v 1.20 2015/10/16 17:56:07 mmcc Exp $ */
+/* $NetBSD: list.c,v 1.7 1997/07/09 05:23:36 mikel Exp $ */
+
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS 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 REGENTS OR 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.
+ */
+
+#include "rcv.h"
+#include <ctype.h>
+#include "extern.h"
+
+int matchto(char *, int);
+
+/*
+ * Mail -- a mail program
+ *
+ * Message list handling.
+ */
+
+/*
+ * Convert the user string of message numbers and
+ * store the numbers into vector.
+ *
+ * Returns the count of messages picked up or -1 on error.
+ */
+int
+getmsglist(char *buf, int *vector, int flags)
+{
+ int *ip;
+ struct message *mp;
+
+ if (msgCount == 0) {
+ *vector = 0;
+ return(0);
+ }
+ if (markall(buf, flags) < 0)
+ return(-1);
+ ip = vector;
+ for (mp = &message[0]; mp < &message[msgCount]; mp++)
+ if (mp->m_flag & MMARK)
+ *ip++ = mp - &message[0] + 1;
+ *ip = 0;
+ return(ip - vector);
+}
+
+/*
+ * Mark all messages that the user wanted from the command
+ * line in the message structure. Return 0 on success, -1
+ * on error.
+ */
+
+/*
+ * Bit values for colon modifiers.
+ */
+#define CMNEW 01 /* New messages */
+#define CMOLD 02 /* Old messages */
+#define CMUNREAD 04 /* Unread messages */
+#define CMDELETED 010 /* Deleted messages */
+#define CMREAD 020 /* Read messages */
+
+/*
+ * The following table describes the letters which can follow
+ * the colon and gives the corresponding modifier bit.
+ */
+struct coltab {
+ char co_char; /* What to find past : */
+ int co_bit; /* Associated modifier bit */
+ int co_mask; /* m_status bits to mask */
+ int co_equal; /* ... must equal this */
+} coltab[] = {
+ { 'n', CMNEW, MNEW, MNEW },
+ { 'o', CMOLD, MNEW, 0 },
+ { 'u', CMUNREAD, MREAD, 0 },
+ { 'd', CMDELETED, MDELETED, MDELETED },
+ { 'r', CMREAD, MREAD, MREAD },
+ { 0, 0, 0, 0 }
+};
+
+static int lastcolmod;
+
+int
+markall(char *buf, int f)
+{
+ char **np;
+ int i;
+ struct message *mp;
+ char *namelist[NMLSIZE], *bufp;
+ int tok, beg, mc, star, other, valdot, colmod, colresult;
+
+ valdot = dot - &message[0] + 1;
+ colmod = 0;
+ for (i = 1; i <= msgCount; i++)
+ unmark(i);
+ bufp = buf;
+ mc = 0;
+ np = &namelist[0];
+ scaninit();
+ tok = scan(&bufp);
+ star = 0;
+ other = 0;
+ beg = 0;
+ while (tok != TEOL) {
+ switch (tok) {
+ case TNUMBER:
+number:
+ if (star) {
+ puts("No numbers mixed with *");
+ return(-1);
+ }
+ mc++;
+ other++;
+ if (beg != 0) {
+ if (check(lexnumber, f))
+ return(-1);
+ for (i = beg; i <= lexnumber; i++)
+ if (f == MDELETED || (message[i - 1].m_flag & MDELETED) == 0)
+ mark(i);
+ beg = 0;
+ break;
+ }
+ beg = lexnumber;
+ if (check(beg, f))
+ return(-1);
+ tok = scan(&bufp);
+ regret(tok);
+ if (tok != TDASH) {
+ mark(beg);
+ beg = 0;
+ }
+ break;
+
+ case TPLUS:
+ if (beg != 0) {
+ puts("Non-numeric second argument");
+ return(-1);
+ }
+ i = valdot;
+ do {
+ i++;
+ if (i > msgCount) {
+ puts("Referencing beyond EOF");
+ return(-1);
+ }
+ } while ((message[i - 1].m_flag & MDELETED) != f);
+ mark(i);
+ break;
+
+ case TDASH:
+ if (beg == 0) {
+ i = valdot;
+ do {
+ i--;
+ if (i <= 0) {
+ puts("Referencing before 1");
+ return(-1);
+ }
+ } while ((message[i - 1].m_flag & MDELETED) != f);
+ mark(i);
+ }
+ break;
+
+ case TSTRING:
+ if (beg != 0) {
+ puts("Non-numeric second argument");
+ return(-1);
+ }
+ other++;
+ if (lexstring[0] == ':') {
+ colresult = evalcol(lexstring[1]);
+ if (colresult == 0) {
+ printf("Unknown colon modifier \"%s\"\n",
+ lexstring);
+ return(-1);
+ }
+ colmod |= colresult;
+ } else {
+ if ((com->c_argtype & ~(F|P|I|M|T|W|R))
+ != (MSGLIST|STRLIST))
+ *np++ = savestr(lexstring);
+ }
+ break;
+
+ case TDOLLAR:
+ case TUP:
+ case TDOT:
+ lexnumber = metamess(lexstring[0], f);
+ if (lexnumber == -1)
+ return(-1);
+ goto number;
+
+ case TSTAR:
+ if (other) {
+ puts("Can't mix \"*\" with anything");
+ return(-1);
+ }
+ star++;
+ break;
+
+ case TERROR:
+ return(-1);
+ }
+ tok = scan(&bufp);
+ }
+ lastcolmod = colmod;
+ *np = NULL;
+ mc = 0;
+ if (star) {
+ for (i = 0; i < msgCount; i++)
+ if ((message[i].m_flag & MDELETED) == f) {
+ mark(i+1);
+ mc++;
+ }
+ if (mc == 0) {
+ puts("No applicable messages.");
+ return(-1);
+ }
+ return(0);
+ }
+
+ /*
+ * If no numbers were given, mark all of the messages,
+ * so that we can unmark any whose sender was not selected
+ * if any user names were given.
+ */
+ if ((np > namelist || colmod != 0) && mc == 0)
+ for (i = 1; i <= msgCount; i++)
+ if ((message[i-1].m_flag & MDELETED) == f)
+ mark(i);
+
+ /*
+ * If any names were given, go through and eliminate any
+ * messages whose senders were not requested.
+ */
+ if (np > namelist) {
+ for (i = 1; i <= msgCount; i++) {
+ for (mc = 0, np = &namelist[0]; *np != NULL; np++)
+ if (**np == '/') {
+ if (matchsubj(*np, i)) {
+ mc++;
+ break;
+ }
+ }
+ else {
+ if (matchsender(*np, i)) {
+ mc++;
+ break;
+ }
+ }
+ if (mc == 0)
+ unmark(i);
+ }
+
+ /*
+ * Make sure we got some decent messages.
+ */
+ mc = 0;
+ for (i = 1; i <= msgCount; i++)
+ if (message[i-1].m_flag & MMARK) {
+ mc++;
+ break;
+ }
+ if (mc == 0) {
+ printf("No applicable messages from {%s",
+ namelist[0]);
+ for (np = &namelist[1]; *np != NULL; np++)
+ printf(", %s", *np);
+ puts("}");
+ return(-1);
+ }
+ }
+
+ /*
+ * If any colon modifiers were given, go through and
+ * unmark any messages which do not satisfy the modifiers.
+ */
+ if (colmod != 0) {
+ for (i = 1; i <= msgCount; i++) {
+ struct coltab *colp;
+
+ mp = &message[i - 1];
+ for (colp = &coltab[0]; colp->co_char; colp++)
+ if (colp->co_bit & colmod)
+ if ((mp->m_flag & colp->co_mask)
+ != colp->co_equal)
+ unmark(i);
+ }
+ for (mp = &message[0]; mp < &message[msgCount]; mp++)
+ if (mp->m_flag & MMARK)
+ break;
+ if (mp >= &message[msgCount]) {
+ struct coltab *colp;
+
+ fputs("No messages satisfy", stdout);
+ for (colp = &coltab[0]; colp->co_char; colp++)
+ if (colp->co_bit & colmod)
+ printf(" :%c", colp->co_char);
+ putchar('\n');
+ return(-1);
+ }
+ }
+ return(0);
+}
+
+/*
+ * Turn the character after a colon modifier into a bit
+ * value.
+ */
+int
+evalcol(int col)
+{
+ struct coltab *colp;
+
+ if (col == 0)
+ return(lastcolmod);
+ for (colp = &coltab[0]; colp->co_char; colp++)
+ if (colp->co_char == col)
+ return(colp->co_bit);
+ return(0);
+}
+
+/*
+ * Check the passed message number for legality and proper flags.
+ * If f is MDELETED, then either kind will do. Otherwise, the message
+ * has to be undeleted.
+ */
+int
+check(int mesg, int f)
+{
+ struct message *mp;
+
+ if (mesg < 1 || mesg > msgCount) {
+ printf("%d: Invalid message number\n", mesg);
+ return(-1);
+ }
+ mp = &message[mesg-1];
+ if (f != MDELETED && (mp->m_flag & MDELETED) != 0) {
+ printf("%d: Inappropriate message\n", mesg);
+ return(-1);
+ }
+ return(0);
+}
+
+/*
+ * Scan out the list of string arguments, shell style
+ * for a RAWLIST.
+ */
+int
+getrawlist(char *line, char **argv, int argc)
+{
+ char c, *cp, *cp2, quotec;
+ int argn;
+ char *linebuf, *linebuf2;
+ size_t newsize, linebufsize = BUFSIZ;
+
+ if ((linebuf = malloc(linebufsize)) == NULL)
+ err(1, "malloc");
+
+ argn = 0;
+ cp = line;
+ for (;;) {
+ for (; *cp == ' ' || *cp == '\t'; cp++)
+ ;
+ if (*cp == '\0')
+ break;
+ if (argn >= argc - 1) {
+ puts("Too many elements in the list; excess discarded.");
+ break;
+ }
+ cp2 = linebuf;
+ quotec = '\0';
+ while ((c = *cp) != '\0') {
+ /* Alloc more space if necessary */
+ if (cp2 - linebuf == linebufsize - 1) {
+ newsize = linebufsize + BUFSIZ;
+ linebuf2 = realloc(linebuf, newsize);
+ if (linebuf2 == NULL)
+ err(1, "realloc");
+ linebuf = linebuf2;
+ linebufsize = newsize;
+ cp2 = linebuf + linebufsize - BUFSIZ - 1;
+ }
+ cp++;
+ if (quotec != '\0') {
+ if (c == quotec)
+ quotec = '\0';
+ else if (c == '\\')
+ switch (c = *cp++) {
+ case '\0':
+ *cp2++ = '\\';
+ cp--;
+ break;
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ c -= '0';
+ if (*cp >= '0' && *cp <= '7')
+ c = c * 8 + *cp++ - '0';
+ if (*cp >= '0' && *cp <= '7')
+ c = c * 8 + *cp++ - '0';
+ *cp2++ = c;
+ break;
+ case 'b':
+ *cp2++ = '\b';
+ break;
+ case 'f':
+ *cp2++ = '\f';
+ break;
+ case 'n':
+ *cp2++ = '\n';
+ break;
+ case 'r':
+ *cp2++ = '\r';
+ break;
+ case 't':
+ *cp2++ = '\t';
+ break;
+ case 'v':
+ *cp2++ = '\v';
+ break;
+ default:
+ *cp2++ = c;
+ }
+ else if (c == '^') {
+ c = *cp++;
+ if (c == '?')
+ *cp2++ = '\177';
+ /* null doesn't show up anyway */
+ else if ((c >= 'A' && c <= '_') ||
+ (c >= 'a' && c <= 'z'))
+ *cp2++ = c & 037;
+ else {
+ *cp2++ = '^';
+ cp--;
+ }
+ } else
+ *cp2++ = c;
+ } else if (c == '"' || c == '\'')
+ quotec = c;
+ else if (c == ' ' || c == '\t')
+ break;
+ else
+ *cp2++ = c;
+ }
+ *cp2 = '\0';
+ argv[argn++] = savestr(linebuf);
+ }
+ argv[argn] = NULL;
+ (void)free(linebuf);
+ return(argn);
+}
+
+/*
+ * Scan out a single lexical item and return its token number,
+ * updating the string pointer passed **p. Also, store the value
+ * of the number or string scanned in lexnumber or lexstring as
+ * appropriate. In any event, store the scanned `thing' in lexstring.
+ */
+struct lex {
+ char l_char;
+ char l_token;
+} singles[] = {
+ { '$', TDOLLAR },
+ { '.', TDOT },
+ { '^', TUP },
+ { '*', TSTAR },
+ { '-', TDASH },
+ { '+', TPLUS },
+ { '(', TOPEN },
+ { ')', TCLOSE },
+ { 0, 0 }
+};
+
+int
+scan(char **sp)
+{
+ char *cp, *cp2;
+ int c;
+ struct lex *lp;
+ int quotec;
+
+ if (regretp >= 0) {
+ strlcpy(lexstring, string_stack[regretp], STRINGLEN);
+ lexnumber = numberstack[regretp];
+ return(regretstack[regretp--]);
+ }
+ cp = *sp;
+ cp2 = lexstring;
+ c = (unsigned char)*cp++;
+
+ /*
+ * strip away leading white space.
+ */
+ while (c == ' ' || c == '\t')
+ c = (unsigned char)*cp++;
+
+ /*
+ * If no characters remain, we are at end of line,
+ * so report that.
+ */
+ if (c == '\0') {
+ *sp = --cp;
+ return(TEOL);
+ }
+
+ /*
+ * If the leading character is a digit, scan
+ * the number and convert it on the fly.
+ * Return TNUMBER when done.
+ */
+ if (isdigit(c)) {
+ lexnumber = 0;
+ while (isdigit(c)) {
+ lexnumber = lexnumber*10 + c - '0';
+ if (cp2 - lexstring < STRINGLEN - 1)
+ *cp2++ = c;
+ c = (unsigned char)*cp++;
+ }
+ *cp2 = '\0';
+ *sp = --cp;
+ return(TNUMBER);
+ }
+
+ /*
+ * Check for single character tokens; return such
+ * if found.
+ */
+ for (lp = &singles[0]; lp->l_char != 0; lp++)
+ if (c == lp->l_char) {
+ lexstring[0] = c;
+ lexstring[1] = '\0';
+ *sp = cp;
+ return(lp->l_token);
+ }
+
+ /*
+ * We've got a string! Copy all the characters
+ * of the string into lexstring, until we see
+ * a null, space, or tab.
+ * If the lead character is a " or ', save it
+ * and scan until you get another.
+ */
+ quotec = 0;
+ if (c == '\'' || c == '"') {
+ quotec = c;
+ c = (unsigned char)*cp++;
+ }
+ while (c != '\0') {
+ if (c == quotec) {
+ cp++;
+ break;
+ }
+ if (quotec == 0 && (c == ' ' || c == '\t'))
+ break;
+ if (cp2 - lexstring < STRINGLEN-1)
+ *cp2++ = c;
+ c = (unsigned char)*cp++;
+ }
+ if (quotec && c == 0) {
+ fprintf(stderr, "Missing %c\n", quotec);
+ return(TERROR);
+ }
+ *sp = --cp;
+ *cp2 = '\0';
+ return(TSTRING);
+}
+
+/*
+ * Unscan the named token by pushing it onto the regret stack.
+ */
+void
+regret(int token)
+{
+
+ if (++regretp >= REGDEP)
+ errx(1, "Too many regrets");
+ regretstack[regretp] = token;
+ lexstring[STRINGLEN-1] = '\0';
+ string_stack[regretp] = savestr(lexstring);
+ numberstack[regretp] = lexnumber;
+}
+
+/*
+ * Reset all the scanner global variables.
+ */
+void
+scaninit(void)
+{
+
+ regretp = -1;
+}
+
+/*
+ * Find the first message whose flags & m == f and return
+ * its message number.
+ */
+int
+first(int f, int m)
+{
+ struct message *mp;
+
+ if (msgCount == 0)
+ return(0);
+ f &= MDELETED;
+ m &= MDELETED;
+ for (mp = dot; mp < &message[msgCount]; mp++)
+ if ((mp->m_flag & m) == f)
+ return(mp - message + 1);
+ for (mp = dot-1; mp >= &message[0]; mp--)
+ if ((mp->m_flag & m) == f)
+ return(mp - message + 1);
+ return(0);
+}
+
+/*
+ * See if the passed name sent the passed message number. Return true
+ * if so.
+ */
+int
+matchsender(char *str, int mesg)
+{
+ char *cp;
+
+ if (!*str) /* null string matches nothing instead of everything */
+ return(0);
+ cp = nameof(&message[mesg - 1], 0);
+ return (strcasestr(cp, str) != NULL);
+}
+
+/*
+ * See if the passed name received the passed message number. Return true
+ * if so.
+ */
+static char *to_fields[] = { "to", "cc", "bcc", NULL };
+
+int
+matchto(char *str, int mesg)
+{
+ struct message *mp;
+ char *cp, **to;
+
+ str++;
+
+ if (*str == 0) /* null string matches nothing instead of everything */
+ return(0);
+
+ mp = &message[mesg-1];
+
+ for (to = to_fields; *to; to++) {
+ cp = hfield(*to, mp);
+ if (cp != NULL && strcasestr(cp, str) != NULL)
+ return(1);
+ }
+ return(0);
+}
+
+/*
+ * See if the given string matches inside the subject field of the
+ * given message. For the purpose of the scan, we ignore case differences.
+ * If it does, return true. The string search argument is assumed to
+ * have the form "/search-string." If it is of the form "/," we use the
+ * previous search string.
+ */
+char lastscan[STRINGLEN];
+
+int
+matchsubj(char *str, int mesg)
+{
+ struct message *mp;
+ char *cp, *cp2;
+
+ str++;
+ if (*str == '\0')
+ str = lastscan;
+ else
+ strlcpy(lastscan, str, sizeof(lastscan));
+ mp = &message[mesg-1];
+
+ /*
+ * Now look, ignoring case, for the word in the string.
+ */
+ if (value("searchheaders") && (cp = strchr(str, ':'))) {
+ /* Check for special case "/To:" */
+ if (strncasecmp(str, "to:", 3) == 0)
+ return(matchto(cp, mesg));
+ *cp++ = '\0';
+ cp2 = hfield(*str ? str : "subject", mp);
+ cp[-1] = ':';
+ str = cp;
+ cp = cp2;
+ } else {
+ cp = hfield("subject", mp);
+ }
+ if (cp == NULL)
+ return(0);
+
+ return (strcasestr(cp, str) != NULL);
+}
+
+/*
+ * Mark the named message by setting its mark bit.
+ */
+void
+mark(int mesg)
+{
+ int i;
+
+ i = mesg;
+ if (i < 1 || i > msgCount)
+ errx(1, "Bad message number to mark");
+ message[i-1].m_flag |= MMARK;
+}
+
+/*
+ * Unmark the named message.
+ */
+void
+unmark(int mesg)
+{
+ int i;
+
+ i = mesg;
+ if (i < 1 || i > msgCount)
+ errx(1, "Bad message number to unmark");
+ message[i-1].m_flag &= ~MMARK;
+}
+
+/*
+ * Return the message number corresponding to the passed meta character.
+ */
+int
+metamess(int meta, int f)
+{
+ int c, m;
+ struct message *mp;
+
+ c = meta;
+ switch (c) {
+ case '^':
+ /*
+ * First 'good' message left.
+ */
+ for (mp = &message[0]; mp < &message[msgCount]; mp++)
+ if ((mp->m_flag & MDELETED) == f)
+ return(mp - &message[0] + 1);
+ puts("No applicable messages");
+ return(-1);
+
+ case '$':
+ /*
+ * Last 'good message left.
+ */
+ for (mp = &message[msgCount-1]; mp >= &message[0]; mp--)
+ if ((mp->m_flag & MDELETED) == f)
+ return(mp - &message[0] + 1);
+ puts("No applicable messages");
+ return(-1);
+
+ case '.':
+ /*
+ * Current message.
+ */
+ m = dot - &message[0] + 1;
+ if ((dot->m_flag & MDELETED) != f) {
+ printf("%d: Inappropriate message\n", m);
+ return(-1);
+ }
+ return(m);
+
+ default:
+ printf("Unknown metachar (%c)\n", c);
+ return(-1);
+ }
+}
diff --git a/mail.1 b/mail.1
@@ -0,0 +1,1276 @@
+.\" $OpenBSD: mail.1,v 1.81 2021/03/08 02:47:28 jsg Exp $
+.\"
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS 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 REGENTS OR 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.
+.\"
+.\" @(#)mail.1 8.8 (Berkeley) 4/28/95
+.\"
+.Dd $Mdocdate: March 8 2021 $
+.Dt MAIL 1
+.Os
+.Sh NAME
+.Nm mail ,
+.Nm mailx ,
+.Nm Mail
+.Nd send and receive mail
+.Sh SYNOPSIS
+.Nm mail
+.Bk -words
+.Op Fl dEIinv
+.Op Fl b Ar list
+.Op Fl c Ar list
+.Op Fl r Ar from-addr
+.Op Fl s Ar subject
+.Ar to-addr ...
+.Ek
+.Nm mail
+.Op Fl dEIiNnv
+.Fl f
+.Op Ar file
+.Nm mail
+.Op Fl dEIiNnv
+.Op Fl u Ar user
+.Sh DESCRIPTION
+.Nm mail
+is an intelligent mail processing system which has
+a command syntax reminiscent of
+.Xr ed 1
+with lines replaced by messages.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl b Ar list
+Send blind carbon copies to
+.Ar list .
+.It Fl c Ar list
+Send carbon copies to
+.Ar list
+of users.
+.Ar list
+should be a comma separated list of names.
+.It Fl d
+Causes
+.Nm mail
+to output all sorts of information useful for debugging
+.Nm mail .
+.It Fl E
+Don't send messages with an empty body.
+.It Fl f
+Use an alternate mailbox.
+Defaults to the user's
+.Ar mbox
+if no
+.Ar file
+is specified.
+When quit,
+.Nm mail
+writes undeleted messages back to this
+.Ar file .
+.It Fl I
+Forces
+.Nm mail
+to run in interactive mode, even when input is not a terminal.
+In particular, the special
+.Ic ~
+command character, used when sending mail, is only available interactively.
+.It Fl i
+Ignore tty interrupt signals.
+This is
+particularly useful when using
+.Nm mail
+on noisy phone lines.
+.It Fl N
+Inhibits initial display of message headers
+when reading mail or editing a mail folder.
+.It Fl n
+Inhibits reading
+.Pa /etc/mail.rc
+upon startup.
+.It Fl r Ar from-addr
+Use
+.Ar from-addr
+as the from address in the message and envelope.
+Overrides any
+.Ar from
+options in the startup files.
+.It Fl s Ar subject
+Specify subject on command line
+(only the first argument after the
+.Fl s
+flag is used as a subject; be careful to quote subjects
+containing spaces).
+.It Fl u Ar user
+Equivalent to:
+.Pp
+.Dl $ mail -f /var/mail/user
+.Pp
+except that locking is done.
+.It Fl v
+Verbose mode.
+The details of
+delivery are displayed on the user's terminal.
+.El
+.Ss Startup actions
+At startup time,
+.Nm mail
+will execute commands in the system command file,
+.Pa /etc/mail.rc ,
+unless explicitly told not to by using the
+.Fl n
+option.
+Next, the commands in the user's personal command file
+.Pa ~/.mailrc
+are executed.
+.Nm mail
+then examines its command line options to determine whether the user
+requested a new message to be sent or existing messages in a mailbox
+to be examined.
+.Ss Sending mail
+To send a message to one or more people,
+.Nm mail
+can be invoked with arguments which are the names of people to
+whom the mail will be sent.
+You are then expected to type in
+your message, followed
+by a control-D
+.Pq Sq ^D
+at the beginning of a line.
+The section below,
+.Sx Replying to or originating mail ,
+describes some features of
+.Nm mail
+available to help you compose your letter.
+.Ss Reading mail
+In normal usage,
+.Nm mail
+is given no arguments and checks your mail out of the
+post office, then
+prints out a one line header of each message found.
+The current message is initially set to the first message (numbered 1)
+and can be printed using the
+.Ic print
+command (which can be abbreviated
+.Ic p ) .
+Moving among the messages is much like moving between lines in
+.Xr ed 1 ;
+you may use
+.Ic +
+and
+.Ic -
+to shift forwards and backwards, or simply enter a message number to move
+directly.
+.Ss Disposing of mail
+After examining a message you can
+.Ic delete
+.Pq Ic d
+or
+.Ic reply
+.Pq Ic r
+to it.
+Deletion causes the
+.Nm mail
+program to forget about the message.
+This is not irreversible; the message can be
+.Ic undeleted
+.Pq Ic u
+by giving its number, or the
+.Nm mail
+session can be aborted by giving the
+.Ic exit
+.Pq Ic x
+command.
+Deleted messages, however, will usually disappear, never to be seen again.
+.Ss Specifying messages
+Commands such as
+.Ic print
+and
+.Ic delete
+can be given a list of message numbers as arguments to apply
+to a number of messages at once.
+Thus
+.Ic delete 1 2
+deletes messages 1 and 2, while
+.Ic delete 1\-5
+deletes messages 1 through 5.
+.Pp
+Messages may also be selected using one of the following categories:
+.Pp
+.Bl -tag -width Ds -offset indent -compact
+.It *
+all messages
+.It $
+last message
+.It :d
+deleted messages
+.It :n
+new messages
+.It :o
+old messages
+.It :r
+read messages
+.It :u
+unread messages
+.El
+.Pp
+Thus the command
+.Ic top ,
+which prints the first few lines of a message,
+could be used in
+.Ic top *
+to print the first few lines of all messages.
+.Ss Replying to or originating mail
+You can use the
+.Ic reply
+command to
+set up a response to a message, sending it back to the
+person who it was from.
+Text you then type in, up to an end-of-file,
+defines the contents of the message.
+While you are composing a message,
+.Nm mail
+treats lines beginning with the tilde
+.Pq Sq ~
+character specially.
+For instance, typing
+.Ic ~m
+(alone on a line) will place a copy
+of the current message into the response, right shifting it by a single
+tab-stop (see the
+.Va indentprefix
+variable, below).
+Other escapes will set up subject fields, add and delete recipients
+to the message, and allow you to escape to an editor to revise the
+message or to a shell to run some commands.
+(These options
+are given in the summary below.)
+.Ss Ending a mail processing session
+You can end a
+.Nm mail
+session with the
+.Ic quit
+.Pq Ic q
+command.
+Messages which have been examined go to your
+.Ar mbox
+file unless they have been deleted, in which case they are discarded.
+Unexamined messages go back to the post office (see the
+.Fl f
+option above).
+.Ss Personal and system wide distribution lists
+It is also possible to create personal distribution lists so that,
+for instance, you can send mail to
+.Dq Li cohorts
+and have it go
+to a group of people.
+Such lists can be defined by placing a line like
+.Pp
+.Dl alias cohorts bill ozalp jkf mark kridle@ucbcory
+.Pp
+in the file
+.Pa .mailrc
+in your home directory.
+The current list of such aliases can be displayed with the
+.Ic alias
+command in
+.Nm mail .
+System wide distribution lists can be created by editing
+.Pa /etc/mail/aliases
+(see
+.Xr aliases 5 ) ;
+these are kept in a different syntax.
+In mail you send, personal aliases will be expanded in mail sent
+to others so that they will be able to
+.Ic reply
+to the recipients.
+System wide aliases
+are not expanded when the mail is sent,
+but any reply returned to the machine will have the system wide
+alias expanded as all mail goes through an MTA.
+.Ss Recipient address specifications
+Recipient addresses (any of the
+.Dq To ,
+.Dq Cc
+or
+.Dq Bcc
+header fields) are subject to expansion when the
+.Ic expandaddr
+option is set.
+.Pp
+An address may be expanded as follows:
+.Bl -bullet -width Ds
+.It
+An address that starts with a pipe
+.Pq Ql |
+character is treated as a command to run.
+The command immediately following the
+.Ql |
+is executed with the message as its standard input.
+.It
+An address that starts with a
+.Ql +
+character is treated as a folder.
+.It
+An address that contains a
+.Ql /
+character but no
+.Ql \&! ,
+.Ql % ,
+or
+.Ql @
+characters is also treated as a folder.
+.It
+If none of the above apply, the recipient is treated as
+a local or network mail address.
+.El
+.Pp
+If the
+.Ic expandaddr
+option is not set (the default), no expansion is performed and
+the recipient is treated as a local or network mail address.
+.Sh SUMMARY
+(Adapted from the
+.Dq Mail Reference Manual . )
+.Pp
+Each command is typed on a line by itself, and may take arguments
+following the command word.
+The command need not be typed in its
+entirety \(em the first command which matches the typed prefix is used.
+For commands which take message lists as arguments, if no message
+list is given, then the next message forward which satisfies the
+command's requirements is used.
+If there are no messages forward of
+the current message, the search proceeds backwards, and if there are no
+good messages at all,
+.Nm mail
+types
+.Dq \&No applicable messages
+and
+aborts the command.
+.Bl -tag -width delete
+.It Ic -
+Print out the preceding message.
+If given a numeric
+argument
+.Ar n ,
+goes to the
+.Ar n Ns th
+previous message and prints it.
+.It Ic \&=
+Prints the currently selected message number.
+.It Ic \&?
+Prints a brief summary of commands.
+.It Ic \&!
+Executes the shell
+(see
+.Xr sh 1
+and
+.Xr csh 1 )
+command which follows.
+.It Ic alias
+.Pq Ic a
+With no arguments, prints out all currently defined aliases.
+With one
+argument, prints out that alias.
+With more than one argument, creates
+a new alias or changes an old one.
+.It Ic alternates
+.Pq Ic alt
+The
+.Ic alternates
+command is useful if you have accounts on several machines.
+It can be used to inform
+.Nm mail
+that the listed addresses are really you.
+When you
+.Ic reply
+to messages,
+.Nm mail
+will not send a copy of the message to any of the addresses
+listed on the
+.Ic alternates
+list.
+If the
+.Ic alternates
+command is given with no argument, the current set of alternate
+names is displayed.
+.It Ic chdir
+.Pf ( Ic cd
+or
+.Ic ch )
+Changes the user's working directory to that specified, if given.
+If
+no directory is given, then changes to the user's login directory.
+.It Ic copy
+.Pq Ic c
+The
+.Ic copy
+command does the same thing that
+.Ic save
+does, except that it does not mark the messages it
+is used on for deletion when you quit.
+.It Ic delete
+.Pq Ic d
+Takes a list of messages as argument and marks them all as deleted.
+Deleted messages will not be saved in
+.Ar mbox ,
+nor will they be available for most other commands.
+.It Ic dp
+(also
+.Ic dt )
+Deletes the current message and prints the next message.
+If there is no next message,
+.Nm mail
+says
+.Dq Li "\&No more messages."
+.It Ic edit
+.Pq Ic e
+Takes a list of messages and points the text editor at each one in
+turn.
+On return from the editor, the message is read back in.
+.It Ic exit
+.Pf ( Ic ex
+or
+.Ic x )
+Effects an immediate return to the shell without
+modifying the user's system mailbox, his
+.Ar mbox
+file, or his edit file in
+.Fl f .
+.It Ic file
+.Pq Ic fi
+The same as
+.Ic folder .
+.It Ic folder
+.Pq Ic fo
+The
+.Ic folder
+command switches to a new mail file or folder.
+With no
+arguments, it tells you which file you are currently reading.
+If you give it an argument, it will write out changes (such
+as deletions) you have made in the current file and read in
+the new file.
+Some special conventions are recognized for
+the name.
+# means the previous file, % means your system
+mailbox, %user means user's system mailbox, & means
+your
+.Ar mbox
+file, and
++folder means a file in your folder
+directory.
+.It Ic folders
+List the names of the folders in your folder directory.
+.It Ic from
+.Pq Ic f
+Takes a list of messages and prints their message headers.
+.It Ic headers
+.Pq Ic h
+Lists the current windowful of headers.
+To view the next or previous group of headers, see the
+.Ic z
+command.
+.It Ic help
+A synonym for
+.Ic \&? .
+.It Ic hold
+.Pf ( Ic ho ,
+also
+.Ic preserve )
+Takes a message list and marks each
+message therein to be saved in the
+user's system mailbox instead of in
+.Ar mbox .
+Does not override the
+.Ic delete
+command.
+.It Ic ignore
+Add the list of header fields named to the
+.Ar ignored list .
+Header fields in the ignore list are not printed
+on your terminal when you print a message.
+This
+command is very handy for suppression of certain machine-generated
+header fields.
+The
+.Ic Type
+and
+.Ic Print
+commands can be used to print a message in its entirety, including
+ignored fields.
+If
+.Ic ignore
+is executed with no arguments, it lists the current set of
+ignored fields.
+.It Ic inc
+Incorporate any new messages that have arrived while mail
+is being read.
+The new messages are added to the end of the message list,
+and the current message is reset to be the first new mail message.
+This does not renumber the existing message list, nor
+does it cause any changes made so far to be saved.
+.It Ic list
+.Pq Ic l
+List the valid
+.Nm
+commands.
+.It Ic mail
+.Pq Ic m
+Takes as argument login names and distribution group names and sends
+mail to those people.
+.It Ic mbox
+Indicate that a list of messages be sent to
+.Ar mbox
+in your home directory when you quit.
+This is the default
+action for messages if you do
+.Em not
+have the
+.Ic hold
+option set.
+.It Ic more
+.Pq Ic \&mo
+Takes a message list and invokes the pager on that list.
+.It Ic next
+.Pq Ic n
+(like
+.Ic +
+or CR)
+Goes to the next message in sequence and types it.
+With an argument list, types the next matching message.
+.It Ic preserve
+.Pq Ic pre
+A synonym for
+.Ic hold .
+.It Ic Print
+.Pq Ic P
+Like
+.Ic print
+but also prints out ignored header fields.
+See also
+.Ic print ,
+.Ic ignore ,
+and
+.Ic retain .
+.It Ic print
+.Pq Ic p
+Takes a message list and types out each message on the user's terminal.
+.It Ic quit
+.Pq Ic q
+Terminates the session, saving all undeleted, unsaved messages in
+the user's
+.Ar mbox
+file in his login directory, preserving all messages marked with
+.Ic hold
+or
+.Ic preserve
+or never referenced
+in his system mailbox, and removing all other messages from his system
+mailbox.
+If new mail has arrived during the session, the message
+.Dq Li "You have new mail"
+is given.
+If given while editing a
+mailbox file with the
+.Fl f
+flag, then the edit file is rewritten.
+A return to the shell is
+effected, unless the rewrite of edit file fails, in which case the user
+can escape with the
+.Ic exit
+command.
+.It Ic Reply
+.Pq Ic R
+Reply to originator.
+Does not reply to other
+recipients of the original message.
+.It Ic reply
+.Pq Ic r
+Takes a message list and sends mail to the sender and all
+recipients of the specified message.
+The default message must not be deleted.
+.It Ic respond
+A synonym for
+.Ic reply .
+.It Ic retain
+Add the list of header fields named to the
+.Ar retained list .
+Only the header fields in the retain list
+are shown on your terminal when you print a message.
+All other header fields are suppressed.
+The
+.Ic Type
+and
+.Ic Print
+commands can be used to print a message in its entirety.
+If
+.Ic retain
+is executed with no arguments, it lists the current set of
+retained fields.
+.It Ic save
+.Pq Ic s
+Takes a message list and a filename and appends each message in
+turn to the end of the file.
+The filename in quotes, followed by the line
+count and character count is echoed on the user's terminal.
+.It Ic saveignore
+.Ic saveignore
+is to
+.Ic save
+what
+.Ic ignore
+is to
+.Ic print
+and
+.Ic type .
+Header fields thus marked are filtered out when
+saving a message by
+.Ic save
+or when automatically saving to
+.Ar mbox .
+.It Ic saveretain
+.Ic saveretain
+is to
+.Ic save
+what
+.Ic retain
+is to
+.Ic print
+and
+.Ic type .
+Header fields thus marked are the only ones saved
+with a message when saving by
+.Ic save
+or when automatically saving to
+.Ar mbox .
+.Ic saveretain
+overrides
+.Ic saveignore .
+.It Ic set
+.Pq Ic se
+With no arguments, prints all variable values.
+Otherwise, sets
+option.
+Arguments are of the form
+.Ar option=value
+(no space before or after =) or
+.Ar option .
+Quotation marks may be placed around any part of the assignment statement to
+quote blanks or tabs, i.e.,
+.Ic set indentprefix="->" .
+.It Ic shell
+.Pq Ic sh
+Invokes an interactive version of the shell.
+.It Ic size
+Takes a message list and prints out the size in characters of each
+message.
+.It Ic source
+The
+.Ic source
+command reads
+commands from a file.
+.It Ic top
+Takes a message list and prints the top few lines of each.
+The number of
+lines printed is controlled by the variable
+.Ic toplines
+and defaults to five.
+.It Ic Type
+.Pq Ic T
+Identical to the
+.Ic Print
+command.
+.It Ic type
+.Pq Ic t
+A synonym for
+.Ic print .
+.It Ic unalias
+Takes a list of names defined by
+.Ic alias
+commands and discards the remembered groups of users.
+The group names
+no longer have any significance.
+.It Ic undelete
+.Pq Ic u
+Takes a message list and marks each message as not being deleted.
+.It Ic unread
+.Pq Ic U
+Takes a message list and marks each message as not having been read.
+.It Ic unset
+Takes a list of option names and discards their remembered values;
+the inverse of
+.Ic set .
+.It Ic visual
+.Pq Ic v
+Takes a message list and invokes the display editor on each message.
+.It Ic write
+.Pq Ic w
+Similar to
+.Ic save ,
+except that
+.Ic only
+the message body
+(without the header)
+is saved.
+Extremely useful for such tasks as sending and receiving source
+program text over the message system.
+.It Ic xit
+.Pq Ic x
+A synonym for
+.Ic exit .
+.It Ic z
+.Nm mail
+presents message headers in windowfuls as described under the
+.Ic headers
+command.
+You can move
+.Nm mail Ns 's
+attention forward to the next window with the
+.Ic z
+command.
+Also, you can move to the previous window by using
+.Ic z- .
+.El
+.Ss Tilde/escapes
+Here is a summary of the tilde escapes,
+which are used when composing messages to perform
+special functions.
+Tilde escapes are only recognized at the beginning
+of lines.
+The name
+.Dq tilde escape
+is somewhat of a misnomer since the actual escape character can be set
+by the option
+.Ic escape .
+.Pp
+.Bl -tag -width Ds -compact
+.It Ic ~b Ns Ar name ...
+Add the given names to the list of carbon copy recipients but do not make
+the names visible in the Cc: line ("blind" carbon copy).
+.Pp
+.It Ic ~c Ns Ar name ...
+Add the given names to the list of carbon copy recipients.
+.Pp
+.It Ic ~d
+Read the file
+.Pa dead.letter
+from your home directory into the message.
+.Pp
+.It Ic ~e
+Invoke the text editor on the message collected so far.
+After the
+editing session is finished, you may continue appending text to the
+message.
+.Pp
+.It Ic ~F Ns Ar messages
+Identical to
+.Ic ~f ,
+except all message headers are included.
+.Pp
+.It Ic ~f Ns Ar messages
+Read the named messages into the message being sent.
+If no messages are specified, read in the current message.
+Message headers currently being ignored (by the
+.Ic ignore
+or
+.Ic retain
+command) are not included.
+.Pp
+.It Ic ~h
+Edit the message header fields by typing each one in turn and allowing
+the user to append text to the end or modify the field by using the
+current terminal erase and kill characters.
+.Pp
+.It Ic ~M Ns Ar messages
+Identical to
+.Ic ~m ,
+except all message headers are included.
+.Pp
+.It Ic ~m Ns Ar messages
+Read the named messages into the message being sent, indented by a
+tab or by the value of
+.Va indentprefix .
+If no messages are specified,
+read the current message.
+Message headers currently being ignored (by the
+.Ic ignore
+or
+.Ic retain
+command) are not included.
+.Pp
+.It Ic ~p
+Print out the message collected so far, prefaced by the message header
+fields.
+.Pp
+.It Ic ~q
+Abort the message being sent, copying the message to
+.Pa dead.letter
+in your home directory if
+.Ic save
+is set.
+.Pp
+.It Ic ~r Ns Ar filename
+.It Ic ~< Ns Ar filename
+Read the named file into the message.
+.Pp
+.It Ic ~s Ns Ar string
+Cause the named string to become the current subject field.
+.Pp
+.It Ic ~t Ns Ar name ...
+Add the given names to the direct recipient list.
+.Pp
+.It Ic ~v
+Invoke an alternate editor (defined by the
+.Ev VISUAL
+option) on the
+message collected so far.
+Usually, the alternate editor will be a
+screen editor.
+After you quit the editor, you may resume appending
+text to the end of your message.
+.Pp
+.It Ic ~w Ns Ar filename
+Write the message onto the named file.
+.Pp
+.It Ic ~x
+Abort the message being sent.
+No message is copied to
+.Pa ~/dead.letter ,
+even if
+.Ic save
+is set.
+.Pp
+.It Ic ~?
+Prints a brief summary of tilde escapes.
+.Pp
+.It Ic ~! Ns Ar command
+Execute the indicated shell command, then return to the message.
+.Pp
+.It Ic ~| Ns Ar command
+Pipe the message through the command as a filter.
+If the command gives
+no output or terminates abnormally, retain the original text of the
+message.
+The command
+.Xr fmt 1
+is often used as
+.Ic command
+to rejustify the message.
+.Pp
+.It Ic ~: Ns Ar mail-command
+.It Ic ~_ Ns Ar mail-command
+Execute the given mail command.
+Not all commands, however, are allowed.
+.Pp
+.It Ic ~~ Ns Ar string
+Insert the string of text in the message prefaced by a single ~.
+If
+you have changed the escape character, then you should double
+that character in order to send it.
+.Pp
+.It Ic ~.
+Simulate end of file on input.
+.El
+.Ss Mail options
+A number of options can be set in the
+.Pa .mailrc
+file to alter the behavior of
+.Nm ,
+controlled via the
+.Ic set
+and
+.Ic unset
+commands.
+Options may be either binary, in which case it is only
+significant to see whether they are set or not; or string, in which
+case the actual value is of interest.
+The binary options include the following:
+.Bl -tag -width append
+.It Ar append
+Causes messages saved in
+.Ar mbox
+to be appended to the end rather than prepended.
+This should always be set (perhaps in
+.Pa /etc/mail.rc ) .
+.It Ar ask , asksub
+Causes
+.Nm mail
+to prompt you for the subject of each message you send.
+If
+you respond with simply a newline, no subject field will be sent.
+.It Ar askbcc
+Causes you to be prompted for additional blind carbon copy recipients at the
+end of each message.
+Responding with a newline indicates your
+satisfaction with the current list.
+.It Ar askcc
+Causes you to be prompted for additional carbon copy recipients at the
+end of each message.
+Responding with a newline indicates your
+satisfaction with the current list.
+.It Ar autoinc
+Causes new mail to be automatically incorporated when it arrives.
+Setting this is similar to issuing the
+.Ic inc
+command at each prompt, except that the current message is not
+reset when new mail arrives.
+.It Ar autoprint
+Causes the
+.Ic delete
+command to behave like
+.Ic dp ;
+thus, after deleting a message, the next one will be typed
+automatically.
+.It Ar debug
+Setting the binary option
+.Ar debug
+is the same as specifying
+.Fl d
+on the command line and causes
+.Nm mail
+to output all sorts of information useful for debugging
+.Nm mail .
+.It Ar dot
+The binary option
+.Ar dot
+causes
+.Nm mail
+to interpret a period alone on a line as the terminator
+of a message you are sending.
+.It Ar expandaddr
+Causes
+.Nm mail
+to expand message recipient addresses, as explained in the section
+.Sx Recipient address specifications .
+.It Ar from
+Causes
+.Nm mail
+to use the specified sender address in the
+.Dq From:
+field of the message header.
+A stripped down version of the address is also used in the message envelope.
+If unset, the message will not include an explicit sender address and
+a default value will be added by the MTA, typically
+.Dq user@host .
+This value can be overridden by specifying the
+.Fl r
+flag on the command line.
+.It Ar hold
+This option is used to hold messages in the system mailbox
+by default.
+.It Ar ignore
+Causes interrupt signals from your terminal to be ignored and echoed as
+@'s.
+.It Ar ignoreeof
+An option related to
+.Ar dot
+is
+.Ar ignoreeof
+which makes
+.Nm mail
+refuse to accept a control-D as the end of a message.
+.Ar ignoreeof
+also applies to
+.Nm mail
+command mode.
+.It Ar keep
+Setting this option causes
+.Nm
+to truncate your system mailbox instead of deleting it
+when it's empty.
+.It Ar keepsave
+Messages saved with the
+.Ic save
+command are not normally saved in
+.Ar mbox
+at quit time.
+Use this option to retain those messages.
+.It Ar metoo
+Usually, when a group is expanded that contains the sender, the sender
+is removed from the expansion.
+Setting this option causes the sender
+to be included in the group.
+.It Ar noheader
+Setting the option
+.Ar noheader
+is the same as giving the
+.Fl N
+flag on the command line.
+.It Ar nosave
+Normally, when you abort a message with two interrupt characters
+(usually control-C),
+.Nm mail
+copies the partial letter to the file
+.Pa dead.letter
+in your home directory.
+Setting the binary option
+.Ar nosave
+prevents this.
+.It Ar quiet
+Suppresses the printing of the version when first invoked.
+.It Ar Replyall
+Reverses the sense of
+.Ic reply
+and
+.Ic Reply
+commands.
+.It Ar searchheaders
+If this option is set, then a message-list specifier in the form
+.Dq /x:y
+will expand to all messages containing the substring
+.Sq y
+in the header
+field
+.Sq x .
+The string search is case insensitive.
+If
+.Sq x
+is omitted, it will default to the
+.Dq Subject
+header field.
+The form
+.Dq /to:y
+is a special case, and will expand
+to all messages containing the substring
+.Sq y
+in the
+.Dq To ,
+.Dq Cc
+or
+.Dq Bcc
+header fields.
+The check for
+.Dq to
+is case sensitive, so that
+.Dq /To:y
+can be used to limit the search for
+.Sq y
+to just the
+.Dq To:
+field.
+.It Ar skipempty
+Don't send messages with an empty body.
+.It Ar verbose
+Setting the option
+.Ar verbose
+is the same as using the
+.Fl v
+flag on the command line.
+When
+.Nm
+runs in verbose mode,
+the actual delivery of messages is displayed on the user's
+terminal.
+.El
+.Ss Option string values
+.Bl -tag -width Va
+.It Ev EDITOR
+Pathname of the text editor to use in the
+.Ic edit
+command and
+.Ic ~e
+escape.
+If not defined,
+.Pa /usr/bin/ex
+is used.
+.It Ev LISTER
+Pathname of the directory lister to use in the
+.Ic folders
+command.
+Default is
+.Pa /bin/ls .
+.It Ev MBOX
+The name of the
+.Ar mbox
+file.
+It can be the name of a folder.
+The default is
+.Dq Li mbox
+in the user's home directory.
+.It Ev PAGER
+Pathname of the program to use in the
+.Ic more
+command or when the
+.Ar crt
+variable is set.
+The default paginator
+.Xr more 1
+is used if this option is not defined.
+.It Ev SHELL
+Pathname of the shell to use in the
+.Ic !\&
+command and the
+.Ic ~!\&
+escape.
+A default shell is used if this option is
+not defined.
+.It Ev VISUAL
+Pathname of the text editor to use in the
+.Ic visual
+command and
+.Ic ~v
+escape.
+If not defined,
+.Pa /usr/bin/vi
+is used.
+.It Ar crt
+The valued option
+.Ar crt
+is used as a threshold to determine how long a message must
+be before
+.Ev PAGER
+is used to read it.
+If
+.Ar crt
+is set without a value,
+then the height of the terminal screen stored in the system
+is used to compute the threshold (see
+.Xr stty 1 ) .
+.It Ar escape
+If defined, the first character of this option gives the character to
+use in the place of ~ to denote escapes.
+.It Ar folder
+The name of the directory to use for storing folders of
+messages.
+If this name begins with a
+.Ql / ,
+.Nm mail
+considers it to be an absolute pathname; otherwise, the
+folder directory is found relative to your home directory.
+.It Ar indentprefix
+String used by the
+.Ic ~m
+tilde escape for indenting messages, in place of the normal tab character
+.Pq Sq ^I .
+Be sure to quote the value if it contains
+spaces or tabs.
+.It Ar record
+If defined, gives the pathname of the file used to record all outgoing
+mail.
+If not defined, then outgoing mail is not so saved.
+.It Ar screen
+Size of window of message headers for
+.Ic z .
+.It Ar sendmail
+Pathname to an alternative mail delivery system.
+.It Ar toplines
+If defined, gives the number of lines of a message to be printed out
+with the
+.Ic top
+command; normally, the first five lines are printed.
+.El
+.Sh ENVIRONMENT
+.Nm mail
+utilizes the
+.Ev HOME ,
+.Ev LOGNAME ,
+.Ev MAIL ,
+.Ev MAILRC ,
+and
+.Ev USER
+environment variables.
+.Pp
+If the
+.Ev MAIL
+environment variable is set, its value is used as the path to the
+user's mail spool.
+.Sh FILES
+.Bl -tag -width /usr/share/misc/mail.*help -compact
+.It Pa /var/mail/*
+post office (unless overridden by the
+.Ev MAIL
+environment variable)
+.It Pa ~/mbox
+user's old mail
+.It Pa ~/.mailrc
+file giving initial mail commands; can be overridden by setting the
+.Ev MAILRC
+environment variable
+.It Pa /tmp/R*
+temporary files
+.It Pa /usr/share/misc/mail.*help
+help files
+.It Pa /etc/mail.rc
+system initialization file
+.El
+.Sh EXIT STATUS
+.Ex -std mail
+.Sh SEE ALSO
+.Xr fmt 1 ,
+.Xr lockspool 1 ,
+.Xr vacation 1 ,
+.Xr aliases 5 ,
+.Xr mail.local 8 ,
+.Xr newaliases 8 ,
+.Xr sendmail 8 ,
+.Xr smtpd 8
+.Rs
+.\" 4.4BSD USD:7
+.%A Kurt Shoens
+.%T Mail Reference Manual
+.%B 4.4BSD User's Supplementary Documents (USD)
+.Re
+.Sh STANDARDS
+The
+.Nm mailx
+utility is compliant with the
+.St -p1003.1-2008
+specification.
+.Pp
+The flags
+.Op Fl iNnu
+are marked by
+.St -p1003.1-2008
+as being optional.
+.Pp
+The flags
+.Op Fl eFH
+are marked by
+.St -p1003.1-2008
+as being optional,
+and are not supported by this implementation of
+.Nm mailx .
+.Pp
+The flags
+.Op Fl bcdEIrv
+are extensions to the specification.
+.Sh HISTORY
+A
+.Nm mail
+command appeared in
+.At v1 .
+This man page is derived from the
+.%T "Mail Reference Manual"
+originally written by Kurt Shoens.
+.Sh BUGS
+Usually,
+.Nm mail
+and
+.Nm mailx
+are just links to
+.Nm Mail ,
+which can be confusing.
diff --git a/main.c b/main.c
@@ -0,0 +1,345 @@
+/* $OpenBSD: main.c,v 1.35 2021/01/26 18:21:47 deraadt Exp $ */
+/* $NetBSD: main.c,v 1.7 1997/05/13 06:15:57 mikel Exp $ */
+
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS 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 REGENTS OR 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.
+ */
+
+#include "rcv.h"
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include "extern.h"
+
+int msgCount; /* Count of messages read in */
+int rcvmode; /* True if receiving mail */
+int sawcom; /* Set after first command */
+int senderr; /* An error while checking */
+int edit; /* Indicates editing a file */
+int readonly; /* Will be unable to rewrite file */
+int noreset; /* String resets suspended */
+int sourcing; /* Currently reading variant file */
+int loading; /* Loading user definitions */
+int cond; /* Current state of conditional exc. */
+FILE *itf; /* Input temp file buffer */
+FILE *otf; /* Output temp file buffer */
+int image; /* File descriptor for image of msg */
+FILE *input; /* Current command input file */
+char mailname[PATHSIZE]; /* Name of current file */
+char prevfile[PATHSIZE]; /* Name of previous file */
+char *homedir; /* Path name of home directory */
+const char
+ *myname; /* My login name */
+off_t mailsize; /* Size of system mailbox */
+int lexnumber; /* Number of TNUMBER from scan() */
+char lexstring[STRINGLEN]; /* String from TSTRING, scan() */
+int regretp; /* Pointer to TOS of regret tokens */
+int regretstack[REGDEP]; /* Stack of regretted tokens */
+char *string_stack[REGDEP]; /* Stack of regretted strings */
+int numberstack[REGDEP]; /* Stack of regretted numbers */
+struct message *dot; /* Pointer to current message */
+struct message *message; /* The actual message structure */
+struct var *variables[HSHSIZE]; /* Pointer to active var list */
+struct grouphead *groups[HSHSIZE];/* Pointer to active groups */
+struct ignoretab ignore[2]; /* ignored and retained fields
+ 0 is ignore, 1 is retain */
+struct ignoretab saveignore[2]; /* ignored and retained fields
+ on save to folder */
+struct ignoretab ignoreall[2]; /* special, ignore all headers */
+char **altnames; /* List of alternate names for user */
+int debug; /* Debug flag set */
+int screenwidth; /* Screen width, or best guess */
+int screenheight; /* Screen height, or best guess,
+ for "header" command */
+int realscreenheight; /* the real screen height */
+int uflag; /* Are we in -u mode? */
+sigset_t intset; /* Signal set that is just SIGINT */
+
+/*
+ * The pointers for the string allocation routines,
+ * there are NSPACE independent areas.
+ * The first holds STRINGSIZE bytes, the next
+ * twice as much, and so on.
+ */
+struct strings stringdope[NSPACE];
+
+__dead void usage(void);
+ int main(int, char **);
+
+/*
+ * Mail -- a mail program
+ *
+ * Startup -- interface with user.
+ */
+
+int
+main(int argc, char **argv)
+{
+ int i;
+ struct name *to, *cc, *bcc, *smopts;
+ char *fromaddr;
+ char *subject;
+ char *ef;
+ char nosrc = 0;
+ char *rc;
+ extern const char version[];
+
+ if (pledge("stdio rpath wpath cpath getpw tmppath fattr tty flock proc exec",
+ NULL) == -1)
+ err(1, "pledge");
+
+ /*
+ * Set up a reasonable environment.
+ * Figure out whether we are being run interactively,
+ * start the SIGCHLD catcher, and so forth.
+ */
+ (void)signal(SIGCHLD, sigchild);
+ (void)signal(SIGPIPE, SIG_IGN);
+ if (isatty(0))
+ assign("interactive", "");
+ image = -1;
+ /*
+ * Now, determine how we are being used.
+ * We successively pick off - flags.
+ * If there is anything left, it is the base of the list
+ * of users to mail to. Argp will be set to point to the
+ * first of these users.
+ */
+ ef = NULL;
+ to = NULL;
+ cc = NULL;
+ bcc = NULL;
+ smopts = NULL;
+ fromaddr = NULL;
+ subject = NULL;
+ while ((i = getopt(argc, argv, "EINb:c:dfinr:s:u:v")) != -1) {
+ switch (i) {
+ case 'u':
+ /*
+ * Next argument is person to pretend to be.
+ */
+ if (strlen(optarg) >= LOGIN_NAME_MAX)
+ errx(1, "username `%s' too long", optarg);
+ unsetenv("MAIL");
+ myname = optarg;
+ uflag = 1;
+ break;
+ case 'i':
+ /*
+ * User wants to ignore interrupts.
+ * Set the variable "ignore"
+ */
+ assign("ignore", "");
+ break;
+ case 'd':
+ debug++;
+ break;
+ case 'r':
+ /*
+ * Set From: address
+ */
+ fromaddr = optarg;
+ break;
+ case 's':
+ /*
+ * Give a subject field for sending from
+ * non terminal
+ */
+ subject = optarg;
+ break;
+ case 'f':
+ /*
+ * User is specifying file to "edit" with Mail,
+ * as opposed to reading system mailbox.
+ * We read his mbox file unless another file
+ * is specified after the arguments.
+ */
+ ef = "&";
+ break;
+ case 'n':
+ /*
+ * User doesn't want to source /usr/lib/Mail.rc
+ */
+ nosrc = 1;
+ break;
+ case 'N':
+ /*
+ * Avoid initial header printing.
+ */
+ assign("noheader", "");
+ break;
+ case 'v':
+ /*
+ * Send mailer verbose flag
+ */
+ assign("verbose", "");
+ break;
+ case 'I':
+ /*
+ * We're interactive
+ */
+ assign("interactive", "");
+ break;
+ case 'c':
+ /*
+ * Get Carbon Copy Recipient list
+ */
+ cc = cat(cc, nalloc(optarg, GCC));
+ break;
+ case 'b':
+ /*
+ * Get Blind Carbon Copy Recipient list
+ */
+ bcc = cat(bcc, nalloc(optarg, GBCC));
+ break;
+ case 'E':
+ /*
+ * Don't send messages with an empty body.
+ */
+ assign("skipempty", "");
+ break;
+ default:
+ usage();
+ /*NOTREACHED*/
+ }
+ }
+ if (ef != NULL) {
+ /* Check for optional mailbox file name. */
+ if (optind < argc) {
+ ef = argv[optind++];
+ if (optind < argc)
+ errx(1, "Cannot give -f and people to send to");
+ }
+ } else {
+ for (i = optind; argv[i]; i++)
+ to = cat(to, nalloc(argv[i], GTO));
+ }
+ /*
+ * Check for inconsistent arguments.
+ */
+ if (to == NULL && (subject != NULL || cc != NULL || bcc != NULL ||
+ fromaddr != NULL))
+ errx(1, "You must specify direct recipients with -s, -c, -b, "
+ "or -r");
+ /*
+ * Block SIGINT except where we install an explicit handler for it.
+ */
+ sigemptyset(&intset);
+ sigaddset(&intset, SIGINT);
+ (void)sigprocmask(SIG_BLOCK, &intset, NULL);
+ /*
+ * Initialization.
+ */
+ tinit();
+ setscreensize();
+ input = stdin;
+ rcvmode = !to;
+ spreserve();
+ if (!nosrc)
+ load(_PATH_MASTER_RC);
+ /*
+ * Expand returns a savestr, but load only uses the file name
+ * for fopen, so it's safe to do this.
+ */
+ if ((rc = getenv("MAILRC")) == 0)
+ rc = "~/.mailrc";
+ load(expand(rc));
+ if (!rcvmode) {
+ mail(to, cc, bcc, smopts, fromaddr, subject);
+ /*
+ * why wait?
+ */
+ exit(senderr);
+ }
+ /*
+ * Ok, we are reading mail.
+ * Decide whether we are editing a mailbox or reading
+ * the system mailbox, and open up the right stuff.
+ */
+ if (ef == NULL)
+ ef = "%";
+ if (setfile(ef) < 0)
+ exit(1); /* error already reported */
+
+ if (value("quiet") == NULL)
+ (void)printf("Mail version %s. Type ? for help.\n",
+ version);
+ announce();
+ (void)fflush(stdout);
+ commands();
+ (void)ignoresig(SIGHUP, NULL, NULL);
+ (void)ignoresig(SIGINT, NULL, NULL);
+ (void)ignoresig(SIGQUIT, NULL, NULL);
+ quit();
+ exit(0);
+}
+
+/*
+ * Compute what the screen size for printing headers should be.
+ * We use the following algorithm for the height:
+ * If baud rate < 1200, use 9
+ * If baud rate = 1200, use 14
+ * If baud rate > 1200, use 24 or ws_row
+ * Width is either 80 or ws_col;
+ */
+void
+setscreensize(void)
+{
+ struct termios tbuf;
+ struct winsize ws;
+ speed_t ospeed;
+
+ if (ioctl(1, TIOCGWINSZ, (char *) &ws) == -1)
+ ws.ws_col = ws.ws_row = 0;
+ if (tcgetattr(1, &tbuf) == -1)
+ ospeed = 9600;
+ else
+ ospeed = cfgetospeed(&tbuf);
+ if (ospeed < B1200)
+ screenheight = 9;
+ else if (ospeed == B1200)
+ screenheight = 14;
+ else if (ws.ws_row != 0)
+ screenheight = ws.ws_row;
+ else
+ screenheight = 24;
+ if ((realscreenheight = ws.ws_row) == 0)
+ realscreenheight = 24;
+ if ((screenwidth = ws.ws_col) == 0)
+ screenwidth = 80;
+}
+
+__dead void
+usage(void)
+{
+
+ fprintf(stderr, "usage: %s [-dEIinv] [-b list] [-c list] "
+ "[-r from-addr] [-s subject] to-addr ...\n", __progname);
+ fprintf(stderr, " %s [-dEIiNnv] -f [file]\n", __progname);
+ fprintf(stderr, " %s [-dEIiNnv] [-u user]\n", __progname);
+ exit(1);
+}
diff --git a/misc/mail.help b/misc/mail.help
@@ -0,0 +1,38 @@
+Mail Command Description
+------------------------- --------------------------------------------
+t [message list] type message(s).
+more [message list] read message(s), through the $PAGER
+n goto and type next message.
+e [message list] edit message(s).
+f [message list] give head lines of messages.
+d [message list] delete message(s).
+s [message list] <file> append message(s) to file.
+u [message list] undelete message(s).
+R [message list] reply to message sender(s).
+r [message list] reply to message sender(s) and all recipients.
+p [message list] print message list.
+pre [message list] make messages go back to /var/mail.
+m <recipient list> mail to specific recipient(s).
+q quit, saving unresolved messages in mbox.
+x quit, do not remove system mailbox.
+h print out active message headers.
+! shell escape.
+| [msglist] command pipe message(s) to shell command.
+pi [msglist] command pipe message(s) to shell command.
+cd [directory] chdir to directory or home if none given
+fi <file> switch to file (%=system inbox, %user=user's
+ system inbox). + searches in your folder
+ directory for the file.
+set variable[=value] set Mail variable.
+
+A [message list] consists of integers, ranges of same, :status, /subject, or
+user names separated by spaces. If omitted, Mail uses the current message.
+The pipe command doesn't accept user names or subject message list, since
+that might conflict with the command string.
+
+A <recipient list> consists of recipient addresses or aliases separated by
+spaces. Aliases are defined in .mailrc in your home directory.
+
+<file> is a full or relative pathname, +folder, % (system inbox), %user
+(specified user's system inbox), # (previous file), & (mbox file), or an
+expression understood by ${SHELL:-/bin/sh} -c 'echo ...'.
diff --git a/misc/mail.rc b/misc/mail.rc
@@ -0,0 +1,2 @@
+set append dot save asksub
+ignore Received Message-Id Resent-Message-Id Status Mail-From Return-Path Via
diff --git a/misc/mail.tildehelp b/misc/mail.tildehelp
@@ -0,0 +1,26 @@
+------------------------------------------------------------------------------
+The following ~ escapes are defined:
+~b name ... Add names to "blind" Cc: list.
+~c name ... Add names to Cc: field.
+~d Read dead.letter into message.
+~e Invoke text editor on partial message.
+~F messages Same as ~f, but includes all headers.
+~f messages Read in messages.
+~h Edit the header fields.
+~M messages Same as ~m, but includes all headers.
+~m messages Read in messages, right shifted by a tab.
+~p Print (show) the message buffer.
+~q Abort message; optionally save copy to ~/dead.letter.
+~r file | ~< file Read a file into the message buffer.
+~s string Set Subject: field to string.
+~t name ... Add names to To: field.
+~v Invoke display editor on message.
+~w filename Write message to file.
+~x Abort message; no copy is saved.
+~? Print a brief summary of tilde escapes.
+~!command Execute shell command.
+~|command Pipe message through command.
+~:command | ~_command Execute a Mail command.
+~~string Quote a single tilde.
+~. Simulate end of file on input.
+------------------------------------------------------------------------------
diff --git a/names.c b/names.c
@@ -0,0 +1,628 @@
+/* $OpenBSD: names.c,v 1.25 2019/06/28 13:35:02 deraadt Exp $ */
+/* $NetBSD: names.c,v 1.5 1996/06/08 19:48:32 christos Exp $ */
+
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS 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 REGENTS OR 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.
+ */
+
+/*
+ * Mail -- a mail program
+ *
+ * Handle name lists.
+ */
+
+#include "rcv.h"
+#include <fcntl.h>
+#include "extern.h"
+
+/*
+ * Allocate a single element of a name list,
+ * initialize its name field to the passed
+ * name and return it.
+ */
+struct name *
+nalloc(char *str, int ntype)
+{
+ struct name *np;
+
+ np = (struct name *)salloc(sizeof(*np));
+ np->n_flink = NULL;
+ np->n_blink = NULL;
+ np->n_type = ntype;
+ np->n_name = savestr(str);
+ return(np);
+}
+
+/*
+ * Find the tail of a list and return it.
+ */
+struct name *
+tailof(struct name *name)
+{
+ struct name *np;
+
+ np = name;
+ if (np == NULL)
+ return(NULL);
+ while (np->n_flink != NULL)
+ np = np->n_flink;
+ return(np);
+}
+
+/*
+ * Extract a list of names from a line,
+ * and make a list of names from it.
+ * Return the list or NULL if none found.
+ */
+struct name *
+extract(char *line, int ntype)
+{
+ char *cp;
+ struct name *top, *np, *t;
+ char *nbuf;
+
+ if (line == NULL || *line == '\0')
+ return(NULL);
+ if ((nbuf = malloc(strlen(line) + 1)) == NULL)
+ err(1, "malloc");
+ top = NULL;
+ np = NULL;
+ cp = line;
+ while ((cp = yankword(cp, nbuf)) != NULL) {
+ t = nalloc(nbuf, ntype);
+ if (top == NULL)
+ top = t;
+ else
+ np->n_flink = t;
+ t->n_blink = np;
+ np = t;
+ }
+ (void)free(nbuf);
+ return(top);
+}
+
+/*
+ * Turn a list of names into a string of the same names.
+ */
+char *
+detract(struct name *np, int ntype)
+{
+ int s, comma;
+ char *cp, *top;
+ struct name *p;
+
+ comma = ntype & GCOMMA;
+ if (np == NULL)
+ return(NULL);
+ ntype &= ~GCOMMA;
+ s = 0;
+ if (debug && comma)
+ fputs("detract asked to insert commas\n", stderr);
+ for (p = np; p != NULL; p = p->n_flink) {
+ if (ntype && (p->n_type & GMASK) != ntype)
+ continue;
+ s += strlen(p->n_name) + 1;
+ if (comma)
+ s++;
+ }
+ if (s == 0)
+ return(NULL);
+ s += 2;
+ top = salloc(s);
+ cp = top;
+ for (p = np; p != NULL; p = p->n_flink) {
+ if (ntype && (p->n_type & GMASK) != ntype)
+ continue;
+ cp = copy(p->n_name, cp);
+ if (comma && p->n_flink != NULL)
+ *cp++ = ',';
+ *cp++ = ' ';
+ }
+ *--cp = 0;
+ if (comma && *--cp == ',')
+ *cp = 0;
+ return(top);
+}
+
+/*
+ * Grab a single word (liberal word)
+ * Throw away things between ()'s, and take anything between <>.
+ */
+char *
+yankword(char *ap, char *wbuf)
+{
+ char *cp, *cp2;
+
+ cp = ap;
+ for (;;) {
+ if (*cp == '\0')
+ return(NULL);
+ if (*cp == '(') {
+ int nesting = 0;
+
+ while (*cp != '\0') {
+ switch (*cp++) {
+ case '(':
+ nesting++;
+ break;
+ case ')':
+ --nesting;
+ break;
+ }
+ if (nesting <= 0)
+ break;
+ }
+ } else if (*cp == ' ' || *cp == '\t' || *cp == ',')
+ cp++;
+ else
+ break;
+ }
+ if (*cp == '<')
+ for (cp2 = wbuf; *cp && (*cp2++ = *cp++) != '>';)
+ ;
+ else
+ for (cp2 = wbuf; *cp && !strchr(" \t,(", *cp); *cp2++ = *cp++)
+ ;
+ *cp2 = '\0';
+ return(cp);
+}
+
+/*
+ * For each recipient in the passed name list with a /
+ * in the name, append the message to the end of the named file
+ * and remove him from the recipient list.
+ *
+ * Recipients whose name begins with | are piped through the given
+ * program and removed.
+ */
+struct name *
+outof(struct name *names, FILE *fo, struct header *hp)
+{
+ int c, ispipe;
+ struct name *np, *top;
+ time_t now;
+ char *date, *fname;
+ FILE *fout, *fin;
+
+ if (value("expandaddr") == NULL)
+ return(names);
+
+ top = names;
+ np = names;
+ (void)time(&now);
+ date = ctime(&now);
+ while (np != NULL) {
+ if (!isfileaddr(np->n_name) && np->n_name[0] != '|') {
+ np = np->n_flink;
+ continue;
+ }
+ ispipe = np->n_name[0] == '|';
+ if (ispipe)
+ fname = np->n_name+1;
+ else
+ fname = expand(np->n_name);
+
+ /*
+ * See if we have copied the complete message out yet.
+ * If not, do so.
+ */
+ if (image < 0) {
+ int fd;
+ char tempname[PATHSIZE];
+
+ (void)snprintf(tempname, sizeof(tempname),
+ "%s/mail.ReXXXXXXXXXX", tmpdir);
+ if ((fd = mkstemp(tempname)) == -1 ||
+ (fout = Fdopen(fd, "a")) == NULL) {
+ warn("%s", tempname);
+ senderr++;
+ goto cant;
+ }
+ image = open(tempname, O_RDWR | O_CLOEXEC);
+ (void)rm(tempname);
+ if (image == -1) {
+ warn("%s", tempname);
+ senderr++;
+ (void)Fclose(fout);
+ goto cant;
+ }
+ fprintf(fout, "From %s %s", myname, date);
+ puthead(hp, fout, GTO|GSUBJECT|GCC|GNL);
+ while ((c = getc(fo)) != EOF)
+ (void)putc(c, fout);
+ rewind(fo);
+ (void)putc('\n', fout);
+ (void)fflush(fout);
+ if (ferror(fout))
+ warn("%s", tempname);
+ (void)Fclose(fout);
+ }
+
+ /*
+ * Now either copy "image" to the desired file
+ * or give it as the standard input to the desired
+ * program as appropriate.
+ */
+ if (ispipe) {
+ pid_t pid;
+ char *shell;
+ sigset_t nset;
+
+ /*
+ * XXX
+ * We can't really reuse the same image file,
+ * because multiple piped recipients will
+ * share the same lseek location and trample
+ * on one another.
+ */
+ shell = value("SHELL");
+ sigemptyset(&nset);
+ sigaddset(&nset, SIGHUP);
+ sigaddset(&nset, SIGINT);
+ sigaddset(&nset, SIGQUIT);
+ pid = start_command(shell, &nset,
+ image, -1, "-c", fname, NULL);
+ if (pid < 0) {
+ senderr++;
+ goto cant;
+ }
+ free_child(pid);
+ } else {
+ int f;
+ if ((fout = Fopen(fname, "a")) == NULL) {
+ warn("%s", fname);
+ senderr++;
+ goto cant;
+ }
+ if ((f = dup(image)) == -1) {
+ warn("dup");
+ fin = NULL;
+ } else
+ fin = Fdopen(f, "r");
+ if (fin == NULL) {
+ fputs("Can't reopen image\n", stderr);
+ (void)Fclose(fout);
+ senderr++;
+ goto cant;
+ }
+ rewind(fin);
+ while ((c = getc(fin)) != EOF)
+ (void)putc(c, fout);
+ if (ferror(fout)) {
+ senderr++;
+ warn("%s", fname);
+ }
+ (void)Fclose(fout);
+ (void)Fclose(fin);
+ }
+cant:
+ /*
+ * In days of old we removed the entry from the
+ * the list; now for sake of header expansion
+ * we leave it in and mark it as deleted.
+ */
+ np->n_type |= GDEL;
+ np = np->n_flink;
+ }
+ if (image >= 0) {
+ (void)close(image);
+ image = -1;
+ }
+ return(top);
+}
+
+/*
+ * Determine if the passed address is a local "send to file" address.
+ * If any of the network metacharacters precedes any slashes, it can't
+ * be a filename. We cheat with .'s to allow path names like ./...
+ */
+int
+isfileaddr(char *name)
+{
+ char *cp;
+
+ if (*name == '+')
+ return(1);
+ for (cp = name; *cp; cp++) {
+ if (*cp == '!' || *cp == '%' || *cp == '@')
+ return(0);
+ if (*cp == '/')
+ return(1);
+ }
+ return(0);
+}
+
+/*
+ * Map all of the aliased users in the invoker's mailrc
+ * file and insert them into the list.
+ * Changed after all these months of service to recursively
+ * expand names (2/14/80).
+ */
+struct name *
+usermap(struct name *names)
+{
+ struct name *new, *np, *cp;
+ struct grouphead *gh;
+ int metoo;
+
+ new = NULL;
+ np = names;
+ metoo = (value("metoo") != NULL);
+ while (np != NULL) {
+ if (np->n_name[0] == '\\') {
+ cp = np->n_flink;
+ new = put(new, np);
+ np = cp;
+ continue;
+ }
+ gh = findgroup(np->n_name);
+ cp = np->n_flink;
+ if (gh != NULL)
+ new = gexpand(new, gh, metoo, np->n_type);
+ else
+ new = put(new, np);
+ np = cp;
+ }
+ return(new);
+}
+
+/*
+ * Recursively expand a group name. We limit the expansion to some
+ * fixed level to keep things from going haywire.
+ * Direct recursion is not expanded for convenience.
+ */
+struct name *
+gexpand(struct name *nlist, struct grouphead *gh, int metoo, int ntype)
+{
+ struct group *gp;
+ struct grouphead *ngh;
+ struct name *np;
+ static int depth;
+ char *cp;
+
+ if (depth > MAXEXP) {
+ printf("Expanding alias to depth larger than %d\n", MAXEXP);
+ return(nlist);
+ }
+ depth++;
+ for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) {
+ cp = gp->ge_name;
+ if (*cp == '\\')
+ goto quote;
+ if (strcmp(cp, gh->g_name) == 0)
+ goto quote;
+ if ((ngh = findgroup(cp)) != NULL) {
+ nlist = gexpand(nlist, ngh, metoo, ntype);
+ continue;
+ }
+quote:
+ np = nalloc(cp, ntype);
+ /*
+ * At this point should allow to expand
+ * to self if only person in group
+ */
+ if (gp == gh->g_list && gp->ge_link == NULL)
+ goto skip;
+ if (!metoo && strcmp(cp, myname) == 0)
+ np->n_type |= GDEL;
+skip:
+ nlist = put(nlist, np);
+ }
+ depth--;
+ return(nlist);
+}
+
+/*
+ * Concatenate the two passed name lists, return the result.
+ */
+struct name *
+cat(struct name *n1, struct name *n2)
+{
+ struct name *tail;
+
+ if (n1 == NULL)
+ return(n2);
+ if (n2 == NULL)
+ return(n1);
+ tail = tailof(n1);
+ tail->n_flink = n2;
+ n2->n_blink = tail;
+ return(n1);
+}
+
+/*
+ * Remove all of the duplicates from the passed name list by
+ * insertion sorting them, then checking for dups.
+ * Return the head of the new list.
+ */
+struct name *
+elide(struct name *names)
+{
+ struct name *np, *t, *new;
+ struct name *x;
+
+ if (names == NULL)
+ return(NULL);
+ new = names;
+ np = names;
+ np = np->n_flink;
+ if (np != NULL)
+ np->n_blink = NULL;
+ new->n_flink = NULL;
+ while (np != NULL) {
+ t = new;
+ while (strcasecmp(t->n_name, np->n_name) < 0) {
+ if (t->n_flink == NULL)
+ break;
+ t = t->n_flink;
+ }
+
+ /*
+ * If we ran out of t's, put the new entry after
+ * the current value of t.
+ */
+ if (strcasecmp(t->n_name, np->n_name) < 0) {
+ t->n_flink = np;
+ np->n_blink = t;
+ t = np;
+ np = np->n_flink;
+ t->n_flink = NULL;
+ continue;
+ }
+
+ /*
+ * Otherwise, put the new entry in front of the
+ * current t. If at the front of the list,
+ * the new guy becomes the new head of the list.
+ */
+ if (t == new) {
+ t = np;
+ np = np->n_flink;
+ t->n_flink = new;
+ new->n_blink = t;
+ t->n_blink = NULL;
+ new = t;
+ continue;
+ }
+
+ /*
+ * The normal case -- we are inserting into the
+ * middle of the list.
+ */
+ x = np;
+ np = np->n_flink;
+ x->n_flink = t;
+ x->n_blink = t->n_blink;
+ t->n_blink->n_flink = x;
+ t->n_blink = x;
+ }
+
+ /*
+ * Now the list headed up by new is sorted.
+ * Go through it and remove duplicates.
+ */
+ np = new;
+ while (np != NULL) {
+ t = np;
+ while (t->n_flink != NULL &&
+ strcasecmp(np->n_name, t->n_flink->n_name) == 0)
+ t = t->n_flink;
+ if (t == np || t == NULL) {
+ np = np->n_flink;
+ continue;
+ }
+
+ /*
+ * Now t points to the last entry with the same name
+ * as np. Make np point beyond t.
+ */
+ np->n_flink = t->n_flink;
+ if (t->n_flink != NULL)
+ t->n_flink->n_blink = np;
+ np = np->n_flink;
+ }
+ return(new);
+}
+
+/*
+ * Put another node onto a list of names and return
+ * the list.
+ */
+struct name *
+put(struct name *list, struct name *node)
+{
+ node->n_flink = list;
+ node->n_blink = NULL;
+ if (list != NULL)
+ list->n_blink = node;
+ return(node);
+}
+
+/*
+ * Determine the number of undeleted elements in
+ * a name list and return it.
+ */
+int
+count(struct name *np)
+{
+ int c;
+
+ for (c = 0; np != NULL; np = np->n_flink)
+ if ((np->n_type & GDEL) == 0)
+ c++;
+ return(c);
+}
+
+/*
+ * Delete the given name from a namelist.
+ */
+struct name *
+delname(struct name *np, const char *name)
+{
+ struct name *p;
+
+ for (p = np; p != NULL; p = p->n_flink)
+ if ((strcasecmp(p->n_name, name) == 0) ||
+ (value("allnet") &&
+ strncasecmp(p->n_name, name, strlen(name)) == 0 &&
+ *(p->n_name+strlen(name)) == '@')) {
+ if (p->n_blink == NULL) {
+ if (p->n_flink != NULL)
+ p->n_flink->n_blink = NULL;
+ np = p->n_flink;
+ continue;
+ }
+ if (p->n_flink == NULL) {
+ if (p->n_blink != NULL)
+ p->n_blink->n_flink = NULL;
+ continue;
+ }
+ p->n_blink->n_flink = p->n_flink;
+ p->n_flink->n_blink = p->n_blink;
+ }
+ return(np);
+}
+
+/*
+ * Pretty print a name list
+ * Uncomment it if you need it.
+ */
+#if 0
+void
+prettyprint(struct name *name)
+{
+ struct name *np;
+
+ np = name;
+ while (np != NULL) {
+ fprintf(stderr, "%s(%d) ", np->n_name, np->n_type);
+ np = np->n_flink;
+ }
+ putc('\n', stderr);
+}
+#endif
diff --git a/pathnames.h b/pathnames.h
@@ -0,0 +1,49 @@
+/* $OpenBSD: pathnames.h,v 1.7 2003/06/03 02:56:11 millert Exp $ */
+/* $NetBSD: pathnames.h,v 1.4 1996/06/08 19:48:34 christos Exp $ */
+
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS 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 REGENTS OR 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.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ * $NetBSD: pathnames.h,v 1.4 1996/06/08 19:48:34 christos Exp $
+ */
+
+#include <paths.h>
+
+/* executables */
+#define _PATH_EX "/usr/bin/ex"
+#define _PATH_MORE "/usr/bin/more"
+#define _PATH_LS "/bin/ls"
+#define _PATH_LOCKSPOOL "/usr/libexec/lockspool"
+
+/* directories & files */
+#define _PATH_MAILDIR "/var/mail"
+#define _PATH_HELP "/usr/share/misc/mail.help"
+#define _PATH_TILDE "/usr/share/misc/mail.tildehelp"
+#define _PATH_MASTER_RC "/etc/mail.rc"
+#define _PATH_LOCTMP "/tmp/local.XXXXXXXXXX"
diff --git a/popen.c b/popen.c
@@ -0,0 +1,477 @@
+/* $OpenBSD: popen.c,v 1.39 2019/06/28 13:35:02 deraadt Exp $ */
+/* $NetBSD: popen.c,v 1.6 1997/05/13 06:48:42 mikel Exp $ */
+
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS 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 REGENTS OR 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.
+ */
+
+#include "rcv.h"
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdarg.h>
+#include "extern.h"
+
+#define READ 0
+#define WRITE 1
+
+struct fp {
+ FILE *fp;
+ int pipe;
+ pid_t pid;
+ struct fp *link;
+};
+static struct fp *fp_head;
+
+struct child {
+ pid_t pid;
+ char done;
+ char free;
+ int status;
+ struct child *link;
+};
+static struct child *child, *child_freelist = NULL;
+
+static struct child *findchild(pid_t, int);
+static void delchild(struct child *);
+static pid_t file_pid(FILE *);
+static int handle_spool_locks(int);
+
+FILE *
+Fopen(char *file, char *mode)
+{
+ FILE *fp;
+
+ if ((fp = fopen(file, mode)) != NULL) {
+ register_file(fp, 0, 0);
+ (void)fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
+ }
+ return(fp);
+}
+
+FILE *
+Fdopen(int fd, char *mode)
+{
+ FILE *fp;
+
+ if ((fp = fdopen(fd, mode)) != NULL) {
+ register_file(fp, 0, 0);
+ (void)fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
+ }
+ return(fp);
+}
+
+int
+Fclose(FILE *fp)
+{
+
+ unregister_file(fp);
+ return(fclose(fp));
+}
+
+FILE *
+Popen(char *cmd, char *mode)
+{
+ int p[2];
+ int myside, hisside, fd0, fd1;
+ pid_t pid;
+ sigset_t nset;
+ FILE *fp;
+
+ if (pipe(p) == -1)
+ return(NULL);
+ (void)fcntl(p[READ], F_SETFD, FD_CLOEXEC);
+ (void)fcntl(p[WRITE], F_SETFD, FD_CLOEXEC);
+ if (*mode == 'r') {
+ myside = p[READ];
+ hisside = fd0 = fd1 = p[WRITE];
+ } else {
+ myside = p[WRITE];
+ hisside = fd0 = p[READ];
+ fd1 = -1;
+ }
+ sigemptyset(&nset);
+ pid = start_command(value("SHELL"), &nset, fd0, fd1, "-c", cmd, NULL);
+ if (pid < 0) {
+ (void)close(p[READ]);
+ (void)close(p[WRITE]);
+ return(NULL);
+ }
+ (void)close(hisside);
+ if ((fp = fdopen(myside, mode)) != NULL)
+ register_file(fp, 1, pid);
+ return(fp);
+}
+
+int
+Pclose(FILE *ptr)
+{
+ int i;
+ sigset_t nset, oset;
+
+ i = file_pid(ptr);
+ unregister_file(ptr);
+ (void)fclose(ptr);
+ sigemptyset(&nset);
+ sigaddset(&nset, SIGINT);
+ sigaddset(&nset, SIGHUP);
+ sigprocmask(SIG_BLOCK, &nset, &oset);
+ i = wait_child(i);
+ sigprocmask(SIG_SETMASK, &oset, NULL);
+ return(i);
+}
+
+void
+close_all_files(void)
+{
+
+ while (fp_head)
+ if (fp_head->pipe)
+ (void)Pclose(fp_head->fp);
+ else
+ (void)Fclose(fp_head->fp);
+}
+
+void
+register_file(FILE *fp, int pipe, pid_t pid)
+{
+ struct fp *fpp;
+
+ if ((fpp = malloc(sizeof(*fpp))) == NULL)
+ err(1, "malloc");
+ fpp->fp = fp;
+ fpp->pipe = pipe;
+ fpp->pid = pid;
+ fpp->link = fp_head;
+ fp_head = fpp;
+}
+
+void
+unregister_file(FILE *fp)
+{
+ struct fp **pp, *p;
+
+ for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link)
+ if (p->fp == fp) {
+ *pp = p->link;
+ (void)free(p);
+ return;
+ }
+ errx(1, "Invalid file pointer");
+}
+
+static pid_t
+file_pid(FILE *fp)
+{
+ struct fp *p;
+
+ for (p = fp_head; p; p = p->link)
+ if (p->fp == fp)
+ return(p->pid);
+ errx(1, "Invalid file pointer");
+ /*NOTREACHED*/
+}
+
+/*
+ * Run a command without a shell, with optional arguments and splicing
+ * of stdin (-1 means none) and stdout. The command name can be a sequence
+ * of words.
+ * Signals must be handled by the caller.
+ * "nset" contains the signals to ignore in the new process.
+ * SIGINT is enabled unless it's in "nset".
+ */
+pid_t
+start_commandv(char *cmd, sigset_t *nset, int infd, int outfd, va_list args)
+{
+ pid_t pid;
+
+ if ((pid = fork()) == -1) {
+ warn("fork");
+ return(-1);
+ }
+ if (pid == 0) {
+ char *argv[100];
+ int i = getrawlist(cmd, argv, sizeof(argv)/ sizeof(*argv));
+
+ while ((argv[i++] = va_arg(args, char *)))
+ ;
+ argv[i] = NULL;
+ prepare_child(nset, infd, outfd);
+ execvp(argv[0], argv);
+ warn("%s", argv[0]);
+ _exit(1);
+ }
+ return(pid);
+}
+
+int
+run_command(char *cmd, sigset_t *nset, int infd, int outfd, ...)
+{
+ pid_t pid;
+ va_list args;
+
+ va_start(args, outfd);
+ pid = start_commandv(cmd, nset, infd, outfd, args);
+ va_end(args);
+ if (pid < 0)
+ return(-1);
+ return(wait_command(pid));
+}
+
+int
+start_command(char *cmd, sigset_t *nset, int infd, int outfd, ...)
+{
+ va_list args;
+ int r;
+
+ va_start(args, outfd);
+ r = start_commandv(cmd, nset, infd, outfd, args);
+ va_end(args);
+ return(r);
+}
+
+void
+prepare_child(sigset_t *nset, int infd, int outfd)
+{
+ int i;
+ sigset_t eset;
+
+ /*
+ * All file descriptors other than 0, 1, and 2 are supposed to be
+ * close-on-exec.
+ */
+ if (infd > 0) {
+ dup2(infd, 0);
+ } else if (infd != 0) {
+ /* we don't want the child stealing my stdin input */
+ close(0);
+ open(_PATH_DEVNULL, O_RDONLY, 0);
+ }
+ if (outfd >= 0 && outfd != 1)
+ dup2(outfd, 1);
+ if (nset == NULL)
+ return;
+ if (nset != NULL) {
+ for (i = 1; i < NSIG; i++)
+ if (sigismember(nset, i))
+ (void)signal(i, SIG_IGN);
+ }
+ if (nset == NULL || !sigismember(nset, SIGINT))
+ (void)signal(SIGINT, SIG_DFL);
+ sigemptyset(&eset);
+ (void)sigprocmask(SIG_SETMASK, &eset, NULL);
+}
+
+int
+wait_command(pid_t pid)
+{
+
+ if (wait_child(pid) < 0) {
+ puts("Fatal error in process.");
+ return(-1);
+ }
+ return(0);
+}
+
+static struct child *
+findchild(pid_t pid, int dont_alloc)
+{
+ struct child **cpp;
+
+ for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid;
+ cpp = &(*cpp)->link)
+ ;
+ if (*cpp == NULL) {
+ if (dont_alloc)
+ return(NULL);
+ if (child_freelist) {
+ *cpp = child_freelist;
+ child_freelist = (*cpp)->link;
+ } else {
+ *cpp = malloc(sizeof(struct child));
+ if (*cpp == NULL)
+ err(1, "malloc");
+ }
+ (*cpp)->pid = pid;
+ (*cpp)->done = (*cpp)->free = 0;
+ (*cpp)->link = NULL;
+ }
+ return(*cpp);
+}
+
+static void
+delchild(struct child *cp)
+{
+ struct child **cpp;
+
+ for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link)
+ ;
+ *cpp = cp->link;
+ cp->link = child_freelist;
+ child_freelist = cp;
+}
+
+/* ARGSUSED */
+void
+sigchild(int signo)
+{
+ pid_t pid;
+ int status;
+ struct child *cp;
+ int save_errno = errno;
+
+ while ((pid = waitpid((pid_t)-1, &status, WNOHANG)) > 0) {
+ cp = findchild(pid, 1);
+ if (!cp)
+ continue;
+ if (cp->free)
+ delchild(cp);
+ else {
+ cp->done = 1;
+ cp->status = status;
+ }
+ }
+ errno = save_errno;
+}
+
+int wait_status;
+
+/*
+ * Wait for a specific child to die.
+ */
+int
+wait_child(pid_t pid)
+{
+ struct child *cp;
+ sigset_t nset, oset;
+ pid_t rv = 0;
+
+ sigemptyset(&nset);
+ sigaddset(&nset, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &nset, &oset);
+ /*
+ * If we have not already waited on the pid (via sigchild)
+ * wait on it now. Otherwise, use the wait status stashed
+ * by sigchild.
+ */
+ cp = findchild(pid, 1);
+ if (cp == NULL || !cp->done)
+ rv = waitpid(pid, &wait_status, 0);
+ else
+ wait_status = cp->status;
+ if (cp != NULL)
+ delchild(cp);
+ sigprocmask(SIG_SETMASK, &oset, NULL);
+ if (rv == -1 || (WIFEXITED(wait_status) && WEXITSTATUS(wait_status)))
+ return(-1);
+ else
+ return(0);
+}
+
+/*
+ * Mark a child as don't care.
+ */
+void
+free_child(pid_t pid)
+{
+ struct child *cp;
+ sigset_t nset, oset;
+
+ sigemptyset(&nset);
+ sigaddset(&nset, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &nset, &oset);
+ if ((cp = findchild(pid, 0)) != NULL) {
+ if (cp->done)
+ delchild(cp);
+ else
+ cp->free = 1;
+ }
+ sigprocmask(SIG_SETMASK, &oset, NULL);
+}
+
+/*
+ * Lock(1)/unlock(0) mail spool using lockspool(1).
+ * Returns 1 for success, 0 for failure, -1 for bad usage.
+ */
+static int
+handle_spool_locks(int action)
+{
+ static FILE *lockfp = NULL;
+
+ if (action == 0) {
+ /* Clear the lock */
+ if (lockfp == NULL) {
+ fputs("handle_spool_locks: no spool lock to remove.\n",
+ stderr);
+ return(-1);
+ }
+ (void)Pclose(lockfp);
+ lockfp = NULL;
+ } else if (action == 1) {
+ char *cmd;
+ char buf[sizeof(_PATH_LOCKSPOOL) + LOGIN_NAME_MAX + 1];
+
+ /* XXX - lockspool requires root for user arg, we do not */
+ if (uflag) {
+ snprintf(buf, sizeof(buf), "%s %s", _PATH_LOCKSPOOL,
+ myname);
+ cmd = buf;
+ } else
+ cmd = _PATH_LOCKSPOOL;
+
+ /* Create the lock */
+ lockfp = Popen(cmd, "r");
+ if (lockfp == NULL)
+ return(0);
+ if (getc(lockfp) != '1') {
+ Pclose(lockfp);
+ lockfp = NULL;
+ return(0);
+ }
+ } else {
+ (void)fprintf(stderr, "handle_spool_locks: unknown action %d\n",
+ action);
+ return(-1);
+ }
+
+ return(1);
+}
+
+int
+spool_lock(void)
+{
+
+ return(handle_spool_locks(1));
+}
+
+int
+spool_unlock(void)
+{
+
+ return(handle_spool_locks(0));
+}
diff --git a/quit.c b/quit.c
@@ -0,0 +1,486 @@
+/* $OpenBSD: quit.c,v 1.23 2016/07/19 06:43:27 deraadt Exp $ */
+/* $NetBSD: quit.c,v 1.6 1996/12/28 07:11:07 tls Exp $ */
+
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS 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 REGENTS OR 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.
+ */
+
+#include "rcv.h"
+#include <fcntl.h>
+#include "extern.h"
+
+/*
+ * Rcv -- receive mail rationally.
+ *
+ * Termination processing.
+ */
+
+/*
+ * The "quit" command.
+ */
+int
+quitcmd(void *v)
+{
+ /*
+ * If we are sourcing, then return 1 so execute() can handle it.
+ * Otherwise, return -1 to abort command loop.
+ */
+ if (sourcing)
+ return(1);
+ return(-1);
+}
+
+/*
+ * Save all of the undetermined messages at the top of "mbox"
+ * Save all untouched messages back in the system mailbox.
+ * Remove the system mailbox, if none saved there.
+ */
+int
+quit(void)
+{
+ int mcount, p, modify, autohold, anystat, holdbit, nohold;
+ FILE *ibuf = NULL, *obuf, *fbuf, *rbuf, *abuf;
+ struct message *mp;
+ int c, fd;
+ struct stat minfo;
+ char *mbox, tempname[PATHSIZE];
+
+ /*
+ * If we are read only, we can't do anything,
+ * so just return quickly.
+ */
+ if (readonly)
+ return(0);
+
+ /*
+ * If editing (not reading system mail box), then do the work
+ * in edstop()
+ */
+ if (edit)
+ return(edstop());
+
+ /*
+ * See if there any messages to save in mbox. If no, we
+ * can save copying mbox to /tmp and back.
+ *
+ * Check also to see if any files need to be preserved.
+ * Delete all untouched messages to keep them out of mbox.
+ * If all the messages are to be preserved, just exit with
+ * a message.
+ */
+ fbuf = Fopen(mailname, "r+");
+ if (fbuf == NULL)
+ goto newmail;
+ if (flock(fileno(fbuf), LOCK_EX) == -1) {
+ warn("Unable to lock mailbox");
+ (void)Fclose(fbuf);
+ return(-1);
+ }
+ if (!spool_lock()) {
+ (void)Fclose(fbuf);
+ return(-1); /* lockspool printed the error for us */
+ }
+ rbuf = NULL;
+ if (fstat(fileno(fbuf), &minfo) >= 0 && minfo.st_size > mailsize) {
+ puts("New mail has arrived.");
+ (void)snprintf(tempname, sizeof(tempname),
+ "%s/mail.RqXXXXXXXXXX", tmpdir);
+ if ((fd = mkstemp(tempname)) == -1 ||
+ (rbuf = Fdopen(fd, "w")) == NULL)
+ goto newmail;
+#ifdef APPEND
+ fseek(fbuf, (long)mailsize, SEEK_SET);
+ while ((c = getc(fbuf)) != EOF)
+ (void)putc(c, rbuf);
+#else
+ p = minfo.st_size - mailsize;
+ while (p-- > 0) {
+ c = getc(fbuf);
+ if (c == EOF)
+ goto newmail;
+ (void)putc(c, rbuf);
+ }
+#endif
+ (void)Fclose(rbuf);
+ if ((rbuf = Fopen(tempname, "r")) == NULL)
+ goto newmail;
+ (void)rm(tempname);
+ }
+
+ /*
+ * Adjust the message flags in each message.
+ */
+ anystat = 0;
+ autohold = value("hold") != NULL;
+ holdbit = autohold ? MPRESERVE : MBOX;
+ nohold = MBOX|MSAVED|MDELETED|MPRESERVE;
+ if (value("keepsave") != NULL)
+ nohold &= ~MSAVED;
+ for (mp = &message[0]; mp < &message[msgCount]; mp++) {
+ if (mp->m_flag & MNEW) {
+ mp->m_flag &= ~MNEW;
+ mp->m_flag |= MSTATUS;
+ }
+ if (mp->m_flag & MSTATUS)
+ anystat++;
+ if ((mp->m_flag & MTOUCH) == 0)
+ mp->m_flag |= MPRESERVE;
+ if ((mp->m_flag & nohold) == 0)
+ mp->m_flag |= holdbit;
+ }
+ modify = 0;
+ for (c = 0, p = 0, mp = &message[0]; mp < &message[msgCount]; mp++) {
+ if (mp->m_flag & MBOX)
+ c++;
+ if (mp->m_flag & MPRESERVE)
+ p++;
+ if (mp->m_flag & MODIFY)
+ modify++;
+ }
+ if (p == msgCount && !modify && !anystat) {
+ printf("Held %d message%s in %s\n",
+ p, p == 1 ? "" : "s", mailname);
+ (void)Fclose(fbuf);
+ spool_unlock();
+ return(0);
+ }
+ if (c == 0) {
+ if (p != 0) {
+ writeback(rbuf);
+ (void)Fclose(fbuf);
+ spool_unlock();
+ return(0);
+ }
+ goto cream;
+ }
+
+ /*
+ * Create another temporary file and copy user's mbox file
+ * darin. If there is no mbox, copy nothing.
+ * If he has specified "append" don't copy his mailbox,
+ * just copy saveable entries at the end.
+ */
+ mbox = expand("&");
+ mcount = c;
+ if (value("append") == NULL) {
+ int fdx;
+
+ (void)snprintf(tempname, sizeof(tempname),
+ "%s/mail.RmXXXXXXXXXX", tmpdir);
+ if ((fd = mkstemp(tempname)) == -1 ||
+ (obuf = Fdopen(fd, "w")) == NULL) {
+ warn("%s", tempname);
+ (void)Fclose(fbuf);
+ spool_unlock();
+ return(-1);
+ }
+ if ((ibuf = Fopen(tempname, "r")) == NULL) {
+ warn("%s", tempname);
+ (void)rm(tempname);
+ (void)Fclose(obuf);
+ (void)Fclose(fbuf);
+ spool_unlock();
+ return(-1);
+ }
+ (void)rm(tempname);
+ if ((abuf = Fopen(mbox, "r")) != NULL) {
+ while ((c = getc(abuf)) != EOF)
+ (void)putc(c, obuf);
+ (void)Fclose(abuf);
+ }
+ if (ferror(obuf)) {
+ warn("%s", tempname);
+ (void)Fclose(ibuf);
+ (void)Fclose(obuf);
+ (void)Fclose(fbuf);
+ spool_unlock();
+ return(-1);
+ }
+ (void)Fclose(obuf);
+ if ((fdx = open(mbox, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1)
+ close(fdx);
+ if ((obuf = Fopen(mbox, "r+")) == NULL) {
+ warn("%s", mbox);
+ (void)Fclose(ibuf);
+ (void)Fclose(fbuf);
+ spool_unlock();
+ return(-1);
+ }
+ } else {
+ if ((obuf = Fopen(mbox, "a")) == NULL) {
+ warn("%s", mbox);
+ (void)Fclose(fbuf);
+ spool_unlock();
+ return(-1);
+ }
+ fchmod(fileno(obuf), 0600);
+ }
+ for (mp = &message[0]; mp < &message[msgCount]; mp++)
+ if (mp->m_flag & MBOX)
+ if (sendmessage(mp, obuf, saveignore, NULL) < 0) {
+ warn("%s", mbox);
+ (void)Fclose(ibuf);
+ (void)Fclose(obuf);
+ (void)Fclose(fbuf);
+ spool_unlock();
+ return(-1);
+ }
+
+ /*
+ * Copy the user's old mbox contents back
+ * to the end of the stuff we just saved.
+ * If we are appending, this is unnecessary.
+ */
+ if (value("append") == NULL) {
+ rewind(ibuf);
+ c = getc(ibuf);
+ while (c != EOF) {
+ (void)putc(c, obuf);
+ if (ferror(obuf))
+ break;
+ c = getc(ibuf);
+ }
+ (void)Fclose(ibuf);
+ fflush(obuf);
+ }
+ trunc(obuf);
+ if (ferror(obuf)) {
+ warn("%s", mbox);
+ (void)Fclose(obuf);
+ (void)Fclose(fbuf);
+ spool_unlock();
+ return(-1);
+ }
+ (void)Fclose(obuf);
+ if (mcount == 1)
+ puts("Saved 1 message in mbox");
+ else
+ printf("Saved %d messages in mbox\n", mcount);
+
+ /*
+ * Now we are ready to copy back preserved files to
+ * the system mailbox, if any were requested.
+ */
+ if (p != 0) {
+ writeback(rbuf);
+ (void)Fclose(fbuf);
+ spool_unlock();
+ return(0);
+ }
+
+ /*
+ * Finally, remove his /var/mail file.
+ * If new mail has arrived, copy it back.
+ */
+cream:
+ if (rbuf != NULL) {
+ abuf = Fopen(mailname, "r+");
+ if (abuf == NULL)
+ goto newmail;
+ while ((c = getc(rbuf)) != EOF)
+ (void)putc(c, abuf);
+ (void)Fclose(rbuf);
+ trunc(abuf);
+ (void)Fclose(abuf);
+ alter(mailname);
+ (void)Fclose(fbuf);
+ spool_unlock();
+ return(0);
+ }
+ demail();
+ (void)Fclose(fbuf);
+ spool_unlock();
+ return(0);
+
+newmail:
+ puts("Thou hast new mail.");
+ if (fbuf != NULL) {
+ (void)Fclose(fbuf);
+ spool_unlock();
+ }
+ return(0);
+}
+
+/*
+ * Preserve all the appropriate messages back in the system
+ * mailbox, and print a nice message indicated how many were
+ * saved. On any error, just return -1. Else return 0.
+ * Incorporate the any new mail that we found.
+ */
+int
+writeback(FILE *res)
+{
+ struct message *mp;
+ int p, c;
+ FILE *obuf;
+
+ p = 0;
+ if ((obuf = Fopen(mailname, "r+")) == NULL) {
+ warn("%s", mailname);
+ return(-1);
+ }
+#ifndef APPEND
+ if (res != NULL)
+ while ((c = getc(res)) != EOF)
+ (void)putc(c, obuf);
+#endif
+ for (mp = &message[0]; mp < &message[msgCount]; mp++)
+ if ((mp->m_flag&MPRESERVE)||(mp->m_flag&MTOUCH)==0) {
+ p++;
+ if (sendmessage(mp, obuf, NULL, NULL) < 0) {
+ warn("%s", mailname);
+ (void)Fclose(obuf);
+ return(-1);
+ }
+ }
+#ifdef APPEND
+ if (res != NULL)
+ while ((c = getc(res)) != EOF)
+ (void)putc(c, obuf);
+#endif
+ fflush(obuf);
+ trunc(obuf);
+ if (ferror(obuf)) {
+ warn("%s", mailname);
+ (void)Fclose(obuf);
+ return(-1);
+ }
+ if (res != NULL)
+ (void)Fclose(res);
+ (void)Fclose(obuf);
+ alter(mailname);
+ if (p == 1)
+ printf("Held 1 message in %s\n", mailname);
+ else
+ printf("Held %d messages in %s\n", p, mailname);
+ return(0);
+}
+
+/*
+ * Terminate an editing session by attempting to write out the user's
+ * file from the temporary. Save any new stuff appended to the file.
+ */
+int
+edstop(void)
+{
+ int gotcha, c;
+ struct message *mp;
+ FILE *obuf, *ibuf;
+ struct stat statb;
+ char tempname[PATHSIZE];
+
+ if (readonly)
+ return(0);
+ holdsigs();
+ for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) {
+ if (mp->m_flag & MNEW) {
+ mp->m_flag &= ~MNEW;
+ mp->m_flag |= MSTATUS;
+ }
+ if (mp->m_flag & (MODIFY|MDELETED|MSTATUS))
+ gotcha++;
+ }
+ if (!gotcha)
+ goto done;
+ ibuf = NULL;
+ if (stat(mailname, &statb) >= 0 && statb.st_size > mailsize) {
+ int fd;
+
+ (void)snprintf(tempname, sizeof(tempname), "%s/mbox.XXXXXXXXXX",
+ tmpdir);
+ if ((fd = mkstemp(tempname)) == -1 ||
+ (obuf = Fdopen(fd, "w")) == NULL) {
+ warn("%s", tempname);
+ if (fd != -1)
+ close(fd);
+ relsesigs();
+ return(-1);
+ }
+ if ((ibuf = Fopen(mailname, "r")) == NULL) {
+ warn("%s", mailname);
+ (void)Fclose(obuf);
+ (void)rm(tempname);
+ relsesigs();
+ return(-1);
+ }
+ fseek(ibuf, (long)mailsize, SEEK_SET);
+ while ((c = getc(ibuf)) != EOF)
+ (void)putc(c, obuf);
+ (void)Fclose(ibuf);
+ (void)Fclose(obuf);
+ if ((ibuf = Fopen(tempname, "r")) == NULL) {
+ warn("%s", tempname);
+ (void)rm(tempname);
+ relsesigs();
+ return(-1);
+ }
+ (void)rm(tempname);
+ }
+ printf("\"%s\" ", mailname);
+ fflush(stdout);
+ if ((obuf = Fopen(mailname, "r+")) == NULL) {
+ warn("%s", mailname);
+ relsesigs();
+ return(-1);
+ }
+ trunc(obuf);
+ c = 0;
+ for (mp = &message[0]; mp < &message[msgCount]; mp++) {
+ if ((mp->m_flag & MDELETED) != 0)
+ continue;
+ c++;
+ if (sendmessage(mp, obuf, NULL, NULL) < 0) {
+ warn("%s", mailname);
+ relsesigs();
+ return(-1);
+ }
+ }
+ gotcha = (c == 0 && ibuf == NULL);
+ if (ibuf != NULL) {
+ while ((c = getc(ibuf)) != EOF)
+ (void)putc(c, obuf);
+ (void)Fclose(ibuf);
+ }
+ fflush(obuf);
+ if (ferror(obuf)) {
+ warn("%s", mailname);
+ relsesigs();
+ return(-1);
+ }
+ (void)Fclose(obuf);
+ if (gotcha) {
+ (void)rm(mailname);
+ puts("removed");
+ } else
+ puts("complete");
+ fflush(stdout);
+
+done:
+ relsesigs();
+ return(0);
+}
diff --git a/rcv.h b/rcv.h
@@ -0,0 +1,44 @@
+/* $OpenBSD: rcv.h,v 1.3 2003/06/03 02:56:11 millert Exp $ */
+/* $NetBSD: rcv.h,v 1.4 1996/06/08 19:48:38 christos Exp $ */
+
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS 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 REGENTS OR 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.
+ *
+ * @(#)rcv.h 8.1 (Berkeley) 6/6/93
+ * $NetBSD: rcv.h,v 1.4 1996/06/08 19:48:38 christos Exp $
+ */
+
+/*
+ * Mail -- a mail program
+ *
+ * This file is included by normal files which want both
+ * globals and declarations.
+ */
+
+#include "def.h"
+#include "glob.h"
diff --git a/send.c b/send.c
@@ -0,0 +1,614 @@
+/* $OpenBSD: send.c,v 1.25 2019/03/19 13:26:27 millert Exp $ */
+/* $NetBSD: send.c,v 1.6 1996/06/08 19:48:39 christos Exp $ */
+
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS 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 REGENTS OR 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.
+ */
+
+#include "rcv.h"
+#include "extern.h"
+
+static volatile sig_atomic_t sendsignal; /* Interrupted by a signal? */
+
+/*
+ * Mail -- a mail program
+ *
+ * Mail to others.
+ */
+
+/*
+ * Send message described by the passed pointer to the
+ * passed output buffer. Return -1 on error.
+ * Adjust the status: field if need be.
+ * If doign is given, suppress ignored header fields.
+ * prefix is a string to prepend to each output line.
+ */
+int
+sendmessage(struct message *mp, FILE *obuf, struct ignoretab *doign,
+ char *prefix)
+{
+ int count;
+ FILE *ibuf;
+ char line[LINESIZE];
+ char visline[4 * LINESIZE - 3];
+ int ishead, infld, ignoring = 0, dostat, firstline;
+ char *cp, *cp2;
+ int c = 0;
+ int length;
+ int prefixlen = 0;
+ int rval;
+ int dovis;
+ struct sigaction act, saveint;
+ sigset_t oset;
+
+ sendsignal = 0;
+ rval = -1;
+ dovis = isatty(fileno(obuf));
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+ act.sa_handler = sendint;
+ (void)sigaction(SIGINT, &act, &saveint);
+ (void)sigprocmask(SIG_UNBLOCK, &intset, &oset);
+
+ /*
+ * Compute the prefix string, without trailing whitespace
+ */
+ if (prefix != NULL) {
+ cp2 = 0;
+ for (cp = prefix; *cp; cp++)
+ if (*cp != ' ' && *cp != '\t')
+ cp2 = cp;
+ prefixlen = cp2 == 0 ? 0 : cp2 - prefix + 1;
+ }
+ ibuf = setinput(mp);
+ count = mp->m_size;
+ ishead = 1;
+ dostat = doign == 0 || !isign("status", doign);
+ infld = 0;
+ firstline = 1;
+ /*
+ * Process headers first
+ */
+ while (count > 0 && ishead) {
+ if (fgets(line, sizeof(line), ibuf) == NULL)
+ break;
+ count -= length = strlen(line);
+ if (firstline) {
+ /*
+ * First line is the From line, so no headers
+ * there to worry about
+ */
+ firstline = 0;
+ ignoring = doign == ignoreall;
+ } else if (line[0] == '\n') {
+ /*
+ * If line is blank, we've reached end of
+ * headers, so force out status: field
+ * and note that we are no longer in header
+ * fields
+ */
+ if (dostat) {
+ if (statusput(mp, obuf, prefix) == -1)
+ goto out;
+ dostat = 0;
+ }
+ ishead = 0;
+ ignoring = doign == ignoreall;
+ } else if (infld && (line[0] == ' ' || line[0] == '\t')) {
+ /*
+ * If this line is a continuation (via space or tab)
+ * of a previous header field, just echo it
+ * (unless the field should be ignored).
+ * In other words, nothing to do.
+ */
+ } else {
+ /*
+ * Pick up the header field if we have one.
+ */
+ for (cp = line;
+ (c = (unsigned char)*cp++) && c != ':' && !isspace(c); )
+ ;
+ cp2 = --cp;
+ while (isspace((unsigned char)*cp++))
+ ;
+ if (cp[-1] != ':') {
+ /*
+ * Not a header line, force out status:
+ * This happens in uucp style mail where
+ * there are no headers at all.
+ */
+ if (dostat) {
+ if (statusput(mp, obuf, prefix) == -1)
+ goto out;
+ dostat = 0;
+ }
+ if (doign != ignoreall)
+ /* add blank line */
+ (void)putc('\n', obuf);
+ ishead = 0;
+ ignoring = 0;
+ } else {
+ /*
+ * If it is an ignored field and
+ * we care about such things, skip it.
+ */
+ *cp2 = 0; /* temporarily null terminate */
+ if (doign && isign(line, doign))
+ ignoring = 1;
+ else if (strcasecmp(line, "status") == 0) {
+ /*
+ * If the field is "status," go compute
+ * and print the real Status: field
+ */
+ if (dostat) {
+ if (statusput(mp, obuf, prefix) == -1)
+ goto out;
+ dostat = 0;
+ }
+ ignoring = 1;
+ } else {
+ ignoring = 0;
+ *cp2 = c; /* restore */
+ }
+ infld = 1;
+ }
+ }
+ if (!ignoring) {
+ /*
+ * Strip trailing whitespace from prefix
+ * if line is blank.
+ */
+ if (prefix != NULL) {
+ if (length > 1)
+ fputs(prefix, obuf);
+ else
+ (void)fwrite(prefix, sizeof(*prefix),
+ prefixlen, obuf);
+ }
+ if (dovis) {
+ length = strvis(visline, line, VIS_SAFE|VIS_NOSLASH);
+ (void)fwrite(visline, sizeof(*visline), length, obuf);
+ } else
+ (void)fwrite(line, sizeof(*line), length, obuf);
+ if (ferror(obuf))
+ goto out;
+ }
+ if (sendsignal == SIGINT)
+ goto out;
+ }
+ /*
+ * Copy out message body
+ */
+ if (doign == ignoreall)
+ count--; /* skip final blank line */
+ while (count > 0) {
+ if (fgets(line, sizeof(line), ibuf) == NULL) {
+ c = 0;
+ break;
+ }
+ count -= c = strlen(line);
+ if (prefix != NULL) {
+ /*
+ * Strip trailing whitespace from prefix
+ * if line is blank.
+ */
+ if (c > 1)
+ fputs(prefix, obuf);
+ else
+ (void)fwrite(prefix, sizeof(*prefix),
+ prefixlen, obuf);
+ }
+ /*
+ * We can't read the record file (or inbox for recipient)
+ * properly with 'From ' lines in the message body (from
+ * forwarded messages or sentences starting with "From "),
+ * so we will prepend those lines with a '>'.
+ */
+ if (strncmp(line, "From ", 5) == 0)
+ (void)fwrite(">", 1, 1, obuf); /* '>' before 'From ' */
+ if (dovis) {
+ length = strvis(visline, line, VIS_SAFE|VIS_NOSLASH);
+ (void)fwrite(visline, sizeof(*visline), length, obuf);
+ } else
+ (void)fwrite(line, sizeof(*line), c, obuf);
+ if (ferror(obuf) || sendsignal == SIGINT)
+ goto out;
+ }
+ if (doign == ignoreall && c > 0 && line[c - 1] != '\n')
+ /* no final blank line */
+ if ((c = getc(ibuf)) != EOF && putc(c, obuf) == EOF)
+ goto out;
+ rval = 0;
+out:
+ sendsignal = 0;
+ (void)sigprocmask(SIG_SETMASK, &oset, NULL);
+ (void)sigaction(SIGINT, &saveint, NULL);
+ return(rval);
+}
+
+/*
+ * Output a reasonable looking status field.
+ */
+int
+statusput(struct message *mp, FILE *obuf, char *prefix)
+{
+ char statout[3];
+ char *cp = statout;
+
+ if (mp->m_flag & MREAD)
+ *cp++ = 'R';
+ if ((mp->m_flag & MNEW) == 0)
+ *cp++ = 'O';
+ *cp = 0;
+ if (statout[0]) {
+ fprintf(obuf, "%sStatus: %s\n",
+ prefix == NULL ? "" : prefix, statout);
+ return(ferror(obuf) ? -1 : 0);
+ }
+ return(0);
+}
+
+/*
+ * Interface between the argument list and the mail1 routine
+ * which does all the dirty work.
+ */
+int
+mail(struct name *to, struct name *cc, struct name *bcc, struct name *smopts,
+ char *fromaddr, char *subject)
+{
+ struct header head;
+
+ head.h_to = to;
+ head.h_from = fromaddr;
+ head.h_subject = subject;
+ head.h_cc = cc;
+ head.h_bcc = bcc;
+ head.h_smopts = smopts;
+ mail1(&head, 0);
+ return(0);
+}
+
+/*
+ * Send mail to a bunch of user names. The interface is through
+ * the mail routine below.
+ */
+int
+sendmail(void *v)
+{
+ char *str = v;
+ struct header head;
+
+ head.h_to = extract(str, GTO);
+ head.h_from = NULL;
+ head.h_subject = NULL;
+ head.h_cc = NULL;
+ head.h_bcc = NULL;
+ head.h_smopts = NULL;
+ mail1(&head, 0);
+ return(0);
+}
+
+/*
+ * Mail a message on standard input to the people indicated
+ * in the passed header. (Internal interface).
+ */
+void
+mail1(struct header *hp, int printheaders)
+{
+ char *cp, *envfrom = NULL;
+ char *argv[8];
+ char **ap = argv;
+ pid_t pid;
+ struct name *to;
+ FILE *mtf;
+
+ /*
+ * Collect user's mail from standard input.
+ * Get the result as mtf.
+ */
+ if ((mtf = collect(hp, printheaders)) == NULL)
+ return;
+ if (fsize(mtf) == 0) {
+ if (value("skipempty") != NULL)
+ goto out;
+ if (hp->h_subject == NULL || *hp->h_subject == '\0')
+ puts("No message, no subject; hope that's ok");
+ else
+ puts("Null message body; hope that's ok");
+ }
+ /*
+ * Now, take the user names from the combined
+ * to and cc lists and do all the alias
+ * processing.
+ */
+ senderr = 0;
+ to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc)));
+ if (to == NULL) {
+ puts("No recipients specified");
+ senderr++;
+ }
+ /*
+ * Look through the recipient list for names with /'s
+ * in them which we write to as files directly.
+ */
+ to = outof(to, mtf, hp);
+ if (senderr)
+ savedeadletter(mtf);
+ to = elide(to);
+ if (count(to) == 0)
+ goto out;
+ fixhead(hp, to);
+ if ((mtf = infix(hp, mtf)) == NULL) {
+ fputs(". . . message lost, sorry.\n", stderr);
+ return;
+ }
+ if ((cp = value("record")) != NULL)
+ (void)savemail(expand(cp), mtf);
+
+ /* Setup sendmail arguments. */
+ *ap++ = "sendmail";
+ *ap++ = "-i";
+ *ap++ = "-t";
+ cp = hp->h_from ? hp->h_from : value("from");
+ if (cp != NULL) {
+ envfrom = skin(cp);
+ *ap++ = "-f";
+ *ap++ = envfrom;
+ if (envfrom == cp)
+ envfrom = NULL;
+ }
+ if (value("metoo") != NULL)
+ *ap++ = "-m";
+ if (value("verbose") != NULL)
+ *ap++ = "-v";
+ *ap = NULL;
+ if (debug) {
+ fputs("Sendmail arguments:", stdout);
+ for (ap = argv; *ap != NULL; ap++)
+ printf(" \"%s\"", *ap);
+ putchar('\n');
+ goto out;
+ }
+ /*
+ * Fork, set up the temporary mail file as standard
+ * input for "mail", and exec with the user list we generated
+ * far above.
+ */
+ pid = fork();
+ if (pid == -1) {
+ warn("fork");
+ savedeadletter(mtf);
+ goto out;
+ }
+ if (pid == 0) {
+ sigset_t nset;
+
+ sigemptyset(&nset);
+ sigaddset(&nset, SIGHUP);
+ sigaddset(&nset, SIGINT);
+ sigaddset(&nset, SIGQUIT);
+ sigaddset(&nset, SIGTSTP);
+ sigaddset(&nset, SIGTTIN);
+ sigaddset(&nset, SIGTTOU);
+ prepare_child(&nset, fileno(mtf), -1);
+ if ((cp = value("sendmail")) != NULL)
+ cp = expand(cp);
+ else
+ cp = _PATH_SENDMAIL;
+ execv(cp, argv);
+ warn("%s", cp);
+ _exit(1);
+ }
+ free(envfrom);
+ if (value("verbose") != NULL)
+ (void)wait_child(pid);
+ else
+ free_child(pid);
+out:
+ (void)Fclose(mtf);
+}
+
+/*
+ * Fix the header by glopping all of the expanded names from
+ * the distribution list into the appropriate fields.
+ */
+void
+fixhead(struct header *hp, struct name *tolist)
+{
+ struct name *np;
+
+ hp->h_to = NULL;
+ hp->h_cc = NULL;
+ hp->h_bcc = NULL;
+ for (np = tolist; np != NULL; np = np->n_flink)
+ if ((np->n_type & GMASK) == GTO)
+ hp->h_to =
+ cat(hp->h_to, nalloc(np->n_name, np->n_type));
+ else if ((np->n_type & GMASK) == GCC)
+ hp->h_cc =
+ cat(hp->h_cc, nalloc(np->n_name, np->n_type));
+ else if ((np->n_type & GMASK) == GBCC)
+ hp->h_bcc =
+ cat(hp->h_bcc, nalloc(np->n_name, np->n_type));
+}
+
+/*
+ * Prepend a header in front of the collected stuff
+ * and return the new file.
+ */
+FILE *
+infix(struct header *hp, FILE *fi)
+{
+ FILE *nfo, *nfi;
+ int c, fd;
+ char tempname[PATHSIZE];
+
+ (void)snprintf(tempname, sizeof(tempname),
+ "%s/mail.RsXXXXXXXXXX", tmpdir);
+ if ((fd = mkstemp(tempname)) == -1 ||
+ (nfo = Fdopen(fd, "w")) == NULL) {
+ warn("%s", tempname);
+ return(fi);
+ }
+ if ((nfi = Fopen(tempname, "r")) == NULL) {
+ warn("%s", tempname);
+ (void)Fclose(nfo);
+ (void)rm(tempname);
+ return(fi);
+ }
+ (void)rm(tempname);
+ (void)puthead(hp, nfo, GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA);
+ c = getc(fi);
+ while (c != EOF) {
+ (void)putc(c, nfo);
+ c = getc(fi);
+ }
+ if (ferror(fi)) {
+ warn("read");
+ rewind(fi);
+ return(fi);
+ }
+ (void)fflush(nfo);
+ if (ferror(nfo)) {
+ warn("%s", tempname);
+ (void)Fclose(nfo);
+ (void)Fclose(nfi);
+ rewind(fi);
+ return(fi);
+ }
+ (void)Fclose(nfo);
+ (void)Fclose(fi);
+ rewind(nfi);
+ return(nfi);
+}
+
+/*
+ * Dump the to, subject, cc header on the
+ * passed file buffer.
+ */
+int
+puthead(struct header *hp, FILE *fo, int w)
+{
+ int gotcha;
+ char *from;
+
+ gotcha = 0;
+ from = hp->h_from ? hp->h_from : value("from");
+ if (from != NULL)
+ fprintf(fo, "From: %s\n", from), gotcha++;
+ if (hp->h_to != NULL && w & GTO)
+ fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++;
+ if (hp->h_subject != NULL && w & GSUBJECT)
+ fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
+ if (hp->h_cc != NULL && w & GCC)
+ fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++;
+ if (hp->h_bcc != NULL && w & GBCC)
+ fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++;
+ if (gotcha && w & GNL)
+ (void)putc('\n', fo);
+ return(0);
+}
+
+/*
+ * Format the given header line to not exceed 72 characters.
+ */
+void
+fmt(char *str, struct name *np, FILE *fo, int comma)
+{
+ int col, len;
+
+ comma = comma ? 1 : 0;
+ col = strlen(str);
+ if (col)
+ fputs(str, fo);
+ for (; np != NULL; np = np->n_flink) {
+ if (np->n_flink == NULL)
+ comma = 0;
+ len = strlen(np->n_name);
+ col++; /* for the space */
+ if (col + len + comma > 72 && col > 4) {
+ fputs("\n ", fo);
+ col = 4;
+ } else
+ putc(' ', fo);
+ fputs(np->n_name, fo);
+ if (comma)
+ putc(',', fo);
+ col += len + comma;
+ }
+ putc('\n', fo);
+}
+
+/*
+ * Save the outgoing mail on the passed file.
+ */
+/*ARGSUSED*/
+int
+savemail(char *name, FILE *fi)
+{
+ FILE *fo;
+ char buf[BUFSIZ];
+ time_t now;
+ mode_t m;
+
+ m = umask(077);
+ fo = Fopen(name, "a");
+ (void)umask(m);
+ if (fo == NULL) {
+ warn("%s", name);
+ return(-1);
+ }
+ (void)time(&now);
+ fprintf(fo, "From %s %s", myname, ctime(&now));
+ while (fgets(buf, sizeof(buf), fi) == buf) {
+ /*
+ * We can't read the record file (or inbox for recipient)
+ * in the message body (from forwarded messages or sentences
+ * starting with "From "), so we will prepend those lines with
+ * a '>'.
+ */
+ if (strncmp(buf, "From ", 5) == 0)
+ (void)fwrite(">", 1, 1, fo); /* '>' before 'From ' */
+ (void)fwrite(buf, 1, strlen(buf), fo);
+ }
+ (void)putc('\n', fo);
+ (void)fflush(fo);
+ if (ferror(fo))
+ warn("%s", name);
+ (void)Fclose(fo);
+ rewind(fi);
+ return(0);
+}
+
+/*ARGSUSED*/
+void
+sendint(int s)
+{
+
+ sendsignal = s;
+}
diff --git a/strings.c b/strings.c
@@ -0,0 +1,120 @@
+/* $OpenBSD: strings.c,v 1.10 2015/10/16 17:56:07 mmcc Exp $ */
+/* $NetBSD: strings.c,v 1.5 1996/06/08 19:48:40 christos Exp $ */
+
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS 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 REGENTS OR 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.
+ */
+
+/*
+ * Mail -- a mail program
+ *
+ * String allocation routines.
+ * Strings handed out here are reclaimed at the top of the command
+ * loop each time, so they need not be freed.
+ */
+
+#include "rcv.h"
+#include "extern.h"
+
+/*
+ * Allocate size more bytes of space and return the address of the
+ * first byte to the caller. An even number of bytes are always
+ * allocated so that the space will always be on a word boundary.
+ * The string spaces are of exponentially increasing size, to satisfy
+ * the occasional user with enormous string size requests.
+ */
+char *
+salloc(int size)
+{
+ char *t;
+ int s;
+ struct strings *sp;
+ int index;
+
+ s = size;
+ s += (sizeof(char *) - 1);
+ s &= ~(sizeof(char *) - 1);
+ index = 0;
+ for (sp = &stringdope[0]; sp < &stringdope[NSPACE]; sp++) {
+ if (sp->s_topFree == NULL && (STRINGSIZE << index) >= s)
+ break;
+ if (sp->s_nleft >= s)
+ break;
+ index++;
+ }
+ if (sp >= &stringdope[NSPACE])
+ errx(1, "String too large");
+ if (sp->s_topFree == NULL) {
+ index = sp - &stringdope[0];
+ sp->s_topFree = malloc(STRINGSIZE << index);
+ if (sp->s_topFree == NULL)
+ err(1, "malloc");
+ sp->s_nextFree = sp->s_topFree;
+ sp->s_nleft = STRINGSIZE << index;
+ }
+ sp->s_nleft -= s;
+ t = sp->s_nextFree;
+ sp->s_nextFree += s;
+ return(t);
+}
+
+/*
+ * Reset the string area to be empty.
+ * Called to free all strings allocated
+ * since last reset.
+ */
+void
+sreset(void)
+{
+ struct strings *sp;
+ int index;
+
+ if (noreset)
+ return;
+ index = 0;
+ for (sp = &stringdope[0]; sp < &stringdope[NSPACE]; sp++) {
+ if (sp->s_topFree == NULL)
+ continue;
+ sp->s_nextFree = sp->s_topFree;
+ sp->s_nleft = STRINGSIZE << index;
+ index++;
+ }
+}
+
+/*
+ * Make the string area permanent.
+ * Meant to be called in main, after initialization.
+ */
+void
+spreserve(void)
+{
+ struct strings *sp;
+
+ for (sp = &stringdope[0]; sp < &stringdope[NSPACE]; sp++)
+ sp->s_topFree = NULL;
+}
diff --git a/temp.c b/temp.c
@@ -0,0 +1,86 @@
+/* $OpenBSD: temp.c,v 1.18 2018/09/16 02:38:57 millert Exp $ */
+/* $NetBSD: temp.c,v 1.5 1996/06/08 19:48:42 christos Exp $ */
+
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS 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 REGENTS OR 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.
+ */
+
+#include "rcv.h"
+#include <pwd.h>
+#include "extern.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * Give names to all the temporary files that we will need.
+ */
+
+char *tmpdir;
+
+void
+tinit(void)
+{
+ char *cp;
+
+ tmpdir = _PATH_TMP;
+ if ((tmpdir = strdup(tmpdir)) == NULL)
+ err(1, "strdup");
+
+ /* Strip trailing '/' if necessary */
+ cp = tmpdir + strlen(tmpdir) - 1;
+ while (cp > tmpdir && *cp == '/') {
+ *cp = '\0';
+ cp--;
+ }
+
+ /*
+ * It's okay to call savestr in here because main will
+ * do a spreserve() after us.
+ */
+ if (myname != NULL) {
+ uid_t uid;
+
+ if (uid_from_user(myname, &uid) == -1)
+ errx(1, "\"%s\" is not a user of this system", myname);
+ } else {
+ if ((myname = username()) == NULL) {
+ myname = "nobody";
+ if (rcvmode)
+ exit(1);
+ } else
+ myname = savestr(myname);
+ }
+ if ((cp = getenv("HOME")) == NULL || *cp == '\0' ||
+ strlen(cp) >= PATHSIZE)
+ homedir = NULL;
+ else
+ homedir = savestr(cp);
+ if (debug)
+ printf("user = %s, homedir = %s\n", myname,
+ homedir ? homedir : "NONE");
+}
diff --git a/tty.c b/tty.c
@@ -0,0 +1,408 @@
+/* $OpenBSD: tty.c,v 1.22 2019/06/28 13:35:02 deraadt Exp $ */
+/* $NetBSD: tty.c,v 1.7 1997/07/09 05:25:46 mikel Exp $ */
+
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS 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 REGENTS OR 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.
+ */
+
+/*
+ * Mail -- a mail program
+ *
+ * Generally useful tty stuff.
+ */
+
+#include "rcv.h"
+#include "extern.h"
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#define TABWIDTH 8
+
+struct tty {
+ int fdin;
+ int fdout;
+ int flags;
+#define TTY_ALTWERASE 0x1
+#define TTY_ERR 0x2
+ cc_t *keys;
+ char *buf;
+ size_t size;
+ size_t len;
+ size_t cursor;
+};
+
+static void tty_flush(struct tty *);
+static int tty_getc(struct tty *);
+static int tty_insert(struct tty *, int, int);
+static void tty_putc(struct tty *, int);
+static void tty_reset(struct tty *);
+static void tty_visc(struct tty *, int);
+
+static struct tty tty;
+static volatile sig_atomic_t ttysignal; /* Interrupted by a signal? */
+
+/*
+ * Read all relevant header fields.
+ */
+int
+grabh(struct header *hp, int gflags)
+{
+ struct termios newtio, oldtio;
+#ifdef TIOCEXT
+ int extproc;
+ int flag;
+#endif
+ struct sigaction savetstp;
+ struct sigaction savettou;
+ struct sigaction savettin;
+ struct sigaction act;
+ char *s;
+ int error;
+
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+ act.sa_handler = SIG_DFL;
+ (void)sigaction(SIGTSTP, &act, &savetstp);
+ (void)sigaction(SIGTTOU, &act, &savettou);
+ (void)sigaction(SIGTTIN, &act, &savettin);
+ error = 1;
+ memset(&tty, 0, sizeof(tty));
+ tty.fdin = fileno(stdin);
+ tty.fdout = fileno(stdout);
+ if (tcgetattr(tty.fdin, &oldtio) == -1) {
+ warn("tcgetattr");
+ return(-1);
+ }
+ tty.keys = oldtio.c_cc;
+ if (oldtio.c_lflag & ALTWERASE)
+ tty.flags |= TTY_ALTWERASE;
+
+ newtio = oldtio;
+ newtio.c_lflag &= ~(ECHO | ICANON);
+ newtio.c_cc[VMIN] = 1;
+ newtio.c_cc[VTIME] = 0;
+ if (tcsetattr(tty.fdin, TCSADRAIN, &newtio) == -1) {
+ warn("tcsetattr");
+ return(-1);
+ }
+
+#ifdef TIOCEXT
+ extproc = ((oldtio.c_lflag & EXTPROC) ? 1 : 0);
+ if (extproc) {
+ flag = 0;
+ if (ioctl(fileno(stdin), TIOCEXT, &flag) == -1)
+ warn("TIOCEXT: off");
+ }
+#endif
+ if (gflags & GTO) {
+ s = readtty("To: ", detract(hp->h_to, 0));
+ if (s == NULL)
+ goto out;
+ hp->h_to = extract(s, GTO);
+ }
+ if (gflags & GSUBJECT) {
+ s = readtty("Subject: ", hp->h_subject);
+ if (s == NULL)
+ goto out;
+ hp->h_subject = s;
+ }
+ if (gflags & GCC) {
+ s = readtty("Cc: ", detract(hp->h_cc, 0));
+ if (s == NULL)
+ goto out;
+ hp->h_cc = extract(s, GCC);
+ }
+ if (gflags & GBCC) {
+ s = readtty("Bcc: ", detract(hp->h_bcc, 0));
+ if (s == NULL)
+ goto out;
+ hp->h_bcc = extract(s, GBCC);
+ }
+ error = 0;
+out:
+ (void)sigaction(SIGTSTP, &savetstp, NULL);
+ (void)sigaction(SIGTTOU, &savettou, NULL);
+ (void)sigaction(SIGTTIN, &savettin, NULL);
+#ifdef TIOCEXT
+ if (extproc) {
+ flag = 1;
+ if (ioctl(fileno(stdin), TIOCEXT, &flag) == -1)
+ warn("TIOCEXT: on");
+ }
+#endif
+ if (tcsetattr(tty.fdin, TCSADRAIN, &oldtio) == -1)
+ warn("tcsetattr");
+ return(error);
+}
+
+/*
+ * Read up a header from standard input.
+ * The source string has the preliminary contents to
+ * be read.
+ *
+ */
+char *
+readtty(char *pr, char *src)
+{
+ struct sigaction act, saveint;
+ unsigned char canonb[BUFSIZ];
+ char *cp;
+ sigset_t oset;
+ int c, done;
+
+ memset(canonb, 0, sizeof(canonb));
+ tty.buf = canonb;
+ tty.size = sizeof(canonb) - 1;
+
+ for (cp = pr; *cp != '\0'; cp++)
+ tty_insert(&tty, *cp, 1);
+ tty_flush(&tty);
+ tty_reset(&tty);
+
+ if (src != NULL && strlen(src) > sizeof(canonb) - 2) {
+ puts("too long to edit");
+ return(src);
+ }
+ if (src != NULL) {
+ for (cp = src; *cp != '\0'; cp++)
+ tty_insert(&tty, *cp, 1);
+ tty_flush(&tty);
+ }
+
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0; /* Note: will not restart syscalls */
+ act.sa_handler = ttyint;
+ (void)sigaction(SIGINT, &act, &saveint);
+ act.sa_handler = ttystop;
+ (void)sigaction(SIGTSTP, &act, NULL);
+ (void)sigaction(SIGTTOU, &act, NULL);
+ (void)sigaction(SIGTTIN, &act, NULL);
+ (void)sigprocmask(SIG_UNBLOCK, &intset, &oset);
+ for (;;) {
+ c = tty_getc(&tty);
+ switch (ttysignal) {
+ case SIGINT:
+ tty_visc(&tty, '\003'); /* output ^C */
+ /* FALLTHROUGH */
+ case 0:
+ break;
+ default:
+ ttysignal = 0;
+ goto redo;
+ }
+ if (c == 0) {
+ done = 1;
+ } else if (c == '\n') {
+ tty_putc(&tty, c);
+ done = 1;
+ } else {
+ done = tty_insert(&tty, c, 0);
+ tty_flush(&tty);
+ }
+ if (done)
+ break;
+ }
+ act.sa_handler = SIG_DFL;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+ (void)sigprocmask(SIG_SETMASK, &oset, NULL);
+ (void)sigaction(SIGTSTP, &act, NULL);
+ (void)sigaction(SIGTTOU, &act, NULL);
+ (void)sigaction(SIGTTIN, &act, NULL);
+ (void)sigaction(SIGINT, &saveint, NULL);
+ if (tty.flags & TTY_ERR) {
+ if (ttysignal == SIGINT) {
+ ttysignal = 0;
+ return(NULL); /* user hit ^C */
+ }
+
+redo:
+ cp = strlen(canonb) > 0 ? canonb : NULL;
+ /* XXX - make iterative, not recursive */
+ return(readtty(pr, cp));
+ }
+ if (equal("", canonb))
+ return("");
+ return(savestr(canonb));
+}
+
+/*
+ * Receipt continuation.
+ */
+void
+ttystop(int s)
+{
+ struct sigaction act, oact;
+ sigset_t nset;
+ int save_errno;
+
+ /*
+ * Save old handler and set to default.
+ * Unblock receipt of 's' and then resend it.
+ */
+ save_errno = errno;
+ (void)sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+ act.sa_handler = SIG_DFL;
+ (void)sigaction(s, &act, &oact);
+ (void)sigemptyset(&nset);
+ (void)sigaddset(&nset, s);
+ (void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
+ (void)kill(0, s);
+ (void)sigprocmask(SIG_BLOCK, &nset, NULL);
+ (void)sigaction(s, &oact, NULL);
+ ttysignal = s;
+ errno = save_errno;
+}
+
+/*ARGSUSED*/
+void
+ttyint(int s)
+{
+
+ ttysignal = s;
+}
+
+static void
+tty_flush(struct tty *t)
+{
+ size_t i, len;
+ int c;
+
+ if (t->cursor < t->len) {
+ for (; t->cursor < t->len; t->cursor++)
+ tty_visc(t, t->buf[t->cursor]);
+ } else if (t->cursor > t->len) {
+ len = t->cursor - t->len;
+ for (i = len; i > 0; i--) {
+ c = t->buf[--t->cursor];
+ if (c == '\t')
+ len += TABWIDTH - 1;
+ else if (iscntrl(c))
+ len++; /* account for leading ^ */
+ }
+ for (i = 0; i < len; i++)
+ tty_putc(t, '\b');
+ for (i = 0; i < len; i++)
+ tty_putc(t, ' ');
+ for (i = 0; i < len; i++)
+ tty_putc(t, '\b');
+ t->cursor = t->len;
+ }
+
+ t->buf[t->len] = '\0';
+}
+
+static int
+tty_getc(struct tty *t)
+{
+ ssize_t n;
+ unsigned char c;
+
+ n = read(t->fdin, &c, 1);
+ switch (n) {
+ case -1:
+ t->flags |= TTY_ERR;
+ /* FALLTHROUGH */
+ case 0:
+ return 0;
+ default:
+ return c & 0x7f;
+ }
+}
+
+static int
+tty_insert(struct tty *t, int c, int nocntrl)
+{
+ const unsigned char *ws = " \t";
+
+ if (CCEQ(t->keys[VERASE], c)) {
+ if (nocntrl)
+ return 0;
+ if (t->len > 0)
+ t->len--;
+ } else if (CCEQ(t->keys[VWERASE], c)) {
+ if (nocntrl)
+ return 0;
+ for (; t->len > 0; t->len--)
+ if (strchr(ws, t->buf[t->len - 1]) == NULL
+ && ((t->flags & TTY_ALTWERASE) == 0
+ || isalpha(t->buf[t->len - 1])))
+ break;
+ for (; t->len > 0; t->len--)
+ if (strchr(ws, t->buf[t->len - 1]) != NULL
+ || ((t->flags & TTY_ALTWERASE)
+ && !isalpha(t->buf[t->len - 1])))
+ break;
+ } else if (CCEQ(t->keys[VKILL], c)) {
+ if (nocntrl)
+ return 0;
+ t->len = 0;
+ } else {
+ if (t->len == t->size)
+ return 1;
+ t->buf[t->len++] = c;
+ }
+
+ return 0;
+}
+
+static void
+tty_putc(struct tty *t, int c)
+{
+ unsigned char cc = c;
+
+ write(t->fdout, &cc, 1);
+}
+
+static void
+tty_reset(struct tty *t)
+{
+ memset(t->buf, 0, t->len);
+ t->len = t->cursor = 0;
+}
+
+static void
+tty_visc(struct tty *t, int c)
+{
+ int i;
+
+ if (c == '\t') {
+ for (i = 0; i < TABWIDTH; i++)
+ tty_putc(t, ' ');
+ } else if (iscntrl(c)) {
+ tty_putc(t, '^');
+ if (c == 0x7F)
+ tty_putc(t, '?');
+ else
+ tty_putc(t, (c | 0x40));
+ } else {
+ tty_putc(t, c);
+ }
+}
diff --git a/util.c b/util.c
@@ -0,0 +1,643 @@
+/* $OpenBSD: util.c,v 1.1 2020/12/15 00:50:01 daniel Exp $ */
+/* $NetBSD: aux.c,v 1.5 1997/05/13 06:15:52 mikel Exp $ */
+
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS 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 REGENTS OR 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.
+ */
+
+#include "rcv.h"
+#include <fcntl.h>
+#include "extern.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * Auxiliary functions.
+ */
+static char *save2str(char *, char *);
+
+/*
+ * Return a pointer to a dynamic copy of the argument.
+ */
+char *
+savestr(const char *str)
+{
+ char *new;
+ int size = strlen(str) + 1;
+
+ if ((new = salloc(size)) != NULL)
+ (void)memcpy(new, str, size);
+ return(new)