386BSD 0.0 development
authorWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Thu, 18 Apr 1991 22:56:25 +0000 (14:56 -0800)
committerWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Thu, 18 Apr 1991 22:56:25 +0000 (14:56 -0800)
Work on file usr/src/usr.bin/mail/cmd1.c
Work on file usr/src/usr.bin/mail/aux.c
Work on file usr/src/usr.bin/mail/cmd2.c
Work on file usr/src/usr.bin/mail/cmd3.c
Work on file usr/src/usr.bin/mail/cmdtab.c
Work on file usr/src/usr.bin/mail/def.h
Work on file usr/src/usr.bin/mail/edit.c
Work on file usr/src/usr.bin/mail/collect.c
Work on file usr/src/usr.bin/mail/fio.c
Work on file usr/src/usr.bin/mail/getname.c
Work on file usr/src/usr.bin/mail/glob.h
Work on file usr/src/usr.bin/mail/head.c
Work on file usr/src/usr.bin/mail/lex.c
Work on file usr/src/usr.bin/mail/list.c
Work on file usr/src/usr.bin/mail/names.c
Work on file usr/src/usr.bin/mail/main.c
Work on file usr/src/usr.bin/mail/quit.c
Work on file usr/src/usr.bin/mail/popen.c
Work on file usr/src/usr.bin/mail/send.c
Work on file usr/src/usr.bin/mail/rcv.h
Work on file usr/src/usr.bin/mail/v7.local.c
Work on file usr/src/usr.bin/mail/vars.c
Work on file usr/src/usr.bin/mail/tty.c
Work on file usr/src/usr.bin/mail/strings.c
Work on file usr/src/usr.bin/mail/temp.c
Work on file usr/src/usr.bin/mail/version.c

Co-Authored-By: Lynne Greer Jolitz <ljolitz@cardio.ucsf.edu>
Synthesized-from: 386BSD-0.0/src

26 files changed:
usr/src/usr.bin/mail/aux.c [new file with mode: 0644]
usr/src/usr.bin/mail/cmd1.c [new file with mode: 0644]
usr/src/usr.bin/mail/cmd2.c [new file with mode: 0644]
usr/src/usr.bin/mail/cmd3.c [new file with mode: 0644]
usr/src/usr.bin/mail/cmdtab.c [new file with mode: 0644]
usr/src/usr.bin/mail/collect.c [new file with mode: 0644]
usr/src/usr.bin/mail/def.h [new file with mode: 0644]
usr/src/usr.bin/mail/edit.c [new file with mode: 0644]
usr/src/usr.bin/mail/fio.c [new file with mode: 0644]
usr/src/usr.bin/mail/getname.c [new file with mode: 0644]
usr/src/usr.bin/mail/glob.h [new file with mode: 0644]
usr/src/usr.bin/mail/head.c [new file with mode: 0644]
usr/src/usr.bin/mail/lex.c [new file with mode: 0644]
usr/src/usr.bin/mail/list.c [new file with mode: 0644]
usr/src/usr.bin/mail/main.c [new file with mode: 0644]
usr/src/usr.bin/mail/names.c [new file with mode: 0644]
usr/src/usr.bin/mail/popen.c [new file with mode: 0644]
usr/src/usr.bin/mail/quit.c [new file with mode: 0644]
usr/src/usr.bin/mail/rcv.h [new file with mode: 0644]
usr/src/usr.bin/mail/send.c [new file with mode: 0644]
usr/src/usr.bin/mail/strings.c [new file with mode: 0644]
usr/src/usr.bin/mail/temp.c [new file with mode: 0644]
usr/src/usr.bin/mail/tty.c [new file with mode: 0644]
usr/src/usr.bin/mail/v7.local.c [new file with mode: 0644]
usr/src/usr.bin/mail/vars.c [new file with mode: 0644]
usr/src/usr.bin/mail/version.c [new file with mode: 0644]

diff --git a/usr/src/usr.bin/mail/aux.c b/usr/src/usr.bin/mail/aux.c
new file mode 100644 (file)
index 0000000..d74be57
--- /dev/null
@@ -0,0 +1,651 @@
+/*
+ * Copyright (c) 1980 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)aux.c      5.20 (Berkeley) 6/25/90";
+#endif /* not lint */
+
+#include "rcv.h"
+#include <sys/stat.h>
+#include <sys/time.h>
+
+/*
+ * Mail -- a mail program
+ *
+ * Auxiliary functions.
+ */
+
+/*
+ * Return a pointer to a dynamic copy of the argument.
+ */
+char *
+savestr(str)
+       char *str;
+{
+       char *new;
+       int size = strlen(str) + 1;
+
+       if ((new = salloc(size)) != NOSTR)
+               bcopy(str, new, size);
+       return new;
+}
+
+/*
+ * Announce a fatal error and die.
+ */
+
+/*VARARGS1*/
+panic(fmt, a, b)
+       char *fmt;
+{
+       fprintf(stderr, "panic: ");
+       fprintf(stderr, fmt, a, b);
+       putc('\n', stderr);
+       fflush(stdout);
+       abort();
+}
+
+/*
+ * Touch the named message by setting its MTOUCH flag.
+ * Touched messages have the effect of not being sent
+ * back to the system mailbox on exit.
+ */
+touch(mp)
+       register struct message *mp;
+{
+
+       mp->m_flag |= MTOUCH;
+       if ((mp->m_flag & MREAD) == 0)
+               mp->m_flag |= MREAD|MSTATUS;
+}
+
+/*
+ * Test to see if the passed file name is a directory.
+ * Return true if it is.
+ */
+isdir(name)
+       char name[];
+{
+       struct stat sbuf;
+
+       if (stat(name, &sbuf) < 0)
+               return(0);
+       return((sbuf.st_mode & S_IFMT) == S_IFDIR);
+}
+
+/*
+ * Count the number of arguments in the given string raw list.
+ */
+argcount(argv)
+       char **argv;
+{
+       register char **ap;
+
+       for (ap = argv; *ap++ != NOSTR;)
+               ;       
+       return ap - argv - 1;
+}
+
+/*
+ * Return the desired header line from the passed message
+ * pointer (or NOSTR if the desired header field is not available).
+ */
+char *
+hfield(field, mp)
+       char field[];
+       struct message *mp;
+{
+       register FILE *ibuf;
+       char linebuf[LINESIZE];
+       register int lc;
+       register char *hfield;
+       char *colon;
+
+       ibuf = setinput(mp);
+       if ((lc = mp->m_lines - 1) < 0)
+               return NOSTR;
+       if (readline(ibuf, linebuf, LINESIZE) < 0)
+               return NOSTR;
+       while (lc > 0) {
+               if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0)
+                       return NOSTR;
+               if (hfield = ishfield(linebuf, colon, field))
+                       return savestr(hfield);
+       }
+       return NOSTR;
+}
+
+/*
+ * Return the next header field found in the given message.
+ * Return >= 0 if something found, < 0 elsewise.
+ * "colon" is set to point to the colon in the header.
+ * Must deal with \ continuations & other such fraud.
+ */
+gethfield(f, linebuf, rem, colon)
+       register FILE *f;
+       char linebuf[];
+       register int rem;
+       char **colon;
+{
+       char line2[LINESIZE];
+       register char *cp, *cp2;
+       register int c;
+
+       for (;;) {
+               if (--rem < 0)
+                       return -1;
+               if ((c = readline(f, linebuf, LINESIZE)) <= 0)
+                       return -1;
+               for (cp = linebuf; isprint(*cp) && *cp != ' ' && *cp != ':';
+                    cp++)
+                       ;
+               if (*cp != ':' || cp == linebuf)
+                       continue;
+               /*
+                * I guess we got a headline.
+                * Handle wraparounding
+                */
+               *colon = cp;
+               cp = linebuf + c;
+               for (;;) {
+                       while (--cp >= linebuf && (*cp == ' ' || *cp == '\t'))
+                               ;
+                       cp++;
+                       if (rem <= 0)
+                               break;
+                       ungetc(c = getc(f), f);
+                       if (c != ' ' && c != '\t')
+                               break;
+                       if ((c = readline(f, line2, LINESIZE)) < 0)
+                               break;
+                       rem--;
+                       for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++)
+                               ;
+                       c -= cp2 - line2;
+                       if (cp + c >= linebuf + LINESIZE - 2)
+                               break;
+                       *cp++ = ' ';
+                       bcopy(cp2, cp, c);
+                       cp += c;
+               }
+               *cp = 0;
+               return rem;
+       }
+       /* NOTREACHED */
+}
+
+/*
+ * Check whether the passed line is a header line of
+ * the desired breed.  Return the field body, or 0.
+ */
+
+char*
+ishfield(linebuf, colon, field)
+       char linebuf[], field[];
+       char *colon;
+{
+       register char *cp = colon;
+
+       *cp = 0;
+       if (strcasecmp(linebuf, field) != 0) {
+               *cp = ':';
+               return 0;
+       }
+       *cp = ':';
+       for (cp++; *cp == ' ' || *cp == '\t'; cp++)
+               ;
+       return cp;
+}
+
+/*
+ * Copy a string, lowercasing it as we go.
+ */
+istrcpy(dest, src)
+       register char *dest, *src;
+{
+
+       do {
+               if (isupper(*src))
+                       *dest++ = tolower(*src);
+               else
+                       *dest++ = *src;
+       } while (*src++ != 0);
+}
+
+/*
+ * The following code deals with input stacking to do source
+ * commands.  All but the current file pointer are saved on
+ * the stack.
+ */
+
+static int     ssp;                    /* Top of file stack */
+struct sstack {
+       FILE    *s_file;                /* File we were in. */
+       int     s_cond;                 /* Saved state of conditionals */
+       int     s_loading;              /* Loading .mailrc, etc. */
+} sstack[NOFILE];
+
+/*
+ * Pushdown current input file and switch to a new one.
+ * Set the global flag "sourcing" so that others will realize
+ * that they are no longer reading from a tty (in all probability).
+ */
+source(arglist)
+       char **arglist;
+{
+       FILE *fi;
+       char *cp;
+
+       if ((cp = expand(*arglist)) == NOSTR)
+               return(1);
+       if ((fi = Fopen(cp, "r")) == NULL) {
+               perror(cp);
+               return(1);
+       }
+       if (ssp >= NOFILE - 1) {
+               printf("Too much \"sourcing\" going on.\n");
+               Fclose(fi);
+               return(1);
+       }
+       sstack[ssp].s_file = input;
+       sstack[ssp].s_cond = cond;
+       sstack[ssp].s_loading = loading;
+       ssp++;
+       loading = 0;
+       cond = CANY;
+       input = fi;
+       sourcing++;
+       return(0);
+}
+
+/*
+ * Pop the current input back to the previous level.
+ * Update the "sourcing" flag as appropriate.
+ */
+unstack()
+{
+       if (ssp <= 0) {
+               printf("\"Source\" stack over-pop.\n");
+               sourcing = 0;
+               return(1);
+       }
+       Fclose(input);
+       if (cond != CANY)
+               printf("Unmatched \"if\"\n");
+       ssp--;
+       cond = sstack[ssp].s_cond;
+       loading = sstack[ssp].s_loading;
+       input = sstack[ssp].s_file;
+       if (ssp == 0)
+               sourcing = loading;
+       return(0);
+}
+
+/*
+ * Touch the indicated file.
+ * This is nifty for the shell.
+ */
+alter(name)
+       char *name;
+{
+       struct stat sb;
+       struct timeval tv[2];
+       time_t time();
+
+       if (stat(name, &sb))
+               return;
+       tv[0].tv_sec = time((time_t *)0) + 1;
+       tv[1].tv_sec = sb.st_mtime;
+       tv[0].tv_usec = tv[1].tv_usec = 0;
+       (void)utimes(name, tv);
+}
+
+/*
+ * Examine the passed line buffer and
+ * return true if it is all blanks and tabs.
+ */
+blankline(linebuf)
+       char linebuf[];
+{
+       register char *cp;
+
+       for (cp = linebuf; *cp; cp++)
+               if (*cp != ' ' && *cp != '\t')
+                       return(0);
+       return(1);
+}
+
+/*
+ * Get sender's name from this message.  If the message has
+ * a bunch of arpanet stuff in it, we may have to skin the name
+ * before returning it.
+ */
+char *
+nameof(mp, reptype)
+       register struct message *mp;
+{
+       register char *cp, *cp2;
+
+       cp = skin(name1(mp, reptype));
+       if (reptype != 0 || charcount(cp, '!') < 2)
+               return(cp);
+       cp2 = rindex(cp, '!');
+       cp2--;
+       while (cp2 > cp && *cp2 != '!')
+               cp2--;
+       if (*cp2 == '!')
+               return(cp2 + 1);
+       return(cp);
+}
+
+/*
+ * Start of a "comment".
+ * Ignore it.
+ */
+char *
+skip_comment(cp)
+       register char *cp;
+{
+       register nesting = 1;
+
+       for (; nesting > 0 && *cp; cp++) {
+               switch (*cp) {
+               case '\\':
+                       if (cp[1])
+                               cp++;
+                       break;
+               case '(':
+                       nesting++;
+                       break;
+               case ')':
+                       nesting--;
+                       break;
+               }
+       }
+       return cp;
+}
+
+/*
+ * Skin an arpa net address according to the RFC 822 interpretation
+ * of "host-phrase."
+ */
+char *
+skin(name)
+       char *name;
+{
+       register int c;
+       register char *cp, *cp2;
+       char *bufend;
+       int gotlt, lastsp;
+       char nbuf[BUFSIZ];
+
+       if (name == NOSTR)
+               return(NOSTR);
+       if (index(name, '(') == NOSTR && index(name, '<') == NOSTR
+           && index(name, ' ') == NOSTR)
+               return(name);
+       gotlt = 0;
+       lastsp = 0;
+       bufend = nbuf;
+       for (cp = name, cp2 = bufend; c = *cp++; ) {
+               switch (c) {
+               case '(':
+                       cp = skip_comment(cp);
+                       lastsp = 0;
+                       break;
+
+               case '"':
+                       /*
+                        * Start of a "quoted-string".
+                        * Copy it in its entirety.
+                        */
+                       while (c = *cp) {
+                               cp++;
+                               if (c == '"')
+                                       break;
+                               if (c != '\\')
+                                       *cp2++ = c;
+                               else if (c = *cp) {
+                                       *cp2++ = c;
+                                       cp++;
+                               }
+                       }
+                       lastsp = 0;
+                       break;
+
+               case ' ':
+                       if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ')
+                               cp += 3, *cp2++ = '@';
+                       else
+                       if (cp[0] == '@' && cp[1] == ' ')
+                               cp += 2, *cp2++ = '@';
+                       else
+                               lastsp = 1;
+                       break;
+
+               case '<':
+                       cp2 = bufend;
+                       gotlt++;
+                       lastsp = 0;
+                       break;
+
+               case '>':
+                       if (gotlt) {
+                               gotlt = 0;
+                               while ((c = *cp) && c != ',') {
+                                       cp++;
+                                       if (c == '(')
+                                               cp = skip_comment(cp);
+                                       else if (c == '"')
+                                               while (c = *cp) {
+                                                       cp++;
+                                                       if (c == '"')
+                                                               break;
+                                                       if (c == '\\' && *cp)
+                                                               cp++;
+                                               }
+                               }
+                               lastsp = 0;
+                               break;
+                       }
+                       /* Fall into . . . */
+
+               default:
+                       if (lastsp) {
+                               lastsp = 0;
+                               *cp2++ = ' ';
+                       }
+                       *cp2++ = c;
+                       if (c == ',' && !gotlt) {
+                               *cp2++ = ' ';
+                               for (; *cp == ' '; cp++)
+                                       ;
+                               lastsp = 0;
+                               bufend = cp2;
+                       }
+               }
+       }
+       *cp2 = 0;
+
+       return(savestr(nbuf));
+}
+
+/*
+ * Fetch the sender's name from the passed message.
+ * Reptype can be
+ *     0 -- get sender's name for display purposes
+ *     1 -- get sender's name for reply
+ *     2 -- get sender's name for Reply
+ */
+char *
+name1(mp, reptype)
+       register struct message *mp;
+{
+       char namebuf[LINESIZE];
+       char linebuf[LINESIZE];
+       register char *cp, *cp2;
+       register FILE *ibuf;
+       int first = 1;
+
+       if ((cp = hfield("from", mp)) != NOSTR)
+               return cp;
+       if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR)
+               return cp;
+       ibuf = setinput(mp);
+       namebuf[0] = 0;
+       if (readline(ibuf, linebuf, LINESIZE) < 0)
+               return(savestr(namebuf));
+newname:
+       for (cp = linebuf; *cp && *cp != ' '; cp++)
+               ;
+       for (; *cp == ' ' || *cp == '\t'; cp++)
+               ;
+       for (cp2 = &namebuf[strlen(namebuf)];
+            *cp && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;)
+               *cp2++ = *cp++;
+       *cp2 = '\0';
+       if (readline(ibuf, linebuf, LINESIZE) < 0)
+               return(savestr(namebuf));
+       if ((cp = index(linebuf, 'F')) == NULL)
+               return(savestr(namebuf));
+       if (strncmp(cp, "From", 4) != 0)
+               return(savestr(namebuf));
+       while ((cp = index(cp, 'r')) != NULL) {
+               if (strncmp(cp, "remote", 6) == 0) {
+                       if ((cp = index(cp, 'f')) == NULL)
+                               break;
+                       if (strncmp(cp, "from", 4) != 0)
+                               break;
+                       if ((cp = index(cp, ' ')) == NULL)
+                               break;
+                       cp++;
+                       if (first) {
+                               strcpy(namebuf, cp);
+                               first = 0;
+                       } else
+                               strcpy(rindex(namebuf, '!')+1, cp);
+                       strcat(namebuf, "!");
+                       goto newname;
+               }
+               cp++;
+       }
+       return(savestr(namebuf));
+}
+
+/*
+ * Count the occurances of c in str
+ */
+charcount(str, c)
+       char *str;
+{
+       register char *cp;
+       register int i;
+
+       for (i = 0, cp = str; *cp; cp++)
+               if (*cp == c)
+                       i++;
+       return(i);
+}
+
+/*
+ * Are any of the characters in the two strings the same?
+ */
+anyof(s1, s2)
+       register char *s1, *s2;
+{
+
+       while (*s1)
+               if (index(s2, *s1++))
+                       return 1;
+       return 0;
+}
+
+/*
+ * Convert c to upper case
+ */
+raise(c)
+       register c;
+{
+
+       if (islower(c))
+               return toupper(c);
+       return c;
+}
+
+/*
+ * Copy s1 to s2, return pointer to null in s2.
+ */
+char *
+copy(s1, s2)
+       register char *s1, *s2;
+{
+
+       while (*s2++ = *s1++)
+               ;
+       return s2 - 1;
+}
+
+/*
+ * See if the given header field is supposed to be ignored.
+ */
+isign(field, ignore)
+       char *field;
+       struct ignoretab ignore[2];
+{
+       char realfld[BUFSIZ];
+
+       if (ignore == ignoreall)
+               return 1;
+       /*
+        * Lower-case the string, so that "Status" and "status"
+        * will hash to the same place.
+        */
+       istrcpy(realfld, field);
+       if (ignore[1].i_count > 0)
+               return (!member(realfld, ignore + 1));
+       else
+               return (member(realfld, ignore));
+}
+
+member(realfield, table)
+       register char *realfield;
+       struct ignoretab *table;
+{
+       register struct ignore *igp;
+
+       for (igp = table->i_head[hash(realfield)]; igp != 0; igp = igp->i_link)
+               if (*igp->i_field == *realfield &&
+                   equal(igp->i_field, realfield))
+                       return (1);
+       return (0);
+}
diff --git a/usr/src/usr.bin/mail/cmd1.c b/usr/src/usr.bin/mail/cmd1.c
new file mode 100644 (file)
index 0000000..0b306e0
--- /dev/null
@@ -0,0 +1,440 @@
+/*-
+ * Copyright (c) 1980 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmd1.c     5.22 (Berkeley) 4/1/91";
+#endif /* not lint */
+
+#include "rcv.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;
+
+headers(msgvec)
+       int *msgvec;
+{
+       register int n, mesg, flag;
+       register struct message *mp;
+       int size;
+
+       size = screensize();
+       n = msgvec[0];
+       if (n != 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;
+       for (; mp < &message[msgCount]; mp++) {
+               mesg++;
+               if (mp->m_flag & MDELETED)
+                       continue;
+               if (flag++ >= size)
+                       break;
+               printhead(mesg);
+       }
+       if (flag == 0) {
+               printf("No more mail.\n");
+               return(1);
+       }
+       return(0);
+}
+
+/*
+ * Scroll to the next/previous screen
+ */
+scroll(arg)
+       char arg[];
+{
+       register int s, size;
+       int cur[1];
+
+       cur[0] = 0;
+       size = screensize();
+       s = screen;
+       switch (*arg) {
+       case 0:
+       case '+':
+               s++;
+               if (s * size > msgCount) {
+                       printf("On last screenful of messages\n");
+                       return(0);
+               }
+               screen = s;
+               break;
+
+       case '-':
+               if (--s < 0) {
+                       printf("On first screenful of messages\n");
+                       return(0);
+               }
+               screen = s;
+               break;
+
+       default:
+               printf("Unrecognized scrolling command \"%s\"\n", arg);
+               return(1);
+       }
+       return(headers(cur));
+}
+
+/*
+ * Compute screen size.
+ */
+screensize()
+{
+       int s;
+       char *cp;
+
+       if ((cp = value("screen")) != NOSTR && (s = atoi(cp)) > 0)
+               return s;
+       return screenheight - 4;
+}
+
+/*
+ * Print out the headlines for each message
+ * in the passed message list.
+ */
+
+from(msgvec)
+       int *msgvec;
+{
+       register int *ip;
+
+       for (ip = msgvec; *ip != NULL; 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.
+ */
+
+printhead(mesg)
+{
+       struct message *mp;
+       char headline[LINESIZE], wcount[LINESIZE], *subjline, dispc, curind;
+       char pbuf[BUFSIZ];
+       struct headline hl;
+       int subjlen;
+       char *name;
+
+       mp = &message[mesg-1];
+       (void) readline(setinput(mp), headline, LINESIZE);
+       if ((subjline = hfield("subject", mp)) == NOSTR)
+               subjline = hfield("subj", mp);
+       /*
+        * 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);
+       sprintf(wcount, "%3d/%-5ld", mp->m_lines, mp->m_size);
+       subjlen = screenwidth - 50 - strlen(wcount);
+       name = value("show-rcpt") != NOSTR ?
+               skin(hfield("to", mp)) : nameof(mp, 0);
+       if (subjline == NOSTR || subjlen < 0)           /* pretty pathetic */
+               printf("%c%c%3d %-20.20s  %16.16s %s\n",
+                       curind, dispc, mesg, name, hl.l_date, wcount);
+       else
+               printf("%c%c%3d %-20.20s  %16.16s %s \"%.*s\"\n",
+                       curind, dispc, mesg, name, hl.l_date, wcount,
+                       subjlen, subjline);
+}
+
+/*
+ * Print out the value of dot.
+ */
+
+pdot()
+{
+       printf("%d\n", dot - &message[0] + 1);
+       return(0);
+}
+
+/*
+ * Print out all the possible commands.
+ */
+
+pcmdlist()
+{
+       register struct cmd *cp;
+       register int cc;
+       extern struct cmd cmdtab[];
+
+       printf("Commands are:\n");
+       for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) {
+               cc += strlen(cp->c_name) + 2;
+               if (cc > 72) {
+                       printf("\n");
+                       cc = strlen(cp->c_name) + 2;
+               }
+               if ((cp+1)->c_name != NOSTR)
+                       printf("%s, ", cp->c_name);
+               else
+                       printf("%s\n", cp->c_name);
+       }
+       return(0);
+}
+
+/*
+ * Paginate messages, honor ignored fields.
+ */
+more(msgvec)
+       int *msgvec;
+{
+       return (type1(msgvec, 1, 1));
+}
+
+/*
+ * Paginate messages, even printing ignored fields.
+ */
+More(msgvec)
+       int *msgvec;
+{
+
+       return (type1(msgvec, 0, 1));
+}
+
+/*
+ * Type out messages, honor ignored fields.
+ */
+type(msgvec)
+       int *msgvec;
+{
+
+       return(type1(msgvec, 1, 0));
+}
+
+/*
+ * Type out messages, even printing ignored fields.
+ */
+Type(msgvec)
+       int *msgvec;
+{
+
+       return(type1(msgvec, 0, 0));
+}
+
+/*
+ * Type out the messages requested.
+ */
+jmp_buf        pipestop;
+
+type1(msgvec, doign, page)
+       int *msgvec;
+{
+       register *ip;
+       register struct message *mp;
+       register char *cp;
+       int nlines;
+       FILE *obuf;
+       void brokpipe();
+
+       obuf = stdout;
+       if (setjmp(pipestop))
+               goto close_pipe;
+       if (value("interactive") != NOSTR &&
+           (page || (cp = value("crt")) != NOSTR)) {
+               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)) {
+                       cp = value("PAGER");
+                       if (cp == NULL || *cp == '\0')
+                               cp = _PATH_MORE;
+                       obuf = Popen(cp, "w");
+                       if (obuf == NULL) {
+                               perror(cp);
+                               obuf = stdout;
+                       } else
+                               signal(SIGPIPE, brokpipe);
+               }
+       }
+       for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
+               mp = &message[*ip - 1];
+               touch(mp);
+               dot = mp;
+               if (value("quiet") == NOSTR)
+                       fprintf(obuf, "Message %d:\n", *ip);
+               (void) send(mp, obuf, doign ? ignore : 0, NOSTR);
+       }
+close_pipe:
+       if (obuf != stdout) {
+               /*
+                * Ignore SIGPIPE so it can't cause a duplicate close.
+                */
+               signal(SIGPIPE, SIG_IGN);
+               Pclose(obuf);
+               signal(SIGPIPE, SIG_DFL);
+       }
+       return(0);
+}
+
+/*
+ * Respond to a broken pipe signal --
+ * probably caused by quitting more.
+ */
+
+void
+brokpipe()
+{
+       longjmp(pipestop, 1);
+}
+
+/*
+ * Print the top so many lines of each desired message.
+ * The number of lines is taken from the variable "toplines"
+ * and defaults to 5.
+ */
+
+top(msgvec)
+       int *msgvec;
+{
+       register int *ip;
+       register struct message *mp;
+       int c, topl, lines, lineb;
+       char *valtop, linebuf[LINESIZE];
+       FILE *ibuf;
+
+       topl = 5;
+       valtop = value("toplines");
+       if (valtop != NOSTR) {
+               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") == NOSTR)
+                       printf("Message %d:\n", *ip);
+               ibuf = setinput(mp);
+               c = mp->m_lines;
+               if (!lineb)
+                       printf("\n");
+               for (lines = 0; lines < c && lines <= topl; lines++) {
+                       if (readline(ibuf, linebuf, LINESIZE) < 0)
+                               break;
+                       puts(linebuf);
+                       lineb = blankline(linebuf);
+               }
+       }
+       return(0);
+}
+
+/*
+ * Touch all the given messages so that they will
+ * get mboxed.
+ */
+stouch(msgvec)
+       int msgvec[];
+{
+       register 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.
+ */
+
+mboxit(msgvec)
+       int msgvec[];
+{
+       register 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.
+ */
+folders()
+{
+       char dirname[BUFSIZ];
+       char *cmd;
+
+       if (getfold(dirname) < 0) {
+               printf("No value set for \"folder\"\n");
+               return 1;
+       }
+       if ((cmd = value("LISTER")) == NOSTR)
+               cmd = "ls";
+       (void) run_command(cmd, 0, -1, -1, dirname, NOSTR);
+       return 0;
+}
diff --git a/usr/src/usr.bin/mail/cmd2.c b/usr/src/usr.bin/mail/cmd2.c
new file mode 100644 (file)
index 0000000..9efbe4f
--- /dev/null
@@ -0,0 +1,516 @@
+/*
+ * Copyright (c) 1980 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmd2.c     5.14 (Berkeley) 6/25/90";
+#endif /* not lint */
+
+#include "rcv.h"
+#include <sys/wait.h>
+
+/*
+ * Mail -- a mail program
+ *
+ * More user commands.
+ */
+
+/*
+ * 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.
+ */
+
+next(msgvec)
+       int *msgvec;
+{
+       register struct message *mp;
+       register int *ip, *ip2;
+       int list[2], mdot;
+
+       if (*msgvec != NULL) {
+
+               /*
+                * 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 != NULL; ip++)
+                       if (*ip > mdot)
+                               break;
+               if (*ip == NULL)
+                       ip = msgvec;
+               ip2 = ip;
+               do {
+                       mp = &message[*ip2 - 1];
+                       if ((mp->m_flag & MDELETED) == 0) {
+                               dot = mp;
+                               goto hitit;
+                       }
+                       if (*ip2 != NULL)
+                               ip2++;
+                       if (*ip2 == NULL)
+                               ip2 = msgvec;
+               } while (ip2 != ip);
+               printf("No messages applicable\n");
+               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]) {
+               printf("At EOF\n");
+               return(0);
+       }
+       dot = mp;
+hitit:
+       /*
+        * Print dot.
+        */
+
+       list[0] = dot - &message[0] + 1;
+       list[1] = NULL;
+       return(type(list));
+}
+
+/*
+ * Save a message in a file.  Mark the message as saved
+ * so we can discard when the user quits.
+ */
+save(str)
+       char str[];
+{
+
+       return save1(str, 1, "save", saveignore);
+}
+
+/*
+ * Copy a message to a file without affected its saved-ness
+ */
+copycmd(str)
+       char str[];
+{
+
+       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."
+ */
+save1(str, mark, cmd, ignore)
+       char str[];
+       char *cmd;
+       struct ignoretab *ignore;
+{
+       register int *ip;
+       register struct message *mp;
+       char *file, *disp;
+       int f, *msgvec;
+       FILE *obuf;
+
+       msgvec = (int *) salloc((msgCount + 2) * sizeof *msgvec);
+       if ((file = snarf(str, &f)) == NOSTR)
+               return(1);
+       if (!f) {
+               *msgvec = first(0, MMNORM);
+               if (*msgvec == NULL) {
+                       printf("No messages to %s.\n", cmd);
+                       return(1);
+               }
+               msgvec[1] = NULL;
+       }
+       if (f && getmsglist(str, msgvec, 0) < 0)
+               return(1);
+       if ((file = expand(file)) == NOSTR)
+               return(1);
+       printf("\"%s\" ", file);
+       fflush(stdout);
+       if (access(file, 0) >= 0)
+               disp = "[Appended]";
+       else
+               disp = "[New file]";
+       if ((obuf = Fopen(file, "a")) == NULL) {
+               perror(NOSTR);
+               return(1);
+       }
+       for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
+               mp = &message[*ip - 1];
+               touch(mp);
+               if (send(mp, obuf, ignore, NOSTR) < 0) {
+                       perror(file);
+                       Fclose(obuf);
+                       return(1);
+               }
+               if (mark)
+                       mp->m_flag |= MSAVED;
+       }
+       fflush(obuf);
+       if (ferror(obuf))
+               perror(file);
+       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.
+ */
+
+swrite(str)
+       char str[];
+{
+
+       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 NOSTR.  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(linebuf, flag)
+       char linebuf[];
+       int *flag;
+{
+       register char *cp;
+
+       *flag = 1;
+       cp = strlen(linebuf) + linebuf - 1;
+
+       /*
+        * Strip away trailing blanks.
+        */
+
+       while (cp > linebuf && isspace(*cp))
+               cp--;
+       *++cp = 0;
+
+       /*
+        * Now search for the beginning of the file name.
+        */
+
+       while (cp > linebuf && !isspace(*cp))
+               cp--;
+       if (*cp == '\0') {
+               printf("No file specified.\n");
+               return(NOSTR);
+       }
+       if (isspace(*cp))
+               *cp++ = 0;
+       else
+               *flag = 0;
+       return(cp);
+}
+
+/*
+ * Delete messages.
+ */
+
+delete(msgvec)
+       int msgvec[];
+{
+       delm(msgvec);
+       return 0;
+}
+
+/*
+ * Delete messages, then type the new dot.
+ */
+
+deltype(msgvec)
+       int msgvec[];
+{
+       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] = NULL;
+                       return(type(list));
+               }
+               printf("At EOF\n");
+       } else
+               printf("No more messages\n");
+       return(0);
+}
+
+/*
+ * Delete the indicated messages.
+ * Set dot to some nice place afterwards.
+ * Internal interface.
+ */
+
+delm(msgvec)
+       int *msgvec;
+{
+       register struct message *mp;
+       register *ip;
+       int last;
+
+       last = NULL;
+       for (ip = msgvec; *ip != NULL; ip++) {
+               mp = &message[*ip - 1];
+               touch(mp);
+               mp->m_flag |= MDELETED|MTOUCH;
+               mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX);
+               last = *ip;
+       }
+       if (last != NULL) {
+               dot = &message[last-1];
+               last = first(0, MDELETED);
+               if (last != NULL) {
+                       dot = &message[last-1];
+                       return(0);
+               }
+               else {
+                       dot = &message[0];
+                       return(-1);
+               }
+       }
+
+       /*
+        * Following can't happen -- it keeps lint happy
+        */
+
+       return(-1);
+}
+
+/*
+ * Undelete the indicated messages.
+ */
+
+undelete(msgvec)
+       int *msgvec;
+{
+       register struct message *mp;
+       register *ip;
+
+       for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
+               mp = &message[*ip - 1];
+               touch(mp);
+               dot = mp;
+               mp->m_flag &= ~MDELETED;
+       }
+       return 0;
+}
+
+/*
+ * Interactively dump core on "core"
+ */
+
+core()
+{
+       int pid;
+       extern union wait wait_status;
+
+       switch (pid = vfork()) {
+       case -1:
+               perror("fork");
+               return(1);
+       case 0:
+               abort();
+               _exit(1);
+       }
+       printf("Okie dokie");
+       fflush(stdout);
+       wait_child(pid);
+       if (wait_status.w_coredump)
+               printf(" -- Core dumped.\n");
+       else
+               printf(" -- Can't dump core.\n");
+       return 0;
+}
+
+/*
+ * Clobber as many bytes of stack as the user requests.
+ */
+clobber(argv)
+       char **argv;
+{
+       register int times;
+
+       if (argv[0] == 0)
+               times = 1;
+       else
+               times = (atoi(argv[0]) + 511) / 512;
+       clob1(times);
+       return 0;
+}
+
+/*
+ * Clobber the stack.
+ */
+clob1(n)
+{
+       char buf[512];
+       register char *cp;
+
+       if (n <= 0)
+               return;
+       for (cp = buf; cp < &buf[512]; *cp++ = 0xFF)
+               ;
+       clob1(n - 1);
+}
+
+/*
+ * Add the given header fields to the retained list.
+ * If no arguments, print the current list of retained fields.
+ */
+retfield(list)
+       char *list[];
+{
+
+       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.
+ */
+igfield(list)
+       char *list[];
+{
+
+       return ignore1(list, ignore, "ignored");
+}
+
+saveretfield(list)
+       char *list[];
+{
+
+       return ignore1(list, saveignore + 1, "retained");
+}
+
+saveigfield(list)
+       char *list[];
+{
+
+       return ignore1(list, saveignore, "ignored");
+}
+
+ignore1(list, tab, which)
+       char *list[];
+       struct ignoretab *tab;
+       char *which;
+{
+       char field[BUFSIZ];
+       register int h;
+       register struct ignore *igp;
+       char **ap;
+
+       if (*list == NOSTR)
+               return igshow(tab, which);
+       for (ap = list; *ap != 0; ap++) {
+               istrcpy(field, *ap);
+               if (member(field, tab))
+                       continue;
+               h = hash(field);
+               igp = (struct ignore *) calloc(1, sizeof (struct ignore));
+               igp->i_field = calloc((unsigned) strlen(field) + 1,
+                       sizeof (char));
+               strcpy(igp->i_field, field);
+               igp->i_link = tab->i_head[h];
+               tab->i_head[h] = igp;
+               tab->i_count++;
+       }
+       return 0;
+}
+
+/*
+ * Print out all currently retained fields.
+ */
+igshow(tab, which)
+       struct ignoretab *tab;
+       char *which;
+{
+       register int h;
+       struct ignore *igp;
+       char **ap, **ring;
+       int igcomp();
+
+       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((char *) ring, tab->i_count, sizeof (char *), igcomp);
+       for (ap = ring; *ap != 0; ap++)
+               printf("%s\n", *ap);
+       return 0;
+}
+
+/*
+ * Compare two names for sorting ignored field list.
+ */
+igcomp(l, r)
+       char **l, **r;
+{
+
+       return strcmp(*l, *r);
+}
diff --git a/usr/src/usr.bin/mail/cmd3.c b/usr/src/usr.bin/mail/cmd3.c
new file mode 100644 (file)
index 0000000..a5b2df5
--- /dev/null
@@ -0,0 +1,713 @@
+/*
+ * Copyright (c) 1980 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmd3.c     5.24 (Berkeley) 6/25/90";
+#endif /* not lint */
+
+#include "rcv.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * Still more user commands.
+ */
+
+/*
+ * Process a shell escape by saving signals, ignoring signals,
+ * and forking a sh -c
+ */
+shell(str)
+       char *str;
+{
+       sig_t sigint = signal(SIGINT, SIG_IGN);
+       char *shell;
+       char cmd[BUFSIZ];
+
+       (void) strcpy(cmd, str);
+       if (bangexp(cmd) < 0)
+               return 1;
+       if ((shell = value("SHELL")) == NOSTR)
+               shell = _PATH_CSHELL;
+       (void) run_command(shell, 0, -1, -1, "-c", cmd, NOSTR);
+       (void) signal(SIGINT, sigint);
+       printf("!\n");
+       return 0;
+}
+
+/*
+ * Fork an interactive shell.
+ */
+/*ARGSUSED*/
+dosh(str)
+       char *str;
+{
+       sig_t sigint = signal(SIGINT, SIG_IGN);
+       char *shell;
+
+       if ((shell = value("SHELL")) == NOSTR)
+               shell = _PATH_CSHELL;
+       (void) run_command(shell, 0, -1, -1, NOSTR);
+       (void) signal(SIGINT, sigint);
+       putchar('\n');
+       return 0;
+}
+
+/*
+ * Expand the shell escape by expanding unescaped !'s into the
+ * last issued command where possible.
+ */
+
+char   lastbang[128];
+
+bangexp(str)
+       char *str;
+{
+       char bangbuf[BUFSIZ];
+       register char *cp, *cp2;
+       register int n;
+       int changed = 0;
+
+       cp = str;
+       cp2 = bangbuf;
+       n = BUFSIZ;
+       while (*cp) {
+               if (*cp == '!') {
+                       if (n < strlen(lastbang)) {
+overf:
+                               printf("Command buffer overflow\n");
+                               return(-1);
+                       }
+                       changed++;
+                       strcpy(cp2, lastbang);
+                       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) {
+               printf("!%s\n", bangbuf);
+               fflush(stdout);
+       }
+       strcpy(str, bangbuf);
+       strncpy(lastbang, bangbuf, 128);
+       lastbang[127] = 0;
+       return(0);
+}
+
+/*
+ * Print out a nice help message from some file or another.
+ */
+
+help()
+{
+       register c;
+       register FILE *f;
+
+       if ((f = Fopen(_PATH_HELP, "r")) == NULL) {
+               perror(_PATH_HELP);
+               return(1);
+       }
+       while ((c = getc(f)) != EOF)
+               putchar(c);
+       Fclose(f);
+       return(0);
+}
+
+/*
+ * Change user's working directory.
+ */
+schdir(arglist)
+       char **arglist;
+{
+       char *cp;
+
+       if (*arglist == NOSTR)
+               cp = homedir;
+       else
+               if ((cp = expand(*arglist)) == NOSTR)
+                       return(1);
+       if (chdir(cp) < 0) {
+               perror(cp);
+               return(1);
+       }
+       return 0;
+}
+
+respond(msgvec)
+       int *msgvec;
+{
+       if (value("Replyall") == NOSTR)
+               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()
+ */
+_respond(msgvec)
+       int *msgvec;
+{
+       struct message *mp;
+       char *cp, *rcv, *replyto;
+       char **ap;
+       struct name *np;
+       struct header head;
+
+       if (msgvec[1] != 0) {
+               printf("Sorry, can't reply to multiple messages at once\n");
+               return(1);
+       }
+       mp = &message[msgvec[0] - 1];
+       touch(mp);
+       dot = mp;
+       if ((rcv = skin(hfield("from", mp))) == NOSTR)
+               rcv = skin(nameof(mp, 1));
+       if ((replyto = skin(hfield("reply-to", mp))) != NOSTR)
+               np = extract(replyto, GTO);
+       else if ((cp = skin(hfield("to", mp))) != NOSTR)
+               np = extract(cp, GTO);
+       else
+               np = NIL;
+       np = elide(np);
+       /*
+        * 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 != NIL && replyto == NOSTR)
+               np = cat(np, extract(rcv, GTO));
+       else if (np == NIL) {
+               if (replyto != NOSTR)
+                       printf("Empty reply-to field -- replying to author\n");
+               np = extract(rcv, GTO);
+       }
+       head.h_to = np;
+       if ((head.h_subject = hfield("subject", mp)) == NOSTR)
+               head.h_subject = hfield("subj", mp);
+       head.h_subject = reedit(head.h_subject);
+       if (replyto == NOSTR && (cp = skin(hfield("cc", mp))) != NOSTR) {
+               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 = NIL;
+       head.h_bcc = NIL;
+       head.h_smopts = NIL;
+       mail1(&head, 1);
+       return(0);
+}
+
+/*
+ * Modify the subject we are replying to to begin with Re: if
+ * it does not already.
+ */
+char *
+reedit(subj)
+       register char *subj;
+{
+       char *newsubj;
+
+       if (subj == NOSTR)
+               return NOSTR;
+       if ((subj[0] == 'r' || subj[0] == 'R') &&
+           (subj[1] == 'e' || subj[1] == 'E') &&
+           subj[2] == ':')
+               return subj;
+       newsubj = salloc(strlen(subj) + 5);
+       strcpy(newsubj, "Re: ");
+       strcpy(newsubj + 4, subj);
+       return newsubj;
+}
+
+/*
+ * Preserve the named messages, so that they will be sent
+ * back to the system mailbox.
+ */
+
+preserve(msgvec)
+       int *msgvec;
+{
+       register struct message *mp;
+       register int *ip, mesg;
+
+       if (edit) {
+               printf("Cannot \"preserve\" in edit mode\n");
+               return(1);
+       }
+       for (ip = msgvec; *ip != NULL; 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.
+ */
+unread(msgvec)
+       int     msgvec[];
+{
+       register int *ip;
+
+       for (ip = msgvec; *ip != NULL; ip++) {
+               dot = &message[*ip-1];
+               dot->m_flag &= ~(MREAD|MTOUCH);
+               dot->m_flag |= MSTATUS;
+       }
+       return(0);
+}
+
+/*
+ * Print the size of each message.
+ */
+
+messize(msgvec)
+       int *msgvec;
+{
+       register struct message *mp;
+       register int *ip, mesg;
+
+       for (ip = msgvec; *ip != NULL; ip++) {
+               mesg = *ip;
+               mp = &message[mesg-1];
+               printf("%d: %d/%ld\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.
+ */
+
+rexit(e)
+{
+       if (sourcing)
+               return(1);
+       exit(e);
+       /*NOTREACHED*/
+}
+
+/*
+ * Set or display a variable value.  Syntax is similar to that
+ * of csh.
+ */
+
+set(arglist)
+       char **arglist;
+{
+       register struct var *vp;
+       register char *cp, *cp2;
+       char varbuf[BUFSIZ], **ap, **p;
+       int errs, h, s;
+
+       if (*arglist == NOSTR) {
+               for (h = 0, s = 1; h < HSHSIZE; h++)
+                       for (vp = variables[h]; vp != NOVAR; vp = vp->v_link)
+                               s++;
+               ap = (char **) salloc(s * sizeof *ap);
+               for (h = 0, p = ap; h < HSHSIZE; h++)
+                       for (vp = variables[h]; vp != NOVAR; vp = vp->v_link)
+                               *p++ = vp->v_name;
+               *p = NOSTR;
+               sort(ap);
+               for (p = ap; *p != NOSTR; p++)
+                       printf("%s\t%s\n", *p, value(*p));
+               return(0);
+       }
+       errs = 0;
+       for (ap = arglist; *ap != NOSTR; ap++) {
+               cp = *ap;
+               cp2 = varbuf;
+               while (*cp != '=' && *cp != '\0')
+                       *cp2++ = *cp++;
+               *cp2 = '\0';
+               if (*cp == '\0')
+                       cp = "";
+               else
+                       cp++;
+               if (equal(varbuf, "")) {
+                       printf("Non-null variable name required\n");
+                       errs++;
+                       continue;
+               }
+               assign(varbuf, cp);
+       }
+       return(errs);
+}
+
+/*
+ * Unset a bunch of variable values.
+ */
+
+unset(arglist)
+       char **arglist;
+{
+       register struct var *vp, *vp2;
+       int errs, h;
+       char **ap;
+
+       errs = 0;
+       for (ap = arglist; *ap != NOSTR; ap++) {
+               if ((vp2 = lookup(*ap)) == NOVAR) {
+                       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);
+                       cfree((char *)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);
+               cfree((char *) vp2);
+       }
+       return(errs);
+}
+
+/*
+ * Put add users to a group.
+ */
+
+group(argv)
+       char **argv;
+{
+       register struct grouphead *gh;
+       register struct group *gp;
+       register int h;
+       int s;
+       char **ap, *gname, **p;
+
+       if (*argv == NOSTR) {
+               for (h = 0, s = 1; h < HSHSIZE; h++)
+                       for (gh = groups[h]; gh != NOGRP; gh = gh->g_link)
+                               s++;
+               ap = (char **) salloc(s * sizeof *ap);
+               for (h = 0, p = ap; h < HSHSIZE; h++)
+                       for (gh = groups[h]; gh != NOGRP; gh = gh->g_link)
+                               *p++ = gh->g_name;
+               *p = NOSTR;
+               sort(ap);
+               for (p = ap; *p != NOSTR; p++)
+                       printgroup(*p);
+               return(0);
+       }
+       if (argv[1] == NOSTR) {
+               printgroup(*argv);
+               return(0);
+       }
+       gname = *argv;
+       h = hash(gname);
+       if ((gh = findgroup(gname)) == NOGRP) {
+               gh = (struct grouphead *) calloc(sizeof *gh, 1);
+               gh->g_name = vcopy(gname);
+               gh->g_list = NOGE;
+               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 != NOSTR; ap++) {
+               gp = (struct group *) calloc(sizeof *gp, 1);
+               gp->ge_name = vcopy(*ap);
+               gp->ge_link = gh->g_list;
+               gh->g_list = gp;
+       }
+       return(0);
+}
+
+/*
+ * Sort the passed string vecotor into ascending dictionary
+ * order.
+ */
+
+sort(list)
+       char **list;
+{
+       register char **ap;
+       int diction();
+
+       for (ap = list; *ap != NOSTR; ap++)
+               ;
+       if (ap-list < 2)
+               return;
+       qsort((char *)list, ap-list, sizeof *list, diction);
+}
+
+/*
+ * Do a dictionary order comparison of the arguments from
+ * qsort.
+ */
+
+diction(a, b)
+       register char **a, **b;
+{
+       return(strcmp(*a, *b));
+}
+
+/*
+ * The do nothing command for comments.
+ */
+
+/*ARGSUSED*/
+null(e)
+{
+       return 0;
+}
+
+/*
+ * Change to another file.  With no argument, print information about
+ * the current file.
+ */
+file(argv)
+       register char **argv;
+{
+
+       if (argv[0] == NOSTR) {
+               newfileinfo();
+               return 0;
+       }
+       if (setfile(*argv) < 0)
+               return 1;
+       announce();
+       return 0;
+}
+
+/*
+ * Expand file names like echo
+ */
+echo(argv)
+       char **argv;
+{
+       register char **ap;
+       register char *cp;
+
+       for (ap = argv; *ap != NOSTR; ap++) {
+               cp = *ap;
+               if ((cp = expand(cp)) != NOSTR) {
+                       if (ap != argv)
+                               putchar(' ');
+                       printf("%s", cp);
+               }
+       }
+       putchar('\n');
+       return 0;
+}
+
+Respond(msgvec)
+       int *msgvec;
+{
+       if (value("Replyall") == NOSTR)
+               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.
+ */
+_Respond(msgvec)
+       int msgvec[];
+{
+       struct header head;
+       struct message *mp;
+       register int *ap;
+       register char *cp;
+
+       head.h_to = NIL;
+       for (ap = msgvec; *ap != 0; ap++) {
+               mp = &message[*ap - 1];
+               touch(mp);
+               dot = mp;
+               if ((cp = skin(hfield("from", mp))) == NOSTR)
+                       cp = skin(nameof(mp, 2));
+               head.h_to = cat(head.h_to, extract(cp, GTO));
+       }
+       if (head.h_to == NIL)
+               return 0;
+       mp = &message[msgvec[0] - 1];
+       if ((head.h_subject = hfield("subject", mp)) == NOSTR)
+               head.h_subject = hfield("subj", mp);
+       head.h_subject = reedit(head.h_subject);
+       head.h_cc = NIL;
+       head.h_bcc = NIL;
+       head.h_smopts = NIL;
+       mail1(&head, 1);
+       return 0;
+}
+
+/*
+ * Conditional commands.  These allow one to parameterize one's
+ * .mailrc and do some things if sending, others if receiving.
+ */
+
+ifcmd(argv)
+       char **argv;
+{
+       register char *cp;
+
+       if (cond != CANY) {
+               printf("Illegal nested \"if\"\n");
+               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.
+ */
+
+elsecmd()
+{
+
+       switch (cond) {
+       case CANY:
+               printf("\"Else\" without matching \"if\"\n");
+               return(1);
+
+       case CSEND:
+               cond = CRCV;
+               break;
+
+       case CRCV:
+               cond = CSEND;
+               break;
+
+       default:
+               printf("Mail's idea of conditions is screwed up\n");
+               cond = CANY;
+               break;
+       }
+       return(0);
+}
+
+/*
+ * End of if statement.  Just set cond back to anything.
+ */
+
+endifcmd()
+{
+
+       if (cond == CANY) {
+               printf("\"Endif\" without matching \"if\"\n");
+               return(1);
+       }
+       cond = CANY;
+       return(0);
+}
+
+/*
+ * Set the list of alternate names.
+ */
+alternates(namelist)
+       char **namelist;
+{
+       register int c;
+       register char **ap, **ap2, *cp;
+
+       c = argcount(namelist) + 1;
+       if (c == 1) {
+               if (altnames == 0)
+                       return(0);
+               for (ap = altnames; *ap; ap++)
+                       printf("%s ", *ap);
+               printf("\n");
+               return(0);
+       }
+       if (altnames != 0)
+               cfree((char *) altnames);
+       altnames = (char **) calloc((unsigned) c, sizeof (char *));
+       for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
+               cp = (char *) calloc((unsigned) strlen(*ap) + 1, sizeof (char));
+               strcpy(cp, *ap);
+               *ap2 = cp;
+       }
+       *ap2 = 0;
+       return(0);
+}
diff --git a/usr/src/usr.bin/mail/cmdtab.c b/usr/src/usr.bin/mail/cmdtab.c
new file mode 100644 (file)
index 0000000..20aea47
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 1980 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmdtab.c   5.11 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+#include "def.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * Define all of the command names and bindings.
+ */
+
+extern int type(), preserve(), delete(), undelete(), next(), shell(), schdir();
+extern int save(), help(), headers(), pdot(), respond(), editor();
+extern int quitcmd(), rexit(), pcmdlist(), sendmail(), from(), copycmd();
+extern int messize(), psalloc(), deltype(), unset(), set(), source();
+extern int pversion(), group(), top(), core(), null(), stouch(), visual();
+extern int swrite(), dosh(), file(), echo(), Respond(), scroll(), ifcmd();
+extern int elsecmd(), endifcmd(), mboxit(), clobber(), alternates();
+extern int folders(), igfield(), Type(), retfield(), more(), More();
+extern int saveigfield(), saveretfield();
+extern int unread();   /* , Header(); */
+
+struct cmd cmdtab[] = {
+       "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",       delete,         W|P|MSGLIST,    0,      MMNDEL,
+       "dp",           deltype,        W|MSGLIST,      0,      MMNDEL,
+       "dt",           deltype,        W|MSGLIST,      0,      MMNDEL,
+       "undelete",     undelete,       P|MSGLIST,      MDELETED,MMNDEL,
+       "unset",        unset,          M|RAWLIST,      1,      1000,
+       "mail",         sendmail,       R|M|I|STRLIST,  0,      0,
+       "mbox",         mboxit,         W|MSGLIST,      0,      0,
+       "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,
+       "!",            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|NOLIST,     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,
+/*     "Header",       Header,         STRLIST,        0,      1000,   */
+       "core",         core,           M|NOLIST,       0,      0,
+       "#",            null,           M|NOLIST,       0,      0,
+       "clobber",      clobber,        M|RAWLIST,      0,      1,
+       0,              0,              0,              0,      0
+};
diff --git a/usr/src/usr.bin/mail/collect.c b/usr/src/usr.bin/mail/collect.c
new file mode 100644 (file)
index 0000000..4c4038a
--- /dev/null
@@ -0,0 +1,620 @@
+/*
+ * Copyright (c) 1980 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)collect.c  5.24 (Berkeley) 4/1/91";
+#endif /* not lint */
+
+/*
+ * Mail -- a mail program
+ *
+ * Collect input from standard input, handling
+ * ~ escapes.
+ */
+
+#include "rcv.h"
+#include <sys/stat.h>
+
+/*
+ * Read a message from standard output and return a read file to it
+ * or NULL on error.
+ */
+
+/*
+ * The following hokiness with global variables is so that on
+ * receipt of an interrupt signal, the partial message can be salted
+ * away on dead.letter.
+ */
+
+static sig_t   saveint;                /* Previous SIGINT value */
+static sig_t   savehup;                /* Previous SIGHUP value */
+static sig_t   savetstp;               /* Previous SIGTSTP value */
+static sig_t   savettou;               /* Previous SIGTTOU value */
+static sig_t   savettin;               /* Previous SIGTTIN value */
+static FILE    *collf;                 /* File for saving away */
+static int     hadintr;                /* Have seen one SIGINT so far */
+
+static jmp_buf colljmp;                /* To get back to work */
+static int     colljmp_p;              /* whether to long jump */
+static jmp_buf collabort;              /* To end collection with error */
+
+FILE *
+collect(hp, printheaders)
+       struct header *hp;
+{
+       FILE *fbuf;
+       int lc, cc, escape, eofcount;
+       register int c, t;
+       char linebuf[LINESIZE], *cp;
+       extern char tempMail[];
+       char getsub;
+       int omask;
+       void collint(), collhup(), collstop();
+
+       collf = NULL;
+       /*
+        * Start catching signals from here, but we're still die on interrupts
+        * until we're in the main loop.
+        */
+       omask = sigblock(sigmask(SIGINT) | sigmask(SIGHUP));
+       if ((saveint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
+               signal(SIGINT, collint);
+       if ((savehup = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
+               signal(SIGHUP, collhup);
+       savetstp = signal(SIGTSTP, collstop);
+       savettou = signal(SIGTTOU, collstop);
+       savettin = signal(SIGTTIN, collstop);
+       if (setjmp(collabort) || setjmp(colljmp)) {
+               rm(tempMail);
+               goto err;
+       }
+       sigsetmask(omask & ~(sigmask(SIGINT) | sigmask(SIGHUP)));
+
+       noreset++;
+       if ((collf = Fopen(tempMail, "w+")) == NULL) {
+               perror(tempMail);
+               goto err;
+       }
+       unlink(tempMail);
+
+       /*
+        * If we are going to prompt for a subject,
+        * refrain from printing a newline after
+        * the headers (since some people mind).
+        */
+       t = GTO|GSUBJECT|GCC|GNL;
+       getsub = 0;
+       if (hp->h_subject == NOSTR && value("interactive") != NOSTR &&
+           (value("ask") != NOSTR || value("asksub") != NOSTR))
+               t &= ~GNL, getsub++;
+       if (printheaders) {
+               puthead(hp, stdout, t);
+               fflush(stdout);
+       }
+       if ((cp = value("escape")) != NOSTR)
+               escape = *cp;
+       else
+               escape = ESCAPE;
+       eofcount = 0;
+       hadintr = 0;
+
+       if (!setjmp(colljmp)) {
+               if (getsub)
+                       grabh(hp, GSUBJECT);
+       } else {
+               /*
+                * Come here for printing the after-signal message.
+                * Duplicate messages won't be printed because
+                * the write is aborted if we get a SIGTTOU.
+                */
+cont:
+               if (hadintr) {
+                       fflush(stdout);
+                       fprintf(stderr,
+                       "\n(Interrupt -- one more to kill letter)\n");
+               } else {
+                       printf("(continue)\n");
+                       fflush(stdout);
+               }
+       }
+       for (;;) {
+               colljmp_p = 1;
+               c = readline(stdin, linebuf, LINESIZE);
+               colljmp_p = 0;
+               if (c < 0) {
+                       if (value("interactive") != NOSTR &&
+                           value("ignoreeof") != NOSTR && ++eofcount < 25) {
+                               printf("Use \".\" to terminate letter\n");
+                               continue;
+                       }
+                       break;
+               }
+               eofcount = 0;
+               hadintr = 0;
+               if (linebuf[0] == '.' && linebuf[1] == '\0' &&
+                   value("interactive") != NOSTR &&
+                   (value("dot") != NOSTR || value("ignoreeof") != NOSTR))
+                       break;
+               if (linebuf[0] != escape || value("interactive") == NOSTR) {
+                       if (putline(collf, linebuf) < 0)
+                               goto err;
+                       continue;
+               }
+               c = linebuf[1];
+               switch (c) {
+               default:
+                       /*
+                        * On double escape, just send the single one.
+                        * Otherwise, it's an error.
+                        */
+                       if (c == escape) {
+                               if (putline(collf, &linebuf[1]) < 0)
+                                       goto err;
+                               else
+                                       break;
+                       }
+                       printf("Unknown tilde escape.\n");
+                       break;
+               case 'C':
+                       /*
+                        * Dump core.
+                        */
+                       core();
+                       break;
+               case '!':
+                       /*
+                        * Shell escape, send the balance of the
+                        * line to sh -c.
+                        */
+                       shell(&linebuf[2]);
+                       break;
+               case ':':
+                       /*
+                        * Escape to command mode, but be nice!
+                        */
+                       execute(&linebuf[2], 1);
+                       goto cont;
+               case '.':
+                       /*
+                        * Simulate end of file on input.
+                        */
+                       goto out;
+               case 'q':
+                       /*
+                        * Force a quit of sending mail.
+                        * Act like an interrupt happened.
+                        */
+                       hadintr++;
+                       collint(SIGINT);
+                       exit(1);
+               case 'h':
+                       /*
+                        * Grab a bunch of headers.
+                        */
+                       grabh(hp, GTO|GSUBJECT|GCC|GBCC);
+                       goto cont;
+               case 't':
+                       /*
+                        * Add to the To list.
+                        */
+                       hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO));
+                       break;
+               case 's':
+                       /*
+                        * Set the Subject list.
+                        */
+                       cp = &linebuf[2];
+                       while (isspace(*cp))
+                               cp++;
+                       hp->h_subject = savestr(cp);
+                       break;
+               case 'c':
+                       /*
+                        * Add to the CC list.
+                        */
+                       hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC));
+                       break;
+               case 'b':
+                       /*
+                        * Add stuff to blind carbon copies list.
+                        */
+                       hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC));
+                       break;
+               case 'd':
+                       strcpy(linebuf + 2, getdeadletter());
+                       /* fall into . . . */
+               case 'r':
+                       /*
+                        * Invoke a file:
+                        * Search for the file name,
+                        * then open it and copy the contents to collf.
+                        */
+                       cp = &linebuf[2];
+                       while (isspace(*cp))
+                               cp++;
+                       if (*cp == '\0') {
+                               printf("Interpolate what file?\n");
+                               break;
+                       }
+                       cp = expand(cp);
+                       if (cp == NOSTR)
+                               break;
+                       if (isdir(cp)) {
+                               printf("%s: Directory\n", cp);
+                               break;
+                       }
+                       if ((fbuf = Fopen(cp, "r")) == NULL) {
+                               perror(cp);
+                               break;
+                       }
+                       printf("\"%s\" ", cp);
+                       fflush(stdout);
+                       lc = 0;
+                       cc = 0;
+                       while (readline(fbuf, linebuf, LINESIZE) >= 0) {
+                               lc++;
+                               if ((t = putline(collf, linebuf)) < 0) {
+                                       Fclose(fbuf);
+                                       goto err;
+                               }
+                               cc += t;
+                       }
+                       Fclose(fbuf);
+                       printf("%d/%d\n", lc, cc);
+                       break;
+               case 'w':
+                       /*
+                        * Write the message on a file.
+                        */
+                       cp = &linebuf[2];
+                       while (*cp == ' ' || *cp == '\t')
+                               cp++;
+                       if (*cp == '\0') {
+                               fprintf(stderr, "Write what file!?\n");
+                               break;
+                       }
+                       if ((cp = expand(cp)) == NOSTR)
+                               break;
+                       rewind(collf);
+                       exwrite(cp, collf, 1);
+                       break;
+               case 'm':
+               case 'M':
+               case 'f':
+               case 'F':
+                       /*
+                        * Interpolate the named messages, if we
+                        * are in receiving mail mode.  Does the
+                        * standard list processing garbage.
+                        * If ~f is given, we don't shift over.
+                        */
+                       if (forward(linebuf + 2, collf, c) < 0)
+                               goto err;
+                       goto cont;
+               case '?':
+                       if ((fbuf = Fopen(_PATH_TILDE, "r")) == NULL) {
+                               perror(_PATH_TILDE);
+                               break;
+                       }
+                       while ((t = getc(fbuf)) != EOF)
+                               (void) putchar(t);
+                       Fclose(fbuf);
+                       break;
+               case 'p':
+                       /*
+                        * Print out the current state of the
+                        * message without altering anything.
+                        */
+                       rewind(collf);
+                       printf("-------\nMessage contains:\n");
+                       puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL);
+                       while ((t = getc(collf)) != EOF)
+                               (void) putchar(t);
+                       goto cont;
+               case '|':
+                       /*
+                        * Pipe message through command.
+                        * Collect output as new message.
+                        */
+                       rewind(collf);
+                       mespipe(collf, &linebuf[2]);
+                       goto cont;
+               case 'v':
+               case 'e':
+                       /*
+                        * Edit the current message.
+                        * 'e' means to use EDITOR
+                        * 'v' means to use VISUAL
+                        */
+                       rewind(collf);
+                       mesedit(collf, c);
+                       goto cont;
+               }
+       }
+       goto out;
+err:
+       if (collf != NULL) {
+               Fclose(collf);
+               collf = NULL;
+       }
+out:
+       if (collf != NULL)
+               rewind(collf);
+       noreset--;
+       sigblock(sigmask(SIGINT) | sigmask(SIGHUP));
+       signal(SIGINT, saveint);
+       signal(SIGHUP, savehup);
+       signal(SIGTSTP, savetstp);
+       signal(SIGTTOU, savettou);
+       signal(SIGTTIN, savettin);
+       sigsetmask(omask);
+       return collf;
+}
+
+/*
+ * Write a file, ex-like if f set.
+ */
+
+exwrite(name, fp, f)
+       char name[];
+       FILE *fp;
+{
+       register FILE *of;
+       register int c;
+       long cc;
+       int lc;
+       struct stat junk;
+
+       if (f) {
+               printf("\"%s\" ", name);
+               fflush(stdout);
+       }
+       if (stat(name, &junk) >= 0 && (junk.st_mode & S_IFMT) == S_IFREG) {
+               if (!f)
+                       fprintf(stderr, "%s: ", name);
+               fprintf(stderr, "File exists\n");
+               return(-1);
+       }
+       if ((of = Fopen(name, "w")) == NULL) {
+               perror(NOSTR);
+               return(-1);
+       }
+       lc = 0;
+       cc = 0;
+       while ((c = getc(fp)) != EOF) {
+               cc++;
+               if (c == '\n')
+                       lc++;
+               (void) putc(c, of);
+               if (ferror(of)) {
+                       perror(name);
+                       Fclose(of);
+                       return(-1);
+               }
+       }
+       Fclose(of);
+       printf("%d/%ld\n", lc, cc);
+       fflush(stdout);
+       return(0);
+}
+
+/*
+ * Edit the message being collected on fp.
+ * On return, make the edit file the new temp file.
+ */
+mesedit(fp, c)
+       FILE *fp;
+{
+       sig_t sigint = signal(SIGINT, SIG_IGN);
+       FILE *nf = run_editor(fp, (off_t)-1, c, 0);
+
+       if (nf != NULL) {
+               fseek(nf, (off_t)0, 2);
+               collf = nf;
+               Fclose(fp);
+       }
+       (void) signal(SIGINT, sigint);
+}
+
+/*
+ * Pipe the message through the command.
+ * Old message is on stdin of command;
+ * New message collected from stdout.
+ * Sh -c must return 0 to accept the new message.
+ */
+mespipe(fp, cmd)
+       FILE *fp;
+       char cmd[];
+{
+       FILE *nf;
+       sig_t sigint = signal(SIGINT, SIG_IGN);
+       extern char tempEdit[];
+
+       if ((nf = Fopen(tempEdit, "w+")) == NULL) {
+               perror(tempEdit);
+               goto out;
+       }
+       (void) unlink(tempEdit);
+       /*
+        * stdin = current message.
+        * stdout = new message.
+        */
+       if (run_command(cmd, 0, fileno(fp), fileno(nf), NOSTR) < 0) {
+               (void) Fclose(nf);
+               goto out;
+       }
+       if (fsize(nf) == 0) {
+               fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
+               (void) Fclose(nf);
+               goto out;
+       }
+       /*
+        * Take new files.
+        */
+       (void) fseek(nf, 0L, 2);
+       collf = nf;
+       (void) Fclose(fp);
+out:
+       (void) signal(SIGINT, sigint);
+}
+
+/*
+ * Interpolate the named messages into the current
+ * message, preceding each line with a tab.
+ * Return a count of the number of characters now in
+ * the message, or -1 if an error is encountered writing
+ * the message temporary.  The flag argument is 'm' if we
+ * should shift over and 'f' if not.
+ */
+forward(ms, fp, f)
+       char ms[];
+       FILE *fp;
+{
+       register int *msgvec;
+       extern char tempMail[];
+       struct ignoretab *ig;
+       char *tabst;
+
+       msgvec = (int *) salloc((msgCount+1) * sizeof *msgvec);
+       if (msgvec == (int *) NOSTR)
+               return(0);
+       if (getmsglist(ms, msgvec, 0) < 0)
+               return(0);
+       if (*msgvec == 0) {
+               *msgvec = first(0, MMNORM);
+               if (*msgvec == NULL) {
+                       printf("No appropriate messages\n");
+                       return(0);
+               }
+               msgvec[1] = NULL;
+       }
+       if (f == 'f' || f == 'F')
+               tabst = NOSTR;
+       else if ((tabst = value("indentprefix")) == NOSTR)
+               tabst = "\t";
+       ig = isupper(f) ? NULL : ignore;
+       printf("Interpolating:");
+       for (; *msgvec != 0; msgvec++) {
+               struct message *mp = message + *msgvec - 1;
+
+               touch(mp);
+               printf(" %d", *msgvec);
+               if (send(mp, fp, ig, tabst) < 0) {
+                       perror(tempMail);
+                       return(-1);
+               }
+       }
+       printf("\n");
+       return(0);
+}
+
+/*
+ * Print (continue) when continued after ^Z.
+ */
+/*ARGSUSED*/
+void
+collstop(s)
+{
+       sig_t old_action = signal(s, SIG_DFL);
+
+       sigsetmask(sigblock(0) & ~sigmask(s));
+       kill(0, s);
+       sigblock(sigmask(s));
+       signal(s, old_action);
+       if (colljmp_p) {
+               colljmp_p = 0;
+               hadintr = 0;
+               longjmp(colljmp, 1);
+       }
+}
+
+/*
+ * On interrupt, come here to save the partial message in ~/dead.letter.
+ * Then jump out of the collection loop.
+ */
+/*ARGSUSED*/
+void
+collint(s)
+{
+       /*
+        * the control flow is subtle, because we can be called from ~q.
+        */
+       if (!hadintr) {
+               if (value("ignore") != NOSTR) {
+                       puts("@");
+                       fflush(stdout);
+                       clearerr(stdin);
+                       return;
+               }
+               hadintr = 1;
+               longjmp(colljmp, 1);
+       }
+       rewind(collf);
+       if (value("nosave") == NOSTR)
+               savedeadletter(collf);
+       longjmp(collabort, 1);
+}
+
+/*ARGSUSED*/
+void
+collhup(s)
+{
+       rewind(collf);
+       savedeadletter(collf);
+       /*
+        * Let's pretend nobody else wants to clean up,
+        * a true statement at this time.
+        */
+       exit(1);
+}
+
+savedeadletter(fp)
+       register FILE *fp;
+{
+       register FILE *dbuf;
+       register int c;
+       char *cp;
+
+       if (fsize(fp) == 0)
+               return;
+       cp = getdeadletter();
+       c = umask(077);
+       dbuf = Fopen(cp, "a");
+       (void) umask(c);
+       if (dbuf == NULL)
+               return;
+       while ((c = getc(fp)) != EOF)
+               (void) putc(c, dbuf);
+       Fclose(dbuf);
+       rewind(fp);
+}
diff --git a/usr/src/usr.bin/mail/def.h b/usr/src/usr.bin/mail/def.h
new file mode 100644 (file)
index 0000000..a0bdc79
--- /dev/null
@@ -0,0 +1,323 @@
+/*
+ * Copyright (c) 1980 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)def.h       5.22 (Berkeley) 6/25/90
+ */
+
+#include <sys/param.h>         /* includes <sys/types.h> */
+#include <sys/signal.h>
+#include <stdio.h>
+#include <sgtty.h>
+#include <ctype.h>
+#include <string.h>
+#include "pathnames.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * Author: Kurt Shoens (UCB) March 25, 1978
+ */
+
+#define        APPEND                          /* New mail goes to end of mailbox */
+
+#define        ESCAPE          '~'             /* Default escape for sending */
+#define        NMLSIZE         1024            /* max names in a message list */
+#define        PATHSIZE        MAXPATHLEN      /* Size of pathnames throughout */
+#define        HSHSIZE         59              /* Hash size for aliases and vars */
+#define        LINESIZE        BUFSIZ          /* max readable line width */
+#define        STRINGSIZE      ((unsigned) 128)/* Dynamic allocation units */
+#define        MAXARGC         1024            /* Maximum list of raw strings */
+#define        NOSTR           ((char *) 0)    /* Null string pointer */
+#define        MAXEXP          25              /* Maximum expansion of aliases */
+
+#define        equal(a, b)     (strcmp(a,b)==0)/* A nice function to string compare */
+
+struct message {
+       short   m_flag;                 /* flags, see below */
+       short   m_block;                /* block number of this message */
+       short   m_offset;               /* offset in block of message */
+       long    m_size;                 /* Bytes in the message */
+       short   m_lines;                /* Lines in the message */
+};
+
+/*
+ * flag bits.
+ */
+
+#define        MUSED           (1<<0)          /* entry is used, but this bit isn't */
+#define        MDELETED        (1<<1)          /* entry has been deleted */
+#define        MSAVED          (1<<2)          /* entry has been saved */
+#define        MTOUCH          (1<<3)          /* entry has been noticed */
+#define        MPRESERVE       (1<<4)          /* keep entry in sys mailbox */
+#define        MMARK           (1<<5)          /* message is marked! */
+#define        MODIFY          (1<<6)          /* message has been modified */
+#define        MNEW            (1<<7)          /* message has never been seen */
+#define        MREAD           (1<<8)          /* message has been read sometime. */
+#define        MSTATUS         (1<<9)          /* message status has changed */
+#define        MBOX            (1<<10)         /* Send this to mbox, regardless */
+
+/*
+ * Given a file address, determine the block number it represents.
+ */
+#define blockof(off)                   ((int) ((off) / 4096))
+#define offsetof(off)                  ((int) ((off) % 4096))
+#define positionof(block, offset)      ((off_t)(block) * 4096 + (offset))
+
+/*
+ * Format of the command description table.
+ * The actual table is declared and initialized
+ * in lex.c
+ */
+
+struct cmd {
+       char    *c_name;                /* Name of command */
+       int     (*c_func)();            /* Implementor of the command */
+       short   c_argtype;              /* Type of arglist (see below) */
+       short   c_msgflag;              /* Required flags of messages */
+       short   c_msgmask;              /* Relevant flags of messages */
+};
+
+/* Yechh, can't initialize unions */
+
+#define        c_minargs c_msgflag             /* Minimum argcount for RAWLIST */
+#define        c_maxargs c_msgmask             /* Max argcount for RAWLIST */
+
+/*
+ * Argument types.
+ */
+
+#define        MSGLIST  0              /* Message list type */
+#define        STRLIST  1              /* A pure string */
+#define        RAWLIST  2              /* Shell string list */
+#define        NOLIST   3              /* Just plain 0 */
+#define        NDMLIST  4              /* Message list, no defaults */
+
+#define        P       040             /* Autoprint dot after command */
+#define        I       0100            /* Interactive command bit */
+#define        M       0200            /* Legal from send mode bit */
+#define        W       0400            /* Illegal when read only bit */
+#define        F       01000           /* Is a conditional command */
+#define        T       02000           /* Is a transparent command */
+#define        R       04000           /* Cannot be called from collect */
+
+/*
+ * Oft-used mask values
+ */
+
+#define        MMNORM          (MDELETED|MSAVED)/* Look at both save and delete bits */
+#define        MMNDEL          MDELETED        /* Look only at deleted bit */
+
+/*
+ * Structure used to return a break down of a head
+ * line (hats off to Bill Joy!)
+ */
+
+struct headline {
+       char    *l_from;        /* The name of the sender */
+       char    *l_tty;         /* His tty string (if any) */
+       char    *l_date;        /* The entire date string */
+};
+
+#define        GTO     1               /* Grab To: line */
+#define        GSUBJECT 2              /* Likewise, Subject: line */
+#define        GCC     4               /* And the Cc: line */
+#define        GBCC    8               /* And also the Bcc: line */
+#define        GMASK   (GTO|GSUBJECT|GCC|GBCC)
+                               /* Mask of places from whence */
+
+#define        GNL     16              /* Print blank line after */
+#define        GDEL    32              /* Entity removed from list */
+#define        GCOMMA  64              /* detract puts in commas */
+
+/*
+ * Structure used to pass about the current
+ * state of the user-typed message header.
+ */
+
+struct header {
+       struct name *h_to;              /* Dynamic "To:" string */
+       char *h_subject;                /* Subject string */
+       struct name *h_cc;              /* Carbon copies string */
+       struct name *h_bcc;             /* Blind carbon copies */
+       struct name *h_smopts;          /* Sendmail options */
+};
+
+/*
+ * Structure of namelist nodes used in processing
+ * the recipients of mail and aliases and all that
+ * kind of stuff.
+ */
+
+struct name {
+       struct  name *n_flink;          /* Forward link in list. */
+       struct  name *n_blink;          /* Backward list link */
+       short   n_type;                 /* From which list it came */
+       char    *n_name;                /* This fella's name */
+};
+
+/*
+ * Structure of a variable node.  All variables are
+ * kept on a singly-linked list of these, rooted by
+ * "variables"
+ */
+
+struct var {
+       struct  var *v_link;            /* Forward link to next variable */
+       char    *v_name;                /* The variable's name */
+       char    *v_value;               /* And it's current value */
+};
+
+struct group {
+       struct  group *ge_link;         /* Next person in this group */
+       char    *ge_name;               /* This person's user name */
+};
+
+struct grouphead {
+       struct  grouphead *g_link;      /* Next grouphead in list */
+       char    *g_name;                /* Name of this group */
+       struct  group *g_list;          /* Users in group. */
+};
+
+#define        NIL     ((struct name *) 0)     /* The nil pointer for namelists */
+#define        NONE    ((struct cmd *) 0)      /* The nil pointer to command tab */
+#define        NOVAR   ((struct var *) 0)      /* The nil pointer to variables */
+#define        NOGRP   ((struct grouphead *) 0)/* The nil grouphead pointer */
+#define        NOGE    ((struct group *) 0)    /* The nil group pointer */
+
+/*
+ * Structure of the hash table of ignored header fields
+ */
+struct ignoretab {
+       int i_count;                    /* Number of entries */
+       struct ignore {
+               struct ignore *i_link;  /* Next ignored field in bucket */
+               char *i_field;          /* This ignored field */
+       } *i_head[HSHSIZE];
+};
+
+/*
+ * Token values returned by the scanner used for argument lists.
+ * Also, sizes of scanner-related things.
+ */
+
+#define        TEOL    0                       /* End of the command line */
+#define        TNUMBER 1                       /* A message number */
+#define        TDASH   2                       /* A simple dash */
+#define        TSTRING 3                       /* A string (possibly containing -) */
+#define        TDOT    4                       /* A "." */
+#define        TUP     5                       /* An "^" */
+#define        TDOLLAR 6                       /* A "$" */
+#define        TSTAR   7                       /* A "*" */
+#define        TOPEN   8                       /* An '(' */
+#define        TCLOSE  9                       /* A ')' */
+#define TPLUS  10                      /* A '+' */
+#define TERROR 11                      /* A lexical error */
+
+#define        REGDEP  2                       /* Maximum regret depth. */
+#define        STRINGLEN       1024            /* Maximum length of string token */
+
+/*
+ * Constants for conditional commands.  These describe whether
+ * we should be executing stuff or not.
+ */
+
+#define        CANY            0               /* Execute in send or receive mode */
+#define        CRCV            1               /* Execute in receive mode only */
+#define        CSEND           2               /* Execute in send mode only */
+
+/*
+ * Kludges to handle the change from setexit / reset to setjmp / longjmp
+ */
+
+#define        setexit()       setjmp(srbuf)
+#define        reset(x)        longjmp(srbuf, x)
+
+/*
+ * Truncate a file to the last character written. This is
+ * useful just before closing an old file that was opened
+ * for read/write.
+ */
+#define trunc(stream)  ftruncate(fileno(stream), (long) ftell(stream))
+
+/*
+ * Forward declarations of routine types to keep lint and cc happy.
+ */
+
+FILE   *Fopen();
+FILE   *Fdopen();
+FILE   *Popen();
+FILE   *collect();
+FILE   *infix();
+FILE   *run_editor();
+FILE   *setinput();
+char   **unpack();
+char   *calloc();
+char   *copy();
+char   *copyin();
+char   *detract();
+char   *expand();
+char   *getdeadletter();
+char   *gets();
+char   *hfield();
+char   *name1();
+char   *nameof();
+char   *nextword();
+char   *getenv();
+char   *getname();
+char   *fgets();
+char   *ishfield();
+char   *malloc();
+char   *mktemp();
+char   *readtty();
+char   *reedit();
+char   *salloc();
+char   *savestr();
+char   *skin();
+char   *snarf();
+char   *username();
+char   *value();
+char   *vcopy();
+char   *yankword();
+off_t  fsize();
+uid_t  getuid();
+struct cmd     *lex();
+struct grouphead       *findgroup();
+struct name    *nalloc();
+struct name    *cat();
+struct name    *delname();
+struct name    *elide();
+struct name    *extract();
+struct name    *gexpand();
+struct name    *outof();
+struct name    *put();
+struct name    *usermap();
+struct var     *lookup();
diff --git a/usr/src/usr.bin/mail/edit.c b/usr/src/usr.bin/mail/edit.c
new file mode 100644 (file)
index 0000000..8af0437
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 1980 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)edit.c     5.15 (Berkeley) 6/25/90";
+#endif /* not lint */
+
+#include "rcv.h"
+#include <sys/stat.h>
+
+/*
+ * Mail -- a mail program
+ *
+ * Perform message editing functions.
+ */
+
+/*
+ * Edit a message list.
+ */
+
+editor(msgvec)
+       int *msgvec;
+{
+
+       return edit1(msgvec, 'e');
+}
+
+/*
+ * Invoke the visual editor on a message list.
+ */
+
+visual(msgvec)
+       int *msgvec;
+{
+
+       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.
+ */
+edit1(msgvec, type)
+       int *msgvec;
+       char type;
+{
+       register int c;
+       int i;
+       FILE *fp;
+       register struct message *mp;
+       off_t size;
+
+       /*
+        * Deal with each message to be edited . . .
+        */
+       for (i = 0; msgvec[i] && i < msgCount; i++) {
+               sig_t sigint;
+
+               if (i > 0) {
+                       char buf[100];
+                       char *p;
+
+                       printf("Edit message %d [ynq]? ", msgvec[i]);
+                       if (fgets(buf, sizeof buf, stdin) == 0)
+                               break;
+                       for (p = buf; *p == ' ' || *p == '\t'; p++)
+                               ;
+                       if (*p == 'q')
+                               break;
+                       if (*p == 'n')
+                               continue;
+               }
+               dot = mp = &message[msgvec[i] - 1];
+               touch(mp);
+               sigint = signal(SIGINT, SIG_IGN);
+               fp = run_editor(setinput(mp), mp->m_size, type, readonly);
+               if (fp != NULL) {
+                       (void) fseek(otf, (long) 0, 2);
+                       size = ftell(otf);
+                       mp->m_block = blockof(size);
+                       mp->m_offset = offsetof(size);
+                       mp->m_size = fsize(fp);
+                       mp->m_lines = 0;
+                       mp->m_flag |= MODIFY;
+                       rewind(fp);
+                       while ((c = getc(fp)) != EOF) {
+                               if (c == '\n')
+                                       mp->m_lines++;
+                               if (putc(c, otf) == EOF)
+                                       break;
+                       }
+                       if (ferror(otf))
+                               perror("/tmp");
+                       (void) Fclose(fp);
+               }
+               (void) signal(SIGINT, sigint);
+       }
+       return 0;
+}
+
+/*
+ * Run an editor on the file at "fpp" of "size" bytes,
+ * and return a new file pointer.
+ * Signals must be handled by the caller.
+ * "Type" is 'e' for _PATH_EX, 'v' for _PATH_VI.
+ */
+FILE *
+run_editor(fp, size, type, readonly)
+       register FILE *fp;
+       off_t size;
+       char type;
+{
+       register FILE *nf = NULL;
+       register int t;
+       time_t modtime;
+       char *edit;
+       struct stat statb;
+       extern char tempEdit[];
+
+       if ((t = creat(tempEdit, readonly ? 0400 : 0600)) < 0) {
+               perror(tempEdit);
+               goto out;
+       }
+       if ((nf = Fdopen(t, "w")) == NULL) {
+               perror(tempEdit);
+               (void) unlink(tempEdit);
+               goto out;
+       }
+       if (size >= 0)
+               while (--size >= 0 && (t = getc(fp)) != EOF)
+                       (void) putc(t, nf);
+       else
+               while ((t = getc(fp)) != EOF)
+                       (void) putc(t, nf);
+       (void) fflush(nf);
+       if (fstat(fileno(nf), &statb) < 0)
+               modtime = 0;
+       else
+               modtime = statb.st_mtime;
+       if (ferror(nf)) {
+               (void) Fclose(nf);
+               perror(tempEdit);
+               (void) unlink(tempEdit);
+               nf = NULL;
+               goto out;
+       }
+       if (Fclose(nf) < 0) {
+               perror(tempEdit);
+               (void) unlink(tempEdit);
+               nf = NULL;
+               goto out;
+       }
+       nf = NULL;
+       if ((edit = value(type == 'e' ? "EDITOR" : "VISUAL")) == NOSTR)
+               edit = type == 'e' ? _PATH_EX : _PATH_VI;
+       if (run_command(edit, 0, -1, -1, tempEdit, NOSTR) < 0) {
+               (void) unlink(tempEdit);
+               goto out;
+       }
+       /*
+        * If in read only mode or file unchanged, just remove the editor
+        * temporary and return.
+        */
+       if (readonly) {
+               (void) unlink(tempEdit);
+               goto out;
+       }
+       if (stat(tempEdit, &statb) < 0) {
+               perror(tempEdit);
+               goto out;
+       }
+       if (modtime == statb.st_mtime) {
+               (void) unlink(tempEdit);
+               goto out;
+       }
+       /*
+        * Now switch to new file.
+        */
+       if ((nf = Fopen(tempEdit, "a+")) == NULL) {
+               perror(tempEdit);
+               (void) unlink(tempEdit);
+               goto out;
+       }
+       (void) unlink(tempEdit);
+out:
+       return nf;
+}
diff --git a/usr/src/usr.bin/mail/fio.c b/usr/src/usr.bin/mail/fio.c
new file mode 100644 (file)
index 0000000..4eff69b
--- /dev/null
@@ -0,0 +1,419 @@
+/*
+ * Copyright (c) 1980 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)fio.c      5.24 (Berkeley) 2/3/91";
+#endif /* not lint */
+
+#include "rcv.h"
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+#include <paths.h>
+#include <errno.h>
+
+/*
+ * Mail -- a mail program
+ *
+ * File I/O.
+ */
+
+/*
+ * Set up the input pointers while copying the mail file into /tmp.
+ */
+setptr(ibuf)
+       register FILE *ibuf;
+{
+       register int c, count;
+       register char *cp, *cp2;
+       struct message this;
+       FILE *mestmp;
+       off_t offset;
+       int maybe, inhead;
+       char linebuf[LINESIZE];
+
+       /* Get temporary file. */
+       (void)sprintf(linebuf, "%s/mail.XXXXXX", _PATH_TMP);
+       if ((c = mkstemp(linebuf)) == -1 ||
+           (mestmp = Fdopen(c, "r+")) == NULL) {
+               (void)fprintf(stderr, "mail: can't open %s\n", linebuf);
+               exit(1);
+       }
+       (void)unlink(linebuf);
+
+       msgCount = 0;
+       maybe = 1;
+       inhead = 0;
+       offset = 0;
+       this.m_flag = MUSED|MNEW;
+       this.m_size = 0;
+       this.m_lines = 0;
+       this.m_block = 0;
+       this.m_offset = 0;
+       for (;;) {
+               if (fgets(linebuf, LINESIZE, ibuf) == NULL) {
+                       if (append(&this, mestmp)) {
+                               perror("temporary file");
+                               exit(1);
+                       }
+                       makemessage(mestmp);
+                       return;
+               }
+               count = strlen(linebuf);
+               (void) fwrite(linebuf, sizeof *linebuf, count, otf);
+               if (ferror(otf)) {
+                       perror("/tmp");
+                       exit(1);
+               }
+               linebuf[count - 1] = 0;
+               if (maybe && linebuf[0] == 'F' && ishead(linebuf)) {
+                       msgCount++;
+                       if (append(&this, mestmp)) {
+                               perror("temporary file");
+                               exit(1);
+                       }
+                       this.m_flag = MUSED|MNEW;
+                       this.m_size = 0;
+                       this.m_lines = 0;
+                       this.m_block = blockof(offset);
+                       this.m_offset = offsetof(offset);
+                       inhead = 1;
+               } else if (linebuf[0] == 0) {
+                       inhead = 0;
+               } else if (inhead) {
+                       for (cp = linebuf, cp2 = "status";; cp++) {
+                               if ((c = *cp2++) == 0) {
+                                       while (isspace(*cp++))
+                                               ;
+                                       if (cp[-1] != ':')
+                                               break;
+                                       while (c = *cp++)
+                                               if (c == 'R')
+                                                       this.m_flag |= MREAD;
+                                               else if (c == 'O')
+                                                       this.m_flag &= ~MNEW;
+                                       inhead = 0;
+                                       break;
+                               }
+                               if (*cp != c && *cp != toupper(c))
+                                       break;
+                       }
+               }
+               offset += count;
+               this.m_size += count;
+               this.m_lines++;
+               maybe = linebuf[0] == 0;
+       }
+}
+
+/*
+ * Drop the passed line onto the passed output buffer.
+ * If a write error occurs, return -1, else the count of
+ * characters written, including the newline.
+ */
+putline(obuf, linebuf)
+       FILE *obuf;
+       char *linebuf;
+{
+       register int c;
+
+       c = strlen(linebuf);
+       (void) fwrite(linebuf, sizeof *linebuf, c, obuf);
+       (void) putc('\n', obuf);
+       if (ferror(obuf))
+               return (-1);
+       return (c + 1);
+}
+
+/*
+ * Read up a line from the specified input into the line
+ * buffer.  Return the number of characters read.  Do not
+ * include the newline at the end.
+ */
+readline(ibuf, linebuf, linesize)
+       FILE *ibuf;
+       char *linebuf;
+{
+       register int n;
+
+       clearerr(ibuf);
+       if (fgets(linebuf, linesize, ibuf) == NULL)
+               return -1;
+       n = strlen(linebuf);
+       if (n > 0 && linebuf[n - 1] == '\n')
+               linebuf[--n] = '\0';
+       return n;
+}
+
+/*
+ * Return a file buffer all ready to read up the
+ * passed message pointer.
+ */
+FILE *
+setinput(mp)
+       register struct message *mp;
+{
+
+       fflush(otf);
+       if (fseek(itf, positionof(mp->m_block, mp->m_offset), 0) < 0) {
+               perror("fseek");
+               panic("temporary file seek");
+       }
+       return (itf);
+}
+
+/*
+ * Take the data out of the passed ghost file and toss it into
+ * a dynamically allocated message structure.
+ */
+makemessage(f)
+       FILE *f;
+{
+       register size = (msgCount + 1) * sizeof (struct message);
+       off_t lseek();
+
+       if (message != 0)
+               free((char *) message);
+       if ((message = (struct message *) malloc((unsigned) size)) == 0)
+               panic("Insufficient memory for %d messages", msgCount);
+       dot = message;
+       size -= sizeof (struct message);
+       fflush(f);
+       (void) lseek(fileno(f), (long) sizeof *message, 0);
+       if (read(fileno(f), (char *) message, size) != size)
+               panic("Message temporary file corrupted");
+       message[msgCount].m_size = 0;
+       message[msgCount].m_lines = 0;
+       Fclose(f);
+}
+
+/*
+ * Append the passed message descriptor onto the temp file.
+ * If the write fails, return 1, else 0
+ */
+append(mp, f)
+       struct message *mp;
+       FILE *f;
+{
+       return fwrite((char *) mp, sizeof *mp, 1, f) != 1;
+}
+
+/*
+ * Delete a file, but only if the file is a plain file.
+ */
+rm(name)
+       char *name;
+{
+       struct stat sb;
+
+       if (stat(name, &sb) < 0)
+               return(-1);
+       if (!S_ISREG(sb.st_mode)) {
+               errno = EISDIR;
+               return(-1);
+       }
+       return(unlink(name));
+}
+
+static int sigdepth;           /* depth of holdsigs() */
+static int omask;
+/*
+ * Hold signals SIGHUP, SIGINT, and SIGQUIT.
+ */
+holdsigs()
+{
+
+       if (sigdepth++ == 0)
+               omask = sigblock(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT));
+}
+
+/*
+ * Release signals SIGHUP, SIGINT, and SIGQUIT.
+ */
+relsesigs()
+{
+
+       if (--sigdepth == 0)
+               sigsetmask(omask);
+}
+
+/*
+ * Determine the size of the file possessed by
+ * the passed buffer.
+ */
+off_t
+fsize(iob)
+       FILE *iob;
+{
+       struct stat sbuf;
+
+       if (fstat(fileno(iob), &sbuf) < 0)
+               return 0;
+       return sbuf.st_size;
+}
+
+/*
+ * Evaluate the string given as a new mailbox name.
+ * Supported meta characters:
+ *     %       for my system mail box
+ *     %user   for user's system mail box
+ *     #       for previous file
+ *     &       invoker's mbox file
+ *     +file   file in folder directory
+ *     any shell meta character
+ * Return the file name as a dynamic string.
+ */
+char *
+expand(name)
+       register char *name;
+{
+       char xname[PATHSIZE];
+       char cmdbuf[PATHSIZE];          /* also used for file names */
+       register int pid, l;
+       register char *cp, *shell;
+       int pivec[2];
+       struct stat sbuf;
+       extern union wait wait_status;
+
+       /*
+        * The order of evaluation is "%" and "#" expand into constants.
+        * "&" can expand into "+".  "+" can expand into shell meta characters.
+        * Shell meta characters expand into constants.
+        * This way, we make no recursive expansion.
+        */
+       switch (*name) {
+       case '%':
+               findmail(name[1] ? name + 1 : myname, xname);
+               return savestr(xname);
+       case '#':
+               if (name[1] != 0)
+                       break;
+               if (prevfile[0] == 0) {
+                       printf("No previous file\n");
+                       return NOSTR;
+               }
+               return savestr(prevfile);
+       case '&':
+               if (name[1] == 0 && (name = value("MBOX")) == NOSTR)
+                       name = "~/mbox";
+               /* fall through */
+       }
+       if (name[0] == '+' && getfold(cmdbuf) >= 0) {
+               sprintf(xname, "%s/%s", cmdbuf, name + 1);
+               name = savestr(xname);
+       }
+       /* catch the most common shell meta character */
+       if (name[0] == '~' && (name[1] == '/' || name[1] == '\0')) {
+               sprintf(xname, "%s%s", homedir, name + 1);
+               name = savestr(xname);
+       }
+       if (!anyof(name, "~{[*?$`'\"\\"))
+               return name;
+       if (pipe(pivec) < 0) {
+               perror("pipe");
+               return name;
+       }
+       sprintf(cmdbuf, "echo %s", name);
+       if ((shell = value("SHELL")) == NOSTR)
+               shell = _PATH_CSHELL;
+       pid = start_command(shell, 0, -1, pivec[1], "-c", cmdbuf, NOSTR);
+       if (pid < 0) {
+               close(pivec[0]);
+               close(pivec[1]);
+               return NOSTR;
+       }
+       close(pivec[1]);
+       l = read(pivec[0], xname, BUFSIZ);
+       close(pivec[0]);
+       if (wait_child(pid) < 0 && wait_status.w_termsig != SIGPIPE) {
+               fprintf(stderr, "\"%s\": Expansion failed.\n", name);
+               return NOSTR;
+       }
+       if (l < 0) {
+               perror("read");
+               return NOSTR;
+       }
+       if (l == 0) {
+               fprintf(stderr, "\"%s\": No match.\n", name);
+               return NOSTR;
+       }
+       if (l == BUFSIZ) {
+               fprintf(stderr, "\"%s\": Expansion buffer overflow.\n", name);
+               return NOSTR;
+       }
+       xname[l] = 0;
+       for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
+               ;
+       cp[1] = '\0';
+       if (index(xname, ' ') && stat(xname, &sbuf) < 0) {
+               fprintf(stderr, "\"%s\": Ambiguous.\n", name);
+               return NOSTR;
+       }
+       return savestr(xname);
+}
+
+/*
+ * Determine the current folder directory name.
+ */
+getfold(name)
+       char *name;
+{
+       char *folder;
+
+       if ((folder = value("folder")) == NOSTR)
+               return (-1);
+       if (*folder == '/')
+               strcpy(name, folder);
+       else
+               sprintf(name, "%s/%s", homedir, folder);
+       return (0);
+}
+
+/*
+ * Return the name of the dead.letter file.
+ */
+char *
+getdeadletter()
+{
+       register char *cp;
+
+       if ((cp = value("DEAD")) == NOSTR || (cp = expand(cp)) == NOSTR)
+               cp = expand("~/dead.letter");
+       else if (*cp != '/') {
+               char buf[PATHSIZE];
+
+               (void) sprintf(buf, "~/%s", cp);
+               cp = expand(buf);
+       }
+       return cp;
+}
diff --git a/usr/src/usr.bin/mail/getname.c b/usr/src/usr.bin/mail/getname.c
new file mode 100644 (file)
index 0000000..c25ad12
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 1980 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)getname.c  5.8 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <pwd.h>
+
+/*
+ * Getname / getuserid for those with
+ * hashed passwd data base).
+ *
+ */
+
+#include "rcv.h"
+
+/*
+ * Search the passwd file for a uid.  Return name through ref parameter
+ * if found, indicating success with 0 return.  Return -1 on error.
+ */
+char *
+getname(uid)
+{
+       struct passwd *pw;
+
+       if ((pw = getpwuid(uid)) == NULL)
+               return NOSTR;
+       return pw->pw_name;
+}
+
+/*
+ * Convert the passed name to a user id and return it.  Return -1
+ * on error.
+ */
+getuserid(name)
+       char name[];
+{
+       struct passwd *pw;
+
+       if ((pw = getpwnam(name)) == NULL)
+               return -1;
+       return pw->pw_uid;
+}
diff --git a/usr/src/usr.bin/mail/glob.h b/usr/src/usr.bin/mail/glob.h
new file mode 100644 (file)
index 0000000..314c1f4
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 1980 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *     @(#)glob.h      5.20 (Berkeley) 6/25/90
+ */
+
+/*
+ * A bunch of global variable declarations lie herein.
+ * def.h must be included first.
+ */
+
+int    msgCount;                       /* Count of messages read in */
+int    rcvmode;                        /* True if receiving mail */
+int    sawcom;                         /* Set after first command */
+char   *Tflag;                         /* -T temp file for netnews */
+int    senderr;                        /* An error while checking */
+int    edit;                           /* Indicates editing a file */
+int    readonly;                       /* Will be unable to rewrite file */
+int    noreset;                        /* String resets suspended */
+int    sourcing;                       /* Currently reading variant file */
+int    loading;                        /* Loading user definitions */
+int    cond;                           /* Current state of conditional exc. */
+FILE   *itf;                           /* Input temp file buffer */
+FILE   *otf;                           /* Output temp file buffer */
+int    image;                          /* File descriptor for image of msg */
+FILE   *input;                         /* Current command input file */
+char   mailname[PATHSIZE];             /* Name of current file */
+char   prevfile[PATHSIZE];             /* Name of previous file */
+char   *homedir;                       /* Path name of home directory */
+char   *myname;                        /* My login name */
+off_t  mailsize;                       /* Size of system mailbox */
+int    lexnumber;                      /* Number of TNUMBER from scan() */
+char   lexstring[STRINGLEN];           /* String from TSTRING, scan() */
+int    regretp;                        /* Pointer to TOS of regret tokens */
+int    regretstack[REGDEP];            /* Stack of regretted tokens */
+char   *string_stack[REGDEP];          /* Stack of regretted strings */
+int    numberstack[REGDEP];            /* Stack of regretted numbers */
+struct message *dot;                   /* Pointer to current message */
+struct message *message;               /* The actual message structure */
+struct var     *variables[HSHSIZE];    /* Pointer to active var list */
+struct grouphead       *groups[HSHSIZE];/* Pointer to active groups */
+struct ignoretab       ignore[2];      /* ignored and retained fields
+                                          0 is ignore, 1 is retain */
+struct ignoretab       saveignore[2];  /* ignored and retained fields
+                                          on save to folder */
+struct ignoretab       ignoreall[2];   /* special, ignore all headers */
+char   **altnames;                     /* List of alternate names for user */
+int    debug;                          /* Debug flag set */
+int    screenwidth;                    /* Screen width, or best guess */
+int    screenheight;                   /* Screen height, or best guess,
+                                          for "header" command */
+int    realscreenheight;               /* the real screen height */
+
+#include <setjmp.h>
+
+jmp_buf        srbuf;
+
+
+/*
+ * The pointers for the string allocation routines,
+ * there are NSPACE independent areas.
+ * The first holds STRINGSIZE bytes, the next
+ * twice as much, and so on.
+ */
+
+#define        NSPACE  25                      /* Total number of string spaces */
+struct strings {
+       char    *s_topFree;             /* Beginning of this area */
+       char    *s_nextFree;            /* Next alloctable place here */
+       unsigned s_nleft;               /* Number of bytes left here */
+} stringdope[NSPACE];
diff --git a/usr/src/usr.bin/mail/head.c b/usr/src/usr.bin/mail/head.c
new file mode 100644 (file)
index 0000000..b521d1b
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 1980 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)head.c     5.7 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+#include "rcv.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * Routines for processing and detecting headlines.
+ */
+
+/*
+ * See if the passed line buffer is a mail header.
+ * Return true if yes.  Note the extreme pains to
+ * accomodate all funny formats.
+ */
+ishead(linebuf)
+       char linebuf[];
+{
+       register char *cp;
+       struct headline hl;
+       char parbuf[BUFSIZ];
+
+       cp = linebuf;
+       if (*cp++ != 'F' || *cp++ != 'r' || *cp++ != 'o' || *cp++ != 'm' ||
+           *cp++ != ' ')
+               return (0);
+       parse(linebuf, &hl, parbuf);
+       if (hl.l_from == NOSTR || hl.l_date == NOSTR) {
+               fail(linebuf, "No from or date field");
+               return (0);
+       }
+       if (!isdate(hl.l_date)) {
+               fail(linebuf, "Date field not legal date");
+               return (0);
+       }
+       /*
+        * I guess we got it!
+        */
+       return (1);
+}
+
+/*ARGSUSED*/
+fail(linebuf, reason)
+       char linebuf[], reason[];
+{
+
+       /*
+       if (value("debug") == NOSTR)
+               return;
+       fprintf(stderr, "\"%s\"\nnot a header because %s\n", linebuf, reason);
+       */
+}
+
+/*
+ * Split a headline into its useful components.
+ * Copy the line into dynamic string space, then set
+ * pointers into the copied line in the passed headline
+ * structure.  Actually, it scans.
+ */
+parse(line, hl, pbuf)
+       char line[], pbuf[];
+       register struct headline *hl;
+{
+       register char *cp;
+       char *sp;
+       char word[LINESIZE];
+
+       hl->l_from = NOSTR;
+       hl->l_tty = NOSTR;
+       hl->l_date = NOSTR;
+       cp = line;
+       sp = pbuf;
+       /*
+        * Skip over "From" first.
+        */
+       cp = nextword(cp, word);
+       cp = nextword(cp, word);
+       if (*word)
+               hl->l_from = copyin(word, &sp);
+       if (cp != NOSTR && cp[0] == 't' && cp[1] == 't' && cp[2] == 'y') {
+               cp = nextword(cp, word);
+               hl->l_tty = copyin(word, &sp);
+       }
+       if (cp != NOSTR)
+               hl->l_date = copyin(cp, &sp);
+}
+
+/*
+ * Copy the string on the left into the string on the right
+ * and bump the right (reference) string pointer by the length.
+ * Thus, dynamically allocate space in the right string, copying
+ * the left string into it.
+ */
+char *
+copyin(src, space)
+       register char *src;
+       char **space;
+{
+       register char *cp;
+       char *top;
+
+       top = cp = *space;
+       while (*cp++ = *src++)
+               ;
+       *space = cp;
+       return (top);
+}
+
+/*
+ * Test to see if the passed string is a ctime(3) generated
+ * date string as documented in the manual.  The template
+ * below is used as the criterion of correctness.
+ * Also, we check for a possible trailing time zone using
+ * the tmztype template.
+ */
+
+/*
+ * 'A' An upper case char
+ * 'a' A lower case char
+ * ' ' A space
+ * '0' A digit
+ * 'O' An optional digit or space
+ * ':' A colon
+ * 'N' A new line
+ */
+char ctype[] = "Aaa Aaa O0 00:00:00 0000";
+char tmztype[] = "Aaa Aaa O0 00:00:00 AAA 0000";
+
+isdate(date)
+       char date[];
+{
+
+       return cmatch(date, ctype) || cmatch(date, tmztype);
+}
+
+/*
+ * Match the given string (cp) against the given template (tp).
+ * Return 1 if they match, 0 if they don't
+ */
+cmatch(cp, tp)
+       register char *cp, *tp;
+{
+
+       while (*cp && *tp)
+               switch (*tp++) {
+               case 'a':
+                       if (!islower(*cp++))
+                               return 0;
+                       break;
+               case 'A':
+                       if (!isupper(*cp++))
+                               return 0;
+                       break;
+               case ' ':
+                       if (*cp++ != ' ')
+                               return 0;
+                       break;
+               case '0':
+                       if (!isdigit(*cp++))
+                               return 0;
+                       break;
+               case 'O':
+                       if (*cp != ' ' && !isdigit(*cp))
+                               return 0;
+                       cp++;
+                       break;
+               case ':':
+                       if (*cp++ != ':')
+                               return 0;
+                       break;
+               case 'N':
+                       if (*cp++ != '\n')
+                               return 0;
+                       break;
+               }
+       if (*cp || *tp)
+               return 0;
+       return (1);
+}
+
+/*
+ * Collect a liberal (space, tab delimited) word into the word buffer
+ * passed.  Also, return a pointer to the next word following that,
+ * or NOSTR if none follow.
+ */
+char *
+nextword(wp, wbuf)
+       register char *wp, *wbuf;
+{
+       register c;
+
+       if (wp == NOSTR) {
+               *wbuf = 0;
+               return (NOSTR);
+       }
+       while ((c = *wp++) && c != ' ' && c != '\t') {
+               *wbuf++ = c;
+               if (c == '"') {
+                       while ((c = *wp++) && c != '"')
+                               *wbuf++ = c;
+                       if (c == '"')
+                               *wbuf++ = c;
+                       else
+                               wp--;
+               }
+       }
+       *wbuf = '\0';
+       for (; c == ' ' || c == '\t'; c = *wp++)
+               ;
+       if (c == 0)
+               return (NOSTR);
+       return (wp - 1);
+}
diff --git a/usr/src/usr.bin/mail/lex.c b/usr/src/usr.bin/mail/lex.c
new file mode 100644 (file)
index 0000000..e700c8b
--- /dev/null
@@ -0,0 +1,650 @@
+/*
+ * Copyright (c) 1980 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lex.c      5.23 (Berkeley) 4/1/91";
+#endif /* not lint */
+
+#include "rcv.h"
+#include <sys/stat.h>
+#include <errno.h>
+
+/*
+ * Mail -- a mail program
+ *
+ * Lexical processing of commands.
+ */
+
+char   *prompt = "& ";
+
+/*
+ * Set up editing on the given file name.
+ * If the first character of name is %, we are considered to be
+ * editing the file, otherwise we are reading our mail which has
+ * signficance for mbox and so forth.
+ */
+setfile(name)
+       char *name;
+{
+       FILE *ibuf;
+       int i;
+       struct stat stb;
+       char isedit = *name != '%';
+       char *who = name[1] ? name + 1 : myname;
+       static int shudclob;
+       extern char tempMesg[];
+       extern int errno;
+
+       if ((name = expand(name)) == NOSTR)
+               return -1;
+
+       if ((ibuf = Fopen(name, "r")) == NULL) {
+               if (!isedit && errno == ENOENT)
+                       goto nomail;
+               perror(name);
+               return(-1);
+       }
+
+       if (fstat(fileno(ibuf), &stb) < 0) {
+               perror("fstat");
+               Fclose(ibuf);
+               return (-1);
+       }
+
+       switch (stb.st_mode & S_IFMT) {
+       case S_IFDIR:
+               Fclose(ibuf);
+               errno = EISDIR;
+               perror(name);
+               return (-1);
+
+       case S_IFREG:
+               break;
+
+       default:
+               Fclose(ibuf);
+               errno = EINVAL;
+               perror(name);
+               return (-1);
+       }
+
+       /*
+        * Looks like all will be well.  We must now relinquish our
+        * hold on the current set of stuff.  Must hold signals
+        * while we are reading the new file, else we will ruin
+        * the message[] data structure.
+        */
+
+       holdsigs();
+       if (shudclob)
+               quit();
+
+       /*
+        * Copy the messages into /tmp
+        * and set pointers.
+        */
+
+       readonly = 0;
+       if ((i = open(name, 1)) < 0)
+               readonly++;
+       else
+               close(i);
+       if (shudclob) {
+               fclose(itf);
+               fclose(otf);
+       }
+       shudclob = 1;
+       edit = isedit;
+       strcpy(prevfile, mailname);
+       if (name != mailname)
+               strcpy(mailname, name);
+       mailsize = fsize(ibuf);
+       if ((otf = fopen(tempMesg, "w")) == NULL) {
+               perror(tempMesg);
+               exit(1);
+       }
+       if ((itf = fopen(tempMesg, "r")) == NULL) {
+               perror(tempMesg);
+               exit(1);
+       }
+       rm(tempMesg);
+       setptr(ibuf);
+       setmsize(msgCount);
+       Fclose(ibuf);
+       relsesigs();
+       sawcom = 0;
+       if (!edit && msgCount == 0) {
+nomail:
+               fprintf(stderr, "No mail for %s\n", who);
+               return -1;
+       }
+       return(0);
+}
+
+int    *msgvec;
+int    reset_on_stop;                  /* do a reset() if stopped */
+
+/*
+ * Interpret user commands one by one.  If standard input is not a tty,
+ * print no prompt.
+ */
+commands()
+{
+       int eofloop = 0;
+       register int n;
+       char linebuf[LINESIZE];
+       void intr(), stop(), hangup();
+
+       if (!sourcing) {
+               if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+                       signal(SIGINT, intr);
+               if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
+                       signal(SIGHUP, hangup);
+               signal(SIGTSTP, stop);
+               signal(SIGTTOU, stop);
+               signal(SIGTTIN, stop);
+       }
+       setexit();
+       for (;;) {
+               /*
+                * Print the prompt, if needed.  Clear out
+                * string space, and flush the output.
+                */
+               if (!sourcing && value("interactive") != NOSTR) {
+                       reset_on_stop = 1;
+                       printf(prompt);
+               }
+               fflush(stdout);
+               sreset();
+               /*
+                * Read a line of commands from the current input
+                * and handle end of file specially.
+                */
+               n = 0;
+               for (;;) {
+                       if (readline(input, &linebuf[n], LINESIZE - n) < 0) {
+                               if (n == 0)
+                                       n = -1;
+                               break;
+                       }
+                       if ((n = strlen(linebuf)) == 0)
+                               break;
+                       n--;
+                       if (linebuf[n] != '\\')
+                               break;
+                       linebuf[n++] = ' ';
+               }
+               reset_on_stop = 0;
+               if (n < 0) {
+                               /* eof */
+                       if (loading)
+                               break;
+                       if (sourcing) {
+                               unstack();
+                               continue;
+                       }
+                       if (value("interactive") != NOSTR &&
+                           value("ignoreeof") != NOSTR &&
+                           ++eofloop < 25) {
+                               printf("Use \"quit\" to quit.\n");
+                               continue;
+                       }
+                       break;
+               }
+               eofloop = 0;
+               if (execute(linebuf, 0))
+                       break;
+       }
+}
+
+/*
+ * Execute a single command.
+ * Command functions return 0 for success, 1 for error, and -1
+ * for abort.  A 1 or -1 aborts a load or source.  A -1 aborts
+ * the interactive command loop.
+ * Contxt is non-zero if called while composing mail.
+ */
+execute(linebuf, contxt)
+       char linebuf[];
+{
+       char word[LINESIZE];
+       char *arglist[MAXARGC];
+       struct cmd *com;
+       register char *cp, *cp2;
+       register int c;
+       int muvec[2];
+       int e = 1;
+
+       /*
+        * Strip the white space away from the beginning
+        * of the command, then scan out a word, which
+        * consists of anything except digits and white space.
+        *
+        * Handle ! escapes differently to get the correct
+        * lexical conventions.
+        */
+
+       for (cp = linebuf; isspace(*cp); cp++)
+               ;
+       if (*cp == '!') {
+               if (sourcing) {
+                       printf("Can't \"!\" while sourcing\n");
+                       goto out;
+               }
+               shell(cp+1);
+               return(0);
+       }
+       cp2 = word;
+       while (*cp && index(" \t0123456789$^.:/-+*'\"", *cp) == NOSTR)
+               *cp2++ = *cp++;
+       *cp2 = '\0';
+
+       /*
+        * Look up the command; if not found, bitch.
+        * Normally, a blank command would map to the
+        * first command in the table; while sourcing,
+        * however, we ignore blank lines to eliminate
+        * confusion.
+        */
+
+       if (sourcing && *word == '\0')
+               return(0);
+       com = lex(word);
+       if (com == NONE) {
+               printf("Unknown command: \"%s\"\n", word);
+               goto out;
+       }
+
+       /*
+        * See if we should execute the command -- if a conditional
+        * we always execute it, otherwise, check the state of cond.
+        */
+
+       if ((com->c_argtype & F) == 0)
+               if (cond == CRCV && !rcvmode || cond == CSEND && rcvmode)
+                       return(0);
+
+       /*
+        * Process the arguments to the command, depending
+        * on the type he expects.  Default to an error.
+        * If we are sourcing an interactive command, it's
+        * an error.
+        */
+
+       if (!rcvmode && (com->c_argtype & M) == 0) {
+               printf("May not execute \"%s\" while sending\n",
+                   com->c_name);
+               goto out;
+       }
+       if (sourcing && com->c_argtype & I) {
+               printf("May not execute \"%s\" while sourcing\n",
+                   com->c_name);
+               goto out;
+       }
+       if (readonly && com->c_argtype & W) {
+               printf("May not execute \"%s\" -- message file is read only\n",
+                  com->c_name);
+               goto out;
+       }
+       if (contxt && com->c_argtype & R) {
+               printf("Cannot recursively invoke \"%s\"\n", com->c_name);
+               goto out;
+       }
+       switch (com->c_argtype & ~(F|P|I|M|T|W|R)) {
+       case MSGLIST:
+               /*
+                * A message list defaulting to nearest forward
+                * legal message.
+                */
+               if (msgvec == 0) {
+                       printf("Illegal use of \"message list\"\n");
+                       break;
+               }
+               if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
+                       break;
+               if (c  == 0) {
+                       *msgvec = first(com->c_msgflag,
+                               com->c_msgmask);
+                       msgvec[1] = NULL;
+               }
+               if (*msgvec == NULL) {
+                       printf("No applicable messages\n");
+                       break;
+               }
+               e = (*com->c_func)(msgvec);
+               break;
+
+       case NDMLIST:
+               /*
+                * A message list with no defaults, but no error
+                * if none exist.
+                */
+               if (msgvec == 0) {
+                       printf("Illegal use of \"message list\"\n");
+                       break;
+               }
+               if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
+                       break;
+               e = (*com->c_func)(msgvec);
+               break;
+
+       case STRLIST:
+               /*
+                * Just the straight string, with
+                * leading blanks removed.
+                */
+               while (isspace(*cp))
+                       cp++;
+               e = (*com->c_func)(cp);
+               break;
+
+       case RAWLIST:
+               /*
+                * A vector of strings, in shell style.
+                */
+               if ((c = getrawlist(cp, arglist,
+                               sizeof arglist / sizeof *arglist)) < 0)
+                       break;
+               if (c < com->c_minargs) {
+                       printf("%s requires at least %d arg(s)\n",
+                               com->c_name, com->c_minargs);
+                       break;
+               }
+               if (c > com->c_maxargs) {
+                       printf("%s takes no more than %d arg(s)\n",
+                               com->c_name, com->c_maxargs);
+                       break;
+               }
+               e = (*com->c_func)(arglist);
+               break;
+
+       case NOLIST:
+               /*
+                * Just the constant zero, for exiting,
+                * eg.
+                */
+               e = (*com->c_func)(0);
+               break;
+
+       default:
+               panic("Unknown argtype");
+       }
+
+out:
+       /*
+        * Exit the current source file on
+        * error.
+        */
+       if (e) {
+               if (e < 0)
+                       return 1;
+               if (loading)
+                       return 1;
+               if (sourcing)
+                       unstack();
+               return 0;
+       }
+       if (value("autoprint") != NOSTR && com->c_argtype & P)
+               if ((dot->m_flag & MDELETED) == 0) {
+                       muvec[0] = dot - &message[0] + 1;
+                       muvec[1] = 0;
+                       type(muvec);
+               }
+       if (!sourcing && (com->c_argtype & T) == 0)
+               sawcom = 1;
+       return(0);
+}
+
+/*
+ * Set the size of the message vector used to construct argument
+ * lists to message list functions.
+ */
+setmsize(sz)
+{
+
+       if (msgvec != 0)
+               cfree((char *) msgvec);
+       msgvec = (int *) calloc((unsigned) (sz + 1), sizeof *msgvec);
+}
+
+/*
+ * Find the correct command in the command table corresponding
+ * to the passed command "word"
+ */
+
+struct cmd *
+lex(word)
+       char word[];
+{
+       register struct cmd *cp;
+       extern struct cmd cmdtab[];
+
+       for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++)
+               if (isprefix(word, cp->c_name))
+                       return(cp);
+       return(NONE);
+}
+
+/*
+ * Determine if as1 is a valid prefix of as2.
+ * Return true if yep.
+ */
+
+isprefix(as1, as2)
+       char *as1, *as2;
+{
+       register char *s1, *s2;
+
+       s1 = as1;
+       s2 = as2;
+       while (*s1++ == *s2)
+               if (*s2++ == '\0')
+                       return(1);
+       return(*--s1 == '\0');
+}
+
+/*
+ * The following gets called on receipt of an interrupt.  This is
+ * to abort printout of a command, mainly.
+ * Dispatching here when command() is inactive crashes rcv.
+ * Close all open files except 0, 1, 2, and the temporary.
+ * Also, unstack all source files.
+ */
+
+int    inithdr;                        /* am printing startup headers */
+
+/*ARGSUSED*/
+void
+intr(s)
+{
+
+       noreset = 0;
+       if (!inithdr)
+               sawcom++;
+       inithdr = 0;
+       while (sourcing)
+               unstack();
+
+       close_all_files();
+
+       if (image >= 0) {
+               close(image);
+               image = -1;
+       }
+       fprintf(stderr, "Interrupt\n");
+       reset(0);
+}
+
+/*
+ * When we wake up after ^Z, reprint the prompt.
+ */
+void
+stop(s)
+{
+       sig_t old_action = signal(s, SIG_DFL);
+
+       sigsetmask(sigblock(0) & ~sigmask(s));
+       kill(0, s);
+       sigblock(sigmask(s));
+       signal(s, old_action);
+       if (reset_on_stop) {
+               reset_on_stop = 0;
+               reset(0);
+       }
+}
+
+/*
+ * Branch here on hangup signal and simulate "exit".
+ */
+/*ARGSUSED*/
+void
+hangup(s)
+{
+
+       /* nothing to do? */
+       exit(1);
+}
+
+/*
+ * Announce the presence of the current Mail version,
+ * give the message count, and print a header listing.
+ */
+
+announce()
+{
+       int vec[2], mdot;
+
+       mdot = newfileinfo();
+       vec[0] = mdot;
+       vec[1] = 0;
+       dot = &message[mdot - 1];
+       if (msgCount > 0 && value("noheader") == NOSTR) {
+               inithdr++;
+               headers(vec);
+               inithdr = 0;
+       }
+}
+
+/*
+ * Announce information about the file we are editing.
+ * Return a likely place to set dot.
+ */
+newfileinfo()
+{
+       register struct message *mp;
+       register int u, n, mdot, d, s;
+       char fname[BUFSIZ], zname[BUFSIZ], *ename;
+
+       for (mp = &message[0]; mp < &message[msgCount]; mp++)
+               if (mp->m_flag & MNEW)
+                       break;
+       if (mp >= &message[msgCount])
+               for (mp = &message[0]; mp < &message[msgCount]; mp++)
+                       if ((mp->m_flag & MREAD) == 0)
+                               break;
+       if (mp < &message[msgCount])
+               mdot = mp - &message[0] + 1;
+       else
+               mdot = 1;
+       s = d = 0;
+       for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) {
+               if (mp->m_flag & MNEW)
+                       n++;
+               if ((mp->m_flag & MREAD) == 0)
+                       u++;
+               if (mp->m_flag & MDELETED)
+                       d++;
+               if (mp->m_flag & MSAVED)
+                       s++;
+       }
+       ename = mailname;
+       if (getfold(fname) >= 0) {
+               strcat(fname, "/");
+               if (strncmp(fname, mailname, strlen(fname)) == 0) {
+                       sprintf(zname, "+%s", mailname + strlen(fname));
+                       ename = zname;
+               }
+       }
+       printf("\"%s\": ", ename);
+       if (msgCount == 1)
+               printf("1 message");
+       else
+               printf("%d messages", msgCount);
+       if (n > 0)
+               printf(" %d new", n);
+       if (u-n > 0)
+               printf(" %d unread", u);
+       if (d > 0)
+               printf(" %d deleted", d);
+       if (s > 0)
+               printf(" %d saved", s);
+       if (readonly)
+               printf(" [Read only]");
+       printf("\n");
+       return(mdot);
+}
+
+/*
+ * Print the current version number.
+ */
+
+/*ARGSUSED*/
+pversion(e)
+{
+       extern char *version;
+
+       printf("Version %s\n", version);
+       return(0);
+}
+
+/*
+ * Load a file of user definitions.
+ */
+load(name)
+       char *name;
+{
+       register FILE *in, *oldin;
+
+       if ((in = Fopen(name, "r")) == NULL)
+               return;
+       oldin = input;
+       input = in;
+       loading = 1;
+       sourcing = 1;
+       commands();
+       loading = 0;
+       sourcing = 0;
+       input = oldin;
+       Fclose(in);
+}
diff --git a/usr/src/usr.bin/mail/list.c b/usr/src/usr.bin/mail/list.c
new file mode 100644 (file)
index 0000000..15ebf34
--- /dev/null
@@ -0,0 +1,774 @@
+/*
+ * Copyright (c) 1980 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)list.c     5.14 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+#include "rcv.h"
+#include <ctype.h>
+
+/*
+ * Mail -- a mail program
+ *
+ * Message list handling.
+ */
+
+/*
+ * Convert the user string of message numbers and
+ * store the numbers into vector.
+ *
+ * Returns the count of messages picked up or -1 on error.
+ */
+
+getmsglist(buf, vector, flags)
+       char *buf;
+       int *vector;
+{
+       register int *ip;
+       register struct message *mp;
+
+       if (msgCount == 0) {
+               *vector = 0;
+               return 0;
+       }
+       if (markall(buf, flags) < 0)
+               return(-1);
+       ip = vector;
+       for (mp = &message[0]; mp < &message[msgCount]; mp++)
+               if (mp->m_flag & MMARK)
+                       *ip++ = mp - &message[0] + 1;
+       *ip = 0;
+       return(ip - vector);
+}
+
+/*
+ * Mark all messages that the user wanted from the command
+ * line in the message structure.  Return 0 on success, -1
+ * on error.
+ */
+
+/*
+ * Bit values for colon modifiers.
+ */
+
+#define        CMNEW           01              /* New messages */
+#define        CMOLD           02              /* Old messages */
+#define        CMUNREAD        04              /* Unread messages */
+#define        CMDELETED       010             /* Deleted messages */
+#define        CMREAD          020             /* Read messages */
+
+/*
+ * The following table describes the letters which can follow
+ * the colon and gives the corresponding modifier bit.
+ */
+
+struct coltab {
+       char    co_char;                /* What to find past : */
+       int     co_bit;                 /* Associated modifier bit */
+       int     co_mask;                /* m_status bits to mask */
+       int     co_equal;               /* ... must equal this */
+} coltab[] = {
+       'n',            CMNEW,          MNEW,           MNEW,
+       'o',            CMOLD,          MNEW,           0,
+       'u',            CMUNREAD,       MREAD,          0,
+       'd',            CMDELETED,      MDELETED,       MDELETED,
+       'r',            CMREAD,         MREAD,          MREAD,
+       0,              0,              0,              0
+};
+
+static int     lastcolmod;
+
+markall(buf, f)
+       char buf[];
+{
+       register char **np;
+       register int i;
+       register struct message *mp;
+       char *namelist[NMLSIZE], *bufp;
+       int tok, beg, mc, star, other, valdot, colmod, colresult;
+
+       valdot = dot - &message[0] + 1;
+       colmod = 0;
+       for (i = 1; i <= msgCount; i++)
+               unmark(i);
+       bufp = buf;
+       mc = 0;
+       np = &namelist[0];
+       scaninit();
+       tok = scan(&bufp);
+       star = 0;
+       other = 0;
+       beg = 0;
+       while (tok != TEOL) {
+               switch (tok) {
+               case TNUMBER:
+number:
+                       if (star) {
+                               printf("No numbers mixed with *\n");
+                               return(-1);
+                       }
+                       mc++;
+                       other++;
+                       if (beg != 0) {
+                               if (check(lexnumber, f))
+                                       return(-1);
+                               for (i = beg; i <= lexnumber; i++)
+                                       if (f == MDELETED || (message[i - 1].m_flag & MDELETED) == 0)
+                                               mark(i);
+                               beg = 0;
+                               break;
+                       }
+                       beg = lexnumber;
+                       if (check(beg, f))
+                               return(-1);
+                       tok = scan(&bufp);
+                       regret(tok);
+                       if (tok != TDASH) {
+                               mark(beg);
+                               beg = 0;
+                       }
+                       break;
+
+               case TPLUS:
+                       if (beg != 0) {
+                               printf("Non-numeric second argument\n");
+                               return(-1);
+                       }
+                       i = valdot;
+                       do {
+                               i++;
+                               if (i > msgCount) {
+                                       printf("Referencing beyond EOF\n");
+                                       return(-1);
+                               }
+                       } while ((message[i - 1].m_flag & MDELETED) != f);
+                       mark(i);
+                       break;
+
+               case TDASH:
+                       if (beg == 0) {
+                               i = valdot;
+                               do {
+                                       i--;
+                                       if (i <= 0) {
+                                               printf("Referencing before 1\n");
+                                               return(-1);
+                                       }
+                               } while ((message[i - 1].m_flag & MDELETED) != f);
+                               mark(i);
+                       }
+                       break;
+
+               case TSTRING:
+                       if (beg != 0) {
+                               printf("Non-numeric second argument\n");
+                               return(-1);
+                       }
+                       other++;
+                       if (lexstring[0] == ':') {
+                               colresult = evalcol(lexstring[1]);
+                               if (colresult == 0) {
+                                       printf("Unknown colon modifier \"%s\"\n",
+                                           lexstring);
+                                       return(-1);
+                               }
+                               colmod |= colresult;
+                       }
+                       else
+                               *np++ = savestr(lexstring);
+                       break;
+
+               case TDOLLAR:
+               case TUP:
+               case TDOT:
+                       lexnumber = metamess(lexstring[0], f);
+                       if (lexnumber == -1)
+                               return(-1);
+                       goto number;
+
+               case TSTAR:
+                       if (other) {
+                               printf("Can't mix \"*\" with anything\n");
+                               return(-1);
+                       }
+                       star++;
+                       break;
+
+               case TERROR:
+                       return -1;
+               }
+               tok = scan(&bufp);
+       }
+       lastcolmod = colmod;
+       *np = NOSTR;
+       mc = 0;
+       if (star) {
+               for (i = 0; i < msgCount; i++)
+                       if ((message[i].m_flag & MDELETED) == f) {
+                               mark(i+1);
+                               mc++;
+                       }
+               if (mc == 0) {
+                       printf("No applicable messages.\n");
+                       return(-1);
+               }
+               return(0);
+       }
+
+       /*
+        * If no numbers were given, mark all of the messages,
+        * so that we can unmark any whose sender was not selected
+        * if any user names were given.
+        */
+
+       if ((np > namelist || colmod != 0) && mc == 0)
+               for (i = 1; i <= msgCount; i++)
+                       if ((message[i-1].m_flag & MDELETED) == f)
+                               mark(i);
+
+       /*
+        * If any names were given, go through and eliminate any
+        * messages whose senders were not requested.
+        */
+
+       if (np > namelist) {
+               for (i = 1; i <= msgCount; i++) {
+                       for (mc = 0, np = &namelist[0]; *np != NOSTR; np++)
+                               if (**np == '/') {
+                                       if (matchsubj(*np, i)) {
+                                               mc++;
+                                               break;
+                                       }
+                               }
+                               else {
+                                       if (matchsender(*np, i)) {
+                                               mc++;
+                                               break;
+                                       }
+                               }
+                       if (mc == 0)
+                               unmark(i);
+               }
+
+               /*
+                * Make sure we got some decent messages.
+                */
+
+               mc = 0;
+               for (i = 1; i <= msgCount; i++)
+                       if (message[i-1].m_flag & MMARK) {
+                               mc++;
+                               break;
+                       }
+               if (mc == 0) {
+                       printf("No applicable messages from {%s",
+                               namelist[0]);
+                       for (np = &namelist[1]; *np != NOSTR; np++)
+                               printf(", %s", *np);
+                       printf("}\n");
+                       return(-1);
+               }
+       }
+
+       /*
+        * If any colon modifiers were given, go through and
+        * unmark any messages which do not satisfy the modifiers.
+        */
+
+       if (colmod != 0) {
+               for (i = 1; i <= msgCount; i++) {
+                       register struct coltab *colp;
+
+                       mp = &message[i - 1];
+                       for (colp = &coltab[0]; colp->co_char; colp++)
+                               if (colp->co_bit & colmod)
+                                       if ((mp->m_flag & colp->co_mask)
+                                           != colp->co_equal)
+                                               unmark(i);
+                       
+               }
+               for (mp = &message[0]; mp < &message[msgCount]; mp++)
+                       if (mp->m_flag & MMARK)
+                               break;
+               if (mp >= &message[msgCount]) {
+                       register struct coltab *colp;
+
+                       printf("No messages satisfy");
+                       for (colp = &coltab[0]; colp->co_char; colp++)
+                               if (colp->co_bit & colmod)
+                                       printf(" :%c", colp->co_char);
+                       printf("\n");
+                       return(-1);
+               }
+       }
+       return(0);
+}
+
+/*
+ * Turn the character after a colon modifier into a bit
+ * value.
+ */
+evalcol(col)
+{
+       register struct coltab *colp;
+
+       if (col == 0)
+               return(lastcolmod);
+       for (colp = &coltab[0]; colp->co_char; colp++)
+               if (colp->co_char == col)
+                       return(colp->co_bit);
+       return(0);
+}
+
+/*
+ * Check the passed message number for legality and proper flags.
+ * If f is MDELETED, then either kind will do.  Otherwise, the message
+ * has to be undeleted.
+ */
+check(mesg, f)
+{
+       register struct message *mp;
+
+       if (mesg < 1 || mesg > msgCount) {
+               printf("%d: Invalid message number\n", mesg);
+               return(-1);
+       }
+       mp = &message[mesg-1];
+       if (f != MDELETED && (mp->m_flag & MDELETED) != 0) {
+               printf("%d: Inappropriate message\n", mesg);
+               return(-1);
+       }
+       return(0);
+}
+
+/*
+ * Scan out the list of string arguments, shell style
+ * for a RAWLIST.
+ */
+
+getrawlist(line, argv, argc)
+       char line[];
+       char **argv;
+       int  argc;
+{
+       register char c, *cp, *cp2, quotec;
+       int argn;
+       char linebuf[BUFSIZ];
+
+       argn = 0;
+       cp = line;
+       for (;;) {
+               for (; *cp == ' ' || *cp == '\t'; cp++)
+                       ;
+               if (*cp == '\0')
+                       break;
+               if (argn >= argc - 1) {
+                       printf(
+                       "Too many elements in the list; excess discarded.\n");
+                       break;
+               }
+               cp2 = linebuf;
+               quotec = '\0';
+               while ((c = *cp) != '\0') {
+                       cp++;
+                       if (quotec != '\0') {
+                               if (c == quotec)
+                                       quotec = '\0';
+                               else if (c == '\\')
+                                       switch (c = *cp++) {
+                                       case '\0':
+                                               *cp2++ = *--cp;
+                                               break;
+                                       case '0': case '1': case '2': case '3':
+                                       case '4': case '5': case '6': case '7':
+                                               c -= '0';
+                                               if (*cp >= '0' && *cp <= '7')
+                                                       c = c * 8 + *cp++ - '0';
+                                               if (*cp >= '0' && *cp <= '7')
+                                                       c = c * 8 + *cp++ - '0';
+                                               *cp2++ = c;
+                                               break;
+                                       case 'b':
+                                               *cp2++ = '\b';
+                                               break;
+                                       case 'f':
+                                               *cp2++ = '\f';
+                                               break;
+                                       case 'n':
+                                               *cp2++ = '\n';
+                                               break;
+                                       case 'r':
+                                               *cp2++ = '\r';
+                                               break;
+                                       case 't':
+                                               *cp2++ = '\t';
+                                               break;
+                                       case 'v':
+                                               *cp2++ = '\v';
+                                               break;
+                                       }
+                               else if (c == '^') {
+                                       c = *cp++;
+                                       if (c == '?')
+                                               *cp2++ = '\177';
+                                       /* null doesn't show up anyway */
+                                       else if (c >= 'A' && c <= '_' ||
+                                                c >= 'a' && c <= 'z')
+                                               *cp2++ &= 037;
+                                       else
+                                               *cp2++ = *--cp;
+                               } else
+                                       *cp2++ = c;
+                       } else if (c == '"' || c == '\'')
+                               quotec = c;
+                       else if (c == ' ' || c == '\t')
+                               break;
+                       else
+                               *cp2++ = c;
+               }
+               *cp2 = '\0';
+               argv[argn++] = savestr(linebuf);
+       }
+       argv[argn] = NOSTR;
+       return argn;
+}
+
+/*
+ * scan out a single lexical item and return its token number,
+ * updating the string pointer passed **p.  Also, store the value
+ * of the number or string scanned in lexnumber or lexstring as
+ * appropriate.  In any event, store the scanned `thing' in lexstring.
+ */
+
+struct lex {
+       char    l_char;
+       char    l_token;
+} singles[] = {
+       '$',    TDOLLAR,
+       '.',    TDOT,
+       '^',    TUP,
+       '*',    TSTAR,
+       '-',    TDASH,
+       '+',    TPLUS,
+       '(',    TOPEN,
+       ')',    TCLOSE,
+       0,      0
+};
+
+scan(sp)
+       char **sp;
+{
+       register char *cp, *cp2;
+       register int c;
+       register struct lex *lp;
+       int quotec;
+
+       if (regretp >= 0) {
+               strcpy(lexstring, string_stack[regretp]);
+               lexnumber = numberstack[regretp];
+               return(regretstack[regretp--]);
+       }
+       cp = *sp;
+       cp2 = lexstring;
+       c = *cp++;
+
+       /*
+        * strip away leading white space.
+        */
+
+       while (c == ' ' || c == '\t')
+               c = *cp++;
+
+       /*
+        * If no characters remain, we are at end of line,
+        * so report that.
+        */
+
+       if (c == '\0') {
+               *sp = --cp;
+               return(TEOL);
+       }
+
+       /*
+        * If the leading character is a digit, scan
+        * the number and convert it on the fly.
+        * Return TNUMBER when done.
+        */
+
+       if (isdigit(c)) {
+               lexnumber = 0;
+               while (isdigit(c)) {
+                       lexnumber = lexnumber*10 + c - '0';
+                       *cp2++ = c;
+                       c = *cp++;
+               }
+               *cp2 = '\0';
+               *sp = --cp;
+               return(TNUMBER);
+       }
+
+       /*
+        * Check for single character tokens; return such
+        * if found.
+        */
+
+       for (lp = &singles[0]; lp->l_char != 0; lp++)
+               if (c == lp->l_char) {
+                       lexstring[0] = c;
+                       lexstring[1] = '\0';
+                       *sp = cp;
+                       return(lp->l_token);
+               }
+
+       /*
+        * We've got a string!  Copy all the characters
+        * of the string into lexstring, until we see
+        * a null, space, or tab.
+        * If the lead character is a " or ', save it
+        * and scan until you get another.
+        */
+
+       quotec = 0;
+       if (c == '\'' || c == '"') {
+               quotec = c;
+               c = *cp++;
+       }
+       while (c != '\0') {
+               if (c == quotec) {
+                       cp++;
+                       break;
+               }
+               if (quotec == 0 && (c == ' ' || c == '\t'))
+                       break;
+               if (cp2 - lexstring < STRINGLEN-1)
+                       *cp2++ = c;
+               c = *cp++;
+       }
+       if (quotec && c == 0) {
+               fprintf(stderr, "Missing %c\n", quotec);
+               return TERROR;
+       }
+       *sp = --cp;
+       *cp2 = '\0';
+       return(TSTRING);
+}
+
+/*
+ * Unscan the named token by pushing it onto the regret stack.
+ */
+
+regret(token)
+{
+       if (++regretp >= REGDEP)
+               panic("Too many regrets");
+       regretstack[regretp] = token;
+       lexstring[STRINGLEN-1] = '\0';
+       string_stack[regretp] = savestr(lexstring);
+       numberstack[regretp] = lexnumber;
+}
+
+/*
+ * Reset all the scanner global variables.
+ */
+
+scaninit()
+{
+       regretp = -1;
+}
+
+/*
+ * Find the first message whose flags & m == f  and return
+ * its message number.
+ */
+
+first(f, m)
+{
+       register struct message *mp;
+
+       if (msgCount == 0)
+               return 0;
+       f &= MDELETED;
+       m &= MDELETED;
+       for (mp = dot; mp < &message[msgCount]; mp++)
+               if ((mp->m_flag & m) == f)
+                       return mp - message + 1;
+       for (mp = dot-1; mp >= &message[0]; mp--)
+               if ((mp->m_flag & m) == f)
+                       return mp - message + 1;
+       return 0;
+}
+
+/*
+ * See if the passed name sent the passed message number.  Return true
+ * if so.
+ */
+
+matchsender(str, mesg)
+       char *str;
+{
+       register char *cp, *cp2, *backup;
+
+       if (!*str)      /* null string matches nothing instead of everything */
+               return 0;
+       backup = cp2 = nameof(&message[mesg - 1], 0);
+       cp = str;
+       while (*cp2) {
+               if (*cp == 0)
+                       return(1);
+               if (raise(*cp++) != raise(*cp2++)) {
+                       cp2 = ++backup;
+                       cp = str;
+               }
+       }
+       return(*cp == 0);
+}
+
+/*
+ * See if the given string matches inside the subject field of the
+ * given message.  For the purpose of the scan, we ignore case differences.
+ * If it does, return true.  The string search argument is assumed to
+ * have the form "/search-string."  If it is of the form "/," we use the
+ * previous search string.
+ */
+
+char lastscan[128];
+
+matchsubj(str, mesg)
+       char *str;
+{
+       register struct message *mp;
+       register char *cp, *cp2, *backup;
+
+       str++;
+       if (strlen(str) == 0)
+               str = lastscan;
+       else
+               strcpy(lastscan, str);
+       mp = &message[mesg-1];
+       
+       /*
+        * Now look, ignoring case, for the word in the string.
+        */
+
+       cp = str;
+       cp2 = hfield("subject", mp);
+       if (cp2 == NOSTR)
+               return(0);
+       backup = cp2;
+       while (*cp2) {
+               if (*cp == 0)
+                       return(1);
+               if (raise(*cp++) != raise(*cp2++)) {
+                       cp2 = ++backup;
+                       cp = str;
+               }
+       }
+       return(*cp == 0);
+}
+
+/*
+ * Mark the named message by setting its mark bit.
+ */
+
+mark(mesg)
+{
+       register int i;
+
+       i = mesg;
+       if (i < 1 || i > msgCount)
+               panic("Bad message number to mark");
+       message[i-1].m_flag |= MMARK;
+}
+
+/*
+ * Unmark the named message.
+ */
+
+unmark(mesg)
+{
+       register int i;
+
+       i = mesg;
+       if (i < 1 || i > msgCount)
+               panic("Bad message number to unmark");
+       message[i-1].m_flag &= ~MMARK;
+}
+
+/*
+ * Return the message number corresponding to the passed meta character.
+ */
+
+metamess(meta, f)
+{
+       register int c, m;
+       register struct message *mp;
+
+       c = meta;
+       switch (c) {
+       case '^':
+               /*
+                * First 'good' message left.
+                */
+               for (mp = &message[0]; mp < &message[msgCount]; mp++)
+                       if ((mp->m_flag & MDELETED) == f)
+                               return(mp - &message[0] + 1);
+               printf("No applicable messages\n");
+               return(-1);
+
+       case '$':
+               /*
+                * Last 'good message left.
+                */
+               for (mp = &message[msgCount-1]; mp >= &message[0]; mp--)
+                       if ((mp->m_flag & MDELETED) == f)
+                               return(mp - &message[0] + 1);
+               printf("No applicable messages\n");
+               return(-1);
+
+       case '.':
+               /* 
+                * Current message.
+                */
+               m = dot - &message[0] + 1;
+               if ((dot->m_flag & MDELETED) != f) {
+                       printf("%d: Inappropriate message\n", m);
+                       return(-1);
+               }
+               return(m);
+
+       default:
+               printf("Unknown metachar (%c)\n", c);
+               return(-1);
+       }
+}
diff --git a/usr/src/usr.bin/mail/main.c b/usr/src/usr.bin/mail/main.c
new file mode 100644 (file)
index 0000000..0a8cc7d
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 1980 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1980 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c     5.28 (Berkeley) 4/1/91";
+#endif /* not lint */
+
+#include "rcv.h"
+#include <sys/stat.h>
+
+/*
+ * Mail -- a mail program
+ *
+ * Startup -- interface with user.
+ */
+
+jmp_buf        hdrjmp;
+
+main(argc, argv)
+       char **argv;
+{
+       register int i;
+       struct name *to, *cc, *bcc, *smopts;
+       char *subject;
+       char *ef;
+       char nosrc = 0;
+       void hdrstop();
+       sig_t prevint;
+       extern int getopt(), optind, opterr;
+       extern char *optarg;
+       void sigchild();
+
+       /*
+        * Set up a reasonable environment.
+        * Figure out whether we are being run interactively,
+        * start the SIGCHLD catcher, and so forth.
+        */
+       (void) signal(SIGCHLD, sigchild);
+       if (isatty(0))
+               assign("interactive", "");
+       image = -1;
+       /*
+        * Now, determine how we are being used.
+        * We successively pick off - flags.
+        * If there is anything left, it is the base of the list
+        * of users to mail to.  Argp will be set to point to the
+        * first of these users.
+        */
+       ef = NOSTR;
+       to = NIL;
+       cc = NIL;
+       bcc = NIL;
+       smopts = NIL;
+       subject = NOSTR;
+       while ((i = getopt(argc, argv, "INT:b:c:dfins:u:v")) != EOF) {
+               switch (i) {
+               case 'T':
+                       /*
+                        * Next argument is temp file to write which
+                        * articles have been read/deleted for netnews.
+                        */
+                       Tflag = optarg;
+                       if ((i = creat(Tflag, 0600)) < 0) {
+                               perror(Tflag);
+                               exit(1);
+                       }
+                       close(i);
+                       break;
+               case 'u':
+                       /*
+                        * Next argument is person to pretend to be.
+                        */
+                       myname = optarg;
+                       break;
+               case 'i':
+                       /*
+                        * User wants to ignore interrupts.
+                        * Set the variable "ignore"
+                        */
+                       assign("ignore", "");
+                       break;
+               case 'd':
+                       debug++;
+                       break;
+               case 's':
+                       /*
+                        * Give a subject field for sending from
+                        * non terminal
+                        */
+                       subject = optarg;
+                       break;
+               case 'f':
+                       /*
+                        * User is specifying file to "edit" with Mail,
+                        * as opposed to reading system mailbox.
+                        * If no argument is given after -f, we read his
+                        * mbox file.
+                        *
+                        * getopt() can't handle optional arguments, so here
+                        * is an ugly hack to get around it.
+                        */
+                       if ((argv[optind]) && (argv[optind][0] != '-'))
+                               ef = argv[optind++];
+                       else
+                               ef = "&";
+                       break;
+               case 'n':
+                       /*
+                        * User doesn't want to source /usr/lib/Mail.rc
+                        */
+                       nosrc++;
+                       break;
+               case 'N':
+                       /*
+                        * Avoid initial header printing.
+                        */
+                       assign("noheader", "");
+                       break;
+               case 'v':
+                       /*
+                        * Send mailer verbose flag
+                        */
+                       assign("verbose", "");
+                       break;
+               case 'I':
+                       /*
+                        * We're interactive
+                        */
+                       assign("interactive", "");
+                       break;
+               case 'c':
+                       /*
+                        * Get Carbon Copy Recipient list
+                        */
+                       cc = cat(cc, nalloc(optarg, GCC));
+                       break;
+               case 'b':
+                       /*
+                        * Get Blind Carbon Copy Recipient list
+                        */
+                       bcc = cat(bcc, nalloc(optarg, GBCC));
+                       break;
+               case '?':
+                       fputs("\
+Usage: mail [-iInv] [-s subject] [-c cc-addr] [-b bcc-addr] to-addr ...\n\
+            [- sendmail-options ...]\n\
+       mail [-iInNv] -f [name]\n\
+       mail [-iInNv] [-u user]\n",
+                               stderr);
+                       exit(1);
+               }
+       }
+       for (i = optind; (argv[i]) && (*argv[i] != '-'); i++)
+               to = cat(to, nalloc(argv[i], GTO));
+       for (; argv[i]; i++)
+               smopts = cat(smopts, nalloc(argv[i], 0));
+       /*
+        * Check for inconsistent arguments.
+        */
+       if (to == NIL && (subject != NOSTR || cc != NIL || bcc != NIL)) {
+               fputs("You must specify direct recipients with -s, -c, or -b.\n", stderr);
+               exit(1);
+       }
+       if (ef != NOSTR && to != NIL) {
+               fprintf(stderr, "Cannot give -f and people to send to.\n");
+               exit(1);
+       }
+       tinit();
+       setscreensize();
+       input = stdin;
+       rcvmode = !to;
+       spreserve();
+       if (!nosrc)
+               load(_PATH_MASTER_RC);
+       /*
+        * Expand returns a savestr, but load only uses the file name
+        * for fopen, so it's safe to do this.
+        */
+       load(expand("~/.mailrc"));
+       if (!rcvmode) {
+               mail(to, cc, bcc, smopts, subject);
+               /*
+                * why wait?
+                */
+               exit(senderr);
+       }
+       /*
+        * Ok, we are reading mail.
+        * Decide whether we are editing a mailbox or reading
+        * the system mailbox, and open up the right stuff.
+        */
+       if (ef == NOSTR)
+               ef = "%";
+       if (setfile(ef) < 0)
+               exit(1);                /* error already reported */
+       if (setjmp(hdrjmp) == 0) {
+               extern char *version;
+
+               if ((prevint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
+                       signal(SIGINT, hdrstop);
+               if (value("quiet") == NOSTR)
+                       printf("Mail version %s.  Type ? for help.\n",
+                               version);
+               announce();
+               fflush(stdout);
+               signal(SIGINT, prevint);
+       }
+       commands();
+       signal(SIGHUP, SIG_IGN);
+       signal(SIGINT, SIG_IGN);
+       signal(SIGQUIT, SIG_IGN);
+       quit();
+       exit(0);
+}
+
+/*
+ * Interrupt printing of the headers.
+ */
+void
+hdrstop()
+{
+
+       fflush(stdout);
+       fprintf(stderr, "\nInterrupt\n");
+       longjmp(hdrjmp, 1);
+}
+
+/*
+ * Compute what the screen size for printing headers should be.
+ * We use the following algorithm for the height:
+ *     If baud rate < 1200, use  9
+ *     If baud rate = 1200, use 14
+ *     If baud rate > 1200, use 24 or ws_row
+ * Width is either 80 or ws_col;
+ */
+setscreensize()
+{
+       struct sgttyb tbuf;
+       struct winsize ws;
+
+       if (ioctl(1, TIOCGWINSZ, (char *) &ws) < 0)
+               ws.ws_col = ws.ws_row = 0;
+       if (ioctl(1, TIOCGETP, &tbuf) < 0)
+               tbuf.sg_ospeed = B9600;
+       if (tbuf.sg_ospeed < B1200)
+               screenheight = 9;
+       else if (tbuf.sg_ospeed == B1200)
+               screenheight = 14;
+       else if (ws.ws_row != 0)
+               screenheight = ws.ws_row;
+       else
+               screenheight = 24;
+       if ((realscreenheight = ws.ws_row) == 0)
+               realscreenheight = 24;
+       if ((screenwidth = ws.ws_col) == 0)
+               screenwidth = 80;
+}
diff --git a/usr/src/usr.bin/mail/names.c b/usr/src/usr.bin/mail/names.c
new file mode 100644 (file)
index 0000000..a16fcf5
--- /dev/null
@@ -0,0 +1,684 @@
+/*
+ * Copyright (c) 1980 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)names.c    5.16 (Berkeley) 6/25/90";
+#endif /* not lint */
+
+/*
+ * Mail -- a mail program
+ *
+ * Handle name lists.
+ */
+
+#include "rcv.h"
+
+/*
+ * Allocate a single element of a name list,
+ * initialize its name field to the passed
+ * name and return it.
+ */
+struct name *
+nalloc(str, ntype)
+       char str[];
+{
+       register struct name *np;
+
+       np = (struct name *) salloc(sizeof *np);
+       np->n_flink = NIL;
+       np->n_blink = NIL;
+       np->n_type = ntype;
+       np->n_name = savestr(str);
+       return(np);
+}
+
+/*
+ * Find the tail of a list and return it.
+ */
+struct name *
+tailof(name)
+       struct name *name;
+{
+       register struct name *np;
+
+       np = name;
+       if (np == NIL)
+               return(NIL);
+       while (np->n_flink != NIL)
+               np = np->n_flink;
+       return(np);
+}
+
+/*
+ * Extract a list of names from a line,
+ * and make a list of names from it.
+ * Return the list or NIL if none found.
+ */
+struct name *
+extract(line, ntype)
+       char line[];
+{
+       register char *cp;
+       register struct name *top, *np, *t;
+       char nbuf[BUFSIZ];
+
+       if (line == NOSTR || *line == '\0')
+               return NIL;
+       top = NIL;
+       np = NIL;
+       cp = line;
+       while ((cp = yankword(cp, nbuf)) != NOSTR) {
+               t = nalloc(nbuf, ntype);
+               if (top == NIL)
+                       top = t;
+               else
+                       np->n_flink = t;
+               t->n_blink = np;
+               np = t;
+       }
+       return top;
+}
+
+/*
+ * Turn a list of names into a string of the same names.
+ */
+char *
+detract(np, ntype)
+       register struct name *np;
+{
+       register int s;
+       register char *cp, *top;
+       register struct name *p;
+       register int comma;
+
+       comma = ntype & GCOMMA;
+       if (np == NIL)
+               return(NOSTR);
+       ntype &= ~GCOMMA;
+       s = 0;
+       if (debug && comma)
+               fprintf(stderr, "detract asked to insert commas\n");
+       for (p = np; p != NIL; p = p->n_flink) {
+               if (ntype && (p->n_type & GMASK) != ntype)
+                       continue;
+               s += strlen(p->n_name) + 1;
+               if (comma)
+                       s++;
+       }
+       if (s == 0)
+               return(NOSTR);
+       s += 2;
+       top = salloc(s);
+       cp = top;
+       for (p = np; p != NIL; p = p->n_flink) {
+               if (ntype && (p->n_type & GMASK) != ntype)
+                       continue;
+               cp = copy(p->n_name, cp);
+               if (comma && p->n_flink != NIL)
+                       *cp++ = ',';
+               *cp++ = ' ';
+       }
+       *--cp = 0;
+       if (comma && *--cp == ',')
+               *cp = 0;
+       return(top);
+}
+
+/*
+ * Grab a single word (liberal word)
+ * Throw away things between ()'s, and take anything between <>.
+ */
+char *
+yankword(ap, wbuf)
+       char *ap, wbuf[];
+{
+       register char *cp, *cp2;
+
+       cp = ap;
+       for (;;) {
+               if (*cp == '\0')
+                       return NOSTR;
+               if (*cp == '(') {
+                       register int nesting = 0;
+
+                       while (*cp != '\0') {
+                               switch (*cp++) {
+                               case '(':
+                                       nesting++;
+                                       break;
+                               case ')':
+                                       --nesting;
+                                       break;
+                               }
+                               if (nesting <= 0)
+                                       break;
+                       }
+               } else if (*cp == ' ' || *cp == '\t' || *cp == ',')
+                       cp++;
+               else
+                       break;
+       }
+       if (*cp ==  '<')
+               for (cp2 = wbuf; *cp && (*cp2++ = *cp++) != '>';)
+                       ;
+       else
+               for (cp2 = wbuf; *cp && !index(" \t,(", *cp); *cp2++ = *cp++)
+                       ;
+       *cp2 = '\0';
+       return cp;
+}
+
+/*
+ * For each recipient in the passed name list with a /
+ * in the name, append the message to the end of the named file
+ * and remove him from the recipient list.
+ *
+ * Recipients whose name begins with | are piped through the given
+ * program and removed.
+ */
+struct name *
+outof(names, fo, hp)
+       struct name *names;
+       FILE *fo;
+       struct header *hp;
+{
+       register int c;
+       register struct name *np, *top;
+       time_t now, time();
+       char *date, *fname, *ctime();
+       FILE *fout, *fin;
+       int ispipe;
+       extern char tempEdit[];
+
+       top = names;
+       np = names;
+       (void) time(&now);
+       date = ctime(&now);
+       while (np != NIL) {
+               if (!isfileaddr(np->n_name) && np->n_name[0] != '|') {
+                       np = np->n_flink;
+                       continue;
+               }
+               ispipe = np->n_name[0] == '|';
+               if (ispipe)
+                       fname = np->n_name+1;
+               else
+                       fname = expand(np->n_name);
+
+               /*
+                * See if we have copied the complete message out yet.
+                * If not, do so.
+                */
+
+               if (image < 0) {
+                       if ((fout = Fopen(tempEdit, "a")) == NULL) {
+                               perror(tempEdit);
+                               senderr++;
+                               goto cant;
+                       }
+                       image = open(tempEdit, 2);
+                       (void) unlink(tempEdit);
+                       if (image < 0) {
+                               perror(tempEdit);
+                               senderr++;
+                               (void) Fclose(fout);
+                               goto cant;
+                       }
+                       fprintf(fout, "From %s %s", myname, date);
+                       puthead(hp, fout, GTO|GSUBJECT|GCC|GNL);
+                       while ((c = getc(fo)) != EOF)
+                               (void) putc(c, fout);
+                       rewind(fo);
+                       (void) putc('\n', fout);
+                       (void) fflush(fout);
+                       if (ferror(fout))
+                               perror(tempEdit);
+                       (void) Fclose(fout);
+               }
+
+               /*
+                * Now either copy "image" to the desired file
+                * or give it as the standard input to the desired
+                * program as appropriate.
+                */
+
+               if (ispipe) {
+                       int pid;
+                       char *shell;
+
+                       /*
+                        * XXX
+                        * We can't really reuse the same image file,
+                        * because multiple piped recipients will
+                        * share the same lseek location and trample
+                        * on one another.
+                        */
+                       if ((shell = value("SHELL")) == NOSTR)
+                               shell = _PATH_CSHELL;
+                       pid = start_command(shell, sigmask(SIGHUP)|
+                                       sigmask(SIGINT)|sigmask(SIGQUIT),
+                               image, -1, "-c", fname, NOSTR);
+                       if (pid < 0) {
+                               senderr++;
+                               goto cant;
+                       }
+                       free_child(pid);
+               } else {
+                       int f;
+                       if ((fout = Fopen(fname, "a")) == NULL) {
+                               perror(fname);
+                               senderr++;
+                               goto cant;
+                       }
+                       if ((f = dup(image)) < 0) {
+                               perror("dup");
+                               fin = NULL;
+                       } else
+                               fin = Fdopen(f, "r");
+                       if (fin == NULL) {
+                               fprintf(stderr, "Can't reopen image\n");
+                               (void) Fclose(fout);
+                               senderr++;
+                               goto cant;
+                       }
+                       rewind(fin);
+                       while ((c = getc(fin)) != EOF)
+                               (void) putc(c, fout);
+                       if (ferror(fout))
+                               senderr++, perror(fname);
+                       (void) Fclose(fout);
+                       (void) Fclose(fin);
+               }
+cant:
+               /*
+                * In days of old we removed the entry from the
+                * the list; now for sake of header expansion
+                * we leave it in and mark it as deleted.
+                */
+               np->n_type |= GDEL;
+               np = np->n_flink;
+       }
+       if (image >= 0) {
+               (void) close(image);
+               image = -1;
+       }
+       return(top);
+}
+
+/*
+ * Determine if the passed address is a local "send to file" address.
+ * If any of the network metacharacters precedes any slashes, it can't
+ * be a filename.  We cheat with .'s to allow path names like ./...
+ */
+isfileaddr(name)
+       char *name;
+{
+       register char *cp;
+
+       if (*name == '+')
+               return 1;
+       for (cp = name; *cp; cp++) {
+               if (*cp == '!' || *cp == '%' || *cp == '@')
+                       return 0;
+               if (*cp == '/')
+                       return 1;
+       }
+       return 0;
+}
+
+/*
+ * Map all of the aliased users in the invoker's mailrc
+ * file and insert them into the list.
+ * Changed after all these months of service to recursively
+ * expand names (2/14/80).
+ */
+
+struct name *
+usermap(names)
+       struct name *names;
+{
+       register struct name *new, *np, *cp;
+       struct grouphead *gh;
+       register int metoo;
+
+       new = NIL;
+       np = names;
+       metoo = (value("metoo") != NOSTR);
+       while (np != NIL) {
+               if (np->n_name[0] == '\\') {
+                       cp = np->n_flink;
+                       new = put(new, np);
+                       np = cp;
+                       continue;
+               }
+               gh = findgroup(np->n_name);
+               cp = np->n_flink;
+               if (gh != NOGRP)
+                       new = gexpand(new, gh, metoo, np->n_type);
+               else
+                       new = put(new, np);
+               np = cp;
+       }
+       return(new);
+}
+
+/*
+ * Recursively expand a group name.  We limit the expansion to some
+ * fixed level to keep things from going haywire.
+ * Direct recursion is not expanded for convenience.
+ */
+
+struct name *
+gexpand(nlist, gh, metoo, ntype)
+       struct name *nlist;
+       struct grouphead *gh;
+{
+       struct group *gp;
+       struct grouphead *ngh;
+       struct name *np;
+       static int depth;
+       char *cp;
+
+       if (depth > MAXEXP) {
+               printf("Expanding alias to depth larger than %d\n", MAXEXP);
+               return(nlist);
+       }
+       depth++;
+       for (gp = gh->g_list; gp != NOGE; gp = gp->ge_link) {
+               cp = gp->ge_name;
+               if (*cp == '\\')
+                       goto quote;
+               if (strcmp(cp, gh->g_name) == 0)
+                       goto quote;
+               if ((ngh = findgroup(cp)) != NOGRP) {
+                       nlist = gexpand(nlist, ngh, metoo, ntype);
+                       continue;
+               }
+quote:
+               np = nalloc(cp, ntype);
+               /*
+                * At this point should allow to expand
+                * to self if only person in group
+                */
+               if (gp == gh->g_list && gp->ge_link == NOGE)
+                       goto skip;
+               if (!metoo && strcmp(cp, myname) == 0)
+                       np->n_type |= GDEL;
+skip:
+               nlist = put(nlist, np);
+       }
+       depth--;
+       return(nlist);
+}
+
+/*
+ * Concatenate the two passed name lists, return the result.
+ */
+struct name *
+cat(n1, n2)
+       struct name *n1, *n2;
+{
+       register struct name *tail;
+
+       if (n1 == NIL)
+               return(n2);
+       if (n2 == NIL)
+               return(n1);
+       tail = tailof(n1);
+       tail->n_flink = n2;
+       n2->n_blink = tail;
+       return(n1);
+}
+
+/*
+ * Unpack the name list onto a vector of strings.
+ * Return an error if the name list won't fit.
+ */
+char **
+unpack(np)
+       struct name *np;
+{
+       register char **ap, **top;
+       register struct name *n;
+       int t, extra, metoo, verbose;
+
+       n = np;
+       if ((t = count(n)) == 0)
+               panic("No names to unpack");
+       /*
+        * Compute the number of extra arguments we will need.
+        * We need at least two extra -- one for "mail" and one for
+        * the terminating 0 pointer.  Additional spots may be needed
+        * to pass along -f to the host mailer.
+        */
+       extra = 2;
+       extra++;
+       metoo = value("metoo") != NOSTR;
+       if (metoo)
+               extra++;
+       verbose = value("verbose") != NOSTR;
+       if (verbose)
+               extra++;
+       top = (char **) salloc((t + extra) * sizeof *top);
+       ap = top;
+       *ap++ = "send-mail";
+       *ap++ = "-i";
+       if (metoo)
+               *ap++ = "-m";
+       if (verbose)
+               *ap++ = "-v";
+       for (; n != NIL; n = n->n_flink)
+               if ((n->n_type & GDEL) == 0)
+                       *ap++ = n->n_name;
+       *ap = NOSTR;
+       return(top);
+}
+
+/*
+ * Remove all of the duplicates from the passed name list by
+ * insertion sorting them, then checking for dups.
+ * Return the head of the new list.
+ */
+struct name *
+elide(names)
+       struct name *names;
+{
+       register struct name *np, *t, *new;
+       struct name *x;
+
+       if (names == NIL)
+               return(NIL);
+       new = names;
+       np = names;
+       np = np->n_flink;
+       if (np != NIL)
+               np->n_blink = NIL;
+       new->n_flink = NIL;
+       while (np != NIL) {
+               t = new;
+               while (strcasecmp(t->n_name, np->n_name) < 0) {
+                       if (t->n_flink == NIL)
+                               break;
+                       t = t->n_flink;
+               }
+
+               /*
+                * If we ran out of t's, put the new entry after
+                * the current value of t.
+                */
+
+               if (strcasecmp(t->n_name, np->n_name) < 0) {
+                       t->n_flink = np;
+                       np->n_blink = t;
+                       t = np;
+                       np = np->n_flink;
+                       t->n_flink = NIL;
+                       continue;
+               }
+
+               /*
+                * Otherwise, put the new entry in front of the
+                * current t.  If at the front of the list,
+                * the new guy becomes the new head of the list.
+                */
+
+               if (t == new) {
+                       t = np;
+                       np = np->n_flink;
+                       t->n_flink = new;
+                       new->n_blink = t;
+                       t->n_blink = NIL;
+                       new = t;
+                       continue;
+               }
+
+               /*
+                * The normal case -- we are inserting into the
+                * middle of the list.
+                */
+
+               x = np;
+               np = np->n_flink;
+               x->n_flink = t;
+               x->n_blink = t->n_blink;
+               t->n_blink->n_flink = x;
+               t->n_blink = x;
+       }
+
+       /*
+        * Now the list headed up by new is sorted.
+        * Go through it and remove duplicates.
+        */
+
+       np = new;
+       while (np != NIL) {
+               t = np;
+               while (t->n_flink != NIL &&
+                      strcasecmp(np->n_name, t->n_flink->n_name) == 0)
+                       t = t->n_flink;
+               if (t == np || t == NIL) {
+                       np = np->n_flink;
+                       continue;
+               }
+               
+               /*
+                * Now t points to the last entry with the same name
+                * as np.  Make np point beyond t.
+                */
+
+               np->n_flink = t->n_flink;
+               if (t->n_flink != NIL)
+                       t->n_flink->n_blink = np;
+               np = np->n_flink;
+       }
+       return(new);
+}
+
+/*
+ * Put another node onto a list of names and return
+ * the list.
+ */
+struct name *
+put(list, node)
+       struct name *list, *node;
+{
+       node->n_flink = list;
+       node->n_blink = NIL;
+       if (list != NIL)
+               list->n_blink = node;
+       return(node);
+}
+
+/*
+ * Determine the number of undeleted elements in
+ * a name list and return it.
+ */
+count(np)
+       register struct name *np;
+{
+       register int c;
+
+       for (c = 0; np != NIL; np = np->n_flink)
+               if ((np->n_type & GDEL) == 0)
+                       c++;
+       return c;
+}
+
+/*
+ * Delete the given name from a namelist.
+ */
+struct name *
+delname(np, name)
+       register struct name *np;
+       char name[];
+{
+       register struct name *p;
+
+       for (p = np; p != NIL; p = p->n_flink)
+               if (strcasecmp(p->n_name, name) == 0) {
+                       if (p->n_blink == NIL) {
+                               if (p->n_flink != NIL)
+                                       p->n_flink->n_blink = NIL;
+                               np = p->n_flink;
+                               continue;
+                       }
+                       if (p->n_flink == NIL) {
+                               if (p->n_blink != NIL)
+                                       p->n_blink->n_flink = NIL;
+                               continue;
+                       }
+                       p->n_blink->n_flink = p->n_flink;
+                       p->n_flink->n_blink = p->n_blink;
+               }
+       return np;
+}
+
+/*
+ * Pretty print a name list
+ * Uncomment it if you need it.
+ */
+
+/*
+prettyprint(name)
+       struct name *name;
+{
+       register struct name *np;
+
+       np = name;
+       while (np != NIL) {
+               fprintf(stderr, "%s(%d) ", np->n_name, np->n_type);
+               np = np->n_flink;
+       }
+       fprintf(stderr, "\n");
+}
+*/
diff --git a/usr/src/usr.bin/mail/popen.c b/usr/src/usr.bin/mail/popen.c
new file mode 100644 (file)
index 0000000..969c1d2
--- /dev/null
@@ -0,0 +1,339 @@
+/*
+ * Copyright (c) 1980 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)popen.c    5.16 (Berkeley) 4/1/91";
+#endif /* not lint */
+
+#include "rcv.h"
+#include <sys/signal.h>
+#include <sys/wait.h>
+
+#define READ 0
+#define WRITE 1
+static int *pid;
+
+struct fp {
+       FILE *fp;
+       int pipe;
+       struct fp *link;
+};
+static struct fp *fp_head;
+
+FILE *
+Fopen(file, mode)
+       char *file, *mode;
+{
+       FILE *fp;
+
+       if ((fp = fopen(file, mode)) != NULL)
+               register_file(fp, 0);
+       return fp;
+}
+
+FILE *
+Fdopen(fd, mode)
+       char *mode;
+{
+       FILE *fp;
+
+       if ((fp = fdopen(fd, mode)) != NULL)
+               register_file(fp, 0);
+       return fp;
+}
+
+Fclose(fp)
+       FILE *fp;
+{
+       unregister_file(fp);
+       return fclose(fp);
+}
+
+FILE *
+Popen(cmd, mode)
+       char *cmd;
+       char *mode;
+{
+       int p[2];
+       int myside, hisside, fd0, fd1;
+       FILE *fp;
+
+       if (pid == 0)
+               pid = (int *) malloc((unsigned) sizeof (int) * getdtablesize());
+       if (pipe(p) < 0)
+               return NULL;
+       if (*mode == 'r') {
+               myside = p[READ];
+               fd0 = -1;
+               hisside = fd1 = p[WRITE];
+       } else {
+               myside = p[WRITE];
+               hisside = fd0 = p[READ];
+               fd1 = -1;
+       }
+       if ((pid[myside] = start_command(cmd, 0, fd0, fd1, NOSTR)) < 0) {
+               close(p[READ]);
+               close(p[WRITE]);
+               return NULL;
+       }
+       (void) close(hisside);
+       if ((fp = fdopen(myside, mode)) != NULL)
+               register_file(fp, 1);
+       return fp;
+}
+
+Pclose(ptr)
+       FILE *ptr;
+{
+       int i;
+       int omask;
+
+       i = fileno(ptr);
+       unregister_file(ptr);
+       (void) fclose(ptr);
+       omask = sigblock(sigmask(SIGINT)|sigmask(SIGHUP));
+       i = wait_child(pid[i]);
+       sigsetmask(omask);
+       return i;
+}
+
+close_all_files()
+{
+
+       while (fp_head)
+               if (fp_head->pipe)
+                       (void) Pclose(fp_head->fp);
+               else
+                       (void) Fclose(fp_head->fp);
+}
+
+register_file(fp, pipe)
+       FILE *fp;
+{
+       struct fp *fpp;
+
+       if ((fpp = (struct fp *) malloc(sizeof *fpp)) == NULL)
+               panic("Out of memory");
+       fpp->fp = fp;
+       fpp->pipe = pipe;
+       fpp->link = fp_head;
+       fp_head = fpp;
+}
+
+unregister_file(fp)
+       FILE *fp;
+{
+       struct fp **pp, *p;
+
+       for (pp = &fp_head; p = *pp; pp = &p->link)
+               if (p->fp == fp) {
+                       *pp = p->link;
+                       free((char *) p);
+                       return;
+               }
+       /* XXX
+        * Ignore this for now; there may still be uncaught
+        * duplicate closes.
+       panic("Invalid file pointer");
+       */
+}
+
+/*
+ * Run a command without a shell, with optional arguments and splicing
+ * of stdin and stdout.  The command name can be a sequence of words.
+ * Signals must be handled by the caller.
+ * "Mask" contains the signals to ignore in the new process.
+ * SIGINT is enabled unless it's in the mask.
+ */
+/*VARARGS4*/
+run_command(cmd, mask, infd, outfd, a0, a1, a2)
+       char *cmd;
+       int mask, infd, outfd;
+       char *a0, *a1, *a2;
+{
+       int pid;
+
+       if ((pid = start_command(cmd, mask, infd, outfd, a0, a1, a2)) < 0)
+               return -1;
+       return wait_command(pid);
+}
+
+/*VARARGS4*/
+start_command(cmd, mask, infd, outfd, a0, a1, a2)
+       char *cmd;
+       int mask, infd, outfd;
+       char *a0, *a1, *a2;
+{
+       int pid;
+
+       if ((pid = vfork()) < 0) {
+               perror("fork");
+               return -1;
+       }
+       if (pid == 0) {
+               char *argv[100];
+               int i = getrawlist(cmd, argv, sizeof argv / sizeof *argv);
+
+               if ((argv[i++] = a0) != NOSTR &&
+                   (argv[i++] = a1) != NOSTR &&
+                   (argv[i++] = a2) != NOSTR)
+                       argv[i] = NOSTR;
+               prepare_child(mask, infd, outfd);
+               execvp(argv[0], argv);
+               perror(argv[0]);
+               _exit(1);
+       }
+       return pid;
+}
+
+prepare_child(mask, infd, outfd)
+       int mask, infd, outfd;
+{
+       int i;
+
+       if (infd >= 0)
+               dup2(infd, 0);
+       if (outfd >= 0)
+               dup2(outfd, 1);
+       for (i = getdtablesize(); --i > 2;)
+               close(i);
+       for (i = 1; i <= NSIG; i++)
+               if (mask & sigmask(i))
+                       (void) signal(i, SIG_IGN);
+       if ((mask & sigmask(SIGINT)) == 0)
+               (void) signal(SIGINT, SIG_DFL);
+       (void) sigsetmask(0);
+}
+
+wait_command(pid)
+       int pid;
+{
+
+       if (wait_child(pid) < 0) {
+               printf("Fatal error in process.\n");
+               return -1;
+       }
+       return 0;
+}
+
+struct child {
+       int pid;
+       char done;
+       char free;
+       union wait status;
+       struct child *link;
+};
+static struct child *child;
+
+struct child *
+findchild(pid)
+       int pid;
+{
+       register struct child **cpp;
+
+       for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid;
+            cpp = &(*cpp)->link)
+                       ;
+       if (*cpp == NULL) {
+               *cpp = (struct child *) malloc(sizeof (struct child));
+               (*cpp)->pid = pid;
+               (*cpp)->done = (*cpp)->free = 0;
+               (*cpp)->link = NULL;
+       }
+       return *cpp;
+}
+
+delchild(cp)
+       register struct child *cp;
+{
+       register struct child **cpp;
+
+       for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link)
+               ;
+       *cpp = cp->link;
+       free((char *) cp);
+}
+
+void
+sigchild()
+{
+       int pid;
+       union wait status;
+       register struct child *cp;
+
+       while ((pid =
+           wait3((int *)&status, WNOHANG, (struct rusage *)0)) > 0) {
+               cp = findchild(pid);
+               if (cp->free)
+                       delchild(cp);
+               else {
+                       cp->done = 1;
+                       cp->status = status;
+               }
+       }
+}
+
+union wait wait_status;
+
+/*
+ * Wait for a specific child to die.
+ */
+wait_child(pid)
+       int pid;
+{
+       int mask = sigblock(sigmask(SIGCHLD));
+       register struct child *cp = findchild(pid);
+
+       while (!cp->done)
+               sigpause(mask);
+       wait_status = cp->status;
+       delchild(cp);
+       sigsetmask(mask);
+       return wait_status.w_status ? -1 : 0;
+}
+
+/*
+ * Mark a child as don't care.
+ */
+free_child(pid)
+       int pid;
+{
+       int mask = sigblock(sigmask(SIGCHLD));
+       register struct child *cp = findchild(pid);
+
+       if (cp->done)
+               delchild(cp);
+       else
+               cp->free = 1;
+       sigsetmask(mask);
+}
diff --git a/usr/src/usr.bin/mail/quit.c b/usr/src/usr.bin/mail/quit.c
new file mode 100644 (file)
index 0000000..2a4fb00
--- /dev/null
@@ -0,0 +1,487 @@
+/*
+ * Copyright (c) 1980 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)quit.c     5.16 (Berkeley) 2/9/91";
+#endif /* not lint */
+
+#include "rcv.h"
+#include <sys/stat.h>
+#include <sys/file.h>
+
+/*
+ * Rcv -- receive mail rationally.
+ *
+ * Termination processing.
+ */
+
+/*
+ * The "quit" command.
+ */
+quitcmd()
+{
+       /*
+        * 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.
+ */
+
+quit()
+{
+       int mcount, p, modify, autohold, anystat, holdbit, nohold;
+       FILE *ibuf, *obuf, *fbuf, *rbuf, *readstat, *abuf;
+       register struct message *mp;
+       register int c;
+       extern char tempQuit[], tempResid[];
+       struct stat minfo;
+       char *mbox;
+
+       /*
+        * If we are read only, we can't do anything,
+        * so just return quickly.
+        */
+       if (readonly)
+               return;
+       /*
+        * If editing (not reading system mail box), then do the work
+        * in edstop()
+        */
+       if (edit) {
+               edstop();
+               return;
+       }
+
+       /*
+        * See if there any messages to save in mbox.  If no, we
+        * can save copying mbox to /tmp and back.
+        *
+        * Check also to see if any files need to be preserved.
+        * Delete all untouched messages to keep them out of mbox.
+        * If all the messages are to be preserved, just exit with
+        * a message.
+        */
+
+       fbuf = Fopen(mailname, "r");
+       if (fbuf == NULL)
+               goto newmail;
+       flock(fileno(fbuf), LOCK_EX);
+       rbuf = NULL;
+       if (fstat(fileno(fbuf), &minfo) >= 0 && minfo.st_size > mailsize) {
+               printf("New mail has arrived.\n");
+               rbuf = Fopen(tempResid, "w");
+               if (rbuf == NULL || fbuf == NULL)
+                       goto newmail;
+#ifdef APPEND
+               fseek(fbuf, mailsize, 0);
+               while ((c = getc(fbuf)) != EOF)
+                       (void) putc(c, rbuf);
+#else
+               p = minfo.st_size - mailsize;
+               while (p-- > 0) {
+                       c = getc(fbuf);
+                       if (c == EOF)
+                               goto newmail;
+                       (void) putc(c, rbuf);
+               }
+#endif
+               Fclose(rbuf);
+               if ((rbuf = Fopen(tempResid, "r")) == NULL)
+                       goto newmail;
+               rm(tempResid);
+       }
+
+       /*
+        * Adjust the message flags in each message.
+        */
+
+       anystat = 0;
+       autohold = value("hold") != NOSTR;
+       holdbit = autohold ? MPRESERVE : MBOX;
+       nohold = MBOX|MSAVED|MDELETED|MPRESERVE;
+       if (value("keepsave") != NOSTR)
+               nohold &= ~MSAVED;
+       for (mp = &message[0]; mp < &message[msgCount]; mp++) {
+               if (mp->m_flag & MNEW) {
+                       mp->m_flag &= ~MNEW;
+                       mp->m_flag |= MSTATUS;
+               }
+               if (mp->m_flag & MSTATUS)
+                       anystat++;
+               if ((mp->m_flag & MTOUCH) == 0)
+                       mp->m_flag |= MPRESERVE;
+               if ((mp->m_flag & nohold) == 0)
+                       mp->m_flag |= holdbit;
+       }
+       modify = 0;
+       if (Tflag != NOSTR) {
+               if ((readstat = Fopen(Tflag, "w")) == NULL)
+                       Tflag = NOSTR;
+       }
+       for (c = 0, p = 0, mp = &message[0]; mp < &message[msgCount]; mp++) {
+               if (mp->m_flag & MBOX)
+                       c++;
+               if (mp->m_flag & MPRESERVE)
+                       p++;
+               if (mp->m_flag & MODIFY)
+                       modify++;
+               if (Tflag != NOSTR && (mp->m_flag & (MREAD|MDELETED)) != 0) {
+                       char *id;
+
+                       if ((id = hfield("article-id", mp)) != NOSTR)
+                               fprintf(readstat, "%s\n", id);
+               }
+       }
+       if (Tflag != NOSTR)
+               Fclose(readstat);
+       if (p == msgCount && !modify && !anystat) {
+               printf("Held %d message%s in %s\n",
+                       p, p == 1 ? "" : "s", mailname);
+               Fclose(fbuf);
+               return;
+       }
+       if (c == 0) {
+               if (p != 0) {
+                       writeback(rbuf);
+                       Fclose(fbuf);
+                       return;
+               }
+               goto cream;
+       }
+
+       /*
+        * Create another temporary file and copy user's mbox file
+        * darin.  If there is no mbox, copy nothing.
+        * If he has specified "append" don't copy his mailbox,
+        * just copy saveable entries at the end.
+        */
+
+       mbox = expand("&");
+       mcount = c;
+       if (value("append") == NOSTR) {
+               if ((obuf = Fopen(tempQuit, "w")) == NULL) {
+                       perror(tempQuit);
+                       Fclose(fbuf);
+                       return;
+               }
+               if ((ibuf = Fopen(tempQuit, "r")) == NULL) {
+                       perror(tempQuit);
+                       rm(tempQuit);
+                       Fclose(obuf);
+                       Fclose(fbuf);
+                       return;
+               }
+               rm(tempQuit);
+               if ((abuf = Fopen(mbox, "r")) != NULL) {
+                       while ((c = getc(abuf)) != EOF)
+                               (void) putc(c, obuf);
+                       Fclose(abuf);
+               }
+               if (ferror(obuf)) {
+                       perror(tempQuit);
+                       Fclose(ibuf);
+                       Fclose(obuf);
+                       Fclose(fbuf);
+                       return;
+               }
+               Fclose(obuf);
+               close(creat(mbox, 0600));
+               if ((obuf = Fopen(mbox, "r+")) == NULL) {
+                       perror(mbox);
+                       Fclose(ibuf);
+                       Fclose(fbuf);
+                       return;
+               }
+       }
+       if (value("append") != NOSTR) {
+               if ((obuf = Fopen(mbox, "a")) == NULL) {
+                       perror(mbox);
+                       Fclose(fbuf);
+                       return;
+               }
+               fchmod(fileno(obuf), 0600);
+       }
+       for (mp = &message[0]; mp < &message[msgCount]; mp++)
+               if (mp->m_flag & MBOX)
+                       if (send(mp, obuf, saveignore, NOSTR) < 0) {
+                               perror(mbox);
+                               Fclose(ibuf);
+                               Fclose(obuf);
+                               Fclose(fbuf);
+                               return;
+                       }
+
+       /*
+        * Copy the user's old mbox contents back
+        * to the end of the stuff we just saved.
+        * If we are appending, this is unnecessary.
+        */
+
+       if (value("append") == NOSTR) {
+               rewind(ibuf);
+               c = getc(ibuf);
+               while (c != EOF) {
+                       (void) putc(c, obuf);
+                       if (ferror(obuf))
+                               break;
+                       c = getc(ibuf);
+               }
+               Fclose(ibuf);
+               fflush(obuf);
+       }
+       trunc(obuf);
+       if (ferror(obuf)) {
+               perror(mbox);
+               Fclose(obuf);
+               Fclose(fbuf);
+               return;
+       }
+       Fclose(obuf);
+       if (mcount == 1)
+               printf("Saved 1 message in mbox\n");
+       else
+               printf("Saved %d messages in mbox\n", mcount);
+
+       /*
+        * Now we are ready to copy back preserved files to
+        * the system mailbox, if any were requested.
+        */
+
+       if (p != 0) {
+               writeback(rbuf);
+               Fclose(fbuf);
+               return;
+       }
+
+       /*
+        * Finally, remove his /usr/mail file.
+        * If new mail has arrived, copy it back.
+        */
+
+cream:
+       if (rbuf != NULL) {
+               abuf = Fopen(mailname, "r+");
+               if (abuf == NULL)
+                       goto newmail;
+               while ((c = getc(rbuf)) != EOF)
+                       (void) putc(c, abuf);
+               Fclose(rbuf);
+               trunc(abuf);
+               Fclose(abuf);
+               alter(mailname);
+               Fclose(fbuf);
+               return;
+       }
+       demail();
+       Fclose(fbuf);
+       return;
+
+newmail:
+       printf("Thou hast new mail.\n");
+       if (fbuf != NULL)
+               Fclose(fbuf);
+}
+
+/*
+ * Preserve all the appropriate messages back in the system
+ * mailbox, and print a nice message indicated how many were
+ * saved.  On any error, just return -1.  Else return 0.
+ * Incorporate the any new mail that we found.
+ */
+writeback(res)
+       register FILE *res;
+{
+       register struct message *mp;
+       register int p, c;
+       FILE *obuf;
+
+       p = 0;
+       if ((obuf = Fopen(mailname, "r+")) == NULL) {
+               perror(mailname);
+               return(-1);
+       }
+#ifndef APPEND
+       if (res != NULL)
+               while ((c = getc(res)) != EOF)
+                       (void) putc(c, obuf);
+#endif
+       for (mp = &message[0]; mp < &message[msgCount]; mp++)
+               if ((mp->m_flag&MPRESERVE)||(mp->m_flag&MTOUCH)==0) {
+                       p++;
+                       if (send(mp, obuf, (struct ignoretab *)0, NOSTR) < 0) {
+                               perror(mailname);
+                               Fclose(obuf);
+                               return(-1);
+                       }
+               }
+#ifdef APPEND
+       if (res != NULL)
+               while ((c = getc(res)) != EOF)
+                       (void) putc(c, obuf);
+#endif
+       fflush(obuf);
+       trunc(obuf);
+       if (ferror(obuf)) {
+               perror(mailname);
+               Fclose(obuf);
+               return(-1);
+       }
+       if (res != NULL)
+               Fclose(res);
+       Fclose(obuf);
+       alter(mailname);
+       if (p == 1)
+               printf("Held 1 message in %s\n", mailname);
+       else
+               printf("Held %d messages in %s\n", p, mailname);
+       return(0);
+}
+
+/*
+ * Terminate an editing session by attempting to write out the user's
+ * file from the temporary.  Save any new stuff appended to the file.
+ */
+edstop()
+{
+       register int gotcha, c;
+       register struct message *mp;
+       FILE *obuf, *ibuf, *readstat;
+       struct stat statb;
+       char tempname[30];
+       char *mktemp();
+
+       if (readonly)
+               return;
+       holdsigs();
+       if (Tflag != NOSTR) {
+               if ((readstat = Fopen(Tflag, "w")) == NULL)
+                       Tflag = NOSTR;
+       }
+       for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) {
+               if (mp->m_flag & MNEW) {
+                       mp->m_flag &= ~MNEW;
+                       mp->m_flag |= MSTATUS;
+               }
+               if (mp->m_flag & (MODIFY|MDELETED|MSTATUS))
+                       gotcha++;
+               if (Tflag != NOSTR && (mp->m_flag & (MREAD|MDELETED)) != 0) {
+                       char *id;
+
+                       if ((id = hfield("article-id", mp)) != NOSTR)
+                               fprintf(readstat, "%s\n", id);
+               }
+       }
+       if (Tflag != NOSTR)
+               Fclose(readstat);
+       if (!gotcha || Tflag != NOSTR)
+               goto done;
+       ibuf = NULL;
+       if (stat(mailname, &statb) >= 0 && statb.st_size > mailsize) {
+               strcpy(tempname, _PATH_TMP);
+               strcat(tempname, "mboxXXXXXX");
+               mktemp(tempname);
+               if ((obuf = Fopen(tempname, "w")) == NULL) {
+                       perror(tempname);
+                       relsesigs();
+                       reset(0);
+               }
+               if ((ibuf = Fopen(mailname, "r")) == NULL) {
+                       perror(mailname);
+                       Fclose(obuf);
+                       rm(tempname);
+                       relsesigs();
+                       reset(0);
+               }
+               fseek(ibuf, mailsize, 0);
+               while ((c = getc(ibuf)) != EOF)
+                       (void) putc(c, obuf);
+               Fclose(ibuf);
+               Fclose(obuf);
+               if ((ibuf = Fopen(tempname, "r")) == NULL) {
+                       perror(tempname);
+                       rm(tempname);
+                       relsesigs();
+                       reset(0);
+               }
+               rm(tempname);
+       }
+       printf("\"%s\" ", mailname);
+       fflush(stdout);
+       if ((obuf = Fopen(mailname, "r+")) == NULL) {
+               perror(mailname);
+               relsesigs();
+               reset(0);
+       }
+       trunc(obuf);
+       c = 0;
+       for (mp = &message[0]; mp < &message[msgCount]; mp++) {
+               if ((mp->m_flag & MDELETED) != 0)
+                       continue;
+               c++;
+               if (send(mp, obuf, (struct ignoretab *) NULL, NOSTR) < 0) {
+                       perror(mailname);
+                       relsesigs();
+                       reset(0);
+               }
+       }
+       gotcha = (c == 0 && ibuf == NULL);
+       if (ibuf != NULL) {
+               while ((c = getc(ibuf)) != EOF)
+                       (void) putc(c, obuf);
+               Fclose(ibuf);
+       }
+       fflush(obuf);
+       if (ferror(obuf)) {
+               perror(mailname);
+               relsesigs();
+               reset(0);
+       }
+       Fclose(obuf);
+       if (gotcha) {
+               rm(mailname);
+               printf("removed\n");
+       } else
+               printf("complete\n");
+       fflush(stdout);
+
+done:
+       relsesigs();
+}
diff --git a/usr/src/usr.bin/mail/rcv.h b/usr/src/usr.bin/mail/rcv.h
new file mode 100644 (file)
index 0000000..0ecb4bc
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 1980 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)rcv.h       5.5 (Berkeley) 6/1/90
+ */
+
+/*
+ * Mail -- a mail program
+ *
+ * This file is included by normal files which want both
+ * globals and declarations.
+ */
+
+#include "def.h"
+#include "glob.h"
diff --git a/usr/src/usr.bin/mail/send.c b/usr/src/usr.bin/mail/send.c
new file mode 100644 (file)
index 0000000..06588b5
--- /dev/null
@@ -0,0 +1,553 @@
+/*
+ * Copyright (c) 1980 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)send.c     5.23 (Berkeley) 2/9/91";
+#endif /* not lint */
+
+#include "rcv.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * Mail to others.
+ */
+
+/*
+ * Send message described by the passed pointer to the
+ * passed output buffer.  Return -1 on error.
+ * Adjust the status: field if need be.
+ * If doign is given, suppress ignored header fields.
+ * prefix is a string to prepend to each output line.
+ */
+send(mp, obuf, doign, prefix)
+       register struct message *mp;
+       FILE *obuf;
+       struct ignoretab *doign;
+       char *prefix;
+{
+       long count;
+       register FILE *ibuf;
+       char line[LINESIZE];
+       int ishead, infld, ignoring, dostat, firstline;
+       register char *cp, *cp2;
+       register int c;
+       int length;
+       int prefixlen;
+
+       /*
+        * Compute the prefix string, without trailing whitespace
+        */
+       if (prefix != NOSTR) {
+               cp2 = 0;
+               for (cp = prefix; *cp; cp++)
+                       if (*cp != ' ' && *cp != '\t')
+                               cp2 = cp;
+               prefixlen = cp2 == 0 ? 0 : cp2 - prefix + 1;
+       }
+       ibuf = setinput(mp);
+       count = mp->m_size;
+       ishead = 1;
+       dostat = doign == 0 || !isign("status", doign);
+       infld = 0;
+       firstline = 1;
+       /*
+        * Process headers first
+        */
+       while (count > 0 && ishead) {
+               if (fgets(line, LINESIZE, ibuf) == NULL)
+                       break;
+               count -= length = strlen(line);
+               if (firstline) {
+                       /* 
+                        * First line is the From line, so no headers
+                        * there to worry about
+                        */
+                       firstline = 0;
+                       ignoring = doign == ignoreall;
+               } else if (line[0] == '\n') {
+                       /*
+                        * If line is blank, we've reached end of
+                        * headers, so force out status: field
+                        * and note that we are no longer in header
+                        * fields
+                        */
+                       if (dostat) {
+                               statusput(mp, obuf, prefix);
+                               dostat = 0;
+                       }
+                       ishead = 0;
+                       ignoring = doign == ignoreall;
+               } else if (infld && (line[0] == ' ' || line[0] == '\t')) {
+                       /*
+                        * If this line is a continuation (via space or tab)
+                        * of a previous header field, just echo it
+                        * (unless the field should be ignored).
+                        * In other words, nothing to do.
+                        */
+               } else {
+                       /*
+                        * Pick up the header field if we have one.
+                        */
+                       for (cp = line; (c = *cp++) && c != ':' && !isspace(c);)
+                               ;
+                       cp2 = --cp;
+                       while (isspace(*cp++))
+                               ;
+                       if (cp[-1] != ':') {
+                               /*
+                                * Not a header line, force out status:
+                                * This happens in uucp style mail where
+                                * there are no headers at all.
+                                */
+                               if (dostat) {
+                                       statusput(mp, obuf, prefix);
+                                       dostat = 0;
+                               }
+                               if (doign != ignoreall)
+                                       /* add blank line */
+                                       (void) putc('\n', obuf);
+                               ishead = 0;
+                               ignoring = 0;
+                       } else {
+                               /*
+                                * If it is an ignored field and
+                                * we care about such things, skip it.
+                                */
+                               *cp2 = 0;       /* temporarily null terminate */
+                               if (doign && isign(line, doign))
+                                       ignoring = 1;
+                               else if ((line[0] == 's' || line[0] == 'S') &&
+                                        strcasecmp(line, "status") == 0) {
+                                       /*
+                                        * If the field is "status," go compute
+                                        * and print the real Status: field
+                                        */
+                                       if (dostat) {
+                                               statusput(mp, obuf, prefix);
+                                               dostat = 0;
+                                       }
+                                       ignoring = 1;
+                               } else {
+                                       ignoring = 0;
+                                       *cp2 = c;       /* restore */
+                               }
+                               infld = 1;
+                       }
+               }
+               if (!ignoring) {
+                       /*
+                        * Strip trailing whitespace from prefix
+                        * if line is blank.
+                        */
+                       if (prefix != NOSTR)
+                               if (length > 1)
+                                       fputs(prefix, obuf);
+                               else
+                                       (void) fwrite(prefix, sizeof *prefix,
+                                                       prefixlen, obuf);
+                       (void) fwrite(line, sizeof *line, length, obuf);
+                       if (ferror(obuf))
+                               return -1;
+               }
+       }
+       /*
+        * Copy out message body
+        */
+       if (doign == ignoreall)
+               count--;                /* skip final blank line */
+       if (prefix != NOSTR)
+               while (count > 0) {
+                       if (fgets(line, LINESIZE, ibuf) == NULL) {
+                               c = 0;
+                               break;
+                       }
+                       count -= c = strlen(line);
+                       /*
+                        * Strip trailing whitespace from prefix
+                        * if line is blank.
+                        */
+                       if (c > 1)
+                               fputs(prefix, obuf);
+                       else
+                               (void) fwrite(prefix, sizeof *prefix,
+                                               prefixlen, obuf);
+                       (void) fwrite(line, sizeof *line, c, obuf);
+                       if (ferror(obuf))
+                               return -1;
+               }
+       else
+               while (count > 0) {
+                       c = count < LINESIZE ? count : LINESIZE;
+                       if ((c = fread(line, sizeof *line, c, ibuf)) <= 0)
+                               break;
+                       count -= c;
+                       if (fwrite(line, sizeof *line, c, obuf) != c)
+                               return -1;
+               }
+       if (doign == ignoreall && c > 0 && line[c - 1] != '\n')
+               /* no final blank line */
+               if ((c = getc(ibuf)) != EOF && putc(c, obuf) == EOF)
+                       return -1;
+       return 0;
+}
+
+/*
+ * Output a reasonable looking status field.
+ */
+statusput(mp, obuf, prefix)
+       register struct message *mp;
+       FILE *obuf;
+       char *prefix;
+{
+       char statout[3];
+       register char *cp = statout;
+
+       if (mp->m_flag & MREAD)
+               *cp++ = 'R';
+       if ((mp->m_flag & MNEW) == 0)
+               *cp++ = 'O';
+       *cp = 0;
+       if (statout[0])
+               fprintf(obuf, "%sStatus: %s\n",
+                       prefix == NOSTR ? "" : prefix, statout);
+}
+
+/*
+ * Interface between the argument list and the mail1 routine
+ * which does all the dirty work.
+ */
+mail(to, cc, bcc, smopts, subject)
+       struct name *to, *cc, *bcc, *smopts;
+       char *subject;
+{
+       struct header head;
+
+       head.h_to = to;
+       head.h_subject = subject;
+       head.h_cc = cc;
+       head.h_bcc = bcc;
+       head.h_smopts = smopts;
+       mail1(&head, 0);
+       return(0);
+}
+
+
+/*
+ * Send mail to a bunch of user names.  The interface is through
+ * the mail routine below.
+ */
+sendmail(str)
+       char *str;
+{
+       struct header head;
+
+       head.h_to = extract(str, GTO);
+       head.h_subject = NOSTR;
+       head.h_cc = NIL;
+       head.h_bcc = NIL;
+       head.h_smopts = NIL;
+       mail1(&head, 0);
+       return(0);
+}
+
+/*
+ * Mail a message on standard input to the people indicated
+ * in the passed header.  (Internal interface).
+ */
+mail1(hp, printheaders)
+       struct header *hp;
+{
+       char *cp;
+       int pid;
+       char **namelist;
+       struct name *to;
+       FILE *mtf;
+
+       /*
+        * Collect user's mail from standard input.
+        * Get the result as mtf.
+        */
+       if ((mtf = collect(hp, printheaders)) == NULL)
+               return;
+       if (value("interactive") != NOSTR)
+               if (value("askcc") != NOSTR)
+                       grabh(hp, GCC);
+               else {
+                       printf("EOT\n");
+                       (void) fflush(stdout);
+               }
+       if (fsize(mtf) == 0)
+               if (hp->h_subject == NOSTR)
+                       printf("No message, no subject; hope that's ok\n");
+               else
+                       printf("Null message body; hope that's ok\n");
+       /*
+        * Now, take the user names from the combined
+        * to and cc lists and do all the alias
+        * processing.
+        */
+       senderr = 0;
+       to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc)));
+       if (to == NIL) {
+               printf("No recipients specified\n");
+               senderr++;
+       }
+       /*
+        * Look through the recipient list for names with /'s
+        * in them which we write to as files directly.
+        */
+       to = outof(to, mtf, hp);
+       if (senderr)
+               savedeadletter(mtf);
+       to = elide(to);
+       if (count(to) == 0)
+               goto out;
+       fixhead(hp, to);
+       if ((mtf = infix(hp, mtf)) == NULL) {
+               fprintf(stderr, ". . . message lost, sorry.\n");
+               return;
+       }
+       namelist = unpack(cat(hp->h_smopts, to));
+       if (debug) {
+               char **t;
+
+               printf("Sendmail arguments:");
+               for (t = namelist; *t != NOSTR; t++)
+                       printf(" \"%s\"", *t);
+               printf("\n");
+               goto out;
+       }
+       if ((cp = value("record")) != NOSTR)
+               (void) savemail(expand(cp), mtf);
+       /*
+        * Fork, set up the temporary mail file as standard
+        * input for "mail", and exec with the user list we generated
+        * far above.
+        */
+       pid = fork();
+       if (pid == -1) {
+               perror("fork");
+               savedeadletter(mtf);
+               goto out;
+       }
+       if (pid == 0) {
+               if (access(_PATH_MAIL_LOG, 0) == 0) {
+                       FILE *postage;
+
+                       if ((postage = Fopen(_PATH_MAIL_LOG, "a")) != NULL) {
+                               fprintf(postage, "%s %d %ld\n", myname,
+                                   count(to), fsize(mtf));
+                               (void) Fclose(postage);
+                       }
+               }
+               prepare_child(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT)|
+                       sigmask(SIGTSTP)|sigmask(SIGTTIN)|sigmask(SIGTTOU),
+                       fileno(mtf), -1);
+               if ((cp = value("sendmail")) != NOSTR)
+                       cp = expand(cp);
+               else
+                       cp = _PATH_SENDMAIL;
+               execv(cp, namelist);
+               perror(cp);
+               _exit(1);
+       }
+       if (value("verbose") != NOSTR)
+               (void) wait_child(pid);
+       else
+               free_child(pid);
+out:
+       (void) Fclose(mtf);
+}
+
+/*
+ * Fix the header by glopping all of the expanded names from
+ * the distribution list into the appropriate fields.
+ */
+fixhead(hp, tolist)
+       struct header *hp;
+       struct name *tolist;
+{
+       register struct name *np;
+
+       hp->h_to = NIL;
+       hp->h_cc = NIL;
+       hp->h_bcc = NIL;
+       for (np = tolist; np != NIL; np = np->n_flink)
+               if ((np->n_type & GMASK) == GTO)
+                       hp->h_to =
+                               cat(hp->h_to, nalloc(np->n_name, np->n_type));
+               else if ((np->n_type & GMASK) == GCC)
+                       hp->h_cc =
+                               cat(hp->h_cc, nalloc(np->n_name, np->n_type));
+               else if ((np->n_type & GMASK) == GBCC)
+                       hp->h_bcc =
+                               cat(hp->h_bcc, nalloc(np->n_name, np->n_type));
+}
+
+/*
+ * Prepend a header in front of the collected stuff
+ * and return the new file.
+ */
+FILE *
+infix(hp, fi)
+       struct header *hp;
+       FILE *fi;
+{
+       extern char tempMail[];
+       register FILE *nfo, *nfi;
+       register int c;
+
+       if ((nfo = Fopen(tempMail, "w")) == NULL) {
+               perror(tempMail);
+               return(fi);
+       }
+       if ((nfi = Fopen(tempMail, "r")) == NULL) {
+               perror(tempMail);
+               (void) Fclose(nfo);
+               return(fi);
+       }
+       (void) rm(tempMail);
+       (void) puthead(hp, nfo, GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA);
+       c = getc(fi);
+       while (c != EOF) {
+               (void) putc(c, nfo);
+               c = getc(fi);
+       }
+       if (ferror(fi)) {
+               perror("read");
+               rewind(fi);
+               return(fi);
+       }
+       (void) fflush(nfo);
+       if (ferror(nfo)) {
+               perror(tempMail);
+               (void) Fclose(nfo);
+               (void) Fclose(nfi);
+               rewind(fi);
+               return(fi);
+       }
+       (void) Fclose(nfo);
+       (void) Fclose(fi);
+       rewind(nfi);
+       return(nfi);
+}
+
+/*
+ * Dump the to, subject, cc header on the
+ * passed file buffer.
+ */
+puthead(hp, fo, w)
+       struct header *hp;
+       FILE *fo;
+{
+       register int gotcha;
+
+       gotcha = 0;
+       if (hp->h_to != NIL && w & GTO)
+               fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++;
+       if (hp->h_subject != NOSTR && w & GSUBJECT)
+               fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
+       if (hp->h_cc != NIL && w & GCC)
+               fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++;
+       if (hp->h_bcc != NIL && w & GBCC)
+               fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++;
+       if (gotcha && w & GNL)
+               (void) putc('\n', fo);
+       return(0);
+}
+
+/*
+ * Format the given header line to not exceed 72 characters.
+ */
+fmt(str, np, fo, comma)
+       char *str;
+       register struct name *np;
+       FILE *fo;
+       int comma;
+{
+       register col, len;
+
+       comma = comma ? 1 : 0;
+       col = strlen(str);
+       if (col)
+               fputs(str, fo);
+       for (; np != NIL; np = np->n_flink) {
+               if (np->n_flink == NIL)
+                       comma = 0;
+               len = strlen(np->n_name);
+               col++;          /* for the space */
+               if (col + len + comma > 72 && col > 4) {
+                       fputs("\n    ", fo);
+                       col = 4;
+               } else
+                       putc(' ', fo);
+               fputs(np->n_name, fo);
+               if (comma)
+                       putc(',', fo);
+               col += len + comma;
+       }
+       putc('\n', fo);
+}
+
+/*
+ * Save the outgoing mail on the passed file.
+ */
+
+/*ARGSUSED*/
+savemail(name, fi)
+       char name[];
+       register FILE *fi;
+{
+       register FILE *fo;
+       char buf[BUFSIZ];
+       register i;
+       time_t now, time();
+       char *ctime();
+
+       if ((fo = Fopen(name, "a")) == NULL) {
+               perror(name);
+               return (-1);
+       }
+       (void) time(&now);
+       fprintf(fo, "From %s %s", myname, ctime(&now));
+       while ((i = fread(buf, 1, sizeof buf, fi)) > 0)
+               (void) fwrite(buf, 1, i, fo);
+       (void) putc('\n', fo);
+       (void) fflush(fo);
+       if (ferror(fo))
+               perror(name);
+       (void) Fclose(fo);
+       rewind(fi);
+       return (0);
+}
diff --git a/usr/src/usr.bin/mail/strings.c b/usr/src/usr.bin/mail/strings.c
new file mode 100644 (file)
index 0000000..4ed7d91
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 1980 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)strings.c  5.9 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+/*
+ * Mail -- a mail program
+ *
+ * String allocation routines.
+ * Strings handed out here are reclaimed at the top of the command
+ * loop each time, so they need not be freed.
+ */
+
+#include "rcv.h"
+
+/*
+ * Allocate size more bytes of space and return the address of the
+ * first byte to the caller.  An even number of bytes are always
+ * allocated so that the space will always be on a word boundary.
+ * The string spaces are of exponentially increasing size, to satisfy
+ * the occasional user with enormous string size requests.
+ */
+
+char *
+salloc(size)
+{
+       register char *t;
+       register int s;
+       register struct strings *sp;
+       int index;
+
+       s = size;
+       s += 3;
+       s &= ~03;
+       index = 0;
+       for (sp = &stringdope[0]; sp < &stringdope[NSPACE]; sp++) {
+               if (sp->s_topFree == NOSTR && (STRINGSIZE << index) >= s)
+                       break;
+               if (sp->s_nleft >= s)
+                       break;
+               index++;
+       }
+       if (sp >= &stringdope[NSPACE])
+               panic("String too large");
+       if (sp->s_topFree == NOSTR) {
+               index = sp - &stringdope[0];
+               sp->s_topFree = malloc(STRINGSIZE << index);
+               if (sp->s_topFree == NOSTR) {
+                       fprintf(stderr, "No room for space %d\n", index);
+                       panic("Internal error");
+               }
+               sp->s_nextFree = sp->s_topFree;
+               sp->s_nleft = STRINGSIZE << index;
+       }
+       sp->s_nleft -= s;
+       t = sp->s_nextFree;
+       sp->s_nextFree += s;
+       return(t);
+}
+
+/*
+ * Reset the string area to be empty.
+ * Called to free all strings allocated
+ * since last reset.
+ */
+sreset()
+{
+       register struct strings *sp;
+       register int index;
+
+       if (noreset)
+               return;
+       index = 0;
+       for (sp = &stringdope[0]; sp < &stringdope[NSPACE]; sp++) {
+               if (sp->s_topFree == NOSTR)
+                       continue;
+               sp->s_nextFree = sp->s_topFree;
+               sp->s_nleft = STRINGSIZE << index;
+               index++;
+       }
+}
+
+/*
+ * Make the string area permanent.
+ * Meant to be called in main, after initialization.
+ */
+spreserve()
+{
+       register struct strings *sp;
+
+       for (sp = &stringdope[0]; sp < &stringdope[NSPACE]; sp++)
+               sp->s_topFree = NOSTR;
+}
diff --git a/usr/src/usr.bin/mail/temp.c b/usr/src/usr.bin/mail/temp.c
new file mode 100644 (file)
index 0000000..c03684f
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 1980 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)temp.c     5.15 (Berkeley) 2/3/91";
+#endif /* not lint */
+
+#include "rcv.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * Give names to all the temporary files that we will need.
+ */
+
+char   tempMail[24];
+char   tempQuit[24];
+char   tempEdit[24];
+char   tempResid[24];
+char   tempMesg[24];
+
+tinit()
+{
+       register char *cp;
+
+       strcpy(tempMail, _PATH_TMP);
+       mktemp(strcat(tempMail, "RsXXXXXX"));
+       strcpy(tempResid, _PATH_TMP);
+       mktemp(strcat(tempResid, "RqXXXXXX"));
+       strcpy(tempQuit, _PATH_TMP);
+       mktemp(strcat(tempQuit, "RmXXXXXX"));
+       strcpy(tempEdit, _PATH_TMP);
+       mktemp(strcat(tempEdit, "ReXXXXXX"));
+       strcpy(tempMesg, _PATH_TMP);
+       mktemp(strcat(tempMesg, "RxXXXXXX"));
+
+       /*
+        * It's okay to call savestr in here because main will
+        * do a spreserve() after us.
+        */
+       if (myname != NOSTR) {
+               if (getuserid(myname) < 0) {
+                       printf("\"%s\" is not a user of this system\n",
+                           myname);
+                       exit(1);
+               }
+       } else {
+               if ((cp = username()) == NOSTR) {
+                       myname = "ubluit";
+                       if (rcvmode) {
+                               printf("Who are you!?\n");
+                               exit(1);
+                       }
+               } else
+                       myname = savestr(cp);
+       }
+       if ((cp = getenv("HOME")) == NOSTR)
+               cp = ".";
+       homedir = savestr(cp);
+       if (debug)
+               printf("user = %s, homedir = %s\n", myname, homedir);
+}
diff --git a/usr/src/usr.bin/mail/tty.c b/usr/src/usr.bin/mail/tty.c
new file mode 100644 (file)
index 0000000..c74d127
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 1980 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)tty.c      5.12 (Berkeley) 4/1/91";
+#endif /* not lint */
+
+/*
+ * Mail -- a mail program
+ *
+ * Generally useful tty stuff.
+ */
+
+#include "rcv.h"
+
+static int     c_erase;                /* Current erase char */
+static int     c_kill;                 /* Current kill char */
+static jmp_buf rewrite;                /* Place to go when continued */
+static jmp_buf intjmp;                 /* Place to go when interrupted */
+#ifndef TIOCSTI
+static int     ttyset;                 /* We must now do erase/kill */
+#endif
+
+/*
+ * Read all relevant header fields.
+ */
+
+grabh(hp, gflags)
+       struct header *hp;
+{
+       struct sgttyb ttybuf;
+       sig_t saveint;
+#ifndef TIOCSTI
+       sig_t savequit;
+#endif
+       sig_t savetstp;
+       sig_t savettou;
+       sig_t savettin;
+       int errs;
+       void ttyint();
+
+       savetstp = signal(SIGTSTP, SIG_DFL);
+       savettou = signal(SIGTTOU, SIG_DFL);
+       savettin = signal(SIGTTIN, SIG_DFL);
+       errs = 0;
+#ifndef TIOCSTI
+       ttyset = 0;
+#endif
+       if (ioctl(fileno(stdin), TIOCGETP, &ttybuf) < 0) {
+               perror("gtty");
+               return(-1);
+       }
+       c_erase = ttybuf.sg_erase;
+       c_kill = ttybuf.sg_kill;
+#ifndef TIOCSTI
+       ttybuf.sg_erase = 0;
+       ttybuf.sg_kill = 0;
+       if ((saveint = signal(SIGINT, SIG_IGN)) == SIG_DFL)
+               signal(SIGINT, SIG_DFL);
+       if ((savequit = signal(SIGQUIT, SIG_IGN)) == SIG_DFL)
+               signal(SIGQUIT, SIG_DFL);
+#else
+       if (setjmp(intjmp))
+               goto out;
+       saveint = signal(SIGINT, ttyint);
+#endif
+       if (gflags & GTO) {
+#ifndef TIOCSTI
+               if (!ttyset && hp->h_to != NIL)
+                       ttyset++, stty(fileno(stdin), &ttybuf);
+#endif
+               hp->h_to =
+                       extract(readtty("To: ", detract(hp->h_to, 0)), GTO);
+       }
+       if (gflags & GSUBJECT) {
+#ifndef TIOCSTI
+               if (!ttyset && hp->h_subject != NOSTR)
+                       ttyset++, stty(fileno(stdin), &ttybuf);
+#endif
+               hp->h_subject = readtty("Subject: ", hp->h_subject);
+       }
+       if (gflags & GCC) {
+#ifndef TIOCSTI
+               if (!ttyset && hp->h_cc != NIL)
+                       ttyset++, stty(fileno(stdin), &ttybuf);
+#endif
+               hp->h_cc =
+                       extract(readtty("Cc: ", detract(hp->h_cc, 0)), GCC);
+       }
+       if (gflags & GBCC) {
+#ifndef TIOCSTI
+               if (!ttyset && hp->h_bcc != NIL)
+                       ttyset++, stty(fileno(stdin), &ttybuf);
+#endif
+               hp->h_bcc =
+                       extract(readtty("Bcc: ", detract(hp->h_bcc, 0)), GBCC);
+       }
+out:
+       signal(SIGTSTP, savetstp);
+       signal(SIGTTOU, savettou);
+       signal(SIGTTIN, savettin);
+#ifndef TIOCSTI
+       ttybuf.sg_erase = c_erase;
+       ttybuf.sg_kill = c_kill;
+       if (ttyset)
+               stty(fileno(stdin), &ttybuf);
+       signal(SIGQUIT, savequit);
+#endif
+       signal(SIGINT, saveint);
+       return(errs);
+}
+
+/*
+ * Read up a header from standard input.
+ * The source string has the preliminary contents to
+ * be read.
+ *
+ */
+
+char *
+readtty(pr, src)
+       char pr[], src[];
+{
+       char ch, canonb[BUFSIZ];
+       int c;
+       register char *cp, *cp2;
+       void ttystop();
+
+       fputs(pr, stdout);
+       fflush(stdout);
+       if (src != NOSTR && strlen(src) > BUFSIZ - 2) {
+               printf("too long to edit\n");
+               return(src);
+       }
+#ifndef TIOCSTI
+       if (src != NOSTR)
+               cp = copy(src, canonb);
+       else
+               cp = copy("", canonb);
+       fputs(canonb, stdout);
+       fflush(stdout);
+#else
+       cp = src == NOSTR ? "" : src;
+       while (c = *cp++) {
+               if (c == c_erase || c == c_kill) {
+                       ch = '\\';
+                       ioctl(0, TIOCSTI, &ch);
+               }
+               ch = c;
+               ioctl(0, TIOCSTI, &ch);
+       }
+       cp = canonb;
+       *cp = 0;
+#endif
+       cp2 = cp;
+       while (cp2 < canonb + BUFSIZ)
+               *cp2++ = 0;
+       cp2 = cp;
+       if (setjmp(rewrite))
+               goto redo;
+       signal(SIGTSTP, ttystop);
+       signal(SIGTTOU, ttystop);
+       signal(SIGTTIN, ttystop);
+       clearerr(stdin);
+       while (cp2 < canonb + BUFSIZ) {
+               c = getc(stdin);
+               if (c == EOF || c == '\n')
+                       break;
+               *cp2++ = c;
+       }
+       *cp2 = 0;
+       signal(SIGTSTP, SIG_DFL);
+       signal(SIGTTOU, SIG_DFL);
+       signal(SIGTTIN, SIG_DFL);
+       if (c == EOF && ferror(stdin)) {
+redo:
+               cp = strlen(canonb) > 0 ? canonb : NOSTR;
+               clearerr(stdin);
+               return(readtty(pr, cp));
+       }
+#ifndef TIOCSTI
+       if (cp == NOSTR || *cp == '\0')
+               return(src);
+       cp2 = cp;
+       if (!ttyset)
+               return(strlen(canonb) > 0 ? savestr(canonb) : NOSTR);
+       while (*cp != '\0') {
+               c = *cp++;
+               if (c == c_erase) {
+                       if (cp2 == canonb)
+                               continue;
+                       if (cp2[-1] == '\\') {
+                               cp2[-1] = c;
+                               continue;
+                       }
+                       cp2--;
+                       continue;
+               }
+               if (c == c_kill) {
+                       if (cp2 == canonb)
+                               continue;
+                       if (cp2[-1] == '\\') {
+                               cp2[-1] = c;
+                               continue;
+                       }
+                       cp2 = canonb;
+                       continue;
+               }
+               *cp2++ = c;
+       }
+       *cp2 = '\0';
+#endif
+       if (equal("", canonb))
+               return(NOSTR);
+       return(savestr(canonb));
+}
+
+/*
+ * Receipt continuation.
+ */
+void
+ttystop(s)
+{
+       sig_t old_action = signal(s, SIG_DFL);
+
+       sigsetmask(sigblock(0) & ~sigmask(s));
+       kill(0, s);
+       sigblock(sigmask(s));
+       signal(s, old_action);
+       longjmp(rewrite, 1);
+}
+
+/*ARGSUSED*/
+void
+ttyint(s)
+{
+       longjmp(intjmp, 1);
+}
diff --git a/usr/src/usr.bin/mail/v7.local.c b/usr/src/usr.bin/mail/v7.local.c
new file mode 100644 (file)
index 0000000..fb088ee
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 1980 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v7.local.c 5.12 (Berkeley) 2/3/91";
+#endif /* not lint */
+
+/*
+ * Mail -- a mail program
+ *
+ * Version 7
+ *
+ * Local routines that are installation dependent.
+ */
+
+#include "rcv.h"
+
+/*
+ * Locate the user's mailbox file (ie, the place where new, unread
+ * mail is queued).
+ */
+findmail(user, buf)
+       char *user, *buf;
+{
+       (void)sprintf(buf, "%s/%s", _PATH_MAILDIR, user);
+}
+
+/*
+ * Get rid of the queued mail.
+ */
+demail()
+{
+
+       if (value("keep") != NOSTR || rm(mailname) < 0)
+               close(creat(mailname, 0600));
+}
+
+/*
+ * Discover user login name.
+ */
+char*
+username()
+{
+       char *np;
+
+       if ((np = getenv("USER")) != NOSTR)
+               return np;
+       return getname(getuid());
+}
diff --git a/usr/src/usr.bin/mail/vars.c b/usr/src/usr.bin/mail/vars.c
new file mode 100644 (file)
index 0000000..2626aee
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 1980 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)vars.c     5.6 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+#include "rcv.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * Variable handling stuff.
+ */
+
+/*
+ * Assign a value to a variable.
+ */
+
+assign(name, value)
+       char name[], value[];
+{
+       register struct var *vp;
+       register int h;
+
+       h = hash(name);
+       vp = lookup(name);
+       if (vp == NOVAR) {
+               vp = (struct var *) calloc(sizeof *vp, 1);
+               vp->v_name = vcopy(name);
+               vp->v_link = variables[h];
+               variables[h] = vp;
+       }
+       else
+               vfree(vp->v_value);
+       vp->v_value = vcopy(value);
+}
+
+/*
+ * Free up a variable string.  We do not bother to allocate
+ * strings whose value is "" since they are expected to be frequent.
+ * Thus, we cannot free same!
+ */
+
+vfree(cp)
+       char *cp;
+{
+       if (*cp)
+               free(cp);
+}
+
+/*
+ * Copy a variable value into permanent (ie, not collected after each
+ * command) space.  Do not bother to alloc space for ""
+ */
+
+char *
+vcopy(str)
+       char str[];
+{
+       char *new;
+       unsigned len;
+
+       if (*str == '\0')
+               return "";
+       len = strlen(str) + 1;
+       if ((new = malloc(len)) == NULL)
+               panic("Out of memory");
+       bcopy(str, new, (int) len);
+       return new;
+}
+
+/*
+ * Get the value of a variable and return it.
+ * Look in the environment if its not available locally.
+ */
+
+char *
+value(name)
+       char name[];
+{
+       register struct var *vp;
+
+       if ((vp = lookup(name)) == NOVAR)
+               return(getenv(name));
+       return(vp->v_value);
+}
+
+/*
+ * Locate a variable and return its variable
+ * node.
+ */
+
+struct var *
+lookup(name)
+       register char name[];
+{
+       register struct var *vp;
+
+       for (vp = variables[hash(name)]; vp != NOVAR; vp = vp->v_link)
+               if (*vp->v_name == *name && equal(vp->v_name, name))
+                       return(vp);
+       return(NOVAR);
+}
+
+/*
+ * Locate a group name and return it.
+ */
+
+struct grouphead *
+findgroup(name)
+       register char name[];
+{
+       register struct grouphead *gh;
+
+       for (gh = groups[hash(name)]; gh != NOGRP; gh = gh->g_link)
+               if (*gh->g_name == *name && equal(gh->g_name, name))
+                       return(gh);
+       return(NOGRP);
+}
+
+/*
+ * Print a group out on stdout
+ */
+
+printgroup(name)
+       char name[];
+{
+       register struct grouphead *gh;
+       register struct group *gp;
+
+       if ((gh = findgroup(name)) == NOGRP) {
+               printf("\"%s\": not a group\n", name);
+               return;
+       }
+       printf("%s\t", gh->g_name);
+       for (gp = gh->g_list; gp != NOGE; gp = gp->ge_link)
+               printf(" %s", gp->ge_name);
+       putchar('\n');
+}
+
+/*
+ * Hash the passed string and return an index into
+ * the variable or group hash table.
+ */
+
+hash(name)
+       register char *name;
+{
+       register h = 0;
+
+       while (*name) {
+               h <<= 2;
+               h += *name++;
+       }
+       if (h < 0 && (h = -h) < 0)
+               h = 0;
+       return (h % HSHSIZE);
+}
diff --git a/usr/src/usr.bin/mail/version.c b/usr/src/usr.bin/mail/version.c
new file mode 100644 (file)
index 0000000..3b8a1ba
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 1980 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)version.c  5.5 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+/*
+ * Just keep track of the date/sid of this version of Mail.
+ * Load this file first to get a "total" Mail version.
+ */
+char   *version = "5.5 6/1/90";