X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/d1d79bc71f3d26685768243338ec4ea2bd115d35..428c2aebd5a3c81a3c47fcb10b1e547614bd8fc4:/usr/src/usr.bin/mail/lex.c diff --git a/usr/src/usr.bin/mail/lex.c b/usr/src/usr.bin/mail/lex.c index f76c673597..e700c8bb56 100644 --- a/usr/src/usr.bin/mail/lex.c +++ b/usr/src/usr.bin/mail/lex.c @@ -1,6 +1,43 @@ -# +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)lex.c 5.23 (Berkeley) 4/1/91"; +#endif /* not lint */ #include "rcv.h" +#include +#include /* * Mail -- a mail program @@ -8,48 +45,69 @@ * Lexical processing of commands. */ -static char *SccsId = "@(#)lex.c 1.14 %G%"; +char *prompt = "& "; /* * Set up editing on the given file name. - * If isedit is true, we are considered to be editing the file, - * otherwise we are reading our mail which has signficance for - * mbox and so forth. + * If the first character of name is %, we are considered to be + * editing the file, otherwise we are reading our mail which has + * signficance for mbox and so forth. */ - -setfile(name, isedit) +setfile(name) char *name; { FILE *ibuf; int i; + struct stat stb; + char isedit = *name != '%'; + char *who = name[1] ? name + 1 : myname; static int shudclob; - static char efile[128]; extern char tempMesg[]; - int (*sigs[2])(); + extern int errno; + + if ((name = expand(name)) == NOSTR) + return -1; - if ((ibuf = fopen(name, "r")) == NULL) { - if (isedit) - perror(name); - else - printf("No mail for %s\n", myname); + if ((ibuf = Fopen(name, "r")) == NULL) { + if (!isedit && errno == ENOENT) + goto nomail; + perror(name); return(-1); } + if (fstat(fileno(ibuf), &stb) < 0) { + perror("fstat"); + Fclose(ibuf); + return (-1); + } + + switch (stb.st_mode & S_IFMT) { + case S_IFDIR: + Fclose(ibuf); + errno = EISDIR; + perror(name); + return (-1); + + case S_IFREG: + break; + + default: + Fclose(ibuf); + errno = EINVAL; + perror(name); + return (-1); + } + /* * Looks like all will be well. We must now relinquish our - * hold on the current set of stuff. Must ignore signals + * 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. */ - for (i = SIGINT; i <= SIGQUIT; i++) - sigs[i - SIGINT] = signal(i, SIG_IGN); - if (shudclob) { - if (edit) - edstop(); - else - quit(); - } + holdsigs(); + if (shudclob) + quit(); /* * Copy the messages into /tmp @@ -67,8 +125,7 @@ setfile(name, isedit) } shudclob = 1; edit = isedit; - strncpy(efile, name, 128); - editfile = efile; + strcpy(prevfile, mailname); if (name != mailname) strcpy(mailname, name); mailsize = fsize(ibuf); @@ -80,96 +137,65 @@ setfile(name, isedit) perror(tempMesg); exit(1); } - remove(tempMesg); + rm(tempMesg); setptr(ibuf); setmsize(msgCount); - fclose(ibuf); - for (i = SIGINT; i <= SIGQUIT; i++) - signal(i, sigs[i - SIGINT]); - shudann = 1; + Fclose(ibuf); + relsesigs(); sawcom = 0; + if (!edit && msgCount == 0) { +nomail: + fprintf(stderr, "No mail for %s\n", who); + return -1; + } return(0); } +int *msgvec; +int reset_on_stop; /* do a reset() if stopped */ + /* * Interpret user commands one by one. If standard input is not a tty, * print no prompt. */ - -int *msgvec; - commands() { - int prompt, firstsw, stop(); + int eofloop = 0; register int n; char linebuf[LINESIZE]; - - if (rcvmode) - if (signal(SIGINT, SIG_IGN) == SIG_DFL) - signal(SIGINT, stop); - input = stdin; - prompt = 1; - if (!intty) - prompt = 0; - firstsw = 1; + void intr(), stop(), hangup(); + + if (!sourcing) { + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, intr); + if (signal(SIGHUP, SIG_IGN) != SIG_IGN) + signal(SIGHUP, hangup); + signal(SIGTSTP, stop); + signal(SIGTTOU, stop); + signal(SIGTTIN, stop); + } + setexit(); for (;;) { - setexit(); - if (firstsw > 0) { - firstsw = 0; - source1(mailrc); - if (!nosrc) - source1(MASTER); - } - - /* - * How's this for obscure: after we - * finish sourcing for the first time, - * go off and print the headers! - */ - - if (shudann && !sourcing) { - shudann = 0; - if (rcvmode) - announce(edit); - } - /* * Print the prompt, if needed. Clear out * string space, and flush the output. */ - - if (!rcvmode && !sourcing) - return; -top: - if (prompt && !sourcing) - printf("_\r"); - flush(); + if (!sourcing && value("interactive") != NOSTR) { + reset_on_stop = 1; + printf(prompt); + } + fflush(stdout); sreset(); - /* * Read a line of commands from the current input * and handle end of file specially. */ - n = 0; for (;;) { - if (readline(input, &linebuf[n]) <= 0) { - if (n != 0) - break; - if (sourcing) { - unstack(); - goto more; - } - if (value("ignoreeof") != NOSTR && prompt) { - printf("Use \"quit\" to quit.\n"); - goto top; - } - if (!edit) { - signal(SIGINT, SIG_IGN); - return; - } - edstop(); - return; + if (readline(input, &linebuf[n], LINESIZE - n) < 0) { + if (n == 0) + n = -1; + break; } if ((n = strlen(linebuf)) == 0) break; @@ -178,19 +204,36 @@ top: break; linebuf[n++] = ' '; } + reset_on_stop = 0; + if (n < 0) { + /* eof */ + if (loading) + break; + if (sourcing) { + unstack(); + continue; + } + if (value("interactive") != NOSTR && + value("ignoreeof") != NOSTR && + ++eofloop < 25) { + printf("Use \"quit\" to quit.\n"); + continue; + } + break; + } + eofloop = 0; if (execute(linebuf, 0)) - return; -more: ; + break; } } /* - * Execute a single command. If the command executed - * is "quit," then return non-zero so that the caller - * will know to return back to main, if he cares. + * Execute a single command. + * Command functions return 0 for success, 1 for error, and -1 + * for abort. A 1 or -1 aborts a load or source. A -1 aborts + * the interactive command loop. * Contxt is non-zero if called while composing mail. */ - execute(linebuf, contxt) char linebuf[]; { @@ -200,7 +243,7 @@ execute(linebuf, contxt) register char *cp, *cp2; register int c; int muvec[2]; - int edstop(), e; + int e = 1; /* * Strip the white space away from the beginning @@ -211,20 +254,18 @@ execute(linebuf, contxt) * lexical conventions. */ - cp = linebuf; - while (any(*cp, " \t")) - cp++; + for (cp = linebuf; isspace(*cp); cp++) + ; if (*cp == '!') { if (sourcing) { printf("Can't \"!\" while sourcing\n"); - unstack(); - return(0); + goto out; } shell(cp+1); return(0); } cp2 = word; - while (*cp && !any(*cp, " \t0123456789$^.:/-+*'\"")) + while (*cp && index(" \t0123456789$^.:/-+*'\"", *cp) == NOSTR) *cp2++ = *cp++; *cp2 = '\0'; @@ -236,14 +277,12 @@ execute(linebuf, contxt) * confusion. */ - if (sourcing && equal(word, "")) + if (sourcing && *word == '\0') return(0); com = lex(word); if (com == NONE) { - printf("What?\n"); - if (sourcing) - unstack(); - return(0); + printf("Unknown command: \"%s\"\n", word); + goto out; } /* @@ -255,21 +294,6 @@ execute(linebuf, contxt) if (cond == CRCV && !rcvmode || cond == CSEND && rcvmode) return(0); - /* - * Special case so that quit causes a return to - * main, who will call the quit code directly. - * If we are in a source file, just unstack. - */ - - if (com->c_func == edstop && sourcing) { - unstack(); - return(0); - } - if (!edit && com->c_func == edstop) { - signal(SIGINT, SIG_IGN); - return(1); - } - /* * Process the arguments to the command, depending * on the type he expects. Default to an error. @@ -280,34 +304,32 @@ execute(linebuf, contxt) if (!rcvmode && (com->c_argtype & M) == 0) { printf("May not execute \"%s\" while sending\n", com->c_name); - if (sourcing) - unstack(); - return(0); + goto out; } if (sourcing && com->c_argtype & I) { printf("May not execute \"%s\" while sourcing\n", com->c_name); - unstack(); - return(0); + goto out; } if (readonly && com->c_argtype & W) { printf("May not execute \"%s\" -- message file is read only\n", com->c_name); - if (sourcing) - unstack(); - return(0); + goto out; } if (contxt && com->c_argtype & R) { printf("Cannot recursively invoke \"%s\"\n", com->c_name); - return(0); + goto out; } - e = 1; 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) { @@ -327,6 +349,10 @@ execute(linebuf, contxt) * 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); @@ -337,7 +363,7 @@ execute(linebuf, contxt) * Just the straight string, with * leading blanks removed. */ - while (any(*cp, " \t")) + while (isspace(*cp)) cp++; e = (*com->c_func)(cp); break; @@ -346,7 +372,8 @@ execute(linebuf, contxt) /* * A vector of strings, in shell style. */ - if ((c = getrawlist(cp, arglist)) < 0) + 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", @@ -373,15 +400,20 @@ execute(linebuf, contxt) panic("Unknown argtype"); } +out: /* * Exit the current source file on * error. */ - - if (e && sourcing) - unstack(); - if (com->c_func == edstop) - return(1); + if (e) { + if (e < 0) + return 1; + if (loading) + return 1; + if (sourcing) + unstack(); + return 0; + } if (value("autoprint") != NOSTR && com->c_argtype & P) if ((dot->m_flag & MDELETED) == 0) { muvec[0] = dot - &message[0] + 1; @@ -401,8 +433,8 @@ execute(linebuf, contxt) setmsize(sz) { - if (msgvec != (int *) 0) - cfree(msgvec); + if (msgvec != 0) + cfree((char *) msgvec); msgvec = (int *) calloc((unsigned) (sz + 1), sizeof *msgvec); } @@ -443,80 +475,96 @@ isprefix(as1, as2) } /* - * The following gets called on receipt of a rubout. This is + * 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. - * The special call to getuserid() is needed so it won't get - * annoyed about losing its open file. * Also, unstack all source files. */ -stop() +int inithdr; /* am printing startup headers */ + +/*ARGSUSED*/ +void +intr(s) { - register FILE *fp; noreset = 0; - signal(SIGINT, SIG_IGN); - sawcom++; + if (!inithdr) + sawcom++; + inithdr = 0; while (sourcing) unstack(); - getuserid((char *) -1); - for (fp = &_iob[0]; fp < &_iob[_NFILE]; fp++) { - if (fp == stdin || fp == stdout) - continue; - if (fp == itf || fp == otf) - continue; - if (fp == stderr) - continue; - if (fp == pipef) { - pclose(pipef); - pipef = NULL; - continue; - } - fclose(fp); - } + + close_all_files(); + if (image >= 0) { close(image); image = -1; } - clrbuf(stdout); - printf("Interrupt\n"); - signal(SIGINT, stop); + fprintf(stderr, "Interrupt\n"); reset(0); } +/* + * When we wake up after ^Z, reprint the prompt. + */ +void +stop(s) +{ + sig_t old_action = signal(s, SIG_DFL); + + sigsetmask(sigblock(0) & ~sigmask(s)); + kill(0, s); + sigblock(sigmask(s)); + signal(s, old_action); + if (reset_on_stop) { + reset_on_stop = 0; + reset(0); + } +} + +/* + * Branch here on hangup signal and simulate "exit". + */ +/*ARGSUSED*/ +void +hangup(s) +{ + + /* nothing to do? */ + exit(1); +} + /* * Announce the presence of the current Mail version, * give the message count, and print a header listing. */ -char *greeting = "Mail version 2.0 %s. Type ? for help.\n"; - -announce(pr) +announce() { int vec[2], mdot; - extern char *version; mdot = newfileinfo(); vec[0] = mdot; vec[1] = 0; - if (pr && value("quiet") == NOSTR) - printf(greeting, version); dot = &message[mdot - 1]; - if (msgCount > 0 && !noheader) + if (msgCount > 0 && value("noheader") == NOSTR) { + inithdr++; headers(vec); + inithdr = 0; + } } /* * Announce information about the file we are editing. * Return a likely place to set dot. */ - newfileinfo() { register struct message *mp; - register int u, n, mdot; + register int u, n, mdot, d, s; + char fname[BUFSIZ], zname[BUFSIZ], *ename; for (mp = &message[0]; mp < &message[msgCount]; mp++) if (mp->m_flag & MNEW) @@ -529,13 +577,26 @@ newfileinfo() mdot = mp - &message[0] + 1; else mdot = 1; + s = d = 0; for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) { if (mp->m_flag & MNEW) n++; if ((mp->m_flag & MREAD) == 0) u++; + if (mp->m_flag & MDELETED) + d++; + if (mp->m_flag & MSAVED) + s++; + } + ename = mailname; + if (getfold(fname) >= 0) { + strcat(fname, "/"); + if (strncmp(fname, mailname, strlen(fname)) == 0) { + sprintf(zname, "+%s", mailname + strlen(fname)); + ename = zname; + } } - printf("\"%s\": ", mailname); + printf("\"%s\": ", ename); if (msgCount == 1) printf("1 message"); else @@ -544,20 +605,46 @@ newfileinfo() 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); } -strace() {} - /* * Print the current version number. */ +/*ARGSUSED*/ pversion(e) { - printf(greeting, version); + extern char *version; + + printf("Version %s\n", version); return(0); } + +/* + * Load a file of user definitions. + */ +load(name) + char *name; +{ + register FILE *in, *oldin; + + if ((in = Fopen(name, "r")) == NULL) + return; + oldin = input; + input = in; + loading = 1; + sourcing = 1; + commands(); + loading = 0; + sourcing = 0; + input = oldin; + Fclose(in); +}