--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*-
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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
+};
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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();
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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];
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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");
+}
+*/
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * 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"
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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());
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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";