smc

A simple, terminal-based email client.
git clone https://git.sr.ht/~jbauer/smc
Log | Files | Refs | README | LICENSE

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:
ALICENSE | 15+++++++++++++++
AMakefile | 17+++++++++++++++++
MREADME.md | 7+++++--
AUSD.doc/Makefile | 15+++++++++++++++
AUSD.doc/mail0.nr | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AUSD.doc/mail1.nr | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AUSD.doc/mail2.nr | 617+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AUSD.doc/mail3.nr | 131+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AUSD.doc/mail4.nr | 445+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AUSD.doc/mail5.nr | 1105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AUSD.doc/mail6.nr | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AUSD.doc/mail7.nr | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AUSD.doc/mail8.nr | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AUSD.doc/mail9.nr | 214+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AUSD.doc/maila.nr | 31+++++++++++++++++++++++++++++++
Acmd1.c | 524+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acmd2.c | 442+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acmd3.c | 736+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acmdtab.c | 120+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acollect.c | 609+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adef.h | 265+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aedit.c | 286+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aextern.h | 263+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Afio.c | 522+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aglob.h | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahead.c | 272+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alex.c | 714+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alist.c | 797+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amail.1 | 1276+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amain.c | 345+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amisc/mail.help | 38++++++++++++++++++++++++++++++++++++++
Amisc/mail.rc | 2++
Amisc/mail.tildehelp | 26++++++++++++++++++++++++++
Anames.c | 628+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apathnames.h | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Apopen.c | 477+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aquit.c | 486+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arcv.h | 44++++++++++++++++++++++++++++++++++++++++++++
Asend.c | 614+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Astrings.c | 120+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atemp.c | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atty.c | 408+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Autil.c | 643+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Av7.local.c | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Avars.c | 184+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aversion.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)