To dispatch commands typed by the user onto C functions, mail(1) uses an
array of struct with function pointers (cmdtab.c, with implementation in
cmd1.c, cmd2.c and cmd3.c).

I propose merging all these 3+1 files into cmd.c, with all the code that
is not directly a command wrapper moved away to a dedicated source file,
and command wrappers from other files imported to cmd.c.

All command-handling functions are prefixed with cmd_, which avoids
workarounds like ifcmd (now cmd_if).

Now that cmdtab on the same cmd.c, all cmd_* funcitons can be declared
static to make it obvious that they are not used elsewhere.

Special cases:

 - type1() was a regular function and type() a command wrapper,
   they are now type() and cmd_type().  The same for some mail1(),
   name1(), edit1(), save1() and ignore1().

 - "screen" was a static variable, it is now used by both cmd.c and
   print.c, so have a declaration in glob.h to make it a global
   variable.

 - A "cfunc_t" typedef is changed to cmd_fn to match kern(9).
   It should probably be replaced entirely so that it is not
   necessary to cast the function pointer in cmdtab.

"wc -l cmd[123].c" gives 1822, "wc -l cmd.c" gives 1193.


New:

File: action.c          Status: Locally Added
File: cmd.c             Status: Locally Added
File: print.c           Status: Locally Added
File: shell.c           Status: Locally Added

Deleted:

File: no file cmd1.c    Status: Locally Removed
File: no file cmd2.c    Status: Locally Removed
File: no file cmd3.c    Status: Locally Removed
File: no file cmdtab.c  Status: Locally Removed

Modified:

File: Makefile          Status: Locally Modified
File: aux.c             Status: Locally Modified
File: edit.c            Status: Locally Modified
File: extern.h          Status: Locally Modified
File: glob.h            Status: Locally Modified
File: lex.c             Status: Locally Modified
File: quit.c            Status: Locally Modified
File: send.c            Status: Locally Modified


Index: Makefile
===================================================================
RCS file: /cvs/src/usr.bin/mail/Makefile,v
retrieving revision 1.12
diff -u -p -u -p -r1.12 Makefile
--- Makefile    16 Sep 2018 02:38:57 -0000      1.12
+++ Makefile    16 Dec 2019 01:00:57 -0000
@@ -1,9 +1,9 @@
 #      $OpenBSD: Makefile,v 1.12 2018/09/16 02:38:57 millert Exp $
 
 PROG=  mail
-SRCS=  version.c aux.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 vars.c
+SRCS=  version.c aux.c cmd.c collect.c edit.c fio.c head.c v7.local.c \
+        lex.c list.c main.c names.c popen.c print.c quit.c action.c \
+        send.c shell.c strings.c temp.c tty.c vars.c
 SFILES=        mail.help mail.tildehelp
 EFILES=        mail.rc
 LINKS= ${BINDIR}/mail ${BINDIR}/Mail ${BINDIR}/mail ${BINDIR}/mailx
Index: action.c
===================================================================
RCS file: action.c
diff -N action.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ action.c    16 Dec 2019 01:00:57 -0000
@@ -0,0 +1,337 @@
+/*     $OpenBSD: cmd.c,v 1.29 2011/04/06 11:36:26 miod Exp $   */
+/*     $NetBSD: cmd.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 <sys/wait.h>
+#include "rcv.h"
+#include "extern.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * Functions acting on the mails.
+ */
+
+/*
+ * 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);
+}
+
+/*
+ * Save/copy the indicated messages at the end of the passed file name.
+ * If mark is true, mark the message "saved."
+ */
+int
+save(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);
+}
+
+/*
+ * 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);
+}
+
+/*
+ * Compare two names for sorting ignored field list.
+ */
+static int
+igcmp(const void *l, const void *r)
+{
+       return(strcmp(*(char **)l, *(char **)r));
+}
+
+/*
+ * Print out all currently retained fields.
+ */
+static 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 *), igcmp);
+       for (ap = ring; *ap != 0; ap++)
+               puts(*ap);
+       return(0);
+}
+
+int
+ignorem(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);
+}
+
+/*
+ * Reply to a list of messages.  Extract each name from the
+ * message header and send them off to mail1()
+ */
+int
+respond(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);
+}
+
+/*
+ * 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);
+}
Index: aux.c
===================================================================
RCS file: /cvs/src/usr.bin/mail/aux.c,v
retrieving revision 1.31
diff -u -p -u -p -r1.31 aux.c
--- aux.c       28 Jun 2019 13:35:01 -0000      1.31
+++ aux.c       16 Dec 2019 01:00:57 -0000
@@ -269,14 +269,10 @@ struct sstack {
  * that they are no longer reading from a tty (in all probability).
  */
 int
-source(void *v)
+source(char *cp)
 {
-       char **arglist = v;
        FILE *fi;
-       char *cp;
 
-       if ((cp = expand(*arglist)) == NULL)
-               return(1);
        if ((fi = Fopen(cp, "r")) == NULL) {
                warn("%s", cp);
                return(1);
@@ -362,7 +358,7 @@ nameof(struct message *mp, int reptype)
 {
        char *cp, *cp2;
 
-       cp = skin(name1(mp, reptype));
+       cp = skin(name(mp, reptype));
        if (reptype != 0 || charcount(cp, '!') < 2)
                return(cp);
        cp2 = strrchr(cp, '!');
@@ -515,7 +511,7 @@ skin(char *name)
  *     2 -- get sender's name for Reply
  */
 char *
-name1(struct message *mp, int reptype)
+name(struct message *mp, int reptype)
 {
        char namebuf[LINESIZE];
        char linebuf[LINESIZE];
@@ -640,4 +636,40 @@ clearnew(void)
                        mp->m_flag |= MSTATUS;
                }
        }
+}
+
+static int
+dictcmp(const void *a, const void *b)
+{
+       return(strcmp(*(char **)a, *(char **)b));
+}
+
+/*
+ * Sort the passed string vector into ascending dictionary
+ * order.
+ */
+void
+dictsort(char **list)
+{
+       char **ap;
+
+       for (ap = list; *ap != NULL; ap++)
+               ;
+       if (ap-list < 2)
+               return;
+       qsort(list, ap-list, sizeof(*list), dictcmp);
+}
+
+/*
+ * Compute screen size.
+ */
+int
+screensize(void)
+{
+       int s;
+       char *cp;
+
+       if ((cp = value("screen")) != NULL && (s = atoi(cp)) > 0)
+               return(s);
+       return(screenheight - 4);
 }
Index: cmd.c
===================================================================
RCS file: cmd.c
diff -N cmd.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ cmd.c       16 Dec 2019 01:00:58 -0000
@@ -0,0 +1,1193 @@
+/*     $OpenBSD: cmd.c,v 1.29 2011/04/06 11:36:26 miod Exp $   */
+/*     $NetBSD: cmd.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 <sys/wait.h>
+#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
+cmd_headers(void *v)
+{
+       int *msgvec = v;
+
+       return headers(msgvec);
+}
+
+/*
+ * Scroll to the next/previous screen
+ */
+static int
+cmd_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));
+}
+
+/*
+ * Print out the headlines for each message
+ * in the passed message list.
+ */
+static int
+cmd_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 value of dot.
+ */
+static int
+cmd_pdot(void *v)
+{
+       printf("%d\n", (int)(dot - &message[0] + 1));
+       return(0);
+}
+
+/*
+ * Print out all the possible commands.
+ */
+static int
+cmd_list(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
+ */
+static int
+cmd_pipeit(void *ml, void *sl)
+{
+       int  *msgvec = ml;
+       char *cmd    = sl;
+
+       return(type(msgvec, cmd, 0, 0));
+}
+
+/*
+ * Paginate messages, honor ignored fields.
+ */
+static int
+cmd_more(void *v)
+{
+       int *msgvec = v;
+       return(type(msgvec, NULL, 1, 1));
+}
+
+/*
+ * Paginate messages, even printing ignored fields.
+ */
+static int
+cmd_More(void *v)
+{
+       int *msgvec = v;
+
+       return(type(msgvec, NULL, 0, 1));
+}
+
+/*
+ * Type out messages, honor ignored fields.
+ */
+static int
+cmd_type(void *v)
+{
+       int *msgvec = v;
+
+       return(type(msgvec, NULL, 1, 0));
+}
+
+/*
+ * Type out messages, even printing ignored fields.
+ */
+static int
+cmd_Type(void *v)
+{
+       int *msgvec = v;
+
+       return(type(msgvec, NULL, 0, 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.
+ */
+static int
+cmd_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.
+ */
+static int
+cmd_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.
+ */
+static int
+cmd_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.
+ */
+static int
+cmd_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.
+ */
+static int
+cmd_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);
+}
+
+/*
+ * 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.
+ */
+static int
+cmd_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(cmd_type(list));
+}
+
+/*
+ * Save a message in a file.  Mark the message as saved
+ * so we can discard when the user quits.
+ */
+static int
+cmd_save(void *v)
+{
+       char *str = v;
+
+       return(save(str, 1, "save", saveignore));
+}
+
+/*
+ * Copy a message to a file without affected its saved-ness
+ */
+static int
+cmd_copy(void *v)
+{
+       char *str = v;
+
+       return(save(str, 0, "copy", saveignore));
+}
+
+/*
+ * Write the indicated messages at the end of the passed
+ * file name, minus header and trailing blank line.
+ */
+static int
+cmd_write(void *v)
+{
+       char *str = v;
+
+       return(save(str, 1, "write", ignoreall));
+}
+
+/*
+ * Delete messages.
+ */
+static int
+cmd_delete(void *v)
+{
+       int *msgvec = v;
+
+       delm(msgvec);
+       return(0);
+}
+
+/*
+ * Delete messages, then type the new dot.
+ */
+static int
+cmd_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(cmd_type(list));
+               }
+               puts("At EOF");
+       } else
+               puts("No more messages");
+       return(0);
+}
+
+/*
+ * Undelete the indicated messages.
+ */
+static int
+cmd_undelete(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.
+ */
+static int
+cmd_retfield(void *v)
+{
+       char **list = v;
+
+       return(ignorem(list, ignore + 1, "retained"));
+}
+
+/*
+ * Add the given header fields to the ignored list.
+ * If no arguments, print the current list of ignored fields.
+ */
+static int
+cmd_igfield(void *v)
+{
+       char **list = v;
+
+       return(ignorem(list, ignore, "ignored"));
+}
+
+static int
+cmd_saveretfield(void *v)
+{
+       char **list = v;
+
+       return(ignorem(list, saveignore + 1, "retained"));
+}
+
+static int
+cmd_saveigfield(void *v)
+{
+       char **list = v;
+
+       return(ignorem(list, saveignore, "ignored"));
+}
+
+/*
+ * Fork an interactive shell.
+ */
+/*ARGSUSED*/
+static int
+cmd_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);
+}
+
+/*
+ * Process a shell escape by saving signals, ignoring signals,
+ * and forking a sh -c
+ */
+static int
+cmd_shell(void *v)
+{
+       char *str = v;
+
+       return shell(v);
+}
+
+/*
+ * Print out a nice help message from some file or another.
+ */
+static int
+cmd_help(void *v)
+{
+       (void)run_command(value("PAGER"), 0, -1, -1, _PATH_HELP, NULL);
+       return(0);
+}
+
+/*
+ * Change user's working directory.
+ */
+static int
+cmd_chdir(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);
+}
+
+static int
+cmd_respond(void *v)
+{
+       int *msgvec = v;
+
+       if (value("Replyall") == NULL)
+               return(respond(msgvec));
+       else
+               return(Respond(msgvec));
+}
+
+/*
+ * 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.
+ */
+static int
+cmd_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.
+ */
+static int
+cmd_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.
+ */
+static int
+cmd_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.
+ */
+static int
+cmd_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.
+ */
+static int
+cmd_rexit(void *v)
+{
+       if (sourcing)
+               return(1);
+       exit(0);
+       /*NOTREACHED*/
+}
+
+/*
+ * Set or display a variable value.  Syntax is similar to that
+ * of csh.
+ */
+static int
+cmd_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;
+               dictsort(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.
+ */
+static int
+cmd_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.
+ */
+static int
+cmd_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;
+               dictsort(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);
+}
+
+/*
+ * The do nothing command for comments.
+ */
+/*ARGSUSED*/
+static int
+cmd_null(void *v)
+{
+       return(0);
+}
+
+/*
+ * Change to another file.  With no argument, print information about
+ * the current file.
+ */
+static int
+cmd_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
+ */
+static int
+cmd_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);
+}
+
+static int
+cmd_Respond(void *v)
+{
+       int *msgvec = v;
+
+       if (value("Replyall") == NULL)
+               return(Respond(msgvec));
+       else
+               return(respond(msgvec));
+}
+
+/*
+ * Conditional commands.  These allow one to parameterize one's
+ * .mailrc and do some things if sending, others if receiving.
+ */
+static int
+cmd_if(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.
+ */
+static int
+cmd_else(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.
+ */
+static int
+cmd_endif(void *v)
+{
+       if (cond == CANY) {
+               puts("\"Endif\" without matching \"if\"");
+               return(1);
+       }
+       cond = CANY;
+       return(0);
+}
+
+/*
+ * Set the list of alternate names.
+ */
+static int
+cmd_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);
+}
+
+/*
+ * Edit a message list.
+ */
+static int
+cmd_editor(void *v)
+{
+       int *msgvec = v;
+
+       return(edit1(msgvec, 'e'));
+}
+
+/*
+ * Invoke the visual editor on a message list.
+ */
+static int
+cmd_visual(void *v)
+{
+       int *msgvec = v;
+
+       return(edit1(msgvec, 'v'));
+}
+
+/*
+ * Send mail to a bunch of user names.  The interface is through
+ * the mail routine below.
+ */
+static int
+cmd_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);
+}
+
+/*
+ * Pushdown current input file and switch to a new one.
+ */
+static int
+cmd_source(void *v)
+{
+       char **arglist = v;
+       char *cp;
+
+       if ((cp = expand(*arglist)) == NULL)
+               return(1);
+       return source(cp);
+}
+
+/*
+ * Print the current version number.
+ */
+/*ARGSUSED*/
+static int
+cmd_version(void *v)
+{
+       extern const char version[];
+
+       printf("Version %s\n", version);
+       return(0);
+}
+
+/*
+ * The "quit" command.
+ */
+static int
+cmd_quit(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);
+}
+
+typedef int (*cmd_fn)(void *);
+
+/*
+ * Define all of the command names and bindings.
+ */
+const struct cmd cmdtab[] = {
+       /*                                                      msgmask msgflag
+        * command      function                argtype         result  & mask
+        * -------      --------                -------         ------- --------
+        */
+       { "next",       { cmd_next },           NDMLIST,        0,      MMNDEL 
},
+       { "alias",      { cmd_group },          M|RAWLIST,      0,      1000 },
+       { "print",      { cmd_type },           MSGLIST,        0,      MMNDEL 
},
+       { "type",       { cmd_type },           MSGLIST,        0,      MMNDEL 
},
+       { "Type",       { cmd_Type },           MSGLIST,        0,      MMNDEL 
},
+       { "Print",      { cmd_Type },           MSGLIST,        0,      MMNDEL 
},
+       { "visual",     { cmd_visual },         I|MSGLIST,      0,      MMNORM 
},
+       { "top",        { cmd_top },            MSGLIST,        0,      MMNDEL 
},
+       { "touch",      { cmd_stouch },         W|MSGLIST,      0,      MMNDEL 
},
+       { "preserve",   { cmd_preserve },       W|MSGLIST,      0,      MMNDEL 
},
+       { "delete",     { cmd_delete },         W|P|MSGLIST,    0,      MMNDEL 
},
+       { "dp",         { cmd_deltype },        W|MSGLIST,      0,      MMNDEL 
},
+       { "dt",         { cmd_deltype },        W|MSGLIST,      0,      MMNDEL 
},
+       { "undelete",   { cmd_undelete },       P|MSGLIST,      MDELETED,MMNDEL 
},
+       { "unset",      { cmd_unset },          M|RAWLIST,      1,      1000 },
+       { "mail",       { cmd_sendmail },       R|M|I|STRLIST,  0,      0 },
+       { "mbox",       { cmd_mboxit },         W|MSGLIST,      0,      0 },
+       { "pipe",       { (cmd_fn)cmd_pipeit }, MSGLIST|STRLIST,0,      MMNDEL 
},
+       { "|",          { (cmd_fn)cmd_pipeit }, MSGLIST|STRLIST,0,      MMNDEL 
},
+       { "more",       { cmd_more },           MSGLIST,        0,      MMNDEL 
},
+       { "page",       { cmd_more },           MSGLIST,        0,      MMNDEL 
},
+       { "More",       { cmd_More },           MSGLIST,        0,      MMNDEL 
},
+       { "Page",       { cmd_More },           MSGLIST,        0,      MMNDEL 
},
+       { "unread",     { cmd_unread },         MSGLIST,        0,      MMNDEL 
},
+       { "Unread",     { cmd_unread },         MSGLIST,        0,      MMNDEL 
},
+       { "!",          { cmd_shell },          I|STRLIST,      0,      0 },
+       { "copy",       { cmd_copy },           M|STRLIST,      0,      0 },
+       { "chdir",      { cmd_chdir },          M|RAWLIST,      0,      1 },
+       { "cd",         { cmd_chdir },          M|RAWLIST,      0,      1 },
+       { "save",       { cmd_save },           STRLIST,        0,      0 },
+       { "source",     { cmd_source },         M|RAWLIST,      1,      1 },
+       { "set",        { cmd_set },            M|RAWLIST,      0,      1000 },
+       { "shell",      { cmd_dosh },           I|NOLIST,       0,      0 },
+       { "version",    { cmd_version },        M|NOLIST,       0,      0 },
+       { "group",      { cmd_group },          M|RAWLIST,      0,      1000 },
+       { "write",      { cmd_write },          STRLIST,        0,      0 },
+       { "from",       { cmd_from },           MSGLIST,        0,      MMNORM 
},
+       { "file",       { cmd_file },           T|M|RAWLIST,    0,      1 },
+       { "folder",     { cmd_file },           T|M|RAWLIST,    0,      1 },
+       { "folders",    { cmd_folders },        T|M|STRLIST,    0,      0 },
+       { "?",          { cmd_help },           M|NOLIST,       0,      0 },
+       { "z",          { cmd_scroll },         M|STRLIST,      0,      0 },
+       { "headers",    { cmd_headers },        MSGLIST,        0,      MMNDEL 
},
+       { "help",       { cmd_help },           M|NOLIST,       0,      0 },
+       { "=",          { cmd_pdot },           NOLIST,         0,      0 },
+       { "Reply",      { cmd_Respond },        R|I|MSGLIST,    0,      MMNDEL 
},
+       { "Respond",    { cmd_Respond },        R|I|MSGLIST,    0,      MMNDEL 
},
+       { "reply",      { cmd_respond },        R|I|MSGLIST,    0,      MMNDEL 
},
+       { "respond",    { cmd_respond },        R|I|MSGLIST,    0,      MMNDEL 
},
+       { "edit",       { cmd_editor },         I|MSGLIST,      0,      MMNORM 
},
+       { "echo",       { cmd_echo },           M|RAWLIST,      0,      1000 },
+       { "quit",       { cmd_quit },           NOLIST,         0,      0 },
+       { "list",       { cmd_list },           M|NOLIST,       0,      0 },
+       { "xit",        { cmd_rexit },          M|NOLIST,       0,      0 },
+       { "exit",       { cmd_rexit },          M|NOLIST,       0,      0 },
+       { "size",       { cmd_messize },        MSGLIST,        0,      MMNDEL 
},
+       { "hold",       { cmd_preserve },       W|MSGLIST,      0,      MMNDEL 
},
+       { "if",         { cmd_if },             F|M|RAWLIST,    1,      1 },
+       { "else",       { cmd_else },           F|M|RAWLIST,    0,      0 },
+       { "endif",      { cmd_endif },          F|M|RAWLIST,    0,      0 },
+       { "alternates", { cmd_alternates },     M|RAWLIST,      0,      1000 },
+       { "ignore",     { cmd_igfield },        M|RAWLIST,      0,      1000 },
+       { "discard",    { cmd_igfield },        M|RAWLIST,      0,      1000 },
+       { "retain",     { cmd_retfield },       M|RAWLIST,      0,      1000 },
+       { "saveignore", { cmd_saveigfield },    M|RAWLIST,      0,      1000 },
+       { "savediscard",{ cmd_saveigfield },    M|RAWLIST,      0,      1000 },
+       { "saveretain", { cmd_saveretfield },   M|RAWLIST,      0,      1000 },
+#if 0
+       { "Header",     { cmd_Header },         STRLIST,        0,      1000 },
+#endif
+       { "#",          { cmd_null },           M|NOLIST,       0,      0 },
+       { "inc",        { cmd_inc },            T|NOLIST,       0,      0 },
+       { "new",        { cmd_marknew },        MSGLIST,        0,      MMNDEL 
},
+       { 0,            { 0 },                  0,              0,      0 }
+};
Index: cmd1.c
===================================================================
RCS file: cmd1.c
diff -N cmd1.c
--- cmd1.c      6 Apr 2011 11:36:26 -0000       1.29
+++ /dev/null   1 Jan 1970 00:00:00 -0000
@@ -1,524 +0,0 @@
-/*     $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;
-}
Index: cmd2.c
===================================================================
RCS file: cmd2.c
diff -N cmd2.c
--- cmd2.c      16 Oct 2015 17:56:07 -0000      1.22
+++ /dev/null   1 Jan 1970 00:00:00 -0000
@@ -1,442 +0,0 @@
-/*     $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));
-}
Index: cmd3.c
===================================================================
RCS file: cmd3.c
diff -N cmd3.c
--- cmd3.c      28 Jun 2019 13:35:01 -0000      1.28
+++ /dev/null   1 Jan 1970 00:00:00 -0000
@@ -1,736 +0,0 @@
-/*     $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);
-}
Index: cmdtab.c
===================================================================
RCS file: cmdtab.c
diff -N cmdtab.c
--- cmdtab.c    27 Oct 2009 23:59:40 -0000      1.13
+++ /dev/null   1 Jan 1970 00:00:00 -0000
@@ -1,120 +0,0 @@
-/*     $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 }
-};
Index: edit.c
===================================================================
RCS file: /cvs/src/usr.bin/mail/edit.c,v
retrieving revision 1.21
diff -u -p -u -p -r1.21 edit.c
--- edit.c      28 Jun 2019 13:35:01 -0000      1.21
+++ edit.c      16 Dec 2019 01:00:58 -0000
@@ -47,28 +47,6 @@ int editit(const char *, const char *);
  */
 
 /*
- * 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.
Index: extern.h
===================================================================
RCS file: /cvs/src/usr.bin/mail/extern.h,v
retrieving revision 1.29
diff -u -p -u -p -r1.29 extern.h
--- extern.h    16 Sep 2018 02:38:57 -0000      1.29
+++ extern.h    16 Dec 2019 01:00:58 -0000
@@ -59,7 +59,7 @@ struct message;
 char   *hfield(char *, struct message *);
 FILE   *infix(struct header *, FILE *);
 char   *ishfield(char *, char *, char *);
-char   *name1(struct message *, int);
+char   *name(struct message *, int);
 char   *nameof(struct message *, int);
 char   *nextword(char *, char *);
 char   *readtty(char *, char *);
@@ -77,14 +77,10 @@ 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 *);
+int     Respond(int *);
+int     respond(int *);
 void    alter(char *);
-int     alternates(void *);
 void    announce(void);
 int     append(struct message *, FILE *);
 int     argcount(char **);
@@ -98,25 +94,17 @@ 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    dictsort(char **);
 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);
@@ -124,10 +112,8 @@ 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 **);
@@ -135,20 +121,14 @@ 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     ignorem(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 *);
@@ -168,92 +148,61 @@ 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 *);
+int     save(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 *);
+int     shell(char *);
 void    sigchild(int);
-void    sort(char **);
-int     source(void *);
+int     source(char *);
 int     spool_lock(void);
-int     spool_unlock(void);
 void    spreserve(void);
+int     spool_unlock(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 *);
+int     type(int *, char *, int, int);
 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 *);
Index: glob.h
===================================================================
RCS file: /cvs/src/usr.bin/mail/glob.h,v
retrieving revision 1.9
diff -u -p -u -p -r1.9 glob.h
--- glob.h      16 Sep 2018 02:38:57 -0000      1.9
+++ glob.h      16 Dec 2019 01:00:58 -0000
@@ -77,6 +77,7 @@ int   debug;                          /* Debug flag set */
 int    screenwidth;                    /* Screen width, or best guess */
 int    screenheight;                   /* Screen height, or best guess,
                                           for "header" command */
+int    screen;                         /* position across the various screens 
*/
 int    realscreenheight;               /* the real screen height */
 int    uflag;                          /* Are we in -u mode? */
 sigset_t intset;                       /* Signal set that is just SIGINT */
Index: lex.c
===================================================================
RCS file: /cvs/src/usr.bin/mail/lex.c,v
retrieving revision 1.41
diff -u -p -u -p -r1.41 lex.c
--- lex.c       28 Jun 2019 13:35:01 -0000      1.41
+++ lex.c       16 Dec 2019 01:00:58 -0000
@@ -509,7 +509,7 @@ out:
                if ((dot->m_flag & MDELETED) == 0) {
                        muvec[0] = dot - &message[0] + 1;
                        muvec[1] = 0;
-                       type(muvec);
+                       type(muvec, NULL, 1, 0);
                }
        if (!sourcing && (com->c_argtype & T) == 0)
                sawcom = 1;
@@ -677,19 +677,6 @@ newfileinfo(int omsgCount)
                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);
 }
 
 /*
Index: print.c
===================================================================
RCS file: print.c
diff -N print.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ print.c     16 Dec 2019 01:00:58 -0000
@@ -0,0 +1,240 @@
+/*     $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"
+
+static volatile sig_atomic_t gothdrint;
+int screen;
+
+/*
+ * Mail -- a mail program
+ *
+ * Print mail contents out..
+ */
+
+/*
+ * User hit ^C while printing the headers.
+ */
+void
+hdrint(int s)
+{
+       gothdrint = 1;
+}
+
+/*
+ * Print the current active headings.
+ * Don't change dot if invoker didn't give an argument.
+ */
+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);
+}
+
+/*
+ * 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);
+}
+
+/*
+ * Type out the messages requested.
+ */
+int
+type(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);
+}
Index: quit.c
===================================================================
RCS file: /cvs/src/usr.bin/mail/quit.c,v
retrieving revision 1.23
diff -u -p -u -p -r1.23 quit.c
--- quit.c      19 Jul 2016 06:43:27 -0000      1.23
+++ quit.c      16 Dec 2019 01:00:58 -0000
@@ -41,21 +41,6 @@
  */
 
 /*
- * 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.
Index: send.c
===================================================================
RCS file: /cvs/src/usr.bin/mail/send.c,v
retrieving revision 1.25
diff -u -p -u -p -r1.25 send.c
--- send.c      19 Mar 2019 13:26:27 -0000      1.25
+++ send.c      16 Dec 2019 01:00:58 -0000
@@ -294,26 +294,6 @@ mail(struct name *to, struct name *cc, s
 }
 
 /*
- * 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).
  */
Index: shell.c
===================================================================
RCS file: shell.c
diff -N shell.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ shell.c     16 Dec 2019 01:00:58 -0000
@@ -0,0 +1,114 @@
+/*     $OpenBSD: cmd.c,v 1.29 2011/04/06 11:36:26 miod Exp $   */
+/*     $NetBSD: cmd.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
+ *
+ * Shell invocation and handling.
+ */
+
+/*
+ * 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);
+}
+
+/*
+ * Process a shell escape by saving signals, ignoring signals,
+ * and forking a sh -c
+ */
+int
+shell(char *str)
+{
+       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);
+}

Reply via email to