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 * 4. Neither the name of the University nor the names of its contributors
141590Srgrimes *    may be used to endorse or promote products derived from this software
151590Srgrimes *    without specific prior written permission.
161590Srgrimes *
171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271590Srgrimes * SUCH DAMAGE.
281590Srgrimes */
291590Srgrimes
301590Srgrimes#ifndef lint
3174769Smikeh#if 0
3288150Smikehstatic char sccsid[] = "@(#)lex.c	8.2 (Berkeley) 4/20/95";
3374769Smikeh#endif
341590Srgrimes#endif /* not lint */
3599112Sobrien#include <sys/cdefs.h>
3699112Sobrien__FBSDID("$FreeBSD$");
371590Srgrimes
381590Srgrimes#include "rcv.h"
391590Srgrimes#include <errno.h>
401590Srgrimes#include <fcntl.h>
411590Srgrimes#include "extern.h"
421590Srgrimes
431590Srgrimes/*
441590Srgrimes * Mail -- a mail program
451590Srgrimes *
461590Srgrimes * Lexical processing of commands.
471590Srgrimes */
481590Srgrimes
49173439Sddsstatic const char	*prompt = "& ";
501590Srgrimes
5177274Smikehextern const struct cmd cmdtab[];
5277274Smikehextern const char *version;
5377274Smikeh
541590Srgrimes/*
551590Srgrimes * Set up editing on the given file name.
561590Srgrimes * If the first character of name is %, we are considered to be
571590Srgrimes * editing the file, otherwise we are reading our mail which has
581590Srgrimes * signficance for mbox and so forth.
59126415Smikeh *
60126415Smikeh * If the -e option is being passed to mail, this function has a
61126415Smikeh * tri-state return code: -1 on error, 0 on no mail, 1 if there is
62126415Smikeh * mail.
631590Srgrimes */
641590Srgrimesint
65216564Scharniersetfile(char *name)
661590Srgrimes{
671590Srgrimes	FILE *ibuf;
68126415Smikeh	int checkmode, i, fd;
691590Srgrimes	struct stat stb;
7088150Smikeh	char isedit = *name != '%' || getuserid(myname) != getuid();
711590Srgrimes	char *who = name[1] ? name + 1 : myname;
7274769Smikeh	char tempname[PATHSIZE];
731590Srgrimes	static int shudclob;
741590Srgrimes
75126691Smikeh	checkmode = value("checkmode") != NULL;
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	 */
14688227Sache	mailsize = ftello(ibuf);
14777274Smikeh	(void)Fclose(ibuf);
1481590Srgrimes	relsesigs();
1491590Srgrimes	sawcom = 0;
150126415Smikeh
151126415Smikeh	if ((checkmode || !edit) && msgCount == 0) {
1521590Srgrimesnomail:
153126415Smikeh		if (!checkmode) {
154126415Smikeh			fprintf(stderr, "No mail for %s\n", who);
155126415Smikeh			return (-1);
156126415Smikeh		} else
157126415Smikeh			return (0);
1581590Srgrimes	}
159126415Smikeh	return (checkmode ? 1 : 0);
1601590Srgrimes}
1611590Srgrimes
16288150Smikeh/*
16388150Smikeh * Incorporate any new mail that has arrived since we first
16488150Smikeh * started reading mail.
16588150Smikeh */
16688150Smikehint
167216564Scharnierincfile(void)
16888150Smikeh{
16988227Sache	off_t newsize;
17088150Smikeh	int omsgCount = msgCount;
17188150Smikeh	FILE *ibuf;
17288150Smikeh
17388150Smikeh	ibuf = Fopen(mailname, "r");
17488150Smikeh	if (ibuf == NULL)
17588150Smikeh		return (-1);
17688150Smikeh	holdsigs();
17788150Smikeh	newsize = fsize(ibuf);
17888150Smikeh	if (newsize == 0)
17988150Smikeh		return (-1);		/* mail box is now empty??? */
18088150Smikeh	if (newsize < mailsize)
18188150Smikeh		return (-1);		/* mail box has shrunk??? */
18288150Smikeh	if (newsize == mailsize)
18388150Smikeh		return (0);		/* no new mail */
18488150Smikeh	setptr(ibuf, mailsize);
18588150Smikeh	setmsize(msgCount);
18688227Sache	mailsize = ftello(ibuf);
18788150Smikeh	(void)Fclose(ibuf);
18888150Smikeh	relsesigs();
18988150Smikeh	return (msgCount - omsgCount);
19088150Smikeh}
19188150Smikeh
192173439Sddsstatic int	*msgvec;
193173439Sddsstatic int	reset_on_stop;		/* do a reset() if stopped */
1941590Srgrimes
1951590Srgrimes/*
1961590Srgrimes * Interpret user commands one by one.  If standard input is not a tty,
1971590Srgrimes * print no prompt.
1981590Srgrimes */
1991590Srgrimesvoid
200216564Scharniercommands(void)
2011590Srgrimes{
20277274Smikeh	int n, eofloop = 0;
2031590Srgrimes	char linebuf[LINESIZE];
2041590Srgrimes
2051590Srgrimes	if (!sourcing) {
2061590Srgrimes		if (signal(SIGINT, SIG_IGN) != SIG_IGN)
20777274Smikeh			(void)signal(SIGINT, intr);
2081590Srgrimes		if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
20977274Smikeh			(void)signal(SIGHUP, hangup);
21077274Smikeh		(void)signal(SIGTSTP, stop);
21177274Smikeh		(void)signal(SIGTTOU, stop);
21277274Smikeh		(void)signal(SIGTTIN, stop);
2131590Srgrimes	}
2141590Srgrimes	setexit();
2151590Srgrimes	for (;;) {
2161590Srgrimes		/*
2171590Srgrimes		 * Print the prompt, if needed.  Clear out
2181590Srgrimes		 * string space, and flush the output.
2191590Srgrimes		 */
22077274Smikeh		if (!sourcing && value("interactive") != NULL) {
22188150Smikeh			if ((value("autoinc") != NULL) && (incfile() > 0))
22288150Smikeh				printf("New mail has arrived.\n");
2231590Srgrimes			reset_on_stop = 1;
22469249Skris			printf("%s", prompt);
2251590Srgrimes		}
22677274Smikeh		(void)fflush(stdout);
2271590Srgrimes		sreset();
2281590Srgrimes		/*
2291590Srgrimes		 * Read a line of commands from the current input
2301590Srgrimes		 * and handle end of file specially.
2311590Srgrimes		 */
2321590Srgrimes		n = 0;
2331590Srgrimes		for (;;) {
2341590Srgrimes			if (readline(input, &linebuf[n], LINESIZE - n) < 0) {
2351590Srgrimes				if (n == 0)
2361590Srgrimes					n = -1;
2371590Srgrimes				break;
2381590Srgrimes			}
2391590Srgrimes			if ((n = strlen(linebuf)) == 0)
2401590Srgrimes				break;
2411590Srgrimes			n--;
2421590Srgrimes			if (linebuf[n] != '\\')
2431590Srgrimes				break;
2441590Srgrimes			linebuf[n++] = ' ';
2451590Srgrimes		}
2461590Srgrimes		reset_on_stop = 0;
2471590Srgrimes		if (n < 0) {
2481590Srgrimes				/* eof */
2491590Srgrimes			if (loading)
2501590Srgrimes				break;
2511590Srgrimes			if (sourcing) {
2521590Srgrimes				unstack();
2531590Srgrimes				continue;
2541590Srgrimes			}
25577274Smikeh			if (value("interactive") != NULL &&
25677274Smikeh			    value("ignoreeof") != NULL &&
2571590Srgrimes			    ++eofloop < 25) {
2581590Srgrimes				printf("Use \"quit\" to quit.\n");
2591590Srgrimes				continue;
2601590Srgrimes			}
2611590Srgrimes			break;
2621590Srgrimes		}
2631590Srgrimes		eofloop = 0;
2641590Srgrimes		if (execute(linebuf, 0))
2651590Srgrimes			break;
2661590Srgrimes	}
2671590Srgrimes}
2681590Srgrimes
2691590Srgrimes/*
2701590Srgrimes * Execute a single command.
2711590Srgrimes * Command functions return 0 for success, 1 for error, and -1
2721590Srgrimes * for abort.  A 1 or -1 aborts a load or source.  A -1 aborts
2731590Srgrimes * the interactive command loop.
2741590Srgrimes * Contxt is non-zero if called while composing mail.
2751590Srgrimes */
2761590Srgrimesint
277216564Scharnierexecute(char linebuf[], int contxt)
2781590Srgrimes{
2791590Srgrimes	char word[LINESIZE];
2801590Srgrimes	char *arglist[MAXARGC];
28177274Smikeh	const struct cmd *com;
28277274Smikeh	char *cp, *cp2;
28377274Smikeh	int c, muvec[2];
2841590Srgrimes	int e = 1;
2851590Srgrimes
2861590Srgrimes	/*
2871590Srgrimes	 * Strip the white space away from the beginning
2881590Srgrimes	 * of the command, then scan out a word, which
2891590Srgrimes	 * consists of anything except digits and white space.
2901590Srgrimes	 *
2911590Srgrimes	 * Handle ! escapes differently to get the correct
2921590Srgrimes	 * lexical conventions.
2931590Srgrimes	 */
2941590Srgrimes
29588227Sache	for (cp = linebuf; isspace((unsigned char)*cp); cp++)
2961590Srgrimes		;
2971590Srgrimes	if (*cp == '!') {
2981590Srgrimes		if (sourcing) {
2991590Srgrimes			printf("Can't \"!\" while sourcing\n");
3001590Srgrimes			goto out;
3011590Srgrimes		}
3021590Srgrimes		shell(cp+1);
30377274Smikeh		return (0);
3041590Srgrimes	}
3051590Srgrimes	cp2 = word;
30677274Smikeh	while (*cp != '\0' && strchr(" \t0123456789$^.:/-+*'\"", *cp) == NULL)
3071590Srgrimes		*cp2++ = *cp++;
3081590Srgrimes	*cp2 = '\0';
3091590Srgrimes
3101590Srgrimes	/*
3111590Srgrimes	 * Look up the command; if not found, bitch.
3121590Srgrimes	 * Normally, a blank command would map to the
3131590Srgrimes	 * first command in the table; while sourcing,
3141590Srgrimes	 * however, we ignore blank lines to eliminate
3151590Srgrimes	 * confusion.
3161590Srgrimes	 */
3171590Srgrimes
3181590Srgrimes	if (sourcing && *word == '\0')
31977274Smikeh		return (0);
3201590Srgrimes	com = lex(word);
32177274Smikeh	if (com == NULL) {
3221590Srgrimes		printf("Unknown command: \"%s\"\n", word);
3231590Srgrimes		goto out;
3241590Srgrimes	}
3251590Srgrimes
3261590Srgrimes	/*
3271590Srgrimes	 * See if we should execute the command -- if a conditional
3281590Srgrimes	 * we always execute it, otherwise, check the state of cond.
3291590Srgrimes	 */
3301590Srgrimes
3311590Srgrimes	if ((com->c_argtype & F) == 0)
33277274Smikeh		if ((cond == CRCV && !rcvmode) || (cond == CSEND && rcvmode))
33377274Smikeh			return (0);
3341590Srgrimes
3351590Srgrimes	/*
3361590Srgrimes	 * Process the arguments to the command, depending
3371590Srgrimes	 * on the type he expects.  Default to an error.
3381590Srgrimes	 * If we are sourcing an interactive command, it's
3391590Srgrimes	 * an error.
3401590Srgrimes	 */
3411590Srgrimes
3421590Srgrimes	if (!rcvmode && (com->c_argtype & M) == 0) {
3431590Srgrimes		printf("May not execute \"%s\" while sending\n",
3441590Srgrimes		    com->c_name);
3451590Srgrimes		goto out;
3461590Srgrimes	}
3471590Srgrimes	if (sourcing && com->c_argtype & I) {
3481590Srgrimes		printf("May not execute \"%s\" while sourcing\n",
3491590Srgrimes		    com->c_name);
3501590Srgrimes		goto out;
3511590Srgrimes	}
3521590Srgrimes	if (readonly && com->c_argtype & W) {
3531590Srgrimes		printf("May not execute \"%s\" -- message file is read only\n",
3541590Srgrimes		   com->c_name);
3551590Srgrimes		goto out;
3561590Srgrimes	}
3571590Srgrimes	if (contxt && com->c_argtype & R) {
3581590Srgrimes		printf("Cannot recursively invoke \"%s\"\n", com->c_name);
3591590Srgrimes		goto out;
3601590Srgrimes	}
3611590Srgrimes	switch (com->c_argtype & ~(F|P|I|M|T|W|R)) {
3621590Srgrimes	case MSGLIST:
3631590Srgrimes		/*
3641590Srgrimes		 * A message list defaulting to nearest forward
3651590Srgrimes		 * legal message.
3661590Srgrimes		 */
3671590Srgrimes		if (msgvec == 0) {
3681590Srgrimes			printf("Illegal use of \"message list\"\n");
3691590Srgrimes			break;
3701590Srgrimes		}
3711590Srgrimes		if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
3721590Srgrimes			break;
3731590Srgrimes		if (c  == 0) {
37477274Smikeh			*msgvec = first(com->c_msgflag, com->c_msgmask);
37529574Sphk			msgvec[1] = 0;
3761590Srgrimes		}
37729574Sphk		if (*msgvec == 0) {
3781590Srgrimes			printf("No applicable messages\n");
3791590Srgrimes			break;
3801590Srgrimes		}
3811590Srgrimes		e = (*com->c_func)(msgvec);
3821590Srgrimes		break;
3831590Srgrimes
3841590Srgrimes	case NDMLIST:
3851590Srgrimes		/*
3861590Srgrimes		 * A message list with no defaults, but no error
3871590Srgrimes		 * if none exist.
3881590Srgrimes		 */
3891590Srgrimes		if (msgvec == 0) {
3901590Srgrimes			printf("Illegal use of \"message list\"\n");
3911590Srgrimes			break;
3921590Srgrimes		}
3931590Srgrimes		if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
3941590Srgrimes			break;
3951590Srgrimes		e = (*com->c_func)(msgvec);
3961590Srgrimes		break;
3971590Srgrimes
3981590Srgrimes	case STRLIST:
3991590Srgrimes		/*
4001590Srgrimes		 * Just the straight string, with
4011590Srgrimes		 * leading blanks removed.
4021590Srgrimes		 */
40388227Sache		while (isspace((unsigned char)*cp))
4041590Srgrimes			cp++;
4051590Srgrimes		e = (*com->c_func)(cp);
4061590Srgrimes		break;
4071590Srgrimes
4081590Srgrimes	case RAWLIST:
4091590Srgrimes		/*
4101590Srgrimes		 * A vector of strings, in shell style.
4111590Srgrimes		 */
4121590Srgrimes		if ((c = getrawlist(cp, arglist,
41377274Smikeh		    sizeof(arglist) / sizeof(*arglist))) < 0)
4141590Srgrimes			break;
4151590Srgrimes		if (c < com->c_minargs) {
4161590Srgrimes			printf("%s requires at least %d arg(s)\n",
41777274Smikeh			    com->c_name, com->c_minargs);
4181590Srgrimes			break;
4191590Srgrimes		}
4201590Srgrimes		if (c > com->c_maxargs) {
4211590Srgrimes			printf("%s takes no more than %d arg(s)\n",
42277274Smikeh			    com->c_name, com->c_maxargs);
4231590Srgrimes			break;
4241590Srgrimes		}
4251590Srgrimes		e = (*com->c_func)(arglist);
4261590Srgrimes		break;
4271590Srgrimes
4281590Srgrimes	case NOLIST:
4291590Srgrimes		/*
4301590Srgrimes		 * Just the constant zero, for exiting,
4311590Srgrimes		 * eg.
4321590Srgrimes		 */
4331590Srgrimes		e = (*com->c_func)(0);
4341590Srgrimes		break;
4351590Srgrimes
4361590Srgrimes	default:
43774769Smikeh		errx(1, "Unknown argtype");
4381590Srgrimes	}
4391590Srgrimes
4401590Srgrimesout:
4411590Srgrimes	/*
4421590Srgrimes	 * Exit the current source file on
4431590Srgrimes	 * error.
4441590Srgrimes	 */
4451590Srgrimes	if (e) {
4461590Srgrimes		if (e < 0)
44777274Smikeh			return (1);
4481590Srgrimes		if (loading)
44977274Smikeh			return (1);
4501590Srgrimes		if (sourcing)
4511590Srgrimes			unstack();
45277274Smikeh		return (0);
4531590Srgrimes	}
45488150Smikeh	if (com == NULL)
45588150Smikeh		return (0);
45677274Smikeh	if (value("autoprint") != NULL && com->c_argtype & P)
4571590Srgrimes		if ((dot->m_flag & MDELETED) == 0) {
4581590Srgrimes			muvec[0] = dot - &message[0] + 1;
4591590Srgrimes			muvec[1] = 0;
4601590Srgrimes			type(muvec);
4611590Srgrimes		}
4621590Srgrimes	if (!sourcing && (com->c_argtype & T) == 0)
4631590Srgrimes		sawcom = 1;
46477274Smikeh	return (0);
4651590Srgrimes}
4661590Srgrimes
4671590Srgrimes/*
4681590Srgrimes * Set the size of the message vector used to construct argument
4691590Srgrimes * lists to message list functions.
4701590Srgrimes */
4711590Srgrimesvoid
472216564Scharniersetmsize(int sz)
4731590Srgrimes{
4741590Srgrimes
47588150Smikeh	if (msgvec != NULL)
47677274Smikeh		(void)free(msgvec);
47777274Smikeh	msgvec = calloc((unsigned)(sz + 1), sizeof(*msgvec));
4781590Srgrimes}
4791590Srgrimes
4801590Srgrimes/*
4811590Srgrimes * Find the correct command in the command table corresponding
4821590Srgrimes * to the passed command "word"
4831590Srgrimes */
4841590Srgrimes
485228468Sedconst struct cmd *
486216564Scharnierlex(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
512216564Scharnierisprefix(const char *as1, const char *as2)
5131590Srgrimes{
51477274Smikeh	const char *s1, *s2;
5151590Srgrimes
5161590Srgrimes	s1 = as1;
5171590Srgrimes	s2 = as2;
5181590Srgrimes	while (*s1++ == *s2)
5191590Srgrimes		if (*s2++ == '\0')
52077274Smikeh			return (1);
52177274Smikeh	return (*--s1 == '\0');
5221590Srgrimes}
5231590Srgrimes
5241590Srgrimes/*
5251590Srgrimes * The following gets called on receipt of an interrupt.  This is
5261590Srgrimes * to abort printout of a command, mainly.
5271590Srgrimes * Dispatching here when command() is inactive crashes rcv.
5281590Srgrimes * Close all open files except 0, 1, 2, and the temporary.
5291590Srgrimes * Also, unstack all source files.
5301590Srgrimes */
5311590Srgrimes
532173439Sddsstatic int	inithdr;		/* am printing startup headers */
5331590Srgrimes
5341590Srgrimesvoid
535216564Scharnierintr(int s __unused)
5361590Srgrimes{
5371590Srgrimes
5381590Srgrimes	noreset = 0;
5391590Srgrimes	if (!inithdr)
5401590Srgrimes		sawcom++;
5411590Srgrimes	inithdr = 0;
5421590Srgrimes	while (sourcing)
5431590Srgrimes		unstack();
5441590Srgrimes
5451590Srgrimes	close_all_files();
5461590Srgrimes
5471590Srgrimes	if (image >= 0) {
54877274Smikeh		(void)close(image);
5491590Srgrimes		image = -1;
5501590Srgrimes	}
5511590Srgrimes	fprintf(stderr, "Interrupt\n");
5521590Srgrimes	reset(0);
5531590Srgrimes}
5541590Srgrimes
5551590Srgrimes/*
5561590Srgrimes * When we wake up after ^Z, reprint the prompt.
5571590Srgrimes */
5581590Srgrimesvoid
559216564Scharnierstop(int s)
5601590Srgrimes{
5611590Srgrimes	sig_t old_action = signal(s, SIG_DFL);
56288150Smikeh	sigset_t nset;
5631590Srgrimes
56488150Smikeh	(void)sigemptyset(&nset);
56588150Smikeh	(void)sigaddset(&nset, s);
56688150Smikeh	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
56777274Smikeh	(void)kill(0, s);
56888150Smikeh	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
56977274Smikeh	(void)signal(s, old_action);
5701590Srgrimes	if (reset_on_stop) {
5711590Srgrimes		reset_on_stop = 0;
5721590Srgrimes		reset(0);
5731590Srgrimes	}
5741590Srgrimes}
5751590Srgrimes
5761590Srgrimes/*
5771590Srgrimes * Branch here on hangup signal and simulate "exit".
5781590Srgrimes */
5791590Srgrimesvoid
580216564Scharnierhangup(int s __unused)
5811590Srgrimes{
5821590Srgrimes
5831590Srgrimes	/* nothing to do? */
5841590Srgrimes	exit(1);
5851590Srgrimes}
5861590Srgrimes
5871590Srgrimes/*
5881590Srgrimes * Announce the presence of the current Mail version,
5891590Srgrimes * give the message count, and print a header listing.
5901590Srgrimes */
5911590Srgrimesvoid
592216564Scharnierannounce(void)
5931590Srgrimes{
5941590Srgrimes	int vec[2], mdot;
5951590Srgrimes
59688150Smikeh	mdot = newfileinfo(0);
5971590Srgrimes	vec[0] = mdot;
5981590Srgrimes	vec[1] = 0;
5991590Srgrimes	dot = &message[mdot - 1];
60077274Smikeh	if (msgCount > 0 && value("noheader") == NULL) {
6011590Srgrimes		inithdr++;
6021590Srgrimes		headers(vec);
6031590Srgrimes		inithdr = 0;
6041590Srgrimes	}
6051590Srgrimes}
6061590Srgrimes
6071590Srgrimes/*
6081590Srgrimes * Announce information about the file we are editing.
6091590Srgrimes * Return a likely place to set dot.
6101590Srgrimes */
6111590Srgrimesint
612216564Scharniernewfileinfo(int omsgCount)
6131590Srgrimes{
61477274Smikeh	struct message *mp;
61577274Smikeh	int u, n, mdot, d, s;
61674769Smikeh	char fname[PATHSIZE+1], zname[PATHSIZE+1], *ename;
6171590Srgrimes
61888150Smikeh	for (mp = &message[omsgCount]; mp < &message[msgCount]; mp++)
6191590Srgrimes		if (mp->m_flag & MNEW)
6201590Srgrimes			break;
6211590Srgrimes	if (mp >= &message[msgCount])
62288150Smikeh		for (mp = &message[omsgCount]; mp < &message[msgCount]; mp++)
6231590Srgrimes			if ((mp->m_flag & MREAD) == 0)
6241590Srgrimes				break;
6251590Srgrimes	if (mp < &message[msgCount])
6261590Srgrimes		mdot = mp - &message[0] + 1;
6271590Srgrimes	else
62888150Smikeh		mdot = omsgCount + 1;
6291590Srgrimes	s = d = 0;
6301590Srgrimes	for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) {
6311590Srgrimes		if (mp->m_flag & MNEW)
6321590Srgrimes			n++;
6331590Srgrimes		if ((mp->m_flag & MREAD) == 0)
6341590Srgrimes			u++;
6351590Srgrimes		if (mp->m_flag & MDELETED)
6361590Srgrimes			d++;
6371590Srgrimes		if (mp->m_flag & MSAVED)
6381590Srgrimes			s++;
6391590Srgrimes	}
6401590Srgrimes	ename = mailname;
64174769Smikeh	if (getfold(fname, sizeof(fname) - 1) >= 0) {
6421590Srgrimes		strcat(fname, "/");
6431590Srgrimes		if (strncmp(fname, mailname, strlen(fname)) == 0) {
64477274Smikeh			(void)snprintf(zname, sizeof(zname), "+%s",
64577274Smikeh			    mailname + strlen(fname));
6461590Srgrimes			ename = zname;
6471590Srgrimes		}
6481590Srgrimes	}
6491590Srgrimes	printf("\"%s\": ", ename);
6501590Srgrimes	if (msgCount == 1)
6511590Srgrimes		printf("1 message");
6521590Srgrimes	else
6531590Srgrimes		printf("%d messages", msgCount);
6541590Srgrimes	if (n > 0)
6551590Srgrimes		printf(" %d new", n);
6561590Srgrimes	if (u-n > 0)
6571590Srgrimes		printf(" %d unread", u);
6581590Srgrimes	if (d > 0)
6591590Srgrimes		printf(" %d deleted", d);
6601590Srgrimes	if (s > 0)
6611590Srgrimes		printf(" %d saved", s);
6621590Srgrimes	if (readonly)
6631590Srgrimes		printf(" [Read only]");
6641590Srgrimes	printf("\n");
66577274Smikeh	return (mdot);
6661590Srgrimes}
6671590Srgrimes
6681590Srgrimes/*
6691590Srgrimes * Print the current version number.
6701590Srgrimes */
6711590Srgrimes
6721590Srgrimesint
673216564Scharnierpversion(int e __unused)
6741590Srgrimes{
6751590Srgrimes
6761590Srgrimes	printf("Version %s\n", version);
67777274Smikeh	return (0);
6781590Srgrimes}
6791590Srgrimes
6801590Srgrimes/*
6811590Srgrimes * Load a file of user definitions.
6821590Srgrimes */
6831590Srgrimesvoid
684216564Scharnierload(char *name)
6851590Srgrimes{
68677274Smikeh	FILE *in, *oldin;
6871590Srgrimes
6881590Srgrimes	if ((in = Fopen(name, "r")) == NULL)
6891590Srgrimes		return;
6901590Srgrimes	oldin = input;
6911590Srgrimes	input = in;
6921590Srgrimes	loading = 1;
6931590Srgrimes	sourcing = 1;
6941590Srgrimes	commands();
6951590Srgrimes	loading = 0;
6961590Srgrimes	sourcing = 0;
6971590Srgrimes	input = oldin;
69877274Smikeh	(void)Fclose(in);
6991590Srgrimes}
700