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