lex.c revision 88150
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
3874769Smikehstatic const char rcsid[] =
3974769Smikeh  "$FreeBSD: head/usr.bin/mail/lex.c 88150 2001-12-18 20:52:09Z mikeh $";
401590Srgrimes#endif /* not lint */
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.
631590Srgrimes */
641590Srgrimesint
651590Srgrimessetfile(name)
661590Srgrimes	char *name;
671590Srgrimes{
681590Srgrimes	FILE *ibuf;
6974769Smikeh	int i, fd;
701590Srgrimes	struct stat stb;
7188150Smikeh	char isedit = *name != '%' || getuserid(myname) != getuid();
721590Srgrimes	char *who = name[1] ? name + 1 : myname;
7374769Smikeh	char tempname[PATHSIZE];
741590Srgrimes	static int shudclob;
751590Srgrimes
7677274Smikeh	if ((name = expand(name)) == NULL)
7777274Smikeh		return (-1);
781590Srgrimes
791590Srgrimes	if ((ibuf = Fopen(name, "r")) == NULL) {
801590Srgrimes		if (!isedit && errno == ENOENT)
811590Srgrimes			goto nomail;
8274769Smikeh		warn("%s", name);
8377274Smikeh		return (-1);
841590Srgrimes	}
851590Srgrimes
861590Srgrimes	if (fstat(fileno(ibuf), &stb) < 0) {
8774769Smikeh		warn("fstat");
8877274Smikeh		(void)Fclose(ibuf);
891590Srgrimes		return (-1);
901590Srgrimes	}
911590Srgrimes
9274769Smikeh	if (S_ISDIR(stb.st_mode) || !S_ISREG(stb.st_mode)) {
9377274Smikeh		(void)Fclose(ibuf);
9474769Smikeh		errno = S_ISDIR(stb.st_mode) ? EISDIR : EINVAL;
9574769Smikeh		warn("%s", name);
961590Srgrimes		return (-1);
971590Srgrimes	}
981590Srgrimes
991590Srgrimes	/*
1001590Srgrimes	 * Looks like all will be well.  We must now relinquish our
1011590Srgrimes	 * hold on the current set of stuff.  Must hold signals
1021590Srgrimes	 * while we are reading the new file, else we will ruin
1031590Srgrimes	 * the message[] data structure.
1041590Srgrimes	 */
1051590Srgrimes
1061590Srgrimes	holdsigs();
1071590Srgrimes	if (shudclob)
1081590Srgrimes		quit();
1091590Srgrimes
1101590Srgrimes	/*
1111590Srgrimes	 * Copy the messages into /tmp
1121590Srgrimes	 * and set pointers.
1131590Srgrimes	 */
1141590Srgrimes
1151590Srgrimes	readonly = 0;
1161590Srgrimes	if ((i = open(name, 1)) < 0)
1171590Srgrimes		readonly++;
1181590Srgrimes	else
11977274Smikeh		(void)close(i);
1201590Srgrimes	if (shudclob) {
12177274Smikeh		(void)fclose(itf);
12277274Smikeh		(void)fclose(otf);
1231590Srgrimes	}
1241590Srgrimes	shudclob = 1;
1251590Srgrimes	edit = isedit;
12674769Smikeh	strlcpy(prevfile, mailname, sizeof(prevfile));
1271590Srgrimes	if (name != mailname)
12874769Smikeh		strlcpy(mailname, name, sizeof(mailname));
1291590Srgrimes	mailsize = fsize(ibuf);
13077274Smikeh	(void)snprintf(tempname, sizeof(tempname),
13177274Smikeh	    "%s/mail.RxXXXXXXXXXX", tmpdir);
13274769Smikeh	if ((fd = mkstemp(tempname)) == -1 || (otf = fdopen(fd, "w")) == NULL)
13374769Smikeh		err(1, "%s", tempname);
13477274Smikeh	(void)fcntl(fileno(otf), F_SETFD, 1);
13574769Smikeh	if ((itf = fopen(tempname, "r")) == NULL)
13674769Smikeh		err(1, "%s", tempname);
13777274Smikeh	(void)fcntl(fileno(itf), F_SETFD, 1);
13877274Smikeh	(void)rm(tempname);
13988150Smikeh	setptr(ibuf, 0);
1401590Srgrimes	setmsize(msgCount);
14188150Smikeh	/*
14288150Smikeh	 * New mail may have arrived while we were reading
14388150Smikeh	 * the mail file, so reset mailsize to be where
14488150Smikeh	 * we really are in the file...
14588150Smikeh	 */
14688150Smikeh	mailsize = ftell(ibuf);
14777274Smikeh	(void)Fclose(ibuf);
1481590Srgrimes	relsesigs();
1491590Srgrimes	sawcom = 0;
1501590Srgrimes	if (!edit && msgCount == 0) {
1511590Srgrimesnomail:
1521590Srgrimes		fprintf(stderr, "No mail for %s\n", who);
15377274Smikeh		return (-1);
1541590Srgrimes	}
15577274Smikeh	return (0);
1561590Srgrimes}
1571590Srgrimes
15888150Smikeh/*
15988150Smikeh * Incorporate any new mail that has arrived since we first
16088150Smikeh * started reading mail.
16188150Smikeh */
16288150Smikehint
16388150Smikehincfile()
16488150Smikeh{
16588150Smikeh	int newsize;
16688150Smikeh	int omsgCount = msgCount;
16788150Smikeh	FILE *ibuf;
16888150Smikeh
16988150Smikeh	ibuf = Fopen(mailname, "r");
17088150Smikeh	if (ibuf == NULL)
17188150Smikeh		return (-1);
17288150Smikeh	holdsigs();
17388150Smikeh	newsize = fsize(ibuf);
17488150Smikeh	if (newsize == 0)
17588150Smikeh		return (-1);		/* mail box is now empty??? */
17688150Smikeh	if (newsize < mailsize)
17788150Smikeh		return (-1);		/* mail box has shrunk??? */
17888150Smikeh	if (newsize == mailsize)
17988150Smikeh		return (0);		/* no new mail */
18088150Smikeh	setptr(ibuf, mailsize);
18188150Smikeh	setmsize(msgCount);
18288150Smikeh	mailsize = ftell(ibuf);
18388150Smikeh	(void)Fclose(ibuf);
18488150Smikeh	relsesigs();
18588150Smikeh	return (msgCount - omsgCount);
18688150Smikeh}
18788150Smikeh
1881590Srgrimesint	*msgvec;
1891590Srgrimesint	reset_on_stop;			/* do a reset() if stopped */
1901590Srgrimes
1911590Srgrimes/*
1921590Srgrimes * Interpret user commands one by one.  If standard input is not a tty,
1931590Srgrimes * print no prompt.
1941590Srgrimes */
1951590Srgrimesvoid
1961590Srgrimescommands()
1971590Srgrimes{
19877274Smikeh	int n, eofloop = 0;
1991590Srgrimes	char linebuf[LINESIZE];
2001590Srgrimes
2011590Srgrimes	if (!sourcing) {
2021590Srgrimes		if (signal(SIGINT, SIG_IGN) != SIG_IGN)
20377274Smikeh			(void)signal(SIGINT, intr);
2041590Srgrimes		if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
20577274Smikeh			(void)signal(SIGHUP, hangup);
20677274Smikeh		(void)signal(SIGTSTP, stop);
20777274Smikeh		(void)signal(SIGTTOU, stop);
20877274Smikeh		(void)signal(SIGTTIN, stop);
2091590Srgrimes	}
2101590Srgrimes	setexit();
2111590Srgrimes	for (;;) {
2121590Srgrimes		/*
2131590Srgrimes		 * Print the prompt, if needed.  Clear out
2141590Srgrimes		 * string space, and flush the output.
2151590Srgrimes		 */
21677274Smikeh		if (!sourcing && value("interactive") != NULL) {
21788150Smikeh			if ((value("autoinc") != NULL) && (incfile() > 0))
21888150Smikeh				printf("New mail has arrived.\n");
2191590Srgrimes			reset_on_stop = 1;
22069249Skris			printf("%s", prompt);
2211590Srgrimes		}
22277274Smikeh		(void)fflush(stdout);
2231590Srgrimes		sreset();
2241590Srgrimes		/*
2251590Srgrimes		 * Read a line of commands from the current input
2261590Srgrimes		 * and handle end of file specially.
2271590Srgrimes		 */
2281590Srgrimes		n = 0;
2291590Srgrimes		for (;;) {
2301590Srgrimes			if (readline(input, &linebuf[n], LINESIZE - n) < 0) {
2311590Srgrimes				if (n == 0)
2321590Srgrimes					n = -1;
2331590Srgrimes				break;
2341590Srgrimes			}
2351590Srgrimes			if ((n = strlen(linebuf)) == 0)
2361590Srgrimes				break;
2371590Srgrimes			n--;
2381590Srgrimes			if (linebuf[n] != '\\')
2391590Srgrimes				break;
2401590Srgrimes			linebuf[n++] = ' ';
2411590Srgrimes		}
2421590Srgrimes		reset_on_stop = 0;
2431590Srgrimes		if (n < 0) {
2441590Srgrimes				/* eof */
2451590Srgrimes			if (loading)
2461590Srgrimes				break;
2471590Srgrimes			if (sourcing) {
2481590Srgrimes				unstack();
2491590Srgrimes				continue;
2501590Srgrimes			}
25177274Smikeh			if (value("interactive") != NULL &&
25277274Smikeh			    value("ignoreeof") != NULL &&
2531590Srgrimes			    ++eofloop < 25) {
2541590Srgrimes				printf("Use \"quit\" to quit.\n");
2551590Srgrimes				continue;
2561590Srgrimes			}
2571590Srgrimes			break;
2581590Srgrimes		}
2591590Srgrimes		eofloop = 0;
2601590Srgrimes		if (execute(linebuf, 0))
2611590Srgrimes			break;
2621590Srgrimes	}
2631590Srgrimes}
2641590Srgrimes
2651590Srgrimes/*
2661590Srgrimes * Execute a single command.
2671590Srgrimes * Command functions return 0 for success, 1 for error, and -1
2681590Srgrimes * for abort.  A 1 or -1 aborts a load or source.  A -1 aborts
2691590Srgrimes * the interactive command loop.
2701590Srgrimes * Contxt is non-zero if called while composing mail.
2711590Srgrimes */
2721590Srgrimesint
2731590Srgrimesexecute(linebuf, contxt)
2741590Srgrimes	char linebuf[];
2751590Srgrimes	int contxt;
2761590Srgrimes{
2771590Srgrimes	char word[LINESIZE];
2781590Srgrimes	char *arglist[MAXARGC];
27977274Smikeh	const struct cmd *com;
28077274Smikeh	char *cp, *cp2;
28177274Smikeh	int c, muvec[2];
2821590Srgrimes	int e = 1;
2831590Srgrimes
2841590Srgrimes	/*
2851590Srgrimes	 * Strip the white space away from the beginning
2861590Srgrimes	 * of the command, then scan out a word, which
2871590Srgrimes	 * consists of anything except digits and white space.
2881590Srgrimes	 *
2891590Srgrimes	 * Handle ! escapes differently to get the correct
2901590Srgrimes	 * lexical conventions.
2911590Srgrimes	 */
2921590Srgrimes
2931590Srgrimes	for (cp = linebuf; isspace(*cp); cp++)
2941590Srgrimes		;
2951590Srgrimes	if (*cp == '!') {
2961590Srgrimes		if (sourcing) {
2971590Srgrimes			printf("Can't \"!\" while sourcing\n");
2981590Srgrimes			goto out;
2991590Srgrimes		}
3001590Srgrimes		shell(cp+1);
30177274Smikeh		return (0);
3021590Srgrimes	}
3031590Srgrimes	cp2 = word;
30477274Smikeh	while (*cp != '\0' && strchr(" \t0123456789$^.:/-+*'\"", *cp) == NULL)
3051590Srgrimes		*cp2++ = *cp++;
3061590Srgrimes	*cp2 = '\0';
3071590Srgrimes
3081590Srgrimes	/*
3091590Srgrimes	 * Look up the command; if not found, bitch.
3101590Srgrimes	 * Normally, a blank command would map to the
3111590Srgrimes	 * first command in the table; while sourcing,
3121590Srgrimes	 * however, we ignore blank lines to eliminate
3131590Srgrimes	 * confusion.
3141590Srgrimes	 */
3151590Srgrimes
3161590Srgrimes	if (sourcing && *word == '\0')
31777274Smikeh		return (0);
3181590Srgrimes	com = lex(word);
31977274Smikeh	if (com == NULL) {
3201590Srgrimes		printf("Unknown command: \"%s\"\n", word);
3211590Srgrimes		goto out;
3221590Srgrimes	}
3231590Srgrimes
3241590Srgrimes	/*
3251590Srgrimes	 * See if we should execute the command -- if a conditional
3261590Srgrimes	 * we always execute it, otherwise, check the state of cond.
3271590Srgrimes	 */
3281590Srgrimes
3291590Srgrimes	if ((com->c_argtype & F) == 0)
33077274Smikeh		if ((cond == CRCV && !rcvmode) || (cond == CSEND && rcvmode))
33177274Smikeh			return (0);
3321590Srgrimes
3331590Srgrimes	/*
3341590Srgrimes	 * Process the arguments to the command, depending
3351590Srgrimes	 * on the type he expects.  Default to an error.
3361590Srgrimes	 * If we are sourcing an interactive command, it's
3371590Srgrimes	 * an error.
3381590Srgrimes	 */
3391590Srgrimes
3401590Srgrimes	if (!rcvmode && (com->c_argtype & M) == 0) {
3411590Srgrimes		printf("May not execute \"%s\" while sending\n",
3421590Srgrimes		    com->c_name);
3431590Srgrimes		goto out;
3441590Srgrimes	}
3451590Srgrimes	if (sourcing && com->c_argtype & I) {
3461590Srgrimes		printf("May not execute \"%s\" while sourcing\n",
3471590Srgrimes		    com->c_name);
3481590Srgrimes		goto out;
3491590Srgrimes	}
3501590Srgrimes	if (readonly && com->c_argtype & W) {
3511590Srgrimes		printf("May not execute \"%s\" -- message file is read only\n",
3521590Srgrimes		   com->c_name);
3531590Srgrimes		goto out;
3541590Srgrimes	}
3551590Srgrimes	if (contxt && com->c_argtype & R) {
3561590Srgrimes		printf("Cannot recursively invoke \"%s\"\n", com->c_name);
3571590Srgrimes		goto out;
3581590Srgrimes	}
3591590Srgrimes	switch (com->c_argtype & ~(F|P|I|M|T|W|R)) {
3601590Srgrimes	case MSGLIST:
3611590Srgrimes		/*
3621590Srgrimes		 * A message list defaulting to nearest forward
3631590Srgrimes		 * legal message.
3641590Srgrimes		 */
3651590Srgrimes		if (msgvec == 0) {
3661590Srgrimes			printf("Illegal use of \"message list\"\n");
3671590Srgrimes			break;
3681590Srgrimes		}
3691590Srgrimes		if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
3701590Srgrimes			break;
3711590Srgrimes		if (c  == 0) {
37277274Smikeh			*msgvec = first(com->c_msgflag, com->c_msgmask);
37329574Sphk			msgvec[1] = 0;
3741590Srgrimes		}
37529574Sphk		if (*msgvec == 0) {
3761590Srgrimes			printf("No applicable messages\n");
3771590Srgrimes			break;
3781590Srgrimes		}
3791590Srgrimes		e = (*com->c_func)(msgvec);
3801590Srgrimes		break;
3811590Srgrimes
3821590Srgrimes	case NDMLIST:
3831590Srgrimes		/*
3841590Srgrimes		 * A message list with no defaults, but no error
3851590Srgrimes		 * if none exist.
3861590Srgrimes		 */
3871590Srgrimes		if (msgvec == 0) {
3881590Srgrimes			printf("Illegal use of \"message list\"\n");
3891590Srgrimes			break;
3901590Srgrimes		}
3911590Srgrimes		if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
3921590Srgrimes			break;
3931590Srgrimes		e = (*com->c_func)(msgvec);
3941590Srgrimes		break;
3951590Srgrimes
3961590Srgrimes	case STRLIST:
3971590Srgrimes		/*
3981590Srgrimes		 * Just the straight string, with
3991590Srgrimes		 * leading blanks removed.
4001590Srgrimes		 */
4011590Srgrimes		while (isspace(*cp))
4021590Srgrimes			cp++;
4031590Srgrimes		e = (*com->c_func)(cp);
4041590Srgrimes		break;
4051590Srgrimes
4061590Srgrimes	case RAWLIST:
4071590Srgrimes		/*
4081590Srgrimes		 * A vector of strings, in shell style.
4091590Srgrimes		 */
4101590Srgrimes		if ((c = getrawlist(cp, arglist,
41177274Smikeh		    sizeof(arglist) / sizeof(*arglist))) < 0)
4121590Srgrimes			break;
4131590Srgrimes		if (c < com->c_minargs) {
4141590Srgrimes			printf("%s requires at least %d arg(s)\n",
41577274Smikeh			    com->c_name, com->c_minargs);
4161590Srgrimes			break;
4171590Srgrimes		}
4181590Srgrimes		if (c > com->c_maxargs) {
4191590Srgrimes			printf("%s takes no more than %d arg(s)\n",
42077274Smikeh			    com->c_name, com->c_maxargs);
4211590Srgrimes			break;
4221590Srgrimes		}
4231590Srgrimes		e = (*com->c_func)(arglist);
4241590Srgrimes		break;
4251590Srgrimes
4261590Srgrimes	case NOLIST:
4271590Srgrimes		/*
4281590Srgrimes		 * Just the constant zero, for exiting,
4291590Srgrimes		 * eg.
4301590Srgrimes		 */
4311590Srgrimes		e = (*com->c_func)(0);
4321590Srgrimes		break;
4331590Srgrimes
4341590Srgrimes	default:
43574769Smikeh		errx(1, "Unknown argtype");
4361590Srgrimes	}
4371590Srgrimes
4381590Srgrimesout:
4391590Srgrimes	/*
4401590Srgrimes	 * Exit the current source file on
4411590Srgrimes	 * error.
4421590Srgrimes	 */
4431590Srgrimes	if (e) {
4441590Srgrimes		if (e < 0)
44577274Smikeh			return (1);
4461590Srgrimes		if (loading)
44777274Smikeh			return (1);
4481590Srgrimes		if (sourcing)
4491590Srgrimes			unstack();
45077274Smikeh		return (0);
4511590Srgrimes	}
45288150Smikeh	if (com == NULL)
45388150Smikeh		return (0);
45477274Smikeh	if (value("autoprint") != NULL && com->c_argtype & P)
4551590Srgrimes		if ((dot->m_flag & MDELETED) == 0) {
4561590Srgrimes			muvec[0] = dot - &message[0] + 1;
4571590Srgrimes			muvec[1] = 0;
4581590Srgrimes			type(muvec);
4591590Srgrimes		}
4601590Srgrimes	if (!sourcing && (com->c_argtype & T) == 0)
4611590Srgrimes		sawcom = 1;
46277274Smikeh	return (0);
4631590Srgrimes}
4641590Srgrimes
4651590Srgrimes/*
4661590Srgrimes * Set the size of the message vector used to construct argument
4671590Srgrimes * lists to message list functions.
4681590Srgrimes */
4691590Srgrimesvoid
4701590Srgrimessetmsize(sz)
4711590Srgrimes	int sz;
4721590Srgrimes{
4731590Srgrimes
47488150Smikeh	if (msgvec != NULL)
47577274Smikeh		(void)free(msgvec);
47677274Smikeh	msgvec = calloc((unsigned)(sz + 1), sizeof(*msgvec));
4771590Srgrimes}
4781590Srgrimes
4791590Srgrimes/*
4801590Srgrimes * Find the correct command in the command table corresponding
4811590Srgrimes * to the passed command "word"
4821590Srgrimes */
4831590Srgrimes
48477274Smikeh__const struct cmd *
4851590Srgrimeslex(word)
4861590Srgrimes	char word[];
4871590Srgrimes{
48877274Smikeh	const struct cmd *cp;
4891590Srgrimes
49010067Sjoerg	/*
49177274Smikeh	 * ignore trailing chars after `#'
49210067Sjoerg	 *
49310067Sjoerg	 * lines with beginning `#' are comments
49474769Smikeh	 * spaces before `#' are ignored in execute()
49510067Sjoerg	 */
49610067Sjoerg
49710067Sjoerg	if (*word == '#')
49810067Sjoerg	    *(word+1) = '\0';
49910067Sjoerg
50010067Sjoerg
50177274Smikeh	for (cp = &cmdtab[0]; cp->c_name != NULL; cp++)
5021590Srgrimes		if (isprefix(word, cp->c_name))
50377274Smikeh			return (cp);
50477274Smikeh	return (NULL);
5051590Srgrimes}
5061590Srgrimes
5071590Srgrimes/*
5081590Srgrimes * Determine if as1 is a valid prefix of as2.
5091590Srgrimes * Return true if yep.
5101590Srgrimes */
5111590Srgrimesint
5121590Srgrimesisprefix(as1, as2)
51377274Smikeh	const char *as1, *as2;
5141590Srgrimes{
51577274Smikeh	const char *s1, *s2;
5161590Srgrimes
5171590Srgrimes	s1 = as1;
5181590Srgrimes	s2 = as2;
5191590Srgrimes	while (*s1++ == *s2)
5201590Srgrimes		if (*s2++ == '\0')
52177274Smikeh			return (1);
52277274Smikeh	return (*--s1 == '\0');
5231590Srgrimes}
5241590Srgrimes
5251590Srgrimes/*
5261590Srgrimes * The following gets called on receipt of an interrupt.  This is
5271590Srgrimes * to abort printout of a command, mainly.
5281590Srgrimes * Dispatching here when command() is inactive crashes rcv.
5291590Srgrimes * Close all open files except 0, 1, 2, and the temporary.
5301590Srgrimes * Also, unstack all source files.
5311590Srgrimes */
5321590Srgrimes
5331590Srgrimesint	inithdr;			/* am printing startup headers */
5341590Srgrimes
5351590Srgrimes/*ARGSUSED*/
5361590Srgrimesvoid
5371590Srgrimesintr(s)
5381590Srgrimes	int s;
5391590Srgrimes{
5401590Srgrimes
5411590Srgrimes	noreset = 0;
5421590Srgrimes	if (!inithdr)
5431590Srgrimes		sawcom++;
5441590Srgrimes	inithdr = 0;
5451590Srgrimes	while (sourcing)
5461590Srgrimes		unstack();
5471590Srgrimes
5481590Srgrimes	close_all_files();
5491590Srgrimes
5501590Srgrimes	if (image >= 0) {
55177274Smikeh		(void)close(image);
5521590Srgrimes		image = -1;
5531590Srgrimes	}
5541590Srgrimes	fprintf(stderr, "Interrupt\n");
5551590Srgrimes	reset(0);
5561590Srgrimes}
5571590Srgrimes
5581590Srgrimes/*
5591590Srgrimes * When we wake up after ^Z, reprint the prompt.
5601590Srgrimes */
5611590Srgrimesvoid
5621590Srgrimesstop(s)
5631590Srgrimes	int s;
5641590Srgrimes{
5651590Srgrimes	sig_t old_action = signal(s, SIG_DFL);
56688150Smikeh	sigset_t nset;
5671590Srgrimes
56888150Smikeh	(void)sigemptyset(&nset);
56988150Smikeh	(void)sigaddset(&nset, s);
57088150Smikeh	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
57177274Smikeh	(void)kill(0, s);
57288150Smikeh	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
57377274Smikeh	(void)signal(s, old_action);
5741590Srgrimes	if (reset_on_stop) {
5751590Srgrimes		reset_on_stop = 0;
5761590Srgrimes		reset(0);
5771590Srgrimes	}
5781590Srgrimes}
5791590Srgrimes
5801590Srgrimes/*
5811590Srgrimes * Branch here on hangup signal and simulate "exit".
5821590Srgrimes */
5831590Srgrimes/*ARGSUSED*/
5841590Srgrimesvoid
5851590Srgrimeshangup(s)
5861590Srgrimes	int s;
5871590Srgrimes{
5881590Srgrimes
5891590Srgrimes	/* nothing to do? */
5901590Srgrimes	exit(1);
5911590Srgrimes}
5921590Srgrimes
5931590Srgrimes/*
5941590Srgrimes * Announce the presence of the current Mail version,
5951590Srgrimes * give the message count, and print a header listing.
5961590Srgrimes */
5971590Srgrimesvoid
5981590Srgrimesannounce()
5991590Srgrimes{
6001590Srgrimes	int vec[2], mdot;
6011590Srgrimes
60288150Smikeh	mdot = newfileinfo(0);
6031590Srgrimes	vec[0] = mdot;
6041590Srgrimes	vec[1] = 0;
6051590Srgrimes	dot = &message[mdot - 1];
60677274Smikeh	if (msgCount > 0 && value("noheader") == NULL) {
6071590Srgrimes		inithdr++;
6081590Srgrimes		headers(vec);
6091590Srgrimes		inithdr = 0;
6101590Srgrimes	}
6111590Srgrimes}
6121590Srgrimes
6131590Srgrimes/*
6141590Srgrimes * Announce information about the file we are editing.
6151590Srgrimes * Return a likely place to set dot.
6161590Srgrimes */
6171590Srgrimesint
61888150Smikehnewfileinfo(omsgCount)
61988150Smikeh	int omsgCount;
6201590Srgrimes{
62177274Smikeh	struct message *mp;
62277274Smikeh	int u, n, mdot, d, s;
62374769Smikeh	char fname[PATHSIZE+1], zname[PATHSIZE+1], *ename;
6241590Srgrimes
62588150Smikeh	for (mp = &message[omsgCount]; mp < &message[msgCount]; mp++)
6261590Srgrimes		if (mp->m_flag & MNEW)
6271590Srgrimes			break;
6281590Srgrimes	if (mp >= &message[msgCount])
62988150Smikeh		for (mp = &message[omsgCount]; mp < &message[msgCount]; mp++)
6301590Srgrimes			if ((mp->m_flag & MREAD) == 0)
6311590Srgrimes				break;
6321590Srgrimes	if (mp < &message[msgCount])
6331590Srgrimes		mdot = mp - &message[0] + 1;
6341590Srgrimes	else
63588150Smikeh		mdot = omsgCount + 1;
6361590Srgrimes	s = d = 0;
6371590Srgrimes	for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) {
6381590Srgrimes		if (mp->m_flag & MNEW)
6391590Srgrimes			n++;
6401590Srgrimes		if ((mp->m_flag & MREAD) == 0)
6411590Srgrimes			u++;
6421590Srgrimes		if (mp->m_flag & MDELETED)
6431590Srgrimes			d++;
6441590Srgrimes		if (mp->m_flag & MSAVED)
6451590Srgrimes			s++;
6461590Srgrimes	}
6471590Srgrimes	ename = mailname;
64874769Smikeh	if (getfold(fname, sizeof(fname) - 1) >= 0) {
6491590Srgrimes		strcat(fname, "/");
6501590Srgrimes		if (strncmp(fname, mailname, strlen(fname)) == 0) {
65177274Smikeh			(void)snprintf(zname, sizeof(zname), "+%s",
65277274Smikeh			    mailname + strlen(fname));
6531590Srgrimes			ename = zname;
6541590Srgrimes		}
6551590Srgrimes	}
6561590Srgrimes	printf("\"%s\": ", ename);
6571590Srgrimes	if (msgCount == 1)
6581590Srgrimes		printf("1 message");
6591590Srgrimes	else
6601590Srgrimes		printf("%d messages", msgCount);
6611590Srgrimes	if (n > 0)
6621590Srgrimes		printf(" %d new", n);
6631590Srgrimes	if (u-n > 0)
6641590Srgrimes		printf(" %d unread", u);
6651590Srgrimes	if (d > 0)
6661590Srgrimes		printf(" %d deleted", d);
6671590Srgrimes	if (s > 0)
6681590Srgrimes		printf(" %d saved", s);
6691590Srgrimes	if (readonly)
6701590Srgrimes		printf(" [Read only]");
6711590Srgrimes	printf("\n");
67277274Smikeh	return (mdot);
6731590Srgrimes}
6741590Srgrimes
6751590Srgrimes/*
6761590Srgrimes * Print the current version number.
6771590Srgrimes */
6781590Srgrimes
6791590Srgrimes/*ARGSUSED*/
6801590Srgrimesint
6811590Srgrimespversion(e)
6821590Srgrimes	int e;
6831590Srgrimes{
6841590Srgrimes
6851590Srgrimes	printf("Version %s\n", version);
68677274Smikeh	return (0);
6871590Srgrimes}
6881590Srgrimes
6891590Srgrimes/*
6901590Srgrimes * Load a file of user definitions.
6911590Srgrimes */
6921590Srgrimesvoid
6931590Srgrimesload(name)
6941590Srgrimes	char *name;
6951590Srgrimes{
69677274Smikeh	FILE *in, *oldin;
6971590Srgrimes
6981590Srgrimes	if ((in = Fopen(name, "r")) == NULL)
6991590Srgrimes		return;
7001590Srgrimes	oldin = input;
7011590Srgrimes	input = in;
7021590Srgrimes	loading = 1;
7031590Srgrimes	sourcing = 1;
7041590Srgrimes	commands();
7051590Srgrimes	loading = 0;
7061590Srgrimes	sourcing = 0;
7071590Srgrimes	input = oldin;
70877274Smikeh	(void)Fclose(in);
7091590Srgrimes}
710