diff --git a/usr.bin/mail/cmd1.c b/usr.bin/mail/cmd1.c index cace03df9aab..0689c1eb0cb5 100644 --- a/usr.bin/mail/cmd1.c +++ b/usr.bin/mail/cmd1.c @@ -1,477 +1,477 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)cmd1.c 8.2 (Berkeley) 4/20/95"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #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(void *v) { int *msgvec = v; int n, mesg, flag, size; struct message *mp; 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(void *v) { char *arg = v; 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(void) { int s; char *cp; if ((cp = value("screen")) != NULL && (s = atoi(cp)) > 0) return (s); return (screenheight - 4); } /* * Print out the headlines for each message * in the passed message list. */ int from(void *v) { int *msgvec = v; int *ip; for (ip = msgvec; *ip != 0; ip++) printhead(*ip); if (--ip >= msgvec) dot = &message[*ip - 1]; return (0); } /* * Print out the header of a specific message. * This is a slight improvement to the standard one. */ void printhead(int mesg) { struct message *mp; char headline[LINESIZE], 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)) == NULL) 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, "%3ld/%-5ld", mp->m_lines, mp->m_size); subjlen = screenwidth - 50 - strlen(wcount); name = value("show-rcpt") != NULL ? skin(hfield("to", mp)) : nameof(mp, 0); if (subjline == NULL || 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(void) +pdot(void *arg __unused) { printf("%td\n", dot - &message[0] + 1); return (0); } /* * Print out all the possible commands. */ int -pcmdlist(void) +pcmdlist(void *arg __unused) { extern const struct cmd cmdtab[]; const struct cmd *cp; int cc; 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 != NULL) printf("%s, ", cp->c_name); else printf("%s\n", cp->c_name); } return (0); } /* * Paginate messages, honor ignored fields. */ int more(void *v) { int *msgvec = v; return (type1(msgvec, 1, 1)); } /* * Paginate messages, even printing ignored fields. */ int More(void *v) { int *msgvec = v; return (type1(msgvec, 0, 1)); } /* * Type out messages, honor ignored fields. */ int type(void *v) { int *msgvec = v; return (type1(msgvec, 1, 0)); } /* * Type out messages, even printing ignored fields. */ int Type(void *v) { int *msgvec = v; return (type1(msgvec, 0, 0)); } /* * Type out the messages requested. */ static jmp_buf pipestop; int type1(int *msgvec, int doign, int page) { int nlines, *ip; struct message *mp; char *cp; FILE *obuf; obuf = stdout; if (setjmp(pipestop)) goto close_pipe; if (value("interactive") != NULL && (page || (cp = value("crt")) != NULL)) { nlines = 0; if (!page) { for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) nlines += message[*ip - 1].m_lines; } if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) { cp = value("PAGER"); if (cp == NULL || *cp == '\0') cp = _PATH_LESS; obuf = Popen(cp, "w"); if (obuf == NULL) { warnx("%s", cp); obuf = stdout; } else (void)signal(SIGPIPE, brokpipe); } } /* * Send messages to the output. * */ for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) { mp = &message[*ip - 1]; touch(mp); dot = mp; if (value("quiet") == NULL) fprintf(obuf, "Message %d:\n", *ip); (void)sendmessage(mp, obuf, doign ? ignore : 0, NULL); } close_pipe: if (obuf != stdout) { /* * Ignore SIGPIPE so it can't cause a duplicate close. */ (void)signal(SIGPIPE, SIG_IGN); (void)Pclose(obuf); (void)signal(SIGPIPE, SIG_DFL); } return (0); } /* * Respond to a broken pipe signal -- * probably caused by quitting more. */ /*ARGSUSED*/ void brokpipe(int signo __unused) { 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(void *v) { int *msgvec = v; int *ip; struct message *mp; int c, topl, lines, lineb; char *valtop, linebuf[LINESIZE]; FILE *ibuf; topl = 5; valtop = value("toplines"); if (valtop != NULL) { topl = atoi(valtop); if (topl < 0 || topl > 10000) topl = 5; } lineb = 1; for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { mp = &message[*ip - 1]; touch(mp); dot = mp; if (value("quiet") == NULL) printf("Message %d:\n", *ip); ibuf = setinput(mp); c = mp->m_lines; if (!lineb) printf("\n"); for (lines = 0; lines < c && lines <= topl; lines++) { if (readline(ibuf, linebuf, sizeof(linebuf)) < 0) break; puts(linebuf); lineb = strspn(linebuf, " \t") == strlen(linebuf); } } return (0); } /* * Touch all the given messages so that they will * get mboxed. */ int stouch(void *v) { int *msgvec = v; int *ip; for (ip = msgvec; *ip != 0; ip++) { dot = &message[*ip-1]; dot->m_flag |= MTOUCH; dot->m_flag &= ~MPRESERVE; } return (0); } /* * Make sure all passed messages get mboxed. */ int mboxit(void *v) { int *msgvec = v; int *ip; for (ip = msgvec; *ip != 0; ip++) { dot = &message[*ip-1]; dot->m_flag |= MTOUCH|MBOX; dot->m_flag &= ~MPRESERVE; } return (0); } /* * List the folders the user currently has. */ int -folders(void) +folders(void *arg __unused) { char dirname[PATHSIZE]; char *cmd; if (getfold(dirname, sizeof(dirname)) < 0) { printf("No value set for \"folder\"\n"); return (1); } if ((cmd = value("LISTER")) == NULL) cmd = "ls"; (void)run_command(cmd, 0, -1, -1, dirname, NULL); return (0); } /* * Update the mail file with any new messages that have * come in since we started reading mail. */ int inc(void *v __unused) { int nmsg, mdot; nmsg = incfile(); if (nmsg == 0) printf("No new mail.\n"); else if (nmsg > 0) { mdot = newfileinfo(msgCount - nmsg); dot = &message[mdot - 1]; } else printf("\"inc\" command failed...\n"); return (0); } diff --git a/usr.bin/mail/cmd2.c b/usr.bin/mail/cmd2.c index 08b7a252b974..2c0f8d55e299 100644 --- a/usr.bin/mail/cmd2.c +++ b/usr.bin/mail/cmd2.c @@ -1,517 +1,518 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)cmd2.c 8.1 (Berkeley) 6/6/93"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #include "rcv.h" #include #include "extern.h" /* * Mail -- a mail program * * More user commands. */ extern int wait_status; /* * If any arguments were given, go to the next applicable argument * following dot, otherwise, go to the next applicable message. * If given as first command with no arguments, print first message. */ int next(void *v) { struct message *mp; int *msgvec = v; int *ip, *ip2, list[2], mdot; if (*msgvec != 0) { /* * If some messages were supplied, find the * first applicable one following dot using * wrap around. */ mdot = dot - &message[0] + 1; /* * Find the first message in the supplied * message list which follows dot. */ for (ip = msgvec; *ip != 0; ip++) if (*ip > mdot) break; if (*ip == 0) ip = msgvec; ip2 = ip; do { mp = &message[*ip2 - 1]; if ((mp->m_flag & MDELETED) == 0) { dot = mp; goto hitit; } if (*ip2 != 0) ip2++; if (*ip2 == 0) ip2 = msgvec; } while (ip2 != ip); 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] = 0; return (type(list)); } /* * Save a message in a file. Mark the message as saved * so we can discard when the user quits. */ int save(void *v) { char *str = v; return (save1(str, 1, "save", saveignore)); } /* * Copy a message to a file without affected its saved-ness */ int copycmd(void *v) { char *str = v; return (save1(str, 0, "copy", saveignore)); } /* * Save/copy the indicated messages at the end of the passed file name. * If mark is true, mark the message "saved." */ int save1(char str[], int mark, const char *cmd, struct ignoretab *ignore) { struct message *mp; char *file; const char *disp; int f, *msgvec, *ip; FILE *obuf; msgvec = (int *)salloc((msgCount + 2) * sizeof(*msgvec)); if ((file = snarf(str, &f)) == NULL) return (1); if (!f) { *msgvec = first(0, MMNORM); if (*msgvec == 0) { printf("No messages to %s.\n", cmd); return (1); } msgvec[1] = 0; } if (f && getmsglist(str, msgvec, 0) < 0) return (1); if ((file = expand(file)) == NULL) return (1); printf("\"%s\" ", file); (void)fflush(stdout); if (access(file, 0) >= 0) disp = "[Appended]"; else disp = "[New file]"; if ((obuf = Fopen(file, "a")) == NULL) { warn((char *)NULL); return (1); } for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { mp = &message[*ip - 1]; touch(mp); if (sendmessage(mp, obuf, ignore, NULL) < 0) { warnx("%s", file); (void)Fclose(obuf); return (1); } if (mark) mp->m_flag |= MSAVED; } (void)fflush(obuf); if (ferror(obuf)) warn("%s", file); (void)Fclose(obuf); printf("%s\n", disp); return (0); } /* * Write the indicated messages at the end of the passed * file name, minus header and trailing blank line. */ int swrite(void *v) { char *str = v; return (save1(str, 1, "write", ignoreall)); } /* * Snarf the file from the end of the command line and * return a pointer to it. If there is no file attached, * just return NULL. Put a null in front of the file * name so that the message list processing won't see it, * unless the file name is the only thing on the line, in * which case, return 0 in the reference flag variable. */ char * snarf(char *linebuf, int *flag) { char *cp; *flag = 1; cp = strlen(linebuf) + linebuf - 1; /* * Strip away trailing blanks. */ while (cp > linebuf && isspace((unsigned char)*cp)) cp--; *++cp = '\0'; /* * Now search for the beginning of the file name. */ while (cp > linebuf && !isspace((unsigned char)*cp)) cp--; if (*cp == '\0') { printf("No file specified.\n"); return (NULL); } if (isspace((unsigned char)*cp)) *cp++ = '\0'; else *flag = 0; return (cp); } /* * Delete messages. */ int deletecmd(void *v) { int *msgvec = v; delm(msgvec); return (0); } /* * Delete messages, then type the new dot. */ int deltype(void *v) { int *msgvec = v; int list[2]; int lastdot; lastdot = dot - &message[0] + 1; if (delm(msgvec) >= 0) { list[0] = dot - &message[0] + 1; if (list[0] > lastdot) { touch(dot); list[1] = 0; return (type(list)); } 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(int *msgvec) { struct message *mp; int *ip, last; last = 0; for (ip = msgvec; *ip != 0; ip++) { mp = &message[*ip - 1]; touch(mp); mp->m_flag |= MDELETED|MTOUCH; mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX); last = *ip; } if (last != 0) { dot = &message[last-1]; last = first(0, MDELETED); if (last != 0) { dot = &message[last-1]; return (0); } else { dot = &message[0]; return (-1); } } /* * Following can't happen -- it keeps lint happy */ return (-1); } /* * Undelete the indicated messages. */ int undeletecmd(void *v) { int *msgvec = v; int *ip; struct message *mp; for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { mp = &message[*ip - 1]; touch(mp); dot = mp; mp->m_flag &= ~MDELETED; } return (0); } /* * Interactively dump core on "core" */ int -core(void) +core(void *arg __unused) { int pid; switch (pid = fork()) { case -1: warn("fork"); return (1); case 0: abort(); _exit(1); } printf("Okie dokie"); (void)fflush(stdout); wait_child(pid); if (WIFSIGNALED(wait_status) && WCOREDUMP(wait_status)) 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(char **argv) +clobber(void *arg) { + char **argv = arg; int times; if (argv[0] == 0) times = 1; else times = (atoi(argv[0]) + 511) / 512; clob1(times); return (0); } /* * Clobber the stack. */ void clob1(int n) { char buf[512]; 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(void *v) { char **list = v; return (ignore1(list, ignore + 1, "retained")); } /* * Add the given header fields to the ignored list. * If no arguments, print the current list of ignored fields. */ int igfield(void *v) { char **list = v; return (ignore1(list, ignore, "ignored")); } int saveretfield(void *v) { char **list = v; return (ignore1(list, saveignore + 1, "retained")); } int saveigfield(void *v) { char **list = v; return (ignore1(list, saveignore, "ignored")); } int ignore1(char **list, struct ignoretab *tab, const char *which) { char field[LINESIZE]; char **ap; struct ignore *igp; int h; if (*list == NULL) return (igshow(tab, which)); for (ap = list; *ap != 0; ap++) { istrncpy(field, *ap, sizeof(field)); if (member(field, tab)) continue; h = hash(field); igp = 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(struct ignoretab *tab, const char *which) { int h; struct ignore *igp; char **ap, **ring; if (tab->i_count == 0) { printf("No fields currently being %s.\n", which); return (0); } ring = (char **)salloc((tab->i_count + 1) * sizeof(char *)); ap = ring; for (h = 0; h < HSHSIZE; h++) for (igp = tab->i_head[h]; igp != NULL; 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(const void *l, const void *r) { return (strcmp(*(const char **)l, *(const char **)r)); } diff --git a/usr.bin/mail/cmd3.c b/usr.bin/mail/cmd3.c index 68afbaa56913..2b4a20b96bb5 100644 --- a/usr.bin/mail/cmd3.c +++ b/usr.bin/mail/cmd3.c @@ -1,727 +1,731 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)cmd3.c 8.2 (Berkeley) 4/20/95"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #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(char *str) +shell(void *str) { sig_t sigint = signal(SIGINT, SIG_IGN); char *sh; char cmd[BUFSIZ]; if (strlcpy(cmd, str, sizeof(cmd)) >= sizeof(cmd)) return (1); if (bangexp(cmd, sizeof(cmd)) < 0) return (1); if ((sh = value("SHELL")) == NULL) sh = _PATH_CSHELL; (void)run_command(sh, 0, -1, -1, "-c", cmd, NULL); (void)signal(SIGINT, sigint); printf("!\n"); return (0); } /* * Fork an interactive shell. */ /*ARGSUSED*/ int -dosh(char *str __unused) +dosh(void *str __unused) { sig_t sigint = signal(SIGINT, SIG_IGN); char *sh; if ((sh = value("SHELL")) == NULL) sh = _PATH_CSHELL; (void)run_command(sh, 0, -1, -1, NULL); (void)signal(SIGINT, sigint); printf("\n"); return (0); } /* * Expand the shell escape by expanding unescaped !'s into the * last issued command where possible. */ int bangexp(char *str, size_t strsize) { char bangbuf[BUFSIZ]; static char lastbang[BUFSIZ]; char *cp, *cp2; int n, changed = 0; cp = str; cp2 = bangbuf; n = sizeof(bangbuf); while (*cp != '\0') { if (*cp == '!') { if (n < (int)strlen(lastbang)) { overf: printf("Command buffer overflow\n"); return (-1); } changed++; if (strlcpy(cp2, lastbang, sizeof(bangbuf) - (cp2 - bangbuf)) >= sizeof(bangbuf) - (cp2 - bangbuf)) goto overf; 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); (void)fflush(stdout); } if (strlcpy(str, bangbuf, strsize) >= strsize) goto overf; if (strlcpy(lastbang, bangbuf, sizeof(lastbang)) >= sizeof(lastbang)) goto overf; return (0); } /* * Print out a nice help message from some file or another. */ int -help(void) +help(void *arg __unused) { int c; FILE *f; if ((f = Fopen(_PATH_HELP, "r")) == NULL) { warn("%s", _PATH_HELP); return (1); } while ((c = getc(f)) != EOF) putchar(c); (void)Fclose(f); return (0); } /* * Change user's working directory. */ int schdir(void *v) { char **arglist = v; char *cp; if (*arglist == NULL) { if (homedir == NULL) return (1); cp = homedir; } else if ((cp = expand(*arglist)) == NULL) return (1); if (chdir(cp) < 0) { warn("%s", cp); return (1); } return (0); } int respond(void *v) { int *msgvec = v; if (value("Replyall") == NULL && value("flipr") == NULL) return (dorespond(msgvec)); else return (doRespond(msgvec)); } /* * Reply to a list of messages. Extract each name from the * message header and send them off to mail1() */ int dorespond(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))) == NULL) rcv = skin(nameof(mp, 1)); if ((replyto = skin(hfield("reply-to", mp))) != NULL) np = extract(replyto, GTO); else if ((cp = skin(hfield("to", mp))) != NULL) np = extract(cp, GTO); else np = NULL; 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 != NULL; ap++) np = delname(np, *ap); if (np != NULL && replyto == NULL) np = cat(np, extract(rcv, GTO)); else if (np == NULL) { if (replyto != NULL) printf("Empty reply-to field -- replying to author\n"); np = extract(rcv, GTO); } head.h_to = np; if ((head.h_subject = hfield("subject", mp)) == NULL) head.h_subject = hfield("subj", mp); head.h_subject = reedit(head.h_subject); if (replyto == NULL && (cp = skin(hfield("cc", mp))) != NULL) { np = elide(extract(cp, GCC)); np = delname(np, myname); if (altnames != 0) for (ap = altnames; *ap != NULL; ap++) np = delname(np, *ap); head.h_cc = np; } else head.h_cc = NULL; head.h_bcc = NULL; head.h_smopts = NULL; head.h_replyto = value("REPLYTO"); head.h_inreplyto = skin(hfield("message-id", mp)); mail1(&head, 1); return (0); } /* * Modify the message subject to begin with "Re:" if * it does not already. */ char * reedit(char *subj) { char *newsubj; if (subj == NULL) return (NULL); if ((subj[0] == 'r' || subj[0] == 'R') && (subj[1] == 'e' || subj[1] == 'E') && subj[2] == ':') return (subj); newsubj = salloc(strlen(subj) + 5); sprintf(newsubj, "Re: %s", subj); return (newsubj); } /* * Preserve the named messages, so that they will be sent * back to the system mailbox. */ int preserve(void *v) { int *msgvec = v; int *ip, mesg; struct message *mp; if (edit) { printf("Cannot \"preserve\" in edit mode\n"); return (1); } for (ip = msgvec; *ip != 0; ip++) { mesg = *ip; mp = &message[mesg-1]; mp->m_flag |= MPRESERVE; mp->m_flag &= ~MBOX; dot = mp; } return (0); } /* * Mark all given messages as unread. */ int unread(void *v) { int *msgvec = v; int *ip; for (ip = msgvec; *ip != 0; ip++) { dot = &message[*ip-1]; dot->m_flag &= ~(MREAD|MTOUCH); dot->m_flag |= MSTATUS; } return (0); } /* * Print the size of each message. */ int messize(void *v) { int *msgvec = v; struct message *mp; int *ip, mesg; for (ip = msgvec; *ip != 0; ip++) { mesg = *ip; mp = &message[mesg-1]; printf("%d: %ld/%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(void *v) { if (sourcing) return (1); exit(0); /*NOTREACHED*/ } /* * Set or display a variable value. Syntax is similar to that * of csh. */ int set(void *v) { char **arglist = v; struct var *vp; char *cp, *cp2; char varbuf[BUFSIZ], **ap, **p; int errs, h, s; if (*arglist == NULL) { for (h = 0, s = 1; h < HSHSIZE; h++) for (vp = variables[h]; vp != NULL; vp = vp->v_link) s++; ap = (char **)salloc(s * sizeof(*ap)); for (h = 0, p = ap; h < HSHSIZE; h++) for (vp = variables[h]; vp != NULL; vp = vp->v_link) *p++ = vp->v_name; *p = NULL; sort(ap); for (p = ap; *p != NULL; p++) printf("%s\t%s\n", *p, value(*p)); return (0); } errs = 0; for (ap = arglist; *ap != NULL; ap++) { cp = *ap; cp2 = varbuf; while (cp2 < varbuf + sizeof(varbuf) - 1 && *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(void *v) { char **arglist = v; struct var *vp, *vp2; int errs, h; char **ap; errs = 0; for (ap = arglist; *ap != NULL; ap++) { if ((vp2 = lookup(*ap)) == NULL) { if (getenv(*ap)) unsetenv(*ap); else if (!sourcing) { printf("\"%s\": undefined variable\n", *ap); errs++; } continue; } h = hash(*ap); if (vp2 == variables[h]) { variables[h] = variables[h]->v_link; vfree(vp2->v_name); vfree(vp2->v_value); (void)free(vp2); continue; } for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link) ; vp->v_link = vp2->v_link; vfree(vp2->v_name); vfree(vp2->v_value); (void)free(vp2); } return (errs); } /* * Put add users to a group. */ int group(void *v) { char **argv = v; struct grouphead *gh; struct group *gp; char **ap, *gname, **p; int h, s; if (*argv == NULL) { for (h = 0, s = 1; h < HSHSIZE; h++) for (gh = groups[h]; gh != NULL; gh = gh->g_link) s++; ap = (char **)salloc(s * sizeof(*ap)); for (h = 0, p = ap; h < HSHSIZE; h++) for (gh = groups[h]; gh != NULL; gh = gh->g_link) *p++ = gh->g_name; *p = NULL; sort(ap); for (p = ap; *p != NULL; p++) printgroup(*p); return (0); } if (argv[1] == NULL) { printgroup(*argv); return (0); } gname = *argv; h = hash(gname); if ((gh = findgroup(gname)) == NULL) { if ((gh = calloc(1, sizeof(*gh))) == NULL) err(1, "Out of memory"); gh->g_name = vcopy(gname); gh->g_list = NULL; gh->g_link = groups[h]; groups[h] = gh; } /* * Insert names from the command list into the group. * Who cares if there are duplicates? They get tossed * later anyway. */ for (ap = argv+1; *ap != NULL; ap++) { if ((gp = calloc(1, sizeof(*gp))) == NULL) err(1, "Out of memory"); 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(char **list) { char **ap; for (ap = list; *ap != NULL; ap++) ; if (ap-list < 2) return; qsort(list, ap-list, sizeof(*list), diction); } /* * Do a dictionary order comparison of the arguments from * qsort. */ int diction(const void *a, const void *b) { return (strcmp(*(const char **)a, *(const char **)b)); } /* * The do nothing command for comments. */ /*ARGSUSED*/ int -null(int e __unused) +null(void *arg __unused) { return (0); } /* * Change to another file. With no argument, print information about * the current file. */ int -file(char **argv) +file(void *arg) { + char **argv = arg; if (argv[0] == NULL) { newfileinfo(0); return (0); } if (setfile(*argv) < 0) return (1); announce(); return (0); } /* * Expand file names like echo */ int -echo(char **argv) +echo(void *arg) { + char **argv = arg; char **ap, *cp; for (ap = argv; *ap != NULL; ap++) { cp = *ap; if ((cp = expand(cp)) != NULL) { if (ap != argv) printf(" "); printf("%s", cp); } } printf("\n"); return (0); } int -Respond(int *msgvec) +Respond(void *msgvec) { if (value("Replyall") == NULL && value("flipr") == NULL) return (doRespond(msgvec)); else 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 doRespond(int msgvec[]) { struct header head; struct message *mp; int *ap; char *cp, *mid; head.h_to = NULL; for (ap = msgvec; *ap != 0; ap++) { mp = &message[*ap - 1]; touch(mp); dot = mp; if ((cp = skin(hfield("from", mp))) == NULL) cp = skin(nameof(mp, 2)); head.h_to = cat(head.h_to, extract(cp, GTO)); mid = skin(hfield("message-id", mp)); } if (head.h_to == NULL) return (0); mp = &message[msgvec[0] - 1]; if ((head.h_subject = hfield("subject", mp)) == NULL) head.h_subject = hfield("subj", mp); head.h_subject = reedit(head.h_subject); head.h_cc = NULL; head.h_bcc = NULL; head.h_smopts = NULL; head.h_replyto = value("REPLYTO"); 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(char **argv) +ifcmd(void *arg) { + char **argv = arg; 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(void) +elsecmd(void *arg __unused) { 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(void) +endifcmd(void *arg __unused) { if (cond == CANY) { printf("\"Endif\" without matching \"if\"\n"); return (1); } cond = CANY; return (0); } /* * Set the list of alternate names. */ int -alternates(char **namelist) +alternates(void *arg) { + char **namelist = arg; int c; char **ap, **ap2, *cp; c = argcount(namelist) + 1; if (c == 1) { if (altnames == 0) return (0); for (ap = altnames; *ap != NULL; ap++) printf("%s ", *ap); printf("\n"); return (0); } if (altnames != 0) (void)free(altnames); if ((altnames = calloc((unsigned)c, sizeof(char *))) == NULL) err(1, "Out of memory"); for (ap = namelist, ap2 = altnames; *ap != NULL; ap++, ap2++) { cp = calloc((unsigned)strlen(*ap) + 1, sizeof(char)); strcpy(cp, *ap); *ap2 = cp; } *ap2 = 0; return (0); } diff --git a/usr.bin/mail/collect.c b/usr.bin/mail/collect.c index d56f460a8690..e8249a10f867 100644 --- a/usr.bin/mail/collect.c +++ b/usr.bin/mail/collect.c @@ -1,733 +1,733 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)collect.c 8.2 (Berkeley) 4/19/94"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); /* * Mail -- a mail program * * Collect input from standard input, handling * ~ escapes. */ #include "rcv.h" #include #include "extern.h" /* * Read a message from standard input 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(struct header *hp, int printheaders) { FILE *fbuf; int lc, cc, escape, eofcount, fd, c, t; char linebuf[LINESIZE], tempname[PATHSIZE], *cp, getsub; sigset_t nset; int longline, lastlong, rc; /* So we don't make 2 or more lines out of a long input line. */ collf = NULL; /* * Start catching signals from here, but we're still die on interrupts * until we're in the main loop. */ (void)sigemptyset(&nset); (void)sigaddset(&nset, SIGINT); (void)sigaddset(&nset, SIGHUP); (void)sigprocmask(SIG_BLOCK, &nset, NULL); if ((saveint = signal(SIGINT, SIG_IGN)) != SIG_IGN) (void)signal(SIGINT, collint); if ((savehup = signal(SIGHUP, SIG_IGN)) != SIG_IGN) (void)signal(SIGHUP, collhup); savetstp = signal(SIGTSTP, collstop); savettou = signal(SIGTTOU, collstop); savettin = signal(SIGTTIN, collstop); if (setjmp(collabort) || setjmp(colljmp)) { (void)rm(tempname); goto err; } (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); noreset++; (void)snprintf(tempname, sizeof(tempname), "%s/mail.RsXXXXXXXXXX", tmpdir); if ((fd = mkstemp(tempname)) == -1 || (collf = Fdopen(fd, "w+")) == NULL) { warn("%s", tempname); goto err; } (void)rm(tempname); /* * 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 == NULL && value("interactive") != NULL && (value("ask") != NULL || value("asksub") != NULL)) t &= ~GNL, getsub++; if (printheaders) { puthead(hp, stdout, t); (void)fflush(stdout); } if ((cp = value("escape")) != NULL) escape = *cp; else escape = ESCAPE; eofcount = 0; hadintr = 0; longline = 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) { (void)fflush(stdout); fprintf(stderr, "\n(Interrupt -- one more to kill letter)\n"); } else { printf("(continue)\n"); (void)fflush(stdout); } } for (;;) { colljmp_p = 1; c = readline(stdin, linebuf, LINESIZE); colljmp_p = 0; if (c < 0) { if (value("interactive") != NULL && value("ignoreeof") != NULL && ++eofcount < 25) { printf("Use \".\" to terminate letter\n"); continue; } break; } lastlong = longline; longline = c == LINESIZE - 1; eofcount = 0; hadintr = 0; if (linebuf[0] == '.' && linebuf[1] == '\0' && value("interactive") != NULL && !lastlong && (value("dot") != NULL || value("ignoreeof") != NULL)) break; if (linebuf[0] != escape || value("interactive") == NULL || lastlong) { if (putline(collf, linebuf, !longline) < 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], !longline) < 0) goto err; else break; } printf("Unknown tilde escape.\n"); break; case 'C': /* * Dump core. */ - core(); + core(NULL); break; case '!': /* * Shell escape, send the balance of the * line to sh -c. */ shell(&linebuf[2]); break; case ':': 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 'x': /* * Exit, do not save in dead.letter. */ goto err; 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 line. */ cp = &linebuf[2]; while (isspace((unsigned char)*cp)) cp++; hp->h_subject = savestr(cp); break; case 'R': /* * Set the Reply-To line. */ cp = &linebuf[2]; while (isspace((unsigned char)*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 to the BCC list. */ hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC)); break; case 'i': case 'A': case 'a': /* * Insert named variable in message. */ switch(c) { case 'i': cp = &linebuf[2]; while(isspace((unsigned char)*cp)) cp++; break; case 'a': cp = "sign"; break; case 'A': cp = "Sign"; break; default: goto err; } if(*cp != '\0' && (cp = value(cp)) != NULL) { printf("%s\n", cp); if(putline(collf, cp, 1) < 0) goto err; } break; case 'd': /* * Read in the dead letter file. */ if (strlcpy(linebuf + 2, getdeadletter(), sizeof(linebuf) - 2) >= sizeof(linebuf) - 2) { printf("Line buffer overflow\n"); break; } /* FALLTHROUGH */ case 'r': case '<': /* * Invoke a file: * Search for the file name, * then open it and copy the contents to collf. */ cp = &linebuf[2]; while (isspace((unsigned char)*cp)) cp++; if (*cp == '\0') { printf("Interpolate what file?\n"); break; } cp = expand(cp); if (cp == NULL) break; if (*cp == '!') { /* * Insert stdout of command. */ char *sh; int nullfd, tempfd, rc; char tempname2[PATHSIZE]; if ((nullfd = open(_PATH_DEVNULL, O_RDONLY, 0)) == -1) { warn(_PATH_DEVNULL); break; } (void)snprintf(tempname2, sizeof(tempname2), "%s/mail.ReXXXXXXXXXX", tmpdir); if ((tempfd = mkstemp(tempname2)) == -1 || (fbuf = Fdopen(tempfd, "w+")) == NULL) { warn("%s", tempname2); break; } (void)unlink(tempname2); if ((sh = value("SHELL")) == NULL) sh = _PATH_CSHELL; rc = run_command(sh, 0, nullfd, fileno(fbuf), "-c", cp+1, NULL); close(nullfd); if (rc < 0) { (void)Fclose(fbuf); break; } if (fsize(fbuf) == 0) { fprintf(stderr, "No bytes from command \"%s\"\n", cp+1); (void)Fclose(fbuf); break; } rewind(fbuf); } else if (isdir(cp)) { printf("%s: Directory\n", cp); break; } else if ((fbuf = Fopen(cp, "r")) == NULL) { warn("%s", cp); break; } printf("\"%s\" ", cp); (void)fflush(stdout); lc = 0; cc = 0; while ((rc = readline(fbuf, linebuf, LINESIZE)) >= 0) { if (rc != LINESIZE - 1) lc++; if ((t = putline(collf, linebuf, rc != LINESIZE - 1)) < 0) { (void)Fclose(fbuf); goto err; } cc += t; } (void)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)) == NULL) 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, tempname, c) < 0) goto err; goto cont; case '?': if ((fbuf = Fopen(_PATH_TILDE, "r")) == NULL) { warn("%s", _PATH_TILDE); break; } while ((t = getc(fbuf)) != EOF) (void)putchar(t); (void)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) { (void)Fclose(collf); collf = NULL; } out: if (collf != NULL) rewind(collf); noreset--; (void)sigprocmask(SIG_BLOCK, &nset, NULL); (void)signal(SIGINT, saveint); (void)signal(SIGHUP, savehup); (void)signal(SIGTSTP, savetstp); (void)signal(SIGTTOU, savettou); (void)signal(SIGTTIN, savettin); (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); return (collf); } /* * Write a file, ex-like if f set. */ int exwrite(char name[], FILE *fp, int f) { FILE *of; int c, lc; long cc; struct stat junk; if (f) { printf("\"%s\" ", name); (void)fflush(stdout); } if (stat(name, &junk) >= 0 && S_ISREG(junk.st_mode)) { if (!f) fprintf(stderr, "%s: ", name); fprintf(stderr, "File exists\n"); return (-1); } if ((of = Fopen(name, "w")) == NULL) { warn((char *)NULL); return (-1); } lc = 0; cc = 0; while ((c = getc(fp)) != EOF) { cc++; if (c == '\n') lc++; (void)putc(c, of); if (ferror(of)) { warnx("%s", name); (void)Fclose(of); return (-1); } } (void)Fclose(of); printf("%d/%ld\n", lc, cc); (void)fflush(stdout); return (0); } /* * Edit the message being collected on fp. * On return, make the edit file the new temp file. */ void mesedit(FILE *fp, int c) { sig_t sigint = signal(SIGINT, SIG_IGN); FILE *nf = run_editor(fp, (off_t)-1, c, 0); if (nf != NULL) { (void)fseeko(nf, (off_t)0, SEEK_END); collf = nf; (void)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(FILE *fp, char cmd[]) { FILE *nf; int fd; sig_t sigint = signal(SIGINT, SIG_IGN); char *sh, tempname[PATHSIZE]; (void)snprintf(tempname, sizeof(tempname), "%s/mail.ReXXXXXXXXXX", tmpdir); if ((fd = mkstemp(tempname)) == -1 || (nf = Fdopen(fd, "w+")) == NULL) { warn("%s", tempname); goto out; } (void)rm(tempname); /* * stdin = current message. * stdout = new message. */ if ((sh = value("SHELL")) == NULL) sh = _PATH_CSHELL; if (run_command(sh, 0, fileno(fp), fileno(nf), "-c", cmd, NULL) < 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)fseeko(nf, (off_t)0, SEEK_END); 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(char ms[], FILE *fp, char *fn, int f) { int *msgvec; struct ignoretab *ig; char *tabst; msgvec = (int *)salloc((msgCount+1) * sizeof(*msgvec)); if (msgvec == NULL) return (0); if (getmsglist(ms, msgvec, 0) < 0) return (0); if (*msgvec == 0) { *msgvec = first(0, MMNORM); if (*msgvec == 0) { printf("No appropriate messages\n"); return (0); } msgvec[1] = 0; } if (f == 'f' || f == 'F') tabst = NULL; else if ((tabst = value("indentprefix")) == NULL) tabst = "\t"; ig = isupper((unsigned char)f) ? NULL : ignore; printf("Interpolating:"); for (; *msgvec != 0; msgvec++) { struct message *mp = message + *msgvec - 1; touch(mp); printf(" %d", *msgvec); if (sendmessage(mp, fp, ig, tabst) < 0) { warnx("%s", fn); return (-1); } } printf("\n"); return (0); } /* * Print (continue) when continued after ^Z. */ /*ARGSUSED*/ void collstop(int s) { sig_t old_action = signal(s, SIG_DFL); sigset_t nset; (void)sigemptyset(&nset); (void)sigaddset(&nset, s); (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); (void)kill(0, s); (void)sigprocmask(SIG_BLOCK, &nset, NULL); (void)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(int s __unused) { /* * the control flow is subtle, because we can be called from ~q. */ if (!hadintr) { if (value("ignore") != NULL) { printf("@"); (void)fflush(stdout); clearerr(stdin); return; } hadintr = 1; longjmp(colljmp, 1); } rewind(collf); if (value("nosave") == NULL) savedeadletter(collf); longjmp(collabort, 1); } /*ARGSUSED*/ void collhup(int s __unused) { rewind(collf); savedeadletter(collf); /* * Let's pretend nobody else wants to clean up, * a true statement at this time. */ exit(1); } void savedeadletter(FILE *fp) { FILE *dbuf; 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); (void)Fclose(dbuf); rewind(fp); } diff --git a/usr.bin/mail/def.h b/usr.bin/mail/def.h index 02b84478456b..fd41081797c6 100644 --- a/usr.bin/mail/def.h +++ b/usr.bin/mail/def.h @@ -1,276 +1,276 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)def.h 8.4 (Berkeley) 4/20/95 * * $FreeBSD$ */ /* * Mail -- a mail program * * Author: Kurt Shoens (UCB) March 25, 1978 */ #include #include #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 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 boffsetof(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 { const char *c_name; /* Name of command */ - int (*c_func)(); /* Implementor of the command */ + int (*c_func)(void *); /* 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 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 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_bcc; /* Blind carbon copies */ struct name *h_cc; /* Carbon copies string */ struct name *h_smopts; /* Sendmail options */ struct name *h_to; /* Dynamic "To:" string */ char *h_inreplyto; /* Reference */ char *h_replyto; /* Reply address */ char *h_subject; /* Subject string */ }; /* * 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. */ }; /* * 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), ftello(stream)); \ } diff --git a/usr.bin/mail/edit.c b/usr.bin/mail/edit.c index e8d6fcb12ccc..bc7f4beb29fb 100644 --- a/usr.bin/mail/edit.c +++ b/usr.bin/mail/edit.c @@ -1,216 +1,216 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)edit.c 8.1 (Berkeley) 6/6/93"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #include "rcv.h" #include #include "extern.h" /* * Mail -- a mail program * * Perform message editing functions. */ /* * Edit a message list. */ int -editor(int *msgvec) +editor(void *msgvec) { return (edit1(msgvec, 'e')); } /* * Invoke the visual editor on a message list. */ int -visual(int *msgvec) +visual(void *msgvec) { return (edit1(msgvec, 'v')); } /* * Edit a message by writing the message into a funnily-named file * (which should not exist) and forking an editor on it. * We get the editor from the stuff above. */ int edit1(int *msgvec, int type) { int c, i; FILE *fp; struct message *mp; off_t size; /* * Deal with each message to be edited . . . */ for (i = 0; i < msgCount && msgvec[i]; i++) { sig_t sigint; if (i > 0) { char buf[100]; char *p; printf("Edit message %d [ynq]? ", msgvec[i]); if (fgets(buf, sizeof(buf), stdin) == NULL) break; for (p = buf; *p == ' ' || *p == '\t'; p++) ; if (*p == 'q') break; if (*p == 'n') continue; } dot = mp = &message[msgvec[i] - 1]; touch(mp); sigint = signal(SIGINT, SIG_IGN); fp = run_editor(setinput(mp), mp->m_size, type, readonly); if (fp != NULL) { (void)fseeko(otf, (off_t)0, SEEK_END); size = ftello(otf); mp->m_block = blockof(size); mp->m_offset = boffsetof(size); mp->m_size = (long)fsize(fp); mp->m_lines = 0; mp->m_flag |= MODIFY; rewind(fp); while ((c = getc(fp)) != EOF) { if (c == '\n') mp->m_lines++; if (putc(c, otf) == EOF) break; } if (ferror(otf)) warnx("/tmp"); (void)Fclose(fp); } (void)signal(SIGINT, sigint); } return (0); } /* * Run an editor on the file at "fpp" of "size" bytes, * and return a new file pointer. * Signals must be handled by the caller. * "Type" is 'e' for _PATH_EX, 'v' for _PATH_VI. */ FILE * run_editor(FILE *fp, off_t size, int type, int readonly) { FILE *nf = NULL; int t; time_t modtime; char *edit, tempname[PATHSIZE]; struct stat statb; (void)snprintf(tempname, sizeof(tempname), "%s/mail.ReXXXXXXXXXX", tmpdir); if ((t = mkstemp(tempname)) == -1 || (nf = Fdopen(t, "w")) == NULL) { warn("%s", tempname); goto out; } if (readonly && fchmod(t, 0400) == -1) { warn("%s", tempname); (void)rm(tempname); goto out; } if (size >= 0) while (--size >= 0 && (t = getc(fp)) != EOF) (void)putc(t, nf); else while ((t = getc(fp)) != EOF) (void)putc(t, nf); (void)fflush(nf); if (fstat(fileno(nf), &statb) < 0) modtime = 0; else modtime = statb.st_mtime; if (ferror(nf)) { (void)Fclose(nf); warnx("%s", tempname); (void)rm(tempname); nf = NULL; goto out; } if (Fclose(nf) < 0) { warn("%s", tempname); (void)rm(tempname); nf = NULL; goto out; } nf = NULL; if ((edit = value(type == 'e' ? "EDITOR" : "VISUAL")) == NULL) edit = type == 'e' ? _PATH_EX : _PATH_VI; if (run_command(edit, 0, -1, -1, tempname, NULL) < 0) { (void)rm(tempname); goto out; } /* * If in read only mode or file unchanged, just remove the editor * temporary and return. */ if (readonly) { (void)rm(tempname); goto out; } if (stat(tempname, &statb) < 0) { warn("%s", tempname); goto out; } if (modtime == statb.st_mtime) { (void)rm(tempname); goto out; } /* * Now switch to new file. */ if ((nf = Fopen(tempname, "a+")) == NULL) { warn("%s", tempname); (void)rm(tempname); goto out; } (void)rm(tempname); out: return (nf); } diff --git a/usr.bin/mail/extern.h b/usr.bin/mail/extern.h index aa31733dd26a..4f06f77f18a2 100644 --- a/usr.bin/mail/extern.h +++ b/usr.bin/mail/extern.h @@ -1,254 +1,254 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * 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. 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.2 (Berkeley) 4/20/95 * * $FreeBSD$ */ struct name *cat(struct name *, struct name *); struct name *delname(struct name *, char *); struct name *elide(struct name *); struct name *extract(char *, int); struct name *gexpand(struct name *, struct grouphead *, int, int); struct name *nalloc(char *, int); struct name *outof(struct name *, FILE *, struct header *); struct name *put(struct name *, struct name *); struct name *tailof(struct name *); struct name *usermap(struct name *); FILE *Fdopen(int, const char *); FILE *Fopen(const char *, const char *); FILE *Popen(char *, const char *); FILE *collect(struct header *, int); char *copyin(char *, char **); char *detract(struct name *, int); char *expand(char *); char *getdeadletter(void); char *getname(uid_t); char *hfield(const char *, struct message *); FILE *infix(struct header *, FILE *); char *ishfield(char *, char *, const char *); char *name1(struct message *, int); char *nameof(struct message *, int); char *nextword(char *, char *); char *readtty(const char *, char *); char *reedit(char *); FILE *run_editor(FILE *, off_t, int, int); char *salloc(int); char *savestr(char *); FILE *setinput(struct message *); char *skin(char *); char *skip_comment(char *); char *snarf(char *, int *); char *username(void); char *value(const char *); char *vcopy(const char *); char *yankword(char *, char *); char *yanklogin(char *, char *); int Fclose(FILE *); int More(void *); int Pclose(FILE *); -int Respond(int *); +int Respond(void *); int Type(void *); int doRespond(int *); int dorespond(int *); void alter(char *); -int alternates(char **); +int alternates(void *); void announce(void); int append(struct message *, FILE *); int argcount(char **); void assign(const char *, const char *); int bangexp(char *, size_t); void brokpipe(int); int charcount(char *, int); int check(int, int); void clob1(int); -int clobber(char **); +int clobber(void *); void close_all_files(void); int cmatch(char *, char *); void collhup(int); void collint(int); void collstop(int); void commands(void); int copycmd(void *v); -int core(void); +int core(void *); int count(struct name *); int deletecmd(void *); int delm(int *); int deltype(void *); void demail(void); int diction(const void *, const void *); -int dosh(char *); -int echo(char **); +int dosh(void *); +int echo(void *); int edit1(int *, int); -int editor(int *); +int editor(void *); void edstop(void); -int elsecmd(void); -int endifcmd(void); +int elsecmd(void *); +int endifcmd(void *); int evalcol(int); int execute(char *, int); int exwrite(char *, FILE *, int); void fail(const char *, const char *); -int file(char **); +int file(void *); struct grouphead * findgroup(char *); void findmail(char *, char *, int); int first(int, int); void fixhead(struct header *, struct name *); void fmt(const char *, struct name *, FILE *, int); -int folders(void); +int folders(void *); int forward(char *, FILE *, char *, int); void free_child(int); int from(void *); off_t fsize(FILE *); int getfold(char *, int); int gethfield(FILE *, char *, int, char **); int getmsglist(char *, int *, int); int getrawlist(char *, char **, int); uid_t getuserid(char *); int grabh(struct header *, int); int group(void *); void hangup(int); int hash(const char *); void hdrstop(int); int headers(void *); -int help(void); +int help(void *); void holdsigs(void); -int ifcmd(char **); +int ifcmd(void *); int igcomp(const void *, const void *); int igfield(void *); int ignore1(char **, struct ignoretab *, const char *); int igshow(struct ignoretab *, const char *); int inc(void *); int incfile(void); void intr(int); int isdate(char *); int isdir(char *); int isfileaddr(char *); int ishead(char *); int isign(const char *, struct ignoretab [2]); int isprefix(const char *, const char *); void istrncpy(char *, const char *, size_t); const struct cmd * lex(char *); void load(char *); struct var * lookup(const char *); int mail(struct name *, struct name *, struct name *, struct name *, char *, char *); void mail1(struct header *, int); void makemessage(FILE *, int); void mark(int); int markall(char *, int); int matchsender(char *, int); int matchfield(char *, int); int mboxit(void *); int member(char *, struct ignoretab *); void mesedit(FILE *, int); void mespipe(FILE *, char *); int messize(void *); int metamess(int, int); int more(void *); int newfileinfo(int); int next(void *); -int null(int); +int null(void *); void parse(char *, struct headline *, char *); -int pcmdlist(void); -int pdot(void); +int pcmdlist(void *); +int pdot(void *); void prepare_child(sigset_t *, int, int); int preserve(void *); void prettyprint(struct name *); void printgroup(char *); void printhead(int); int puthead(struct header *, FILE *, int); int putline(FILE *, char *, int); -int pversion(int); +int pversion(void *); void quit(void); -int quitcmd(void); +int quitcmd(void *); int readline(FILE *, char *, int); void register_file(FILE *, int, int); void regret(int); void relsesigs(void); int respond(void *); int retfield(void *); int rexit(void *); int rm(char *); int run_command(char *, sigset_t *, int, int, ...); int save(void *v); int save1(char *, int, const char *, struct ignoretab *); void savedeadletter(FILE *); int saveigfield(void *); int savemail(char *, FILE *); int saveretfield(void *); int scan(char **); void scaninit(void); int schdir(void *); int screensize(void); int scroll(void *); int sendmessage(struct message *, FILE *, struct ignoretab *, char *); -int sendmail(char *); +int sendmail(void *); int set(void *); int setfile(char *); void setmsize(int); void setptr(FILE *, off_t); void setscreensize(void); -int shell(char *); +int shell(void *); void sigchild(int); void sort(char **); -int source(char **); +int source(void *); void spreserve(void); void sreset(void); int start_command(char *, sigset_t *, int, int, ...); void statusput(struct message *, FILE *, char *); void stop(int); int stouch(void *); int swrite(void *); void tinit(void); int top(void *); void touch(struct message *); void ttyint(int); void ttystop(int); int type(void *); int type1(int *, int, int); int undeletecmd(void *); void unmark(int); char **unpack(struct name *); int unread(void *); void unregister_file(FILE *); int unset(void *); int unstack(void); void vfree(char *); -int visual(int *); +int visual(void *); int wait_child(int); int wait_command(int); int writeback(FILE *); extern char *__progname; extern char *tmpdir; diff --git a/usr.bin/mail/lex.c b/usr.bin/mail/lex.c index 77004e52f28c..dfca9edeab8e 100644 --- a/usr.bin/mail/lex.c +++ b/usr.bin/mail/lex.c @@ -1,701 +1,701 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)lex.c 8.2 (Berkeley) 4/20/95"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #include "rcv.h" #include #include #include "extern.h" /* * Mail -- a mail program * * Lexical processing of commands. */ static const char *prompt = "& "; extern const struct cmd cmdtab[]; extern const char *version; /* * 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. * * If the -e option is being passed to mail, this function has a * tri-state return code: -1 on error, 0 on no mail, 1 if there is * mail. */ int setfile(char *name) { FILE *ibuf; int checkmode, i, fd; struct stat stb; char isedit = *name != '%' || getuserid(myname) != getuid(); char *who = name[1] ? name + 1 : myname; char tempname[PATHSIZE]; static int shudclob; checkmode = value("checkmode") != NULL; if ((name = expand(name)) == NULL) return (-1); if ((ibuf = Fopen(name, "r")) == NULL) { if (!isedit && errno == ENOENT) goto nomail; warn("%s", name); return (-1); } if (fstat(fileno(ibuf), &stb) < 0) { warn("fstat"); (void)Fclose(ibuf); return (-1); } if (S_ISDIR(stb.st_mode) || !S_ISREG(stb.st_mode)) { (void)Fclose(ibuf); errno = S_ISDIR(stb.st_mode) ? EISDIR : EINVAL; warn("%s", 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 (void)close(i); if (shudclob) { (void)fclose(itf); (void)fclose(otf); } shudclob = 1; edit = isedit; strlcpy(prevfile, mailname, sizeof(prevfile)); if (name != mailname) strlcpy(mailname, name, sizeof(mailname)); mailsize = fsize(ibuf); (void)snprintf(tempname, sizeof(tempname), "%s/mail.RxXXXXXXXXXX", tmpdir); if ((fd = mkstemp(tempname)) == -1 || (otf = fdopen(fd, "w")) == NULL) err(1, "%s", tempname); (void)fcntl(fileno(otf), F_SETFD, 1); if ((itf = fopen(tempname, "r")) == NULL) err(1, "%s", tempname); (void)fcntl(fileno(itf), F_SETFD, 1); (void)rm(tempname); setptr(ibuf, 0); setmsize(msgCount); /* * New mail may have arrived while we were reading * the mail file, so reset mailsize to be where * we really are in the file... */ mailsize = ftello(ibuf); (void)Fclose(ibuf); relsesigs(); sawcom = 0; if ((checkmode || !edit) && msgCount == 0) { nomail: if (!checkmode) { fprintf(stderr, "No mail for %s\n", who); return (-1); } else return (0); } return (checkmode ? 1 : 0); } /* * Incorporate any new mail that has arrived since we first * started reading mail. */ int incfile(void) { off_t newsize; int omsgCount = msgCount; FILE *ibuf; ibuf = Fopen(mailname, "r"); if (ibuf == NULL) return (-1); holdsigs(); newsize = fsize(ibuf); if (newsize == 0) return (-1); /* mail box is now empty??? */ if (newsize < mailsize) return (-1); /* mail box has shrunk??? */ if (newsize == mailsize) return (0); /* no new mail */ setptr(ibuf, mailsize); setmsize(msgCount); mailsize = ftello(ibuf); (void)Fclose(ibuf); relsesigs(); return (msgCount - omsgCount); } static int *msgvec; static 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(void) { int n, eofloop = 0; char linebuf[LINESIZE]; if (!sourcing) { if (signal(SIGINT, SIG_IGN) != SIG_IGN) (void)signal(SIGINT, intr); if (signal(SIGHUP, SIG_IGN) != SIG_IGN) (void)signal(SIGHUP, hangup); (void)signal(SIGTSTP, stop); (void)signal(SIGTTOU, stop); (void)signal(SIGTTIN, stop); } setexit(); for (;;) { /* * Print the prompt, if needed. Clear out * string space, and flush the output. */ if (!sourcing && value("interactive") != NULL) { if ((value("autoinc") != NULL) && (incfile() > 0)) printf("New mail has arrived.\n"); reset_on_stop = 1; printf("%s", prompt); } (void)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") != NULL && value("ignoreeof") != NULL && ++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(char linebuf[], int contxt) { char word[LINESIZE]; char *arglist[MAXARGC]; const struct cmd *com; char *cp, *cp2; int c, 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((unsigned char)*cp); cp++) ; if (*cp == '!') { if (sourcing) { printf("Can't \"!\" while sourcing\n"); goto out; } shell(cp+1); return (0); } cp2 = word; while (*cp != '\0' && strchr(" \t0123456789$^.:/-+*'\"", *cp) == NULL) *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 == NULL) { 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] = 0; } 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((unsigned char)*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: errx(1, "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 (com == NULL) return (0); if (value("autoprint") != NULL && 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(int sz) { if (msgvec != NULL) (void)free(msgvec); msgvec = calloc((unsigned)(sz + 1), sizeof(*msgvec)); } /* * Find the correct command in the command table corresponding * to the passed command "word" */ const struct cmd * lex(char word[]) { const struct cmd *cp; /* * ignore trailing chars after `#' * * lines with beginning `#' are comments * spaces before `#' are ignored in execute() */ if (*word == '#') *(word+1) = '\0'; for (cp = &cmdtab[0]; cp->c_name != NULL; cp++) if (isprefix(word, cp->c_name)) return (cp); return (NULL); } /* * Determine if as1 is a valid prefix of as2. * Return true if yep. */ int isprefix(const char *as1, const char *as2) { const 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. */ static int inithdr; /* am printing startup headers */ void intr(int s __unused) { noreset = 0; if (!inithdr) sawcom++; inithdr = 0; while (sourcing) unstack(); close_all_files(); if (image >= 0) { (void)close(image); image = -1; } fprintf(stderr, "Interrupt\n"); reset(0); } /* * When we wake up after ^Z, reprint the prompt. */ void stop(int s) { sig_t old_action = signal(s, SIG_DFL); sigset_t nset; (void)sigemptyset(&nset); (void)sigaddset(&nset, s); (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); (void)kill(0, s); (void)sigprocmask(SIG_BLOCK, &nset, NULL); (void)signal(s, old_action); if (reset_on_stop) { reset_on_stop = 0; reset(0); } } /* * Branch here on hangup signal and simulate "exit". */ void hangup(int s __unused) { /* nothing to do? */ exit(1); } /* * Announce the presence of the current Mail version, * give the message count, and print a header listing. */ void announce(void) { int vec[2], mdot; mdot = newfileinfo(0); vec[0] = mdot; vec[1] = 0; dot = &message[mdot - 1]; if (msgCount > 0 && value("noheader") == NULL) { inithdr++; headers(vec); inithdr = 0; } } /* * Announce information about the file we are editing. * Return a likely place to set dot. */ int newfileinfo(int omsgCount) { struct message *mp; int u, n, mdot, d, s; char fname[PATHSIZE+1], zname[PATHSIZE+1], *ename; for (mp = &message[omsgCount]; mp < &message[msgCount]; mp++) if (mp->m_flag & MNEW) break; if (mp >= &message[msgCount]) for (mp = &message[omsgCount]; mp < &message[msgCount]; mp++) if ((mp->m_flag & MREAD) == 0) break; if (mp < &message[msgCount]) mdot = mp - &message[0] + 1; else mdot = omsgCount + 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, sizeof(fname) - 1) >= 0) { strcat(fname, "/"); if (strncmp(fname, mailname, strlen(fname)) == 0) { (void)snprintf(zname, sizeof(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. */ int -pversion(int e __unused) +pversion(void *arg __unused) { printf("Version %s\n", version); return (0); } /* * Load a file of user definitions. */ void load(char *name) { 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; (void)Fclose(in); } diff --git a/usr.bin/mail/quit.c b/usr.bin/mail/quit.c index 7b44f386bd53..e9d93285c65e 100644 --- a/usr.bin/mail/quit.c +++ b/usr.bin/mail/quit.c @@ -1,497 +1,497 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)quit.c 8.2 (Berkeley) 4/28/95"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #include "rcv.h" #include #include "extern.h" /* * Rcv -- receive mail rationally. * * Termination processing. */ /* * The "quit" command. */ int -quitcmd(void) +quitcmd(void *arg __unused) { /* * If we are sourcing, then return 1 so execute() can handle it. * Otherwise, return -1 to abort command loop. */ if (sourcing) return (1); return (-1); } /* * Save all of the undetermined messages at the top of "mbox" * Save all untouched messages back in the system mailbox. * Remove the system mailbox, if none saved there. */ void quit(void) { int mcount, p, modify, autohold, anystat, holdbit, nohold; FILE *ibuf, *obuf, *fbuf, *rbuf, *readstat, *abuf; struct message *mp; int c, fd; struct stat minfo; char *mbox, tempname[PATHSIZE]; /* * If we are read only, we can't do anything, * so just return quickly. */ if (readonly) return; /* * If editing (not reading system mail box), then do the work * in edstop() */ if (edit) { edstop(); return; } /* * See if there any messages to save in mbox. If no, we * can save copying mbox to /tmp and back. * * Check also to see if any files need to be preserved. * Delete all untouched messages to keep them out of mbox. * If all the messages are to be preserved, just exit with * a message. */ fbuf = Fopen(mailname, "r"); if (fbuf == NULL) goto newmail; (void)flock(fileno(fbuf), LOCK_EX); rbuf = NULL; if (fstat(fileno(fbuf), &minfo) >= 0 && minfo.st_size > mailsize) { printf("New mail has arrived.\n"); (void)snprintf(tempname, sizeof(tempname), "%s/mail.RqXXXXXXXXXX", tmpdir); if ((fd = mkstemp(tempname)) == -1 || (rbuf = Fdopen(fd, "w")) == NULL) goto newmail; #ifdef APPEND (void)fseeko(fbuf, mailsize, SEEK_SET); while ((c = getc(fbuf)) != EOF) (void)putc(c, rbuf); #else p = minfo.st_size - mailsize; while (p-- > 0) { c = getc(fbuf); if (c == EOF) goto newmail; (void)putc(c, rbuf); } #endif (void)Fclose(rbuf); if ((rbuf = Fopen(tempname, "r")) == NULL) goto newmail; (void)rm(tempname); } /* * Adjust the message flags in each message. */ anystat = 0; autohold = value("hold") != NULL; holdbit = autohold ? MPRESERVE : MBOX; nohold = MBOX|MSAVED|MDELETED|MPRESERVE; if (value("keepsave") != NULL) nohold &= ~MSAVED; for (mp = &message[0]; mp < &message[msgCount]; mp++) { if (mp->m_flag & MNEW) { mp->m_flag &= ~MNEW; mp->m_flag |= MSTATUS; } if (mp->m_flag & MSTATUS) anystat++; if ((mp->m_flag & MTOUCH) == 0) mp->m_flag |= MPRESERVE; if ((mp->m_flag & nohold) == 0) mp->m_flag |= holdbit; } modify = 0; if (Tflag != NULL) { if ((readstat = Fopen(Tflag, "w")) == NULL) Tflag = NULL; } for (c = 0, p = 0, mp = &message[0]; mp < &message[msgCount]; mp++) { if (mp->m_flag & MBOX) c++; if (mp->m_flag & MPRESERVE) p++; if (mp->m_flag & MODIFY) modify++; if (Tflag != NULL && (mp->m_flag & (MREAD|MDELETED)) != 0) { char *id; if ((id = hfield("article-id", mp)) != NULL) fprintf(readstat, "%s\n", id); } } if (Tflag != NULL) (void)Fclose(readstat); if (p == msgCount && !modify && !anystat) { printf("Held %d message%s in %s\n", p, p == 1 ? "" : "s", mailname); (void)Fclose(fbuf); return; } if (c == 0) { if (p != 0) { writeback(rbuf); (void)Fclose(fbuf); return; } goto cream; } /* * Create another temporary file and copy user's mbox file * darin. If there is no mbox, copy nothing. * If he has specified "append" don't copy his mailbox, * just copy saveable entries at the end. */ mbox = expand("&"); mcount = c; if (value("append") == NULL) { (void)snprintf(tempname, sizeof(tempname), "%s/mail.RmXXXXXXXXXX", tmpdir); if ((fd = mkstemp(tempname)) == -1 || (obuf = Fdopen(fd, "w")) == NULL) { warn("%s", tempname); (void)Fclose(fbuf); return; } if ((ibuf = Fopen(tempname, "r")) == NULL) { warn("%s", tempname); (void)rm(tempname); (void)Fclose(obuf); (void)Fclose(fbuf); return; } (void)rm(tempname); if ((abuf = Fopen(mbox, "r")) != NULL) { while ((c = getc(abuf)) != EOF) (void)putc(c, obuf); (void)Fclose(abuf); } if (ferror(obuf)) { warnx("%s", tempname); (void)Fclose(ibuf); (void)Fclose(obuf); (void)Fclose(fbuf); return; } (void)Fclose(obuf); if ((fd = open(mbox, O_CREAT | O_TRUNC | O_WRONLY, 0600)) >= 0) (void)close(fd); if ((obuf = Fopen(mbox, "r+")) == NULL) { warn("%s", mbox); (void)Fclose(ibuf); (void)Fclose(fbuf); return; } } if (value("append") != NULL) { if ((obuf = Fopen(mbox, "a")) == NULL) { warn("%s", mbox); (void)Fclose(fbuf); return; } (void)fchmod(fileno(obuf), 0600); } for (mp = &message[0]; mp < &message[msgCount]; mp++) if (mp->m_flag & MBOX) if (sendmessage(mp, obuf, saveignore, NULL) < 0) { warnx("%s", mbox); (void)Fclose(ibuf); (void)Fclose(obuf); (void)Fclose(fbuf); return; } /* * Copy the user's old mbox contents back * to the end of the stuff we just saved. * If we are appending, this is unnecessary. */ if (value("append") == NULL) { rewind(ibuf); c = getc(ibuf); while (c != EOF) { (void)putc(c, obuf); if (ferror(obuf)) break; c = getc(ibuf); } (void)Fclose(ibuf); } (void)fflush(obuf); trunc(obuf); if (ferror(obuf)) { warn("%s", mbox); (void)Fclose(obuf); (void)Fclose(fbuf); return; } (void)Fclose(obuf); if (mcount == 1) printf("Saved 1 message in mbox\n"); else printf("Saved %d messages in mbox\n", mcount); /* * Now we are ready to copy back preserved files to * the system mailbox, if any were requested. */ if (p != 0) { writeback(rbuf); (void)Fclose(fbuf); return; } /* * Finally, remove his /var/mail file. * If new mail has arrived, copy it back. */ cream: if (rbuf != NULL) { abuf = Fopen(mailname, "r+"); if (abuf == NULL) goto newmail; while ((c = getc(rbuf)) != EOF) (void)putc(c, abuf); (void)Fclose(rbuf); trunc(abuf); (void)Fclose(abuf); alter(mailname); (void)Fclose(fbuf); return; } demail(); (void)Fclose(fbuf); return; newmail: printf("Thou hast new mail.\n"); if (fbuf != NULL) (void)Fclose(fbuf); } /* * Preserve all the appropriate messages back in the system * mailbox, and print a nice message indicated how many were * saved. On any error, just return -1. Else return 0. * Incorporate the any new mail that we found. */ int writeback(FILE *res) { struct message *mp; int p, c; FILE *obuf; p = 0; if ((obuf = Fopen(mailname, "r+")) == NULL) { warn("%s", mailname); return (-1); } #ifndef APPEND if (res != NULL) while ((c = getc(res)) != EOF) (void)putc(c, obuf); #endif for (mp = &message[0]; mp < &message[msgCount]; mp++) if ((mp->m_flag&MPRESERVE)||(mp->m_flag&MTOUCH)==0) { p++; if (sendmessage(mp, obuf, NULL, NULL) < 0) { warnx("%s", mailname); (void)Fclose(obuf); return (-1); } } #ifdef APPEND if (res != NULL) while ((c = getc(res)) != EOF) (void)putc(c, obuf); #endif (void)fflush(obuf); trunc(obuf); if (ferror(obuf)) { warn("%s", mailname); (void)Fclose(obuf); return (-1); } if (res != NULL) (void)Fclose(res); (void)Fclose(obuf); alter(mailname); if (p == 1) printf("Held 1 message in %s\n", mailname); else printf("Held %d messages in %s\n", p, mailname); return (0); } /* * Terminate an editing session by attempting to write out the user's * file from the temporary. Save any new stuff appended to the file. */ void edstop(void) { int gotcha, c; struct message *mp; FILE *obuf, *ibuf, *readstat; struct stat statb; char tempname[PATHSIZE]; if (readonly) return; holdsigs(); if (Tflag != NULL) { if ((readstat = Fopen(Tflag, "w")) == NULL) Tflag = NULL; } for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) { if (mp->m_flag & MNEW) { mp->m_flag &= ~MNEW; mp->m_flag |= MSTATUS; } if (mp->m_flag & (MODIFY|MDELETED|MSTATUS)) gotcha++; if (Tflag != NULL && (mp->m_flag & (MREAD|MDELETED)) != 0) { char *id; if ((id = hfield("article-id", mp)) != NULL) fprintf(readstat, "%s\n", id); } } if (Tflag != NULL) (void)Fclose(readstat); if (!gotcha || Tflag != NULL) goto done; ibuf = NULL; if (stat(mailname, &statb) >= 0 && statb.st_size > mailsize) { int fd; (void)snprintf(tempname, sizeof(tempname), "%s/mbox.XXXXXXXXXX", tmpdir); if ((fd = mkstemp(tempname)) == -1 || (obuf = Fdopen(fd, "w")) == NULL) { warn("%s", tempname); relsesigs(); reset(0); } if ((ibuf = Fopen(mailname, "r")) == NULL) { warn("%s", mailname); (void)Fclose(obuf); (void)rm(tempname); relsesigs(); reset(0); } (void)fseeko(ibuf, mailsize, SEEK_SET); while ((c = getc(ibuf)) != EOF) (void)putc(c, obuf); (void)Fclose(ibuf); (void)Fclose(obuf); if ((ibuf = Fopen(tempname, "r")) == NULL) { warn("%s", tempname); (void)rm(tempname); relsesigs(); reset(0); } (void)rm(tempname); } printf("\"%s\" ", mailname); (void)fflush(stdout); if ((obuf = Fopen(mailname, "r+")) == NULL) { warn("%s", mailname); relsesigs(); reset(0); } trunc(obuf); c = 0; for (mp = &message[0]; mp < &message[msgCount]; mp++) { if ((mp->m_flag & MDELETED) != 0) continue; c++; if (sendmessage(mp, obuf, NULL, NULL) < 0) { warnx("%s", mailname); relsesigs(); reset(0); } } gotcha = (c == 0 && ibuf == NULL); if (ibuf != NULL) { while ((c = getc(ibuf)) != EOF) (void)putc(c, obuf); (void)Fclose(ibuf); } (void)fflush(obuf); if (ferror(obuf)) { warn("%s", mailname); relsesigs(); reset(0); } (void)Fclose(obuf); if (gotcha) { (void)rm(mailname); printf("removed\n"); } else printf("complete\n"); (void)fflush(stdout); done: relsesigs(); } diff --git a/usr.bin/mail/send.c b/usr.bin/mail/send.c index 70bc5261f805..702dbd031216 100644 --- a/usr.bin/mail/send.c +++ b/usr.bin/mail/send.c @@ -1,592 +1,592 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)send.c 8.1 (Berkeley) 6/6/93"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #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 sendmessage(struct message *mp, FILE *obuf, struct ignoretab *doign, char *prefix) { long count; FILE *ibuf; char *cp, *cp2, line[LINESIZE]; int ishead, infld, ignoring, dostat, firstline; int c = 0, length, prefixlen; /* * Compute the prefix string, without trailing whitespace */ if (prefix != NULL) { cp2 = 0; for (cp = prefix; *cp != '\0'; cp++) if (*cp != ' ' && *cp != '\t') cp2 = cp; prefixlen = cp2 == NULL ? 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, sizeof(line), 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++) != '\0' && c != ':' && !isspace((unsigned char)c);) ; cp2 = --cp; while (isspace((unsigned char)*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 != NULL) { 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 != NULL) while (count > 0) { if (fgets(line, sizeof(line), 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(struct message *mp, FILE *obuf, char *prefix) { char statout[3]; char *cp = statout; if (mp->m_flag & MREAD) *cp++ = 'R'; if ((mp->m_flag & MNEW) == 0) *cp++ = 'O'; *cp = '\0'; if (statout[0] != '\0') fprintf(obuf, "%sStatus: %s\n", prefix == NULL ? "" : prefix, statout); } /* * Interface between the argument list and the mail1 routine * which does all the dirty work. */ int mail(struct name *to, struct name *cc, struct name *bcc, struct name *smopts, char *subject, char *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 = NULL; mail1(&head, 0); return (0); } /* * Send mail to a bunch of user names. The interface is through * the mail routine below. */ int -sendmail(char *str) +sendmail(void *str) { struct header head; head.h_to = extract(str, GTO); head.h_subject = NULL; head.h_cc = NULL; head.h_bcc = NULL; head.h_smopts = NULL; head.h_replyto = value("REPLYTO"); head.h_inreplyto = NULL; mail1(&head, 0); return (0); } /* * Mail a message on standard input to the people indicated * in the passed header. (Internal interface). */ void mail1(struct header *hp, int printheaders) { char *cp; char *nbuf; int pid; char **namelist; struct name *to, *nsto; FILE *mtf; /* * Collect user's mail from standard input. * Get the result as mtf. */ if ((mtf = collect(hp, printheaders)) == NULL) return; if (value("interactive") != NULL) { if (value("askcc") != NULL || value("askbcc") != NULL) { if (value("askcc") != NULL) grabh(hp, GCC); if (value("askbcc") != NULL) grabh(hp, GBCC); } else { printf("EOT\n"); (void)fflush(stdout); } } if (fsize(mtf) == 0) { if (value("dontsendempty") != NULL) goto out; if (hp->h_subject == NULL) 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 == NULL) { 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; if (value("recordrecip") != NULL) { /* * Before fixing the header, save old To:. * We do this because elide above has sorted To: list, and * we would like to save message in a file named by the first * recipient the user has entered, not the one being the first * after sorting happened. */ if ((nsto = malloc(sizeof(struct name))) == NULL) err(1, "Out of memory"); bcopy(hp->h_to, nsto, sizeof(struct name)); } 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 != NULL; t++) printf(" \"%s\"", *t); printf("\n"); goto out; } if (value("recordrecip") != NULL) { /* * Extract first recipient username from saved To: and use it * as a filename. */ if ((nbuf = malloc(strlen(detract(nsto, 0)) + 1)) == NULL) err(1, "Out of memory"); if ((cp = yanklogin(detract(nsto, 0), nbuf)) != NULL) (void)savemail(expand(nbuf), mtf); free(nbuf); free(nsto); } else if ((cp = value("record")) != NULL) (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) { warn("fork"); savedeadletter(mtf); goto out; } if (pid == 0) { sigset_t nset; (void)sigemptyset(&nset); (void)sigaddset(&nset, SIGHUP); (void)sigaddset(&nset, SIGINT); (void)sigaddset(&nset, SIGQUIT); (void)sigaddset(&nset, SIGTSTP); (void)sigaddset(&nset, SIGTTIN); (void)sigaddset(&nset, SIGTTOU); prepare_child(&nset, fileno(mtf), -1); if ((cp = value("sendmail")) != NULL) cp = expand(cp); else cp = _PATH_SENDMAIL; execv(cp, namelist); warn("%s", cp); _exit(1); } if (value("verbose") != NULL) (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(struct header *hp, struct name *tolist) { struct name *np; hp->h_to = NULL; hp->h_cc = NULL; hp->h_bcc = NULL; for (np = tolist; np != NULL; np = np->n_flink) { /* Don't copy deleted addresses to the header */ if (np->n_type & GDEL) continue; 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(struct header *hp, FILE *fi) { FILE *nfo, *nfi; int c, fd; char tempname[PATHSIZE]; (void)snprintf(tempname, sizeof(tempname), "%s/mail.RsXXXXXXXXXX", tmpdir); if ((fd = mkstemp(tempname)) == -1 || (nfo = Fdopen(fd, "w")) == NULL) { warn("%s", tempname); return (fi); } if ((nfi = Fopen(tempname, "r")) == NULL) { warn("%s", tempname); (void)Fclose(nfo); (void)rm(tempname); return (fi); } (void)rm(tempname); (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)) { warnx("read"); rewind(fi); return (fi); } (void)fflush(nfo); if (ferror(nfo)) { warn("%s", tempname); (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(struct header *hp, FILE *fo, int w) { int gotcha; gotcha = 0; if (hp->h_to != NULL && w & GTO) fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++; if (hp->h_subject != NULL && w & GSUBJECT) fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++; if (hp->h_cc != NULL && w & GCC) fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++; if (hp->h_bcc != NULL && w & GBCC) fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++; if (hp->h_replyto != NULL && w & GREPLYTO) fprintf(fo, "Reply-To: %s\n", hp->h_replyto), gotcha++; if (hp->h_inreplyto != NULL && 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(const char *str, struct name *np, FILE *fo, int comma) { int col, len; comma = comma ? 1 : 0; col = strlen(str); if (col) fputs(str, fo); for (; np != NULL; np = np->n_flink) { if (np->n_flink == NULL) comma = 0; len = strlen(np->n_name); col++; /* for the space */ if (col + len + comma > 72 && col > 4) { fprintf(fo, "\n "); col = 4; } else fprintf(fo, " "); fputs(np->n_name, fo); if (comma) fprintf(fo, ","); col += len + comma; } fprintf(fo, "\n"); } /* * Save the outgoing mail on the passed file. */ /*ARGSUSED*/ int savemail(char name[], FILE *fi) { FILE *fo; char buf[BUFSIZ]; int i; time_t now; mode_t saved_umask; saved_umask = umask(077); fo = Fopen(name, "a"); umask(saved_umask); if (fo == NULL) { warn("%s", 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); fprintf(fo, "\n"); (void)fflush(fo); if (ferror(fo)) warn("%s", name); (void)Fclose(fo); rewind(fi); return (0); } diff --git a/usr.bin/mail/util.c b/usr.bin/mail/util.c index 558b47245c77..7187d1b520f9 100644 --- a/usr.bin/mail/util.c +++ b/usr.bin/mail/util.c @@ -1,600 +1,601 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)aux.c 8.1 (Berkeley) 6/6/93"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include "rcv.h" #include "extern.h" /* * Mail -- a mail program * * Auxiliary functions. */ static char *save2str(char *, char *); /* * Return a pointer to a dynamic copy of the argument. */ char * savestr(char *str) { char *new; int size = strlen(str) + 1; if ((new = salloc(size)) != NULL) bcopy(str, new, size); return (new); } /* * Make a copy of new argument incorporating old one. */ static char * save2str(char *str, char *old) { char *new; int newsize = strlen(str) + 1; int oldsize = old ? strlen(old) + 1 : 0; if ((new = salloc(newsize + oldsize)) != NULL) { if (oldsize) { bcopy(old, new, oldsize); new[oldsize - 1] = ' '; } bcopy(str, new + oldsize, newsize); } return (new); } /* * Touch the named message by setting its MTOUCH flag. * Touched messages have the effect of not being sent * back to the system mailbox on exit. */ void touch(struct message *mp) { mp->m_flag |= MTOUCH; if ((mp->m_flag & MREAD) == 0) mp->m_flag |= MREAD|MSTATUS; } /* * Test to see if the passed file name is a directory. * Return true if it is. */ int isdir(char name[]) { struct stat sbuf; if (stat(name, &sbuf) < 0) return (0); return (S_ISDIR(sbuf.st_mode)); } /* * Count the number of arguments in the given string raw list. */ int argcount(char **argv) { char **ap; for (ap = argv; *ap++ != NULL;) ; return (ap - argv - 1); } /* * Return the desired header line from the passed message * pointer (or NULL if the desired header field is not available). */ char * hfield(const char *field, struct message *mp) { FILE *ibuf; char linebuf[LINESIZE]; int lc; char *hfield; char *colon, *oldhfield = NULL; ibuf = setinput(mp); if ((lc = mp->m_lines - 1) < 0) return (NULL); if (readline(ibuf, linebuf, LINESIZE) < 0) return (NULL); while (lc > 0) { if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0) return (oldhfield); if ((hfield = ishfield(linebuf, colon, field)) != NULL) oldhfield = save2str(hfield, oldhfield); } return (oldhfield); } /* * Return the next header field found in the given message. * Return >= 0 if something found, < 0 elsewise. * "colon" is set to point to the colon in the header. * Must deal with \ continuations & other such fraud. */ int gethfield(FILE *f, char linebuf[], int rem, char **colon) { char line2[LINESIZE]; char *cp, *cp2; int c; for (;;) { if (--rem < 0) return (-1); if ((c = readline(f, linebuf, LINESIZE)) <= 0) return (-1); for (cp = linebuf; isprint((unsigned char)*cp) && *cp != ' ' && *cp != ':'; cp++) ; if (*cp != ':' || cp == linebuf) continue; /* * I guess we got a headline. * Handle wraparounding */ *colon = cp; cp = linebuf + c; for (;;) { while (--cp >= linebuf && (*cp == ' ' || *cp == '\t')) ; cp++; if (rem <= 0) break; ungetc(c = getc(f), f); if (c != ' ' && c != '\t') break; if ((c = readline(f, line2, LINESIZE)) < 0) break; rem--; for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++) ; c -= cp2 - line2; if (cp + c >= linebuf + LINESIZE - 2) break; *cp++ = ' '; bcopy(cp2, cp, c); cp += c; } *cp = 0; return (rem); } /* NOTREACHED */ } /* * Check whether the passed line is a header line of * the desired breed. Return the field body, or 0. */ char* ishfield(char *linebuf, char *colon, const char *field) { char *cp = colon; *cp = 0; if (strcasecmp(linebuf, field) != 0) { *cp = ':'; return (0); } *cp = ':'; for (cp++; *cp == ' ' || *cp == '\t'; cp++) ; return (cp); } /* * Copy a string and lowercase the result. * dsize: space left in buffer (including space for NULL) */ void istrncpy(char *dest, const char *src, size_t dsize) { strlcpy(dest, src, dsize); for (; *dest; dest++) *dest = tolower((unsigned char)*dest); } /* * The following code deals with input stacking to do source * commands. All but the current file pointer are saved on * the stack. */ static int ssp; /* Top of file stack */ struct sstack { FILE *s_file; /* File we were in. */ int s_cond; /* Saved state of conditionals */ int s_loading; /* Loading .mailrc, etc. */ }; #define SSTACK_SIZE 64 /* XXX was NOFILE. */ static struct sstack sstack[SSTACK_SIZE]; /* * Pushdown current input file and switch to a new one. * Set the global flag "sourcing" so that others will realize * that they are no longer reading from a tty (in all probability). */ int -source(char **arglist) +source(void *arg) { + char **arglist = arg; FILE *fi; char *cp; if ((cp = expand(*arglist)) == NULL) return (1); if ((fi = Fopen(cp, "r")) == NULL) { warn("%s", cp); return (1); } if (ssp >= SSTACK_SIZE - 1) { printf("Too much \"sourcing\" going on.\n"); (void)Fclose(fi); return (1); } sstack[ssp].s_file = input; sstack[ssp].s_cond = cond; sstack[ssp].s_loading = loading; ssp++; loading = 0; cond = CANY; input = fi; sourcing++; return (0); } /* * Pop the current input back to the previous level. * Update the "sourcing" flag as appropriate. */ int unstack(void) { if (ssp <= 0) { printf("\"Source\" stack over-pop.\n"); sourcing = 0; return (1); } (void)Fclose(input); if (cond != CANY) printf("Unmatched \"if\"\n"); ssp--; cond = sstack[ssp].s_cond; loading = sstack[ssp].s_loading; input = sstack[ssp].s_file; if (ssp == 0) sourcing = loading; return (0); } /* * Touch the indicated file. * This is nifty for the shell. */ void alter(char *name) { struct timespec ts[2]; (void)clock_gettime(CLOCK_REALTIME, &ts[0]); ts[0].tv_sec++; ts[1].tv_sec = 0; ts[1].tv_nsec = UTIME_OMIT; (void)utimensat(AT_FDCWD, name, ts, 0); } /* * Get sender's name from this message. If the message has * a bunch of arpanet stuff in it, we may have to skin the name * before returning it. */ char * nameof(struct message *mp, int reptype) { char *cp, *cp2; cp = skin(name1(mp, reptype)); if (reptype != 0 || charcount(cp, '!') < 2) return (cp); cp2 = strrchr(cp, '!'); cp2--; while (cp2 > cp && *cp2 != '!') cp2--; if (*cp2 == '!') return (cp2 + 1); return (cp); } /* * Start of a "comment". * Ignore it. */ char * skip_comment(char *cp) { int nesting = 1; for (; nesting > 0 && *cp; cp++) { switch (*cp) { case '\\': if (cp[1]) cp++; break; case '(': nesting++; break; case ')': nesting--; break; } } return (cp); } /* * Skin an arpa net address according to the RFC 822 interpretation * of "host-phrase." */ char * skin(char *name) { char *nbuf, *bufend, *cp, *cp2; int c, gotlt, lastsp; if (name == NULL) return (NULL); if (strchr(name, '(') == NULL && strchr(name, '<') == NULL && strchr(name, ' ') == NULL) return (name); /* We assume that length(input) <= length(output) */ if ((nbuf = malloc(strlen(name) + 1)) == NULL) err(1, "Out of memory"); gotlt = 0; lastsp = 0; bufend = nbuf; for (cp = name, cp2 = bufend; (c = *cp++) != '\0'; ) { switch (c) { case '(': cp = skip_comment(cp); lastsp = 0; break; case '"': /* * Start of a "quoted-string". * Copy it in its entirety. */ while ((c = *cp) != '\0') { cp++; if (c == '"') break; if (c != '\\') *cp2++ = c; else if ((c = *cp) != '\0') { *cp2++ = c; cp++; } } lastsp = 0; break; case ' ': if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ') cp += 3, *cp2++ = '@'; else if (cp[0] == '@' && cp[1] == ' ') cp += 2, *cp2++ = '@'; else lastsp = 1; break; case '<': cp2 = bufend; gotlt++; lastsp = 0; break; case '>': if (gotlt) { gotlt = 0; while ((c = *cp) != '\0' && c != ',') { cp++; if (c == '(') cp = skip_comment(cp); else if (c == '"') while ((c = *cp) != '\0') { cp++; if (c == '"') break; if (c == '\\' && *cp != '\0') cp++; } } lastsp = 0; break; } /* FALLTHROUGH */ default: if (lastsp) { lastsp = 0; *cp2++ = ' '; } *cp2++ = c; if (c == ',' && !gotlt && (*cp == ' ' || *cp == '"' || *cp == '<')) { *cp2++ = ' '; while (*cp == ' ') cp++; lastsp = 0; bufend = cp2; } } } *cp2 = '\0'; if ((cp = realloc(nbuf, strlen(nbuf) + 1)) != NULL) nbuf = cp; return (nbuf); } /* * Fetch the sender's name from the passed message. * Reptype can be * 0 -- get sender's name for display purposes * 1 -- get sender's name for reply * 2 -- get sender's name for Reply */ char * name1(struct message *mp, int reptype) { char namebuf[LINESIZE]; char linebuf[LINESIZE]; char *cp, *cp2; FILE *ibuf; int first = 1; if ((cp = hfield("from", mp)) != NULL) return (cp); if (reptype == 0 && (cp = hfield("sender", mp)) != NULL) return (cp); ibuf = setinput(mp); namebuf[0] = '\0'; if (readline(ibuf, linebuf, LINESIZE) < 0) return (savestr(namebuf)); newname: for (cp = linebuf; *cp != '\0' && *cp != ' '; cp++) ; for (; *cp == ' ' || *cp == '\t'; cp++) ; for (cp2 = &namebuf[strlen(namebuf)]; *cp != '\0' && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;) *cp2++ = *cp++; *cp2 = '\0'; if (readline(ibuf, linebuf, LINESIZE) < 0) return (savestr(namebuf)); if ((cp = strchr(linebuf, 'F')) == NULL) return (savestr(namebuf)); if (strncmp(cp, "From", 4) != 0) return (savestr(namebuf)); while ((cp = strchr(cp, 'r')) != NULL) { if (strncmp(cp, "remote", 6) == 0) { if ((cp = strchr(cp, 'f')) == NULL) break; if (strncmp(cp, "from", 4) != 0) break; if ((cp = strchr(cp, ' ')) == NULL) break; cp++; if (first) { cp2 = namebuf; first = 0; } else cp2 = strrchr(namebuf, '!') + 1; strlcpy(cp2, cp, sizeof(namebuf) - (cp2 - namebuf) - 1); strcat(namebuf, "!"); goto newname; } cp++; } return (savestr(namebuf)); } /* * Count the occurrences of c in str */ int charcount(char *str, int c) { char *cp; int i; for (i = 0, cp = str; *cp != '\0'; cp++) if (*cp == c) i++; return (i); } /* * See if the given header field is supposed to be ignored. */ int isign(const char *field, struct ignoretab ignore[2]) { char realfld[LINESIZE]; if (ignore == ignoreall) return (1); /* * Lower-case the string, so that "Status" and "status" * will hash to the same place. */ istrncpy(realfld, field, sizeof(realfld)); if (ignore[1].i_count > 0) return (!member(realfld, ignore + 1)); else return (member(realfld, ignore)); } int member(char *realfield, struct ignoretab *table) { struct ignore *igp; for (igp = table->i_head[hash(realfield)]; igp != NULL; igp = igp->i_link) if (*igp->i_field == *realfield && equal(igp->i_field, realfield)) return (1); return (0); }