Index: stable/2.2/usr.bin/mail/cmd1.c =================================================================== --- stable/2.2/usr.bin/mail/cmd1.c (revision 34261) +++ stable/2.2/usr.bin/mail/cmd1.c (revision 34262) @@ -1,451 +1,451 @@ /*- * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. 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 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #include "rcv.h" #include "extern.h" /* * Mail -- a mail program * * User commands. */ /* * Print the current active headings. * Don't change dot if invoker didn't give an argument. */ static int screen; int 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 */ int 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. */ int 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. */ int from(msgvec) int *msgvec; { register int *ip; - for (ip = msgvec; *ip != NULL; ip++) + for (ip = msgvec; *ip != 0; ip++) printhead(*ip); if (--ip >= msgvec) dot = &message[*ip - 1]; return(0); } /* * Print out the header of a specific message. * This is a slight improvement to the standard one. */ void printhead(mesg) int 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. */ int pdot() { printf("%d\n", dot - &message[0] + 1); return(0); } /* * Print out all the possible commands. */ int 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. */ int more(msgvec) int *msgvec; { return (type1(msgvec, 1, 1)); } /* * Paginate messages, even printing ignored fields. */ int More(msgvec) int *msgvec; { return (type1(msgvec, 0, 1)); } /* * Type out messages, honor ignored fields. */ int type(msgvec) int *msgvec; { return(type1(msgvec, 1, 0)); } /* * Type out messages, even printing ignored fields. */ int Type(msgvec) int *msgvec; { return(type1(msgvec, 0, 0)); } /* * Type out the messages requested. */ jmp_buf pipestop; int type1(msgvec, doign, page) int *msgvec; int doign, page; { register *ip; register struct message *mp; register char *cp; int nlines; FILE *obuf; 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(signo) int signo; { 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. */ int 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. */ int 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. */ int 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. */ int 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, NOSTR); return 0; } Index: stable/2.2/usr.bin/mail/cmd2.c =================================================================== --- stable/2.2/usr.bin/mail/cmd2.c (revision 34261) +++ stable/2.2/usr.bin/mail/cmd2.c (revision 34262) @@ -1,530 +1,530 @@ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. 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 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #include "rcv.h" #include #include "extern.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. */ int next(msgvec) int *msgvec; { register struct message *mp; register int *ip, *ip2; int list[2], mdot; - if (*msgvec != NULL) { + if (*msgvec != 0) { /* * If some messages were supplied, find the * first applicable one following dot using * wrap around. */ mdot = dot - &message[0] + 1; /* * Find the first message in the supplied * message list which follows dot. */ - for (ip = msgvec; *ip != NULL; ip++) + for (ip = msgvec; *ip != 0; ip++) if (*ip > mdot) break; - if (*ip == NULL) + if (*ip == 0) ip = msgvec; ip2 = ip; do { mp = &message[*ip2 - 1]; if ((mp->m_flag & MDELETED) == 0) { dot = mp; goto hitit; } - if (*ip2 != NULL) + if (*ip2 != 0) ip2++; - if (*ip2 == NULL) + if (*ip2 == 0) 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; + list[1] = 0; return(type(list)); } /* * Save a message in a file. Mark the message as saved * so we can discard when the user quits. */ int save(str) char str[]; { return save1(str, 1, "save", saveignore); } /* * Copy a message to a file without affected its saved-ness */ int 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." */ int save1(str, mark, cmd, ignore) char str[]; int mark; 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) { + if (*msgvec == 0) { printf("No messages to %s.\n", cmd); return(1); } - msgvec[1] = NULL; + msgvec[1] = 0; } 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. */ int 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. */ int delete(msgvec) int msgvec[]; { delm(msgvec); return 0; } /* * Delete messages, then type the new dot. */ int 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; + list[1] = 0; 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. */ int delm(msgvec) int *msgvec; { register struct message *mp; register *ip; int last; - last = NULL; - for (ip = msgvec; *ip != NULL; ip++) { + last = 0; + for (ip = msgvec; *ip != 0; ip++) { mp = &message[*ip - 1]; touch(mp); mp->m_flag |= MDELETED|MTOUCH; mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX); last = *ip; } - if (last != NULL) { + if (last != 0) { dot = &message[last-1]; last = first(0, MDELETED); - if (last != NULL) { + if (last != 0) { 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. */ int undelete_messages(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" */ int 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. */ int 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. */ void clob1(n) int 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. */ int 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. */ int igfield(list) char *list[]; { return ignore1(list, ignore, "ignored"); } int saveretfield(list) char *list[]; { return ignore1(list, saveignore + 1, "retained"); } int saveigfield(list) char *list[]; { return ignore1(list, saveignore, "ignored"); } int 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. */ int 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(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. */ int igcomp(l, r) const void *l, *r; { return (strcmp(*(char **)l, *(char **)r)); } Index: stable/2.2/usr.bin/mail/cmd3.c =================================================================== --- stable/2.2/usr.bin/mail/cmd3.c (revision 34261) +++ stable/2.2/usr.bin/mail/cmd3.c (revision 34262) @@ -1,730 +1,738 @@ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. 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 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #include "rcv.h" #include "extern.h" /* * Mail -- a mail program * * Still more user commands. */ /* * Process a shell escape by saving signals, ignoring signals, * and forking a sh -c */ int 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*/ int 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, NOSTR, 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]; int 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. */ int 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. */ int 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; } int respond(msgvec) int *msgvec; { if (value("Replyall") == NOSTR) - return (_respond(msgvec)); + return (dorespond(msgvec)); else - return (_Respond(msgvec)); + return (doRespond(msgvec)); } /* * Reply to a list of messages. Extract each name from the * message header and send them off to mail1() */ int -_respond(msgvec) +dorespond(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; + if ((head.h_replyto = getenv("REPLYTO")) == NULL) + head.h_replyto = NOSTR; + head.h_inreplyto = skin(hfield("message-id", mp)); 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. */ int 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++) { + for (ip = msgvec; *ip != 0; ip++) { mesg = *ip; mp = &message[mesg-1]; mp->m_flag |= MPRESERVE; mp->m_flag &= ~MBOX; dot = mp; } return(0); } /* * Mark all given messages as unread. */ int unread(msgvec) int msgvec[]; { register int *ip; - for (ip = msgvec; *ip != NULL; ip++) { + for (ip = msgvec; *ip != 0; ip++) { dot = &message[*ip-1]; dot->m_flag &= ~(MREAD|MTOUCH); dot->m_flag |= MSTATUS; } return(0); } /* * Print the size of each message. */ int messize(msgvec) int *msgvec; { register struct message *mp; register int *ip, mesg; - for (ip = msgvec; *ip != NULL; ip++) { + for (ip = msgvec; *ip != 0; 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. */ int rexit(e) int e; { if (sourcing) return(1); exit(e); /*NOTREACHED*/ } /* * Set or display a variable value. Syntax is similar to that * of csh. */ int 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. */ int 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); free((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); free((char *) vp2); } return(errs); } /* * Put add users to a group. */ int 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. */ void sort(list) char **list; { register char **ap; int diction(); for (ap = list; *ap != NOSTR; ap++) ; if (ap-list < 2) return; qsort(list, ap-list, sizeof(*list), diction); } /* * Do a dictionary order comparison of the arguments from * qsort. */ int diction(a, b) const void *a, *b; { return(strcmp(*(char **)a, *(char **)b)); } /* * The do nothing command for comments. */ /*ARGSUSED*/ int null(e) int e; { return 0; } /* * Change to another file. With no argument, print information about * the current file. */ int 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 */ int 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; } int Respond(msgvec) int *msgvec; { if (value("Replyall") == NOSTR) - return (_Respond(msgvec)); + return (doRespond(msgvec)); else - return (_respond(msgvec)); + return (dorespond(msgvec)); } /* * Reply to a series of messages by simply mailing to the senders * and not messing around with the To: and Cc: lists as in normal * reply. */ int -_Respond(msgvec) +doRespond(msgvec) int msgvec[]; { struct header head; struct message *mp; register int *ap; register char *cp; + char *mid; 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)); + mid = skin(hfield("message-id", mp)); } 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; + if ((head.h_replyto = getenv("REPLYTO")) == NULL) + head.h_replyto = NOSTR; + head.h_inreplyto = mid; mail1(&head, 1); return 0; } /* * Conditional commands. These allow one to parameterize one's * .mailrc and do some things if sending, others if receiving. */ int ifcmd(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. */ int 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. */ int endifcmd() { if (cond == CANY) { printf("\"Endif\" without matching \"if\"\n"); return(1); } cond = CANY; return(0); } /* * Set the list of alternate names. */ int 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) free((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); } Index: stable/2.2/usr.bin/mail/collect.c =================================================================== --- stable/2.2/usr.bin/mail/collect.c (revision 34261) +++ stable/2.2/usr.bin/mail/collect.c (revision 34262) @@ -1,635 +1,644 @@ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. 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 8.2 (Berkeley) 4/19/94"; #endif /* not lint */ /* * Mail -- a mail program * * Collect input from standard input, handling * ~ escapes. */ #include "rcv.h" #include "extern.h" /* * Read a message from standard output and return a read file to it * or NULL on error. */ /* * The following hokiness with global variables is so that on * receipt of an interrupt signal, the partial message can be salted * away on dead.letter. */ static 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; int printheaders; { 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. + * Set the Subject line. */ cp = &linebuf[2]; while (isspace(*cp)) cp++; hp->h_subject = savestr(cp); break; + case 'R': + /* + * Set the Reply-To line. + */ + cp = &linebuf[2]; + while (isspace(*cp)) + cp++; + hp->h_replyto = 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. */ int exwrite(name, fp, f) char name[]; FILE *fp; int f; { 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. */ void mesedit(fp, c) FILE *fp; int c; { sig_t sigint = signal(SIGINT, SIG_IGN); FILE *nf = run_editor(fp, (off_t)-1, c, 0); if (nf != NULL) { fseek(nf, 0L, 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. */ void mespipe(fp, cmd) FILE *fp; char cmd[]; { FILE *nf; sig_t sigint = signal(SIGINT, SIG_IGN); extern char tempEdit[]; char *shell; if ((nf = Fopen(tempEdit, "w+")) == NULL) { perror(tempEdit); goto out; } (void) unlink(tempEdit); /* * stdin = current message. * stdout = new message. */ if ((shell = value("SHELL")) == NOSTR) shell = _PATH_CSHELL; if (run_command(shell, 0, fileno(fp), fileno(nf), "-c", cmd, 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. */ int forward(ms, fp, f) char ms[]; FILE *fp; int f; { 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) { + if (*msgvec == 0) { printf("No appropriate messages\n"); return(0); } - msgvec[1] = NULL; + msgvec[1] = 0; } 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) int 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) int 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) int s; { rewind(collf); savedeadletter(collf); /* * Let's pretend nobody else wants to clean up, * a true statement at this time. */ exit(1); } void 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); } Index: stable/2.2/usr.bin/mail/def.h =================================================================== --- stable/2.2/usr.bin/mail/def.h (revision 34261) +++ stable/2.2/usr.bin/mail/def.h (revision 34262) @@ -1,276 +1,280 @@ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. 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 8.2 (Berkeley) 3/21/94 */ /* * Mail -- a mail program * * Author: Kurt Shoens (UCB) March 25, 1978 */ #include #include #include #include #include #include #include #include #include #include #include "pathnames.h" #define APPEND /* New mail goes to end of mailbox */ #define ESCAPE '~' /* Default escape for sending */ #define NMLSIZE 1024 /* max names in a message list */ #define PATHSIZE 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_offset; /* offset in block of message */ long m_block; /* block number of this message */ long m_size; /* Bytes in the message */ long 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) +#define GREPLYTO 0x10 /* And the Reply-To: line */ +#define GINREPLYTO 0x20 /* The In-Reply-To: line */ +#define GMASK (GTO|GSUBJECT|GCC|GBCC|GREPLYTO|GINREPLYTO) /* 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 */ +#define GNL 0x40 /* Print blank line after */ +#define GDEL 0x80 /* Entity removed from list */ +#define GCOMMA 0x100 /* 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 */ + char *h_replyto; /* Reply address */ + char *h_inreplyto; /* Reference */ 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) { \ (void)fflush(stream); \ (void)ftruncate(fileno(stream), (long)ftell(stream)); \ } Index: stable/2.2/usr.bin/mail/extern.h =================================================================== --- stable/2.2/usr.bin/mail/extern.h (revision 34261) +++ stable/2.2/usr.bin/mail/extern.h (revision 34262) @@ -1,253 +1,253 @@ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. 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. * * @(#)extern.h 8.1 (Berkeley) 6/6/93 */ struct name *cat __P((struct name *, struct name *)); struct name *delname __P((struct name *, char [])); struct name *elide __P((struct name *)); struct name *extract __P((char [], int)); struct name *gexpand __P((struct name *, struct grouphead *, int, int)); struct name *nalloc __P((char [], int)); struct name *outof __P((struct name *, FILE *, struct header *)); struct name *put __P((struct name *, struct name *)); struct name *tailof __P((struct name *)); struct name *usermap __P((struct name *)); FILE *Fdopen __P((int, char *)); FILE *Fopen __P((char *, char *)); FILE *Popen __P((char *, char *)); FILE *collect __P((struct header *, int)); char *copy __P((char *, char *)); char *copyin __P((char *, char **)); char *detract __P((struct name *, int)); char *expand __P((char *)); char *getdeadletter __P((void)); char *getname __P((int)); char *hfield __P((char [], struct message *)); FILE *infix __P((struct header *, FILE *)); char *ishfield __P((char [], char[], char *)); char *name1 __P((struct message *, int)); char *nameof __P((struct message *, int)); char *nextword __P((char *, char *)); char *readtty __P((char [], char [])); char *reedit __P((char *)); FILE *run_editor __P((FILE *, off_t, int, int)); char *salloc __P((int)); char *savestr __P((char *)); FILE *setinput __P((struct message *)); char *skin __P((char *)); char *skip_comment __P((char *)); char *snarf __P((char [], int *)); char *username __P((void)); char *value __P((char [])); char *vcopy __P((char [])); char *yankword __P((char *, char [])); int Fclose __P((FILE *)); int More __P((int *)); int Pclose __P((FILE *)); int Respond __P((int *)); int Type __P((int *)); -int _Respond __P((int [])); -int _respond __P((int *)); +int doRespond __P((int [])); +int dorespond __P((int *)); void alter __P((char *)); int alternates __P((char **)); void announce __P((void)); int anyof __P((char *, char *)); int append __P((struct message *, FILE *)); int argcount __P((char **)); void assign __P((char [], char [])); int bangexp __P((char *)); int blankline __P((char [])); void brokpipe __P((int)); int charcount __P((char *, int)); int check __P((int, int)); void clob1 __P((int)); int clobber __P((char **)); void close_all_files __P((void)); int cmatch __P((char *, char *)); void collhup __P((int)); void collint __P((int)); void collstop __P((int)); void commands __P((void)); int copycmd __P((char [])); int core __P((void)); int count __P((struct name *)); int delete __P((int [])); int delm __P((int [])); int deltype __P((int [])); void demail __P((void)); int diction __P((const void *, const void *)); int dosh __P((char *)); int echo __P((char **)); int edit1 __P((int *, int)); int editor __P((int *)); void edstop __P((void)); int elsecmd __P((void)); int endifcmd __P((void)); int evalcol __P((int)); int execute __P((char [], int)); int exwrite __P((char [], FILE *, int)); void fail __P((char [], char [])); int file __P((char **)); struct grouphead * findgroup __P((char [])); void findmail __P((char *, char *)); int first __P((int, int)); void fixhead __P((struct header *, struct name *)); void fmt __P((char *, struct name *, FILE *, int)); int folders __P((void)); int forward __P((char [], FILE *, int)); void free_child __P((int)); int from __P((int *)); off_t fsize __P((FILE *)); int getfold __P((char *)); int gethfield __P((FILE *, char [], int, char **)); int getmsglist __P((char *, int *, int)); int getrawlist __P((char [], char **, int)); int getuserid __P((char [])); int grabh __P((struct header *, int)); int group __P((char **)); void hangup __P((int)); int hash __P((char *)); void hdrstop __P((int)); int headers __P((int *)); int help __P((void)); void holdsigs __P((void)); int ifcmd __P((char **)); int igcomp __P((const void *, const void *)); int igfield __P((char *[])); int ignore1 __P((char *[], struct ignoretab *, char *)); int igshow __P((struct ignoretab *, char *)); void intr __P((int)); int isdate __P((char [])); int isdir __P((char [])); int isfileaddr __P((char *)); int ishead __P((char [])); int isign __P((char *, struct ignoretab [])); int isprefix __P((char *, char *)); void istrcpy __P((char *, char *)); struct cmd * lex __P((char [])); void load __P((char *)); struct var * lookup __P((char [])); int mail __P((struct name *, - struct name *, struct name *, struct name *, char *)); + struct name *, struct name *, struct name *, char *, char *)); void mail1 __P((struct header *, int)); void makemessage __P((FILE *)); void mark __P((int)); int markall __P((char [], int)); int matchsender __P((char *, int)); int matchsubj __P((char *, int)); int mboxit __P((int [])); int member __P((char *, struct ignoretab *)); void mesedit __P((FILE *, int)); void mespipe __P((FILE *, char [])); int messize __P((int *)); int metamess __P((int, int)); int more __P((int *)); int newfileinfo __P((void)); int next __P((int *)); int null __P((int)); void panic __P((const char *, ...)); void parse __P((char [], struct headline *, char [])); int pcmdlist __P((void)); int pdot __P((void)); void prepare_child __P((int, int, int)); int preserve __P((int *)); void prettyprint __P((struct name *)); void printgroup __P((char [])); void printhead __P((int)); int puthead __P((struct header *, FILE *, int)); int putline __P((FILE *, char *)); int pversion __P((int)); void quit __P((void)); int quitcmd __P((void)); int raise __P((int)); int readline __P((FILE *, char *, int)); void register_file __P((FILE *, int, int)); void regret __P((int)); void relsesigs __P((void)); int respond __P((int *)); int retfield __P((char *[])); int rexit __P((int)); int rm __P((char *)); int run_command __P((char *, int, int, int, char *, char *, char *)); int save __P((char [])); int save1 __P((char [], int, char *, struct ignoretab *)); void savedeadletter __P((FILE *)); int saveigfield __P((char *[])); int savemail __P((char [], FILE *)); int saveretfield __P((char *[])); int scan __P((char **)); void scaninit __P((void)); int schdir __P((char **)); int screensize __P((void)); int scroll __P((char [])); int send __P((struct message *, FILE *, struct ignoretab *, char *)); int sendmail __P((char *)); int set __P((char **)); int setfile __P((char *)); void setmsize __P((int)); void setptr __P((FILE *)); void setscreensize __P((void)); int shell __P((char *)); void sigchild __P((int)); void sort __P((char **)); int source __P((char **)); void spreserve __P((void)); void sreset __P((void)); int start_command __P((char *, int, int, int, char *, char *, char *)); void statusput __P((struct message *, FILE *, char *)); void stop __P((int)); int stouch __P((int [])); int swrite __P((char [])); void tinit __P((void)); int top __P((int *)); void touch __P((struct message *)); void ttyint __P((int)); void ttystop __P((int)); int type __P((int *)); int type1 __P((int *, int, int)); int undelete_messages __P((int *)); void unmark __P((int)); char **unpack __P((struct name *)); int unread __P((int [])); void unregister_file __P((FILE *)); int unset __P((char **)); int unstack __P((void)); void vfree __P((char *)); int visual __P((int *)); int wait_child __P((int)); int wait_command __P((int)); int writeback __P((FILE *)); Index: stable/2.2/usr.bin/mail/lex.c =================================================================== --- stable/2.2/usr.bin/mail/lex.c (revision 34261) +++ stable/2.2/usr.bin/mail/lex.c (revision 34262) @@ -1,676 +1,676 @@ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. 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 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #include "rcv.h" #include #include #include "extern.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. */ int 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); } (void) fcntl(fileno(otf), F_SETFD, 1); if ((itf = fopen(tempMesg, "r")) == NULL) { perror(tempMesg); exit(1); } (void) fcntl(fileno(itf), F_SETFD, 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. */ void 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. */ int execute(linebuf, contxt) char linebuf[]; int contxt; { 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; + msgvec[1] = 0; } - if (*msgvec == NULL) { + if (*msgvec == 0) { 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. */ void setmsize(sz) int sz; { if (msgvec != 0) free((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[]; /* * ignore trailing chars after `#' * * lines with beginning `#' are comments * spaces befor `#' are ignored in execute() */ if (*word == '#') *(word+1) = '\0'; 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. */ int 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) int 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) int 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) int s; { /* nothing to do? */ exit(1); } /* * Announce the presence of the current Mail version, * give the message count, and print a header listing. */ void 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. */ int 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*/ int pversion(e) int e; { extern char *version; printf("Version %s\n", version); return(0); } /* * Load a file of user definitions. */ void 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); } Index: stable/2.2/usr.bin/mail/mail.1 =================================================================== --- stable/2.2/usr.bin/mail/mail.1 (revision 34261) +++ stable/2.2/usr.bin/mail/mail.1 (revision 34262) @@ -1,1046 +1,1053 @@ .\" Copyright (c) 1980, 1990, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. 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. .\" .\" @(#)mail.1 8.2 (Berkeley) 12/30/93 -.\" $Id: mail.1,v 1.8.2.2 1997/09/15 08:41:46 jkh Exp $ +.\" $Id: mail.1,v 1.8.2.3 1998/01/05 00:02:15 jraynard Exp $ .\" .Dd December 30, 1993 .Dt MAIL 1 .Os BSD 4 .Sh NAME .Nm mail .Nd send and receive mail .Sh SYNOPSIS .Nm mail .Op Fl iInv .Op Fl s Ar subject .Op Fl c Ar cc-addr .Op Fl b Ar bcc-addr .Ar to-addr... .Nm mail .Op Fl iInNv .Fl f .Op Ar name .Nm mail .Op Fl iInNv .Op Fl u Ar user .Sh INTRODUCTION .Nm Mail is an intelligent mail processing system, which has a command syntax reminiscent of .Xr \&ed 1 with lines replaced by messages. .Pp .Bl -tag -width flag .It Fl v Verbose mode. The details of delivery are displayed on the user's terminal. .It Fl i Ignore tty interrupt signals. This is particularly useful when using .Nm mail on noisy phone lines. .It Fl I Forces mail to run in interactive mode even when input isn't a terminal. In particular, the .Sq Ic \&~ special character when sending mail is only active in interactive mode. .It Fl n Inhibits reading the system-wide .Pa mail.rc files upon startup. .It Fl N Inhibits the initial display of message headers when reading mail or editing a mail folder. .It Fl s Specify subject on command line (only the first argument after the .Fl s flag is used as a subject; be careful to quote subjects containing spaces.) .It Fl c Send carbon copies to .Ar list of users. .It Fl b Send blind carbon copies to .Ar list . List should be a comma-separated list of names. .It Fl f Read in the contents of your .Ar mbox (or the specified file) for processing; when you .Ar quit , .Nm mail writes undeleted messages back to this file. .It Fl u Is equivalent to: .Pp .Dl mail -f /var/mail/user .El .Ss Sending mail To send a message to one or more people, .Nm mail can be invoked with arguments which are the names of people to whom the mail will be sent. You are then expected to type in your message, followed by a .Sq Li control\-D at the beginning of a line. The section below .Ar Replying to or originating mail , describes some features of .Nm mail available to help you compose your letter. .Pp .Ss Reading mail In normal usage .Nm mail is given no arguments and checks your mail out of the post office, then prints out a one line header of each message found. The current message is initially the first message (numbered 1) and can be printed using the .Ic print command (which can be abbreviated .Ql Ic p ) . You can move among the messages much as you move between lines in .Xr \&ed 1 , with the commands .Ql Ic \&+ and .Ql Ic \&\- moving backwards and forwards, and simple numbers. .Pp .Ss Disposing of mail. After examining a message you can .Ic delete .Pq Ic d the message or .Ic reply .Pq Ic r to it. Deletion causes the .Nm mail program to forget about the message. This is not irreversible; the message can be .Ic undeleted .Pq Ic u by giving its number, or the .Nm mail session can be aborted by giving the .Ic exit .Pq Ic x command. Deleted messages will, however, usually disappear never to be seen again. .Pp .Ss Specifying messages Commands such as .Ic print and .Ic delete can be given a list of message numbers as arguments to apply to a number of messages at once. Thus .Dq Li delete 1 2 deletes messages 1 and 2, while .Dq Li delete 1\-5 deletes messages 1 through 5. The special name .Ql Li \&* addresses all messages, and .Ql Li \&$ addresses the last message; thus the command .Ic top which prints the first few lines of a message could be used in .Dq Li top \&* to print the first few lines of all messages. .Pp .Ss Replying to or originating mail. You can use the .Ic reply command to set up a response to a message, sending it back to the person who it was from. Text you then type in, up to an end-of-file, defines the contents of the message. While you are composing a message, .Nm mail treats lines beginning with the character .Ql Ic \&~ specially. For instance, typing .Ql Ic \&~m (alone on a line) will place a copy of the current message into the response right shifting it by a tabstop (see .Em indentprefix variable, below). Other escapes will set up subject fields, add and delete recipients to the message and allow you to escape to an editor to revise the message or to a shell to run some commands. (These options are given in the summary below.) .Pp .Ss Ending a mail processing session. You can end a .Nm mail session with the .Ic quit .Pq Ic q command. Messages which have been examined go to your .Ar mbox file unless they have been deleted in which case they are discarded. Unexamined messages go back to the post office. (See the .Fl f option above). .Pp .Ss Personal and system wide distribution lists. It is also possible to create a personal distribution lists so that, for instance, you can send mail to .Dq Li cohorts and have it go to a group of people. Such lists can be defined by placing a line like .Pp .Dl alias cohorts bill ozalp jkf mark kridle@ucbcory .Pp in the file .Pa \&.mailrc in your home directory. The current list of such aliases can be displayed with the .Ic alias command in .Nm mail . System wide distribution lists can be created by editing .Pa /etc/aliases , see .Xr aliases 5 and .Xr sendmail 8 ; these are kept in a different syntax. In mail you send, personal aliases will be expanded in mail sent to others so that they will be able to .Ic reply to the recipients. System wide .Ic aliases are not expanded when the mail is sent, but any reply returned to the machine will have the system wide alias expanded as all mail goes through .Xr sendmail . .Pp .Ss Network mail (ARPA, UUCP, Berknet) See .Xr mailaddr 7 for a description of network addresses. .Pp .Nm Mail has a number of options which can be set in the .Pa .mailrc file to alter its behavior; thus .Dq Li set askcc enables the .Ar askcc feature. (These options are summarized below.) .Sh SUMMARY (Adapted from the `Mail Reference Manual') .Pp Each command is typed on a line by itself, and may take arguments following the command word. The command need not be typed in its entirety \- the first command which matches the typed prefix is used. For commands which take message lists as arguments, if no message list is given, then the next message forward which satisfies the command's requirements is used. If there are no messages forward of the current message, the search proceeds backwards, and if there are no good messages at all, .Nm mail types .Dq Li No applicable messages and aborts the command. .Bl -tag -width delete .It Ic \&\- Print out the preceding message. If given a numeric argument .Ar n , goes to the .Ar n Ns 'th previous message and prints it. .It Ic \&# ignore the remainder of the line as a comment. .It Ic \&? Prints a brief summary of commands. .It Ic \&! Executes the shell (see .Xr sh 1 and .Xr csh 1 ) command which follows. .It Ic Print .Pq Ic P Like .Ic print but also prints out ignored header fields. See also .Ic print , .Ic ignore and .Ic retain . .It Ic Reply .Pq Ic R Reply to originator. Does not reply to other recipients of the original message. .It Ic Type .Pq Ic T Identical to the .Ic Print command. .It Ic alias .Pq Ic a With no arguments, prints out all currently-defined aliases. With one argument, prints out that alias. With more than one argument, creates a new alias or changes an old one. .It Ic alternates .Pq Ic alt The .Ic alternates command is useful if you have accounts on several machines. It can be used to inform .Nm mail that the listed addresses are really you. When you .Ic reply to messages, .Nm mail will not send a copy of the message to any of the addresses listed on the .Ic alternates list. If the .Ic alternates command is given with no argument, the current set of alternate names is displayed. .It Ic chdir .Pq Ic c Changes the user's working directory to that specified, if given. If no directory is given, then changes to the user's login directory. .It Ic copy .Pq Ic co The .Ic copy command does the same thing that .Ic save does, except that it does not mark the messages it is used on for deletion when you quit. .It Ic delete .Pq Ic d Takes a list of messages as argument and marks them all as deleted. Deleted messages will not be saved in .Ar mbox , nor will they be available for most other commands. .It Ic dp (also .Ic dt ) Deletes the current message and prints the next message. If there is no next message, .Nm mail says .Dq Li "at EOF" . .It Ic edit .Pq Ic e Takes a list of messages and points the text editor at each one in turn. On return from the editor, the message is read back in. .It Ic exit .Pf ( Ic ex or .Ic x ) Effects an immediate return to the Shell without modifying the user's system mailbox, his .Ar mbox file, or his edit file in .Fl f . .It Ic file .Pq Ic fi The same as .Ic folder . .It Ic folders List the names of the folders in your folder directory. .It Ic folder .Pq Ic fo The .Ic folder command switches to a new mail file or folder. With no arguments, it tells you which file you are currently reading. If you give it an argument, it will write out changes (such as deletions) you have made in the current file and read in the new file. Some special conventions are recognized for the name. # means the previous file, % means your system mailbox, %user means user's system mailbox, & means your .Ar mbox file, and \&+\&folder means a file in your folder directory. .It Ic from .Pq Ic f Takes a list of messages and prints their message headers. .It Ic headers .Pq Ic h Lists the current range of headers, which is an 18\-message group. If a .Ql \&+ argument is given, then the next 18\-message group is printed, and if a .Ql \&\- argument is given, the previous 18\-message group is printed. .It Ic help A synonym for .Ic \&? .It Ic hold .Pf ( Ic ho , also .Ic preserve ) Takes a message list and marks each message therein to be saved in the user's system mailbox instead of in .Ar mbox . Does not override the .Ic delete command. .It Ic ignore Add the list of header fields named to the .Ar ignored list . Header fields in the ignore list are not printed on your terminal when you print a message. This command is very handy for suppression of certain machine-generated header fields. The .Ic Type and .Ic Print commands can be used to print a message in its entirety, including ignored fields. If .Ic ignore is executed with no arguments, it lists the current set of ignored fields. .It Ic mail .Pq Ic m Takes as argument login names and distribution group names and sends mail to those people. .It Ic more .Pq Ic \mo Takes a list of messages and invokes the pager on that list. .It Ic mbox Indicate that a list of messages be sent to .Ic mbox in your home directory when you quit. This is the default action for messages if you do .Em not have the .Ic hold option set. .It Ic next .Pq Ic n like ( .Ic \&+ or .Tn CR ) Goes to the next message in sequence and types it. With an argument list, types the next matching message. .It Ic preserve .Pq Ic pre A synonym for .Ic hold . .It Ic print .Pq Ic p Takes a message list and types out each message on the user's terminal. .It Ic quit .Pq Ic q Terminates the session, saving all undeleted, unsaved messages in the user's .Ar mbox file in his login directory, preserving all messages marked with .Ic hold or .Ic preserve or never referenced in his system mailbox, and removing all other messages from his system mailbox. If new mail has arrived during the session, the message .Dq Li "You have new mail" is given. If given while editing a mailbox file with the .Fl f flag, then the edit file is rewritten. A return to the Shell is effected, unless the rewrite of edit file fails, in which case the user can escape with the .Ic exit command. .It Ic reply .Pq Ic r Takes a message list and sends mail to the sender and all recipients of the specified message. The default message must not be deleted. .It Ic respond A synonym for .Ic reply . .It Ic retain Add the list of header fields named to the .Ar retained list Only the header fields in the retain list are shown on your terminal when you print a message. All other header fields are suppressed. The .Ic Type and .Ic Print commands can be used to print a message in its entirety. If .Ic retain is executed with no arguments, it lists the current set of retained fields. .It Ic save .Pq Ic s Takes a message list and a filename and appends each message in turn to the end of the file. The filename in quotes, followed by the line count and character count is echoed on the user's terminal. .It Ic set .Pq Ic se With no arguments, prints all variable values. Otherwise, sets option. Arguments are of the form .Ar option=value (no space before or after =) or .Ar option . Quotation marks may be placed around any part of the assignment statement to quote blanks or tabs, i.e. .Dq Li "set indentprefix=\*q->\*q" .It Ic saveignore .Ic Saveignore is to .Ic save what .Ic ignore is to .Ic print and .Ic type . Header fields thus marked are filtered out when saving a message by .Ic save or when automatically saving to .Ar mbox . .It Ic saveretain .Ic Saveretain is to .Ic save what .Ic retain is to .Ic print and .Ic type . Header fields thus marked are the only ones saved with a message when saving by .Ic save or when automatically saving to .Ar mbox . .Ic Saveretain overrides .Ic saveignore . .It Ic shell .Pq Ic sh Invokes an interactive version of the shell. .It Ic size Takes a message list and prints out the size in characters of each message. .It Ic source The .Ic source command reads commands from a file. .It Ic top Takes a message list and prints the top few lines of each. The number of lines printed is controlled by the variable .Ic toplines and defaults to five. .It Ic type .Pq Ic t A synonym for .Ic print . .It Ic unalias Takes a list of names defined by .Ic alias commands and discards the remembered groups of users. The group names no longer have any significance. .It Ic undelete .Pq Ic u Takes a message list and marks each message as .Ic not being deleted. .It Ic unread .Pq Ic U Takes a message list and marks each message as .Ic not having been read. .It Ic unset Takes a list of option names and discards their remembered values; the inverse of .Ic set . .It Ic visual .Pq Ic v Takes a message list and invokes the display editor on each message. .It Ic write .Pq Ic w Similar to .Ic save , except that .Ic only the message body .Pq Ar without the header) is saved. Extremely useful for such tasks as sending and receiving source program text over the message system. .It Ic xit .Pq Ic x A synonym for .Ic exit . .It Ic z .Nm Mail presents message headers in windowfuls as described under the .Ic headers command. You can move .Nm mail Ns 's attention forward to the next window with the .Ic \&z command. Also, you can move to the previous window by using .Ic \&z\&\- . .El .Ss Tilde/Escapes .Pp Here is a summary of the tilde escapes, which are used when composing messages to perform special functions. Tilde escapes are only recognized at the beginning of lines. The name .Dq Em tilde\ escape is somewhat of a misnomer since the actual escape character can be set by the option .Ic escape . .Bl -tag -width Ds .It Ic \&~! Ns Ar command Execute the indicated shell command, then return to the message. .It Ic \&~b Ns Ar name ... Add the given names to the list of carbon copy recipients but do not make the names visible in the Cc: line ("blind" carbon copy). .It Ic \&~c Ns Ar name ... Add the given names to the list of carbon copy recipients. .It Ic \&~d Read the file .Dq Pa dead.letter from your home directory into the message. .It Ic \&~e Invoke the text editor on the message collected so far. After the editing session is finished, you may continue appending text to the message. .It Ic \&~f Ns Ar messages Read the named messages into the message being sent. If no messages are specified, read in the current message. Message headers currently being ignored (by the .Ic ignore or .Ic retain command) are not included. .It Ic \&~F Ns Ar messages Identical to .Ic \&~f , except all message headers are included. .It Ic \&~h Edit the message header fields by typing each one in turn and allowing the user to append text to the end or modify the field by using the current terminal erase and kill characters. .It Ic \&~m Ns Ar messages Read the named messages into the message being sent, indented by a tab or by the value of .Ar indentprefix . If no messages are specified, read the current message. Message headers currently being ignored (by the .Ic ignore or .Ic retain command) are not included. .It Ic \&~M Ns Ar messages Identical to .Ic \&~m , except all message headers are included. .It Ic \&~p Print out the message collected so far, prefaced by the message header fields. .It Ic \&~q Abort the message being sent, copying the message to .Dq Pa dead.letter in your home directory if .Ic save is set. .It Ic \&~r Ns Ar filename Read the named file into the message. +.It Ic \&~R Ns Ar string +Use +.Ar string +as the Reply-To field. .It Ic \&~s Ns Ar string Cause the named string to become the current subject field. .It Ic \&~\&t Ns Ar name ... Add the given names to the direct recipient list. .It Ic \&~\&v Invoke an alternate editor (defined by the .Ev VISUAL option) on the message collected so far. Usually, the alternate editor will be a screen editor. After you quit the editor, you may resume appending text to the end of your message. .It Ic \&~w Ns Ar filename Write the message onto the named file. .It Ic \&~\&| Ns Ar command Pipe the message through the command as a filter. If the command gives no output or terminates abnormally, retain the original text of the message. The command .Xr fmt 1 is often used as .Ic command to rejustify the message. .It Ic \&~: Ns Ar mail-command Execute the given mail command. Not all commands, however, are allowed. .It Ic \&~~ Ns Ar string Insert the string of text in the message prefaced by a single ~. If you have changed the escape character, then you should double that character in order to send it. .El .Ss Mail Options Options are controlled via .Ic set and .Ic unset commands. Options may be either binary, in which case it is only significant to see whether they are set or not; or string, in which case the actual value is of interest. The binary options include the following: .Bl -tag -width append .It Ar append Causes messages saved in .Ar mbox to be appended to the end rather than prepended. This should always be set (preferably in one of the system-wide .Pa mail.rc files). .It Ar ask Causes .Nm mail to prompt you for the subject of each message you send. If you respond with simply a newline, no subject field will be sent. .It Ar askcc Causes you to be prompted for additional carbon copy recipients at the end of each message. Responding with a newline indicates your satisfaction with the current list. .It Ar autoprint Causes the .Ic delete command to behave like .Ic dp \- thus, after deleting a message, the next one will be typed automatically. .It Ar debug Setting the binary option .Ar debug is the same as specifying .Fl d on the command line and causes .Nm mail to output all sorts of information useful for debugging .Nm mail . .It Ar dot The binary option .Ar dot causes .Nm mail to interpret a period alone on a line as the terminator of a message you are sending. .It Ar hold This option is used to hold messages in the system mailbox by default. .It Ar ignore Causes interrupt signals from your terminal to be ignored and echoed as @'s. .It Ar ignoreeof An option related to .Ar dot is .Ar ignoreeof which makes .Nm mail refuse to accept a control-d as the end of a message. .Ar Ignoreeof also applies to .Nm mail command mode. .It Ar metoo Usually, when a group is expanded that contains the sender, the sender is removed from the expansion. Setting this option causes the sender to be included in the group. .It Ar noheader Setting the option .Ar noheader is the same as giving the .Fl N flag on the command line. .It Ar nosave Normally, when you abort a message with two .Tn RUBOUT (erase or delete) .Nm mail copies the partial letter to the file .Dq Pa dead.letter in your home directory. Setting the binary option .Ar nosave prevents this. .It Ar Replyall Reverses the sense of .Ic reply and .Ic Reply commands. .It Ar quiet Suppresses the printing of the version when first invoked. .It Ar searchheaders If this option is set, then a message-list specifier in the form ``/x:y'' will expand to all messages containing the substring ``y'' in the header field ``x''. The string search is case insensitive. .It Ar verbose Setting the option .Ar verbose is the same as using the .Fl v flag on the command line. When mail runs in verbose mode, the actual delivery of messages is displayed on the user's terminal. .El .Ss Option String Values .Bl -tag -width Va .It Ev EDITOR Pathname of the text editor to use in the .Ic edit command and .Ic \&~e escape. If not defined, then a default editor is used. .It Ev LISTER Pathname of the directory lister to use in the .Ic folders command. Default is .Pa /bin/ls . .It Ev PAGER Pathname of the program to use in the .Ic more command or when .Ic crt variable is set. The default paginator .Xr more 1 is used if this option is not defined. +.It Ev REPLYTO +If set, will be used to initialize the Reply-To field for outgoing +messages. .It Ev SHELL Pathname of the shell to use in the .Ic \&! command and the .Ic \&~! escape. A default shell is used if this option is not defined. .It Ev VISUAL Pathname of the text editor to use in the .Ic visual command and .Ic \&~v escape. .It Va crt The valued option .Va crt is used as a threshold to determine how long a message must be before .Ev PAGER is used to read it. If .Va crt is set without a value, then the height of the terminal screen stored in the system is used to compute the threshold (see .Xr stty 1 ) . .It Ar escape If defined, the first character of this option gives the character to use in the place of ~ to denote escapes. .It Ar folder The name of the directory to use for storing folders of messages. If this name begins with a `/', .Nm mail considers it to be an absolute pathname; otherwise, the folder directory is found relative to your home directory. .It Ev MBOX The name of the .Ar mbox file. It can be the name of a folder. The default is .Dq Li mbox in the user's home directory. .It Ar record If defined, gives the pathname of the file used to record all outgoing mail. If not defined, then outgoing mail is not so saved. .It Ar indentprefix String used by the ``~m'' tilde escape for indenting messages, in place of the normal tab character (^I). Be sure to quote the value if it contains spaces or tabs. .It Ar toplines If defined, gives the number of lines of a message to be printed out with the .Ic top command; normally, the first five lines are printed. .El .Sh ENVIRONMENT .Nm Mail utilizes the .Ev HOME and .Ev USER environment variables. Also, if the .Ev MAIL environment variable is set, it is used as the location of the user's mailbox instead of the default in /var/mail. .Sh FILES .Bl -tag -width /usr/share/misc/mail.*help -compact .It Pa /var/mail/* Post office. .It ~/mbox User's old mail. .It ~/.mailrc File giving initial mail commands. .It Pa /tmp/R* Temporary files. .It Pa /usr/share/misc/mail.*help Help files. .sp .It Pa /usr/share/misc/mail.rc .It Pa /usr/local/etc/mail.rc .It Pa /etc/mail.rc System-wide initialization files. Each file will be sourced, in order, if it exists. .El .Sh SEE ALSO .Xr fmt 1 , .Xr newaliases 1 , .Xr vacation 1 , .Xr aliases 5 , .Xr mailaddr 7 , .Xr sendmail 8 and .Rs .%T "The Mail Reference Manual" . .Re .Sh HISTORY A .Nm command appeared in .At v1 . This man page is derived from .%T "The Mail Reference Manual" originally written by Kurt Shoens. .Sh BUGS There are some flags that are not documented here. Most are not useful to the general user. .Pp Usually, .Nm mail is just a link to .Nm Mail , which can be confusing. Index: stable/2.2/usr.bin/mail/main.c =================================================================== --- stable/2.2/usr.bin/mail/main.c (revision 34261) +++ stable/2.2/usr.bin/mail/main.c (revision 34262) @@ -1,311 +1,314 @@ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. 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 copyright[] = "@(#) Copyright (c) 1980, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #include "rcv.h" #include #include #include "extern.h" /* * Mail -- a mail program * * Startup -- interface with user. */ jmp_buf hdrjmp; int main(argc, argv) int argc; char *argv[]; { register int i; struct name *to, *cc, *bcc, *smopts; - char *subject; - char *ef; + char *subject, *replyto; + char *ef, *cp; char nosrc = 0; void hdrstop(); sig_t prevint; 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; + replyto = NOSTR; while ((i = getopt(argc, argv, "INT:b:c:dfins:u:v")) != -1) { 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) { char *s, *path_rc; path_rc = malloc(sizeof(_PATH_MASTER_RC)); if (path_rc == NULL) errx(1, "malloc(path_rc) failed"); strcpy(path_rc, _PATH_MASTER_RC); while ((s = strsep(&path_rc, ":")) != NULL) if (*s != '\0') load(s); } + if ((cp = getenv("REPLYTO")) != NULL) + replyto = cp; /* * 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); + mail(to, cc, bcc, smopts, subject, replyto); /* * 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(signo) int signo; { 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; */ void setscreensize() { struct winsize ws; struct termios tio; speed_t speed = 0; if (ioctl(1, TIOCGWINSZ, (char *) &ws) < 0) ws.ws_col = ws.ws_row = 0; if (tcgetattr(1, &tio) != -1) speed = cfgetospeed(&tio); if (speed <= 0) speed = B9600; if (speed < B1200) screenheight = 9; else if (speed == 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; } Index: stable/2.2/usr.bin/mail/misc/mail.tildehelp =================================================================== --- stable/2.2/usr.bin/mail/misc/mail.tildehelp (revision 34261) +++ stable/2.2/usr.bin/mail/misc/mail.tildehelp (revision 34262) @@ -1,22 +1,23 @@ ----------------------------------------------------------- The following ~ escapes are defined: ~~ Quote a single tilde ~b users Add users to "blind" cc list ~c users Add users to cc list ~d Read in dead.letter ~e Edit the message buffer ~f messages Read in messages ~F messages Same as ~f, but keep all header lines ~h Prompt for to list, subject and cc list ~r file Read a file into the message buffer ~p Print the message buffer ~m messages Read in messages, right shifted by a tab ~M messages Same as ~m, but keep all header lines +~R address Set reply-to ~s subject Set subject ~t users Add users to to list ~v Invoke display editor on message ~w file Write message onto file. ~? Print this message ~!command Invoke the shell ~|command Pipe the message through the command ----------------------------------------------------------- Index: stable/2.2/usr.bin/mail/names.c =================================================================== --- stable/2.2/usr.bin/mail/names.c (revision 34261) +++ stable/2.2/usr.bin/mail/names.c (revision 34262) @@ -1,694 +1,695 @@ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. 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 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ /* * Mail -- a mail program * * Handle name lists. */ #include "rcv.h" #include #include "extern.h" /* * Allocate a single element of a name list, * initialize its name field to the passed * name and return it. */ struct name * nalloc(str, ntype) char str[]; int ntype; { 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[]; int ntype; { 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; int ntype; { 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; } (void) fcntl(image, F_SETFD, 1); fprintf(fout, "From %s %s", myname, date); - puthead(hp, fout, GTO|GSUBJECT|GCC|GNL); + puthead(hp, fout, + GTO|GSUBJECT|GCC|GREPLYTO|GINREPLYTO|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 ./... */ int 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; int metoo, ntype; { struct group *gp; struct grouphead *ngh; struct name *np; static int depth; char *cp; if (depth > MAXEXP) { printf("Expanding alias to depth larger than %d\n", MAXEXP); return(nlist); } depth++; for (gp = gh->g_list; gp != 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. */ int 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. */ /* void 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"); } */ Index: stable/2.2/usr.bin/mail/send.c =================================================================== --- stable/2.2/usr.bin/mail/send.c (revision 34261) +++ stable/2.2/usr.bin/mail/send.c (revision 34262) @@ -1,556 +1,566 @@ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. 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 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #include "rcv.h" #include "extern.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. */ int 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. */ void 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. */ int -mail(to, cc, bcc, smopts, subject) +mail(to, cc, bcc, smopts, subject, replyto) struct name *to, *cc, *bcc, *smopts; - char *subject; + char *subject, *replyto; { struct header head; head.h_to = to; head.h_subject = subject; head.h_cc = cc; head.h_bcc = bcc; head.h_smopts = smopts; + head.h_replyto = replyto; + head.h_inreplyto = NOSTR; mail1(&head, 0); return(0); } /* * Send mail to a bunch of user names. The interface is through * the mail routine below. */ int 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; + if ((head.h_replyto = getenv("REPLYTO")) == NULL) + head.h_replyto = NOSTR; + head.h_inreplyto = NOSTR; mail1(&head, 0); return(0); } /* * Mail a message on standard input to the people indicated * in the passed header. (Internal interface). */ void mail1(hp, printheaders) struct header *hp; int printheaders; { 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) { 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. */ void 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); + (void) puthead(hp, nfo, + GTO|GSUBJECT|GCC|GBCC|GREPLYTO|GINREPLYTO|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. */ int puthead(hp, fo, w) struct header *hp; FILE *fo; int w; { 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 (hp->h_replyto != NOSTR && w & GREPLYTO) + fprintf(fo, "Reply-To: %s\n", hp->h_replyto), gotcha++; + if (hp->h_inreplyto != NOSTR && w & GINREPLYTO) + fprintf(fo, "In-Reply-To: <%s>\n", hp->h_inreplyto), gotcha++; if (gotcha && w & GNL) (void) putc('\n', fo); return(0); } /* * Format the given header line to not exceed 72 characters. */ void 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*/ int 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); }