lex.c revision 126691
11590Srgrimes/* 21590Srgrimes * Copyright (c) 1980, 1993 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * Redistribution and use in source and binary forms, with or without 61590Srgrimes * modification, are permitted provided that the following conditions 71590Srgrimes * are met: 81590Srgrimes * 1. Redistributions of source code must retain the above copyright 91590Srgrimes * notice, this list of conditions and the following disclaimer. 101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111590Srgrimes * notice, this list of conditions and the following disclaimer in the 121590Srgrimes * documentation and/or other materials provided with the distribution. 131590Srgrimes * 3. All advertising materials mentioning features or use of this software 141590Srgrimes * must display the following acknowledgement: 151590Srgrimes * This product includes software developed by the University of 161590Srgrimes * California, Berkeley and its contributors. 171590Srgrimes * 4. Neither the name of the University nor the names of its contributors 181590Srgrimes * may be used to endorse or promote products derived from this software 191590Srgrimes * without specific prior written permission. 201590Srgrimes * 211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311590Srgrimes * SUCH DAMAGE. 321590Srgrimes */ 331590Srgrimes 341590Srgrimes#ifndef lint 3574769Smikeh#if 0 3688150Smikehstatic char sccsid[] = "@(#)lex.c 8.2 (Berkeley) 4/20/95"; 3774769Smikeh#endif 381590Srgrimes#endif /* not lint */ 3999112Sobrien#include <sys/cdefs.h> 4099112Sobrien__FBSDID("$FreeBSD: head/usr.bin/mail/lex.c 126691 2004-03-06 13:27:59Z mikeh $"); 411590Srgrimes 421590Srgrimes#include "rcv.h" 431590Srgrimes#include <errno.h> 441590Srgrimes#include <fcntl.h> 451590Srgrimes#include "extern.h" 461590Srgrimes 471590Srgrimes/* 481590Srgrimes * Mail -- a mail program 491590Srgrimes * 501590Srgrimes * Lexical processing of commands. 511590Srgrimes */ 521590Srgrimes 5377274Smikehconst char *prompt = "& "; 541590Srgrimes 5577274Smikehextern const struct cmd cmdtab[]; 5677274Smikehextern const char *version; 5777274Smikeh 581590Srgrimes/* 591590Srgrimes * Set up editing on the given file name. 601590Srgrimes * If the first character of name is %, we are considered to be 611590Srgrimes * editing the file, otherwise we are reading our mail which has 621590Srgrimes * signficance for mbox and so forth. 63126415Smikeh * 64126415Smikeh * If the -e option is being passed to mail, this function has a 65126415Smikeh * tri-state return code: -1 on error, 0 on no mail, 1 if there is 66126415Smikeh * mail. 671590Srgrimes */ 681590Srgrimesint 691590Srgrimessetfile(name) 701590Srgrimes char *name; 711590Srgrimes{ 721590Srgrimes FILE *ibuf; 73126415Smikeh int checkmode, i, fd; 741590Srgrimes struct stat stb; 7588150Smikeh char isedit = *name != '%' || getuserid(myname) != getuid(); 761590Srgrimes char *who = name[1] ? name + 1 : myname; 7774769Smikeh char tempname[PATHSIZE]; 781590Srgrimes static int shudclob; 791590Srgrimes 80126691Smikeh checkmode = value("checkmode") != NULL; 8177274Smikeh if ((name = expand(name)) == NULL) 8277274Smikeh return (-1); 831590Srgrimes 841590Srgrimes if ((ibuf = Fopen(name, "r")) == NULL) { 851590Srgrimes if (!isedit && errno == ENOENT) 861590Srgrimes goto nomail; 8774769Smikeh warn("%s", name); 8877274Smikeh return (-1); 891590Srgrimes } 901590Srgrimes 911590Srgrimes if (fstat(fileno(ibuf), &stb) < 0) { 9274769Smikeh warn("fstat"); 9377274Smikeh (void)Fclose(ibuf); 941590Srgrimes return (-1); 951590Srgrimes } 961590Srgrimes 9774769Smikeh if (S_ISDIR(stb.st_mode) || !S_ISREG(stb.st_mode)) { 9877274Smikeh (void)Fclose(ibuf); 9974769Smikeh errno = S_ISDIR(stb.st_mode) ? EISDIR : EINVAL; 10074769Smikeh warn("%s", name); 1011590Srgrimes return (-1); 1021590Srgrimes } 1031590Srgrimes 1041590Srgrimes /* 1051590Srgrimes * Looks like all will be well. We must now relinquish our 1061590Srgrimes * hold on the current set of stuff. Must hold signals 1071590Srgrimes * while we are reading the new file, else we will ruin 1081590Srgrimes * the message[] data structure. 1091590Srgrimes */ 1101590Srgrimes 1111590Srgrimes holdsigs(); 1121590Srgrimes if (shudclob) 1131590Srgrimes quit(); 1141590Srgrimes 1151590Srgrimes /* 1161590Srgrimes * Copy the messages into /tmp 1171590Srgrimes * and set pointers. 1181590Srgrimes */ 1191590Srgrimes 1201590Srgrimes readonly = 0; 1211590Srgrimes if ((i = open(name, 1)) < 0) 1221590Srgrimes readonly++; 1231590Srgrimes else 12477274Smikeh (void)close(i); 1251590Srgrimes if (shudclob) { 12677274Smikeh (void)fclose(itf); 12777274Smikeh (void)fclose(otf); 1281590Srgrimes } 1291590Srgrimes shudclob = 1; 1301590Srgrimes edit = isedit; 13174769Smikeh strlcpy(prevfile, mailname, sizeof(prevfile)); 1321590Srgrimes if (name != mailname) 13374769Smikeh strlcpy(mailname, name, sizeof(mailname)); 1341590Srgrimes mailsize = fsize(ibuf); 13577274Smikeh (void)snprintf(tempname, sizeof(tempname), 13677274Smikeh "%s/mail.RxXXXXXXXXXX", tmpdir); 13774769Smikeh if ((fd = mkstemp(tempname)) == -1 || (otf = fdopen(fd, "w")) == NULL) 13874769Smikeh err(1, "%s", tempname); 13977274Smikeh (void)fcntl(fileno(otf), F_SETFD, 1); 14074769Smikeh if ((itf = fopen(tempname, "r")) == NULL) 14174769Smikeh err(1, "%s", tempname); 14277274Smikeh (void)fcntl(fileno(itf), F_SETFD, 1); 14377274Smikeh (void)rm(tempname); 14488150Smikeh setptr(ibuf, 0); 1451590Srgrimes setmsize(msgCount); 14688150Smikeh /* 14788150Smikeh * New mail may have arrived while we were reading 14888150Smikeh * the mail file, so reset mailsize to be where 14988150Smikeh * we really are in the file... 15088150Smikeh */ 15188227Sache mailsize = ftello(ibuf); 15277274Smikeh (void)Fclose(ibuf); 1531590Srgrimes relsesigs(); 1541590Srgrimes sawcom = 0; 155126415Smikeh 156126415Smikeh if ((checkmode || !edit) && msgCount == 0) { 1571590Srgrimesnomail: 158126415Smikeh if (!checkmode) { 159126415Smikeh fprintf(stderr, "No mail for %s\n", who); 160126415Smikeh return (-1); 161126415Smikeh } else 162126415Smikeh return (0); 1631590Srgrimes } 164126415Smikeh return (checkmode ? 1 : 0); 1651590Srgrimes} 1661590Srgrimes 16788150Smikeh/* 16888150Smikeh * Incorporate any new mail that has arrived since we first 16988150Smikeh * started reading mail. 17088150Smikeh */ 17188150Smikehint 17288150Smikehincfile() 17388150Smikeh{ 17488227Sache off_t newsize; 17588150Smikeh int omsgCount = msgCount; 17688150Smikeh FILE *ibuf; 17788150Smikeh 17888150Smikeh ibuf = Fopen(mailname, "r"); 17988150Smikeh if (ibuf == NULL) 18088150Smikeh return (-1); 18188150Smikeh holdsigs(); 18288150Smikeh newsize = fsize(ibuf); 18388150Smikeh if (newsize == 0) 18488150Smikeh return (-1); /* mail box is now empty??? */ 18588150Smikeh if (newsize < mailsize) 18688150Smikeh return (-1); /* mail box has shrunk??? */ 18788150Smikeh if (newsize == mailsize) 18888150Smikeh return (0); /* no new mail */ 18988150Smikeh setptr(ibuf, mailsize); 19088150Smikeh setmsize(msgCount); 19188227Sache mailsize = ftello(ibuf); 19288150Smikeh (void)Fclose(ibuf); 19388150Smikeh relsesigs(); 19488150Smikeh return (msgCount - omsgCount); 19588150Smikeh} 19688150Smikeh 1971590Srgrimesint *msgvec; 1981590Srgrimesint reset_on_stop; /* do a reset() if stopped */ 1991590Srgrimes 2001590Srgrimes/* 2011590Srgrimes * Interpret user commands one by one. If standard input is not a tty, 2021590Srgrimes * print no prompt. 2031590Srgrimes */ 2041590Srgrimesvoid 2051590Srgrimescommands() 2061590Srgrimes{ 20777274Smikeh int n, eofloop = 0; 2081590Srgrimes char linebuf[LINESIZE]; 2091590Srgrimes 2101590Srgrimes if (!sourcing) { 2111590Srgrimes if (signal(SIGINT, SIG_IGN) != SIG_IGN) 21277274Smikeh (void)signal(SIGINT, intr); 2131590Srgrimes if (signal(SIGHUP, SIG_IGN) != SIG_IGN) 21477274Smikeh (void)signal(SIGHUP, hangup); 21577274Smikeh (void)signal(SIGTSTP, stop); 21677274Smikeh (void)signal(SIGTTOU, stop); 21777274Smikeh (void)signal(SIGTTIN, stop); 2181590Srgrimes } 2191590Srgrimes setexit(); 2201590Srgrimes for (;;) { 2211590Srgrimes /* 2221590Srgrimes * Print the prompt, if needed. Clear out 2231590Srgrimes * string space, and flush the output. 2241590Srgrimes */ 22577274Smikeh if (!sourcing && value("interactive") != NULL) { 22688150Smikeh if ((value("autoinc") != NULL) && (incfile() > 0)) 22788150Smikeh printf("New mail has arrived.\n"); 2281590Srgrimes reset_on_stop = 1; 22969249Skris printf("%s", prompt); 2301590Srgrimes } 23177274Smikeh (void)fflush(stdout); 2321590Srgrimes sreset(); 2331590Srgrimes /* 2341590Srgrimes * Read a line of commands from the current input 2351590Srgrimes * and handle end of file specially. 2361590Srgrimes */ 2371590Srgrimes n = 0; 2381590Srgrimes for (;;) { 2391590Srgrimes if (readline(input, &linebuf[n], LINESIZE - n) < 0) { 2401590Srgrimes if (n == 0) 2411590Srgrimes n = -1; 2421590Srgrimes break; 2431590Srgrimes } 2441590Srgrimes if ((n = strlen(linebuf)) == 0) 2451590Srgrimes break; 2461590Srgrimes n--; 2471590Srgrimes if (linebuf[n] != '\\') 2481590Srgrimes break; 2491590Srgrimes linebuf[n++] = ' '; 2501590Srgrimes } 2511590Srgrimes reset_on_stop = 0; 2521590Srgrimes if (n < 0) { 2531590Srgrimes /* eof */ 2541590Srgrimes if (loading) 2551590Srgrimes break; 2561590Srgrimes if (sourcing) { 2571590Srgrimes unstack(); 2581590Srgrimes continue; 2591590Srgrimes } 26077274Smikeh if (value("interactive") != NULL && 26177274Smikeh value("ignoreeof") != NULL && 2621590Srgrimes ++eofloop < 25) { 2631590Srgrimes printf("Use \"quit\" to quit.\n"); 2641590Srgrimes continue; 2651590Srgrimes } 2661590Srgrimes break; 2671590Srgrimes } 2681590Srgrimes eofloop = 0; 2691590Srgrimes if (execute(linebuf, 0)) 2701590Srgrimes break; 2711590Srgrimes } 2721590Srgrimes} 2731590Srgrimes 2741590Srgrimes/* 2751590Srgrimes * Execute a single command. 2761590Srgrimes * Command functions return 0 for success, 1 for error, and -1 2771590Srgrimes * for abort. A 1 or -1 aborts a load or source. A -1 aborts 2781590Srgrimes * the interactive command loop. 2791590Srgrimes * Contxt is non-zero if called while composing mail. 2801590Srgrimes */ 2811590Srgrimesint 2821590Srgrimesexecute(linebuf, contxt) 2831590Srgrimes char linebuf[]; 2841590Srgrimes int contxt; 2851590Srgrimes{ 2861590Srgrimes char word[LINESIZE]; 2871590Srgrimes char *arglist[MAXARGC]; 28877274Smikeh const struct cmd *com; 28977274Smikeh char *cp, *cp2; 29077274Smikeh int c, muvec[2]; 2911590Srgrimes int e = 1; 2921590Srgrimes 2931590Srgrimes /* 2941590Srgrimes * Strip the white space away from the beginning 2951590Srgrimes * of the command, then scan out a word, which 2961590Srgrimes * consists of anything except digits and white space. 2971590Srgrimes * 2981590Srgrimes * Handle ! escapes differently to get the correct 2991590Srgrimes * lexical conventions. 3001590Srgrimes */ 3011590Srgrimes 30288227Sache for (cp = linebuf; isspace((unsigned char)*cp); cp++) 3031590Srgrimes ; 3041590Srgrimes if (*cp == '!') { 3051590Srgrimes if (sourcing) { 3061590Srgrimes printf("Can't \"!\" while sourcing\n"); 3071590Srgrimes goto out; 3081590Srgrimes } 3091590Srgrimes shell(cp+1); 31077274Smikeh return (0); 3111590Srgrimes } 3121590Srgrimes cp2 = word; 31377274Smikeh while (*cp != '\0' && strchr(" \t0123456789$^.:/-+*'\"", *cp) == NULL) 3141590Srgrimes *cp2++ = *cp++; 3151590Srgrimes *cp2 = '\0'; 3161590Srgrimes 3171590Srgrimes /* 3181590Srgrimes * Look up the command; if not found, bitch. 3191590Srgrimes * Normally, a blank command would map to the 3201590Srgrimes * first command in the table; while sourcing, 3211590Srgrimes * however, we ignore blank lines to eliminate 3221590Srgrimes * confusion. 3231590Srgrimes */ 3241590Srgrimes 3251590Srgrimes if (sourcing && *word == '\0') 32677274Smikeh return (0); 3271590Srgrimes com = lex(word); 32877274Smikeh if (com == NULL) { 3291590Srgrimes printf("Unknown command: \"%s\"\n", word); 3301590Srgrimes goto out; 3311590Srgrimes } 3321590Srgrimes 3331590Srgrimes /* 3341590Srgrimes * See if we should execute the command -- if a conditional 3351590Srgrimes * we always execute it, otherwise, check the state of cond. 3361590Srgrimes */ 3371590Srgrimes 3381590Srgrimes if ((com->c_argtype & F) == 0) 33977274Smikeh if ((cond == CRCV && !rcvmode) || (cond == CSEND && rcvmode)) 34077274Smikeh return (0); 3411590Srgrimes 3421590Srgrimes /* 3431590Srgrimes * Process the arguments to the command, depending 3441590Srgrimes * on the type he expects. Default to an error. 3451590Srgrimes * If we are sourcing an interactive command, it's 3461590Srgrimes * an error. 3471590Srgrimes */ 3481590Srgrimes 3491590Srgrimes if (!rcvmode && (com->c_argtype & M) == 0) { 3501590Srgrimes printf("May not execute \"%s\" while sending\n", 3511590Srgrimes com->c_name); 3521590Srgrimes goto out; 3531590Srgrimes } 3541590Srgrimes if (sourcing && com->c_argtype & I) { 3551590Srgrimes printf("May not execute \"%s\" while sourcing\n", 3561590Srgrimes com->c_name); 3571590Srgrimes goto out; 3581590Srgrimes } 3591590Srgrimes if (readonly && com->c_argtype & W) { 3601590Srgrimes printf("May not execute \"%s\" -- message file is read only\n", 3611590Srgrimes com->c_name); 3621590Srgrimes goto out; 3631590Srgrimes } 3641590Srgrimes if (contxt && com->c_argtype & R) { 3651590Srgrimes printf("Cannot recursively invoke \"%s\"\n", com->c_name); 3661590Srgrimes goto out; 3671590Srgrimes } 3681590Srgrimes switch (com->c_argtype & ~(F|P|I|M|T|W|R)) { 3691590Srgrimes case MSGLIST: 3701590Srgrimes /* 3711590Srgrimes * A message list defaulting to nearest forward 3721590Srgrimes * legal message. 3731590Srgrimes */ 3741590Srgrimes if (msgvec == 0) { 3751590Srgrimes printf("Illegal use of \"message list\"\n"); 3761590Srgrimes break; 3771590Srgrimes } 3781590Srgrimes if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0) 3791590Srgrimes break; 3801590Srgrimes if (c == 0) { 38177274Smikeh *msgvec = first(com->c_msgflag, com->c_msgmask); 38229574Sphk msgvec[1] = 0; 3831590Srgrimes } 38429574Sphk if (*msgvec == 0) { 3851590Srgrimes printf("No applicable messages\n"); 3861590Srgrimes break; 3871590Srgrimes } 3881590Srgrimes e = (*com->c_func)(msgvec); 3891590Srgrimes break; 3901590Srgrimes 3911590Srgrimes case NDMLIST: 3921590Srgrimes /* 3931590Srgrimes * A message list with no defaults, but no error 3941590Srgrimes * if none exist. 3951590Srgrimes */ 3961590Srgrimes if (msgvec == 0) { 3971590Srgrimes printf("Illegal use of \"message list\"\n"); 3981590Srgrimes break; 3991590Srgrimes } 4001590Srgrimes if (getmsglist(cp, msgvec, com->c_msgflag) < 0) 4011590Srgrimes break; 4021590Srgrimes e = (*com->c_func)(msgvec); 4031590Srgrimes break; 4041590Srgrimes 4051590Srgrimes case STRLIST: 4061590Srgrimes /* 4071590Srgrimes * Just the straight string, with 4081590Srgrimes * leading blanks removed. 4091590Srgrimes */ 41088227Sache while (isspace((unsigned char)*cp)) 4111590Srgrimes cp++; 4121590Srgrimes e = (*com->c_func)(cp); 4131590Srgrimes break; 4141590Srgrimes 4151590Srgrimes case RAWLIST: 4161590Srgrimes /* 4171590Srgrimes * A vector of strings, in shell style. 4181590Srgrimes */ 4191590Srgrimes if ((c = getrawlist(cp, arglist, 42077274Smikeh sizeof(arglist) / sizeof(*arglist))) < 0) 4211590Srgrimes break; 4221590Srgrimes if (c < com->c_minargs) { 4231590Srgrimes printf("%s requires at least %d arg(s)\n", 42477274Smikeh com->c_name, com->c_minargs); 4251590Srgrimes break; 4261590Srgrimes } 4271590Srgrimes if (c > com->c_maxargs) { 4281590Srgrimes printf("%s takes no more than %d arg(s)\n", 42977274Smikeh com->c_name, com->c_maxargs); 4301590Srgrimes break; 4311590Srgrimes } 4321590Srgrimes e = (*com->c_func)(arglist); 4331590Srgrimes break; 4341590Srgrimes 4351590Srgrimes case NOLIST: 4361590Srgrimes /* 4371590Srgrimes * Just the constant zero, for exiting, 4381590Srgrimes * eg. 4391590Srgrimes */ 4401590Srgrimes e = (*com->c_func)(0); 4411590Srgrimes break; 4421590Srgrimes 4431590Srgrimes default: 44474769Smikeh errx(1, "Unknown argtype"); 4451590Srgrimes } 4461590Srgrimes 4471590Srgrimesout: 4481590Srgrimes /* 4491590Srgrimes * Exit the current source file on 4501590Srgrimes * error. 4511590Srgrimes */ 4521590Srgrimes if (e) { 4531590Srgrimes if (e < 0) 45477274Smikeh return (1); 4551590Srgrimes if (loading) 45677274Smikeh return (1); 4571590Srgrimes if (sourcing) 4581590Srgrimes unstack(); 45977274Smikeh return (0); 4601590Srgrimes } 46188150Smikeh if (com == NULL) 46288150Smikeh return (0); 46377274Smikeh if (value("autoprint") != NULL && com->c_argtype & P) 4641590Srgrimes if ((dot->m_flag & MDELETED) == 0) { 4651590Srgrimes muvec[0] = dot - &message[0] + 1; 4661590Srgrimes muvec[1] = 0; 4671590Srgrimes type(muvec); 4681590Srgrimes } 4691590Srgrimes if (!sourcing && (com->c_argtype & T) == 0) 4701590Srgrimes sawcom = 1; 47177274Smikeh return (0); 4721590Srgrimes} 4731590Srgrimes 4741590Srgrimes/* 4751590Srgrimes * Set the size of the message vector used to construct argument 4761590Srgrimes * lists to message list functions. 4771590Srgrimes */ 4781590Srgrimesvoid 4791590Srgrimessetmsize(sz) 4801590Srgrimes int sz; 4811590Srgrimes{ 4821590Srgrimes 48388150Smikeh if (msgvec != NULL) 48477274Smikeh (void)free(msgvec); 48577274Smikeh msgvec = calloc((unsigned)(sz + 1), sizeof(*msgvec)); 4861590Srgrimes} 4871590Srgrimes 4881590Srgrimes/* 4891590Srgrimes * Find the correct command in the command table corresponding 4901590Srgrimes * to the passed command "word" 4911590Srgrimes */ 4921590Srgrimes 49377274Smikeh__const struct cmd * 4941590Srgrimeslex(word) 4951590Srgrimes char word[]; 4961590Srgrimes{ 49777274Smikeh const struct cmd *cp; 4981590Srgrimes 49910067Sjoerg /* 50077274Smikeh * ignore trailing chars after `#' 50110067Sjoerg * 50210067Sjoerg * lines with beginning `#' are comments 50374769Smikeh * spaces before `#' are ignored in execute() 50410067Sjoerg */ 50510067Sjoerg 50610067Sjoerg if (*word == '#') 50710067Sjoerg *(word+1) = '\0'; 50810067Sjoerg 50910067Sjoerg 51077274Smikeh for (cp = &cmdtab[0]; cp->c_name != NULL; cp++) 5111590Srgrimes if (isprefix(word, cp->c_name)) 51277274Smikeh return (cp); 51377274Smikeh return (NULL); 5141590Srgrimes} 5151590Srgrimes 5161590Srgrimes/* 5171590Srgrimes * Determine if as1 is a valid prefix of as2. 5181590Srgrimes * Return true if yep. 5191590Srgrimes */ 5201590Srgrimesint 5211590Srgrimesisprefix(as1, as2) 52277274Smikeh const char *as1, *as2; 5231590Srgrimes{ 52477274Smikeh const char *s1, *s2; 5251590Srgrimes 5261590Srgrimes s1 = as1; 5271590Srgrimes s2 = as2; 5281590Srgrimes while (*s1++ == *s2) 5291590Srgrimes if (*s2++ == '\0') 53077274Smikeh return (1); 53177274Smikeh return (*--s1 == '\0'); 5321590Srgrimes} 5331590Srgrimes 5341590Srgrimes/* 5351590Srgrimes * The following gets called on receipt of an interrupt. This is 5361590Srgrimes * to abort printout of a command, mainly. 5371590Srgrimes * Dispatching here when command() is inactive crashes rcv. 5381590Srgrimes * Close all open files except 0, 1, 2, and the temporary. 5391590Srgrimes * Also, unstack all source files. 5401590Srgrimes */ 5411590Srgrimes 5421590Srgrimesint inithdr; /* am printing startup headers */ 5431590Srgrimes 5441590Srgrimes/*ARGSUSED*/ 5451590Srgrimesvoid 5461590Srgrimesintr(s) 5471590Srgrimes int s; 5481590Srgrimes{ 5491590Srgrimes 5501590Srgrimes noreset = 0; 5511590Srgrimes if (!inithdr) 5521590Srgrimes sawcom++; 5531590Srgrimes inithdr = 0; 5541590Srgrimes while (sourcing) 5551590Srgrimes unstack(); 5561590Srgrimes 5571590Srgrimes close_all_files(); 5581590Srgrimes 5591590Srgrimes if (image >= 0) { 56077274Smikeh (void)close(image); 5611590Srgrimes image = -1; 5621590Srgrimes } 5631590Srgrimes fprintf(stderr, "Interrupt\n"); 5641590Srgrimes reset(0); 5651590Srgrimes} 5661590Srgrimes 5671590Srgrimes/* 5681590Srgrimes * When we wake up after ^Z, reprint the prompt. 5691590Srgrimes */ 5701590Srgrimesvoid 5711590Srgrimesstop(s) 5721590Srgrimes int s; 5731590Srgrimes{ 5741590Srgrimes sig_t old_action = signal(s, SIG_DFL); 57588150Smikeh sigset_t nset; 5761590Srgrimes 57788150Smikeh (void)sigemptyset(&nset); 57888150Smikeh (void)sigaddset(&nset, s); 57988150Smikeh (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); 58077274Smikeh (void)kill(0, s); 58188150Smikeh (void)sigprocmask(SIG_BLOCK, &nset, NULL); 58277274Smikeh (void)signal(s, old_action); 5831590Srgrimes if (reset_on_stop) { 5841590Srgrimes reset_on_stop = 0; 5851590Srgrimes reset(0); 5861590Srgrimes } 5871590Srgrimes} 5881590Srgrimes 5891590Srgrimes/* 5901590Srgrimes * Branch here on hangup signal and simulate "exit". 5911590Srgrimes */ 5921590Srgrimes/*ARGSUSED*/ 5931590Srgrimesvoid 5941590Srgrimeshangup(s) 5951590Srgrimes int s; 5961590Srgrimes{ 5971590Srgrimes 5981590Srgrimes /* nothing to do? */ 5991590Srgrimes exit(1); 6001590Srgrimes} 6011590Srgrimes 6021590Srgrimes/* 6031590Srgrimes * Announce the presence of the current Mail version, 6041590Srgrimes * give the message count, and print a header listing. 6051590Srgrimes */ 6061590Srgrimesvoid 6071590Srgrimesannounce() 6081590Srgrimes{ 6091590Srgrimes int vec[2], mdot; 6101590Srgrimes 61188150Smikeh mdot = newfileinfo(0); 6121590Srgrimes vec[0] = mdot; 6131590Srgrimes vec[1] = 0; 6141590Srgrimes dot = &message[mdot - 1]; 61577274Smikeh if (msgCount > 0 && value("noheader") == NULL) { 6161590Srgrimes inithdr++; 6171590Srgrimes headers(vec); 6181590Srgrimes inithdr = 0; 6191590Srgrimes } 6201590Srgrimes} 6211590Srgrimes 6221590Srgrimes/* 6231590Srgrimes * Announce information about the file we are editing. 6241590Srgrimes * Return a likely place to set dot. 6251590Srgrimes */ 6261590Srgrimesint 62788150Smikehnewfileinfo(omsgCount) 62888150Smikeh int omsgCount; 6291590Srgrimes{ 63077274Smikeh struct message *mp; 63177274Smikeh int u, n, mdot, d, s; 63274769Smikeh char fname[PATHSIZE+1], zname[PATHSIZE+1], *ename; 6331590Srgrimes 63488150Smikeh for (mp = &message[omsgCount]; mp < &message[msgCount]; mp++) 6351590Srgrimes if (mp->m_flag & MNEW) 6361590Srgrimes break; 6371590Srgrimes if (mp >= &message[msgCount]) 63888150Smikeh for (mp = &message[omsgCount]; mp < &message[msgCount]; mp++) 6391590Srgrimes if ((mp->m_flag & MREAD) == 0) 6401590Srgrimes break; 6411590Srgrimes if (mp < &message[msgCount]) 6421590Srgrimes mdot = mp - &message[0] + 1; 6431590Srgrimes else 64488150Smikeh mdot = omsgCount + 1; 6451590Srgrimes s = d = 0; 6461590Srgrimes for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) { 6471590Srgrimes if (mp->m_flag & MNEW) 6481590Srgrimes n++; 6491590Srgrimes if ((mp->m_flag & MREAD) == 0) 6501590Srgrimes u++; 6511590Srgrimes if (mp->m_flag & MDELETED) 6521590Srgrimes d++; 6531590Srgrimes if (mp->m_flag & MSAVED) 6541590Srgrimes s++; 6551590Srgrimes } 6561590Srgrimes ename = mailname; 65774769Smikeh if (getfold(fname, sizeof(fname) - 1) >= 0) { 6581590Srgrimes strcat(fname, "/"); 6591590Srgrimes if (strncmp(fname, mailname, strlen(fname)) == 0) { 66077274Smikeh (void)snprintf(zname, sizeof(zname), "+%s", 66177274Smikeh mailname + strlen(fname)); 6621590Srgrimes ename = zname; 6631590Srgrimes } 6641590Srgrimes } 6651590Srgrimes printf("\"%s\": ", ename); 6661590Srgrimes if (msgCount == 1) 6671590Srgrimes printf("1 message"); 6681590Srgrimes else 6691590Srgrimes printf("%d messages", msgCount); 6701590Srgrimes if (n > 0) 6711590Srgrimes printf(" %d new", n); 6721590Srgrimes if (u-n > 0) 6731590Srgrimes printf(" %d unread", u); 6741590Srgrimes if (d > 0) 6751590Srgrimes printf(" %d deleted", d); 6761590Srgrimes if (s > 0) 6771590Srgrimes printf(" %d saved", s); 6781590Srgrimes if (readonly) 6791590Srgrimes printf(" [Read only]"); 6801590Srgrimes printf("\n"); 68177274Smikeh return (mdot); 6821590Srgrimes} 6831590Srgrimes 6841590Srgrimes/* 6851590Srgrimes * Print the current version number. 6861590Srgrimes */ 6871590Srgrimes 6881590Srgrimes/*ARGSUSED*/ 6891590Srgrimesint 6901590Srgrimespversion(e) 6911590Srgrimes int e; 6921590Srgrimes{ 6931590Srgrimes 6941590Srgrimes printf("Version %s\n", version); 69577274Smikeh return (0); 6961590Srgrimes} 6971590Srgrimes 6981590Srgrimes/* 6991590Srgrimes * Load a file of user definitions. 7001590Srgrimes */ 7011590Srgrimesvoid 7021590Srgrimesload(name) 7031590Srgrimes char *name; 7041590Srgrimes{ 70577274Smikeh FILE *in, *oldin; 7061590Srgrimes 7071590Srgrimes if ((in = Fopen(name, "r")) == NULL) 7081590Srgrimes return; 7091590Srgrimes oldin = input; 7101590Srgrimes input = in; 7111590Srgrimes loading = 1; 7121590Srgrimes sourcing = 1; 7131590Srgrimes commands(); 7141590Srgrimes loading = 0; 7151590Srgrimes sourcing = 0; 7161590Srgrimes input = oldin; 71777274Smikeh (void)Fclose(in); 7181590Srgrimes} 719