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);
+}