lex.c revision 29574
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
351590Srgrimesstatic char sccsid[] = "@(#)lex.c	8.1 (Berkeley) 6/6/93";
361590Srgrimes#endif /* not lint */
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
491590Srgrimeschar	*prompt = "& ";
501590Srgrimes
511590Srgrimes/*
521590Srgrimes * Set up editing on the given file name.
531590Srgrimes * If the first character of name is %, we are considered to be
541590Srgrimes * editing the file, otherwise we are reading our mail which has
551590Srgrimes * signficance for mbox and so forth.
561590Srgrimes */
571590Srgrimesint
581590Srgrimessetfile(name)
591590Srgrimes	char *name;
601590Srgrimes{
611590Srgrimes	FILE *ibuf;
621590Srgrimes	int i;
631590Srgrimes	struct stat stb;
641590Srgrimes	char isedit = *name != '%';
651590Srgrimes	char *who = name[1] ? name + 1 : myname;
661590Srgrimes	static int shudclob;
671590Srgrimes	extern char tempMesg[];
681590Srgrimes	extern int errno;
691590Srgrimes
701590Srgrimes	if ((name = expand(name)) == NOSTR)
711590Srgrimes		return -1;
721590Srgrimes
731590Srgrimes	if ((ibuf = Fopen(name, "r")) == NULL) {
741590Srgrimes		if (!isedit && errno == ENOENT)
751590Srgrimes			goto nomail;
761590Srgrimes		perror(name);
771590Srgrimes		return(-1);
781590Srgrimes	}
791590Srgrimes
801590Srgrimes	if (fstat(fileno(ibuf), &stb) < 0) {
811590Srgrimes		perror("fstat");
821590Srgrimes		Fclose(ibuf);
831590Srgrimes		return (-1);
841590Srgrimes	}
851590Srgrimes
861590Srgrimes	switch (stb.st_mode & S_IFMT) {
871590Srgrimes	case S_IFDIR:
881590Srgrimes		Fclose(ibuf);
891590Srgrimes		errno = EISDIR;
901590Srgrimes		perror(name);
911590Srgrimes		return (-1);
921590Srgrimes
931590Srgrimes	case S_IFREG:
941590Srgrimes		break;
951590Srgrimes
961590Srgrimes	default:
971590Srgrimes		Fclose(ibuf);
981590Srgrimes		errno = EINVAL;
991590Srgrimes		perror(name);
1001590Srgrimes		return (-1);
1011590Srgrimes	}
1021590Srgrimes
1031590Srgrimes	/*
1041590Srgrimes	 * Looks like all will be well.  We must now relinquish our
1051590Srgrimes	 * hold on the current set of stuff.  Must hold signals
1061590Srgrimes	 * while we are reading the new file, else we will ruin
1071590Srgrimes	 * the message[] data structure.
1081590Srgrimes	 */
1091590Srgrimes
1101590Srgrimes	holdsigs();
1111590Srgrimes	if (shudclob)
1121590Srgrimes		quit();
1131590Srgrimes
1141590Srgrimes	/*
1151590Srgrimes	 * Copy the messages into /tmp
1161590Srgrimes	 * and set pointers.
1171590Srgrimes	 */
1181590Srgrimes
1191590Srgrimes	readonly = 0;
1201590Srgrimes	if ((i = open(name, 1)) < 0)
1211590Srgrimes		readonly++;
1221590Srgrimes	else
1231590Srgrimes		close(i);
1241590Srgrimes	if (shudclob) {
1251590Srgrimes		fclose(itf);
1261590Srgrimes		fclose(otf);
1271590Srgrimes	}
1281590Srgrimes	shudclob = 1;
1291590Srgrimes	edit = isedit;
1301590Srgrimes	strcpy(prevfile, mailname);
1311590Srgrimes	if (name != mailname)
1321590Srgrimes		strcpy(mailname, name);
1331590Srgrimes	mailsize = fsize(ibuf);
1341590Srgrimes	if ((otf = fopen(tempMesg, "w")) == NULL) {
1351590Srgrimes		perror(tempMesg);
1361590Srgrimes		exit(1);
1371590Srgrimes	}
1381590Srgrimes	(void) fcntl(fileno(otf), F_SETFD, 1);
1391590Srgrimes	if ((itf = fopen(tempMesg, "r")) == NULL) {
1401590Srgrimes		perror(tempMesg);
1411590Srgrimes		exit(1);
1421590Srgrimes	}
1431590Srgrimes	(void) fcntl(fileno(itf), F_SETFD, 1);
1441590Srgrimes	rm(tempMesg);
1451590Srgrimes	setptr(ibuf);
1461590Srgrimes	setmsize(msgCount);
1471590Srgrimes	Fclose(ibuf);
1481590Srgrimes	relsesigs();
1491590Srgrimes	sawcom = 0;
1501590Srgrimes	if (!edit && msgCount == 0) {
1511590Srgrimesnomail:
1521590Srgrimes		fprintf(stderr, "No mail for %s\n", who);
1531590Srgrimes		return -1;
1541590Srgrimes	}
1551590Srgrimes	return(0);
1561590Srgrimes}
1571590Srgrimes
1581590Srgrimesint	*msgvec;
1591590Srgrimesint	reset_on_stop;			/* do a reset() if stopped */
1601590Srgrimes
1611590Srgrimes/*
1621590Srgrimes * Interpret user commands one by one.  If standard input is not a tty,
1631590Srgrimes * print no prompt.
1641590Srgrimes */
1651590Srgrimesvoid
1661590Srgrimescommands()
1671590Srgrimes{
1681590Srgrimes	int eofloop = 0;
1691590Srgrimes	register int n;
1701590Srgrimes	char linebuf[LINESIZE];
1711590Srgrimes	void intr(), stop(), hangup();
1721590Srgrimes
1731590Srgrimes	if (!sourcing) {
1741590Srgrimes		if (signal(SIGINT, SIG_IGN) != SIG_IGN)
1751590Srgrimes			signal(SIGINT, intr);
1761590Srgrimes		if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
1771590Srgrimes			signal(SIGHUP, hangup);
1781590Srgrimes		signal(SIGTSTP, stop);
1791590Srgrimes		signal(SIGTTOU, stop);
1801590Srgrimes		signal(SIGTTIN, stop);
1811590Srgrimes	}
1821590Srgrimes	setexit();
1831590Srgrimes	for (;;) {
1841590Srgrimes		/*
1851590Srgrimes		 * Print the prompt, if needed.  Clear out
1861590Srgrimes		 * string space, and flush the output.
1871590Srgrimes		 */
1881590Srgrimes		if (!sourcing && value("interactive") != NOSTR) {
1891590Srgrimes			reset_on_stop = 1;
1901590Srgrimes			printf(prompt);
1911590Srgrimes		}
1921590Srgrimes		fflush(stdout);
1931590Srgrimes		sreset();
1941590Srgrimes		/*
1951590Srgrimes		 * Read a line of commands from the current input
1961590Srgrimes		 * and handle end of file specially.
1971590Srgrimes		 */
1981590Srgrimes		n = 0;
1991590Srgrimes		for (;;) {
2001590Srgrimes			if (readline(input, &linebuf[n], LINESIZE - n) < 0) {
2011590Srgrimes				if (n == 0)
2021590Srgrimes					n = -1;
2031590Srgrimes				break;
2041590Srgrimes			}
2051590Srgrimes			if ((n = strlen(linebuf)) == 0)
2061590Srgrimes				break;
2071590Srgrimes			n--;
2081590Srgrimes			if (linebuf[n] != '\\')
2091590Srgrimes				break;
2101590Srgrimes			linebuf[n++] = ' ';
2111590Srgrimes		}
2121590Srgrimes		reset_on_stop = 0;
2131590Srgrimes		if (n < 0) {
2141590Srgrimes				/* eof */
2151590Srgrimes			if (loading)
2161590Srgrimes				break;
2171590Srgrimes			if (sourcing) {
2181590Srgrimes				unstack();
2191590Srgrimes				continue;
2201590Srgrimes			}
2211590Srgrimes			if (value("interactive") != NOSTR &&
2221590Srgrimes			    value("ignoreeof") != NOSTR &&
2231590Srgrimes			    ++eofloop < 25) {
2241590Srgrimes				printf("Use \"quit\" to quit.\n");
2251590Srgrimes				continue;
2261590Srgrimes			}
2271590Srgrimes			break;
2281590Srgrimes		}
2291590Srgrimes		eofloop = 0;
2301590Srgrimes		if (execute(linebuf, 0))
2311590Srgrimes			break;
2321590Srgrimes	}
2331590Srgrimes}
2341590Srgrimes
2351590Srgrimes/*
2361590Srgrimes * Execute a single command.
2371590Srgrimes * Command functions return 0 for success, 1 for error, and -1
2381590Srgrimes * for abort.  A 1 or -1 aborts a load or source.  A -1 aborts
2391590Srgrimes * the interactive command loop.
2401590Srgrimes * Contxt is non-zero if called while composing mail.
2411590Srgrimes */
2421590Srgrimesint
2431590Srgrimesexecute(linebuf, contxt)
2441590Srgrimes	char linebuf[];
2451590Srgrimes	int contxt;
2461590Srgrimes{
2471590Srgrimes	char word[LINESIZE];
2481590Srgrimes	char *arglist[MAXARGC];
2491590Srgrimes	struct cmd *com;
2501590Srgrimes	register char *cp, *cp2;
2511590Srgrimes	register int c;
2521590Srgrimes	int muvec[2];
2531590Srgrimes	int e = 1;
2541590Srgrimes
2551590Srgrimes	/*
2561590Srgrimes	 * Strip the white space away from the beginning
2571590Srgrimes	 * of the command, then scan out a word, which
2581590Srgrimes	 * consists of anything except digits and white space.
2591590Srgrimes	 *
2601590Srgrimes	 * Handle ! escapes differently to get the correct
2611590Srgrimes	 * lexical conventions.
2621590Srgrimes	 */
2631590Srgrimes
2641590Srgrimes	for (cp = linebuf; isspace(*cp); cp++)
2651590Srgrimes		;
2661590Srgrimes	if (*cp == '!') {
2671590Srgrimes		if (sourcing) {
2681590Srgrimes			printf("Can't \"!\" while sourcing\n");
2691590Srgrimes			goto out;
2701590Srgrimes		}
2711590Srgrimes		shell(cp+1);
2721590Srgrimes		return(0);
2731590Srgrimes	}
2741590Srgrimes	cp2 = word;
2751590Srgrimes	while (*cp && index(" \t0123456789$^.:/-+*'\"", *cp) == NOSTR)
2761590Srgrimes		*cp2++ = *cp++;
2771590Srgrimes	*cp2 = '\0';
2781590Srgrimes
2791590Srgrimes	/*
2801590Srgrimes	 * Look up the command; if not found, bitch.
2811590Srgrimes	 * Normally, a blank command would map to the
2821590Srgrimes	 * first command in the table; while sourcing,
2831590Srgrimes	 * however, we ignore blank lines to eliminate
2841590Srgrimes	 * confusion.
2851590Srgrimes	 */
2861590Srgrimes
2871590Srgrimes	if (sourcing && *word == '\0')
2881590Srgrimes		return(0);
2891590Srgrimes	com = lex(word);
2901590Srgrimes	if (com == NONE) {
2911590Srgrimes		printf("Unknown command: \"%s\"\n", word);
2921590Srgrimes		goto out;
2931590Srgrimes	}
2941590Srgrimes
2951590Srgrimes	/*
2961590Srgrimes	 * See if we should execute the command -- if a conditional
2971590Srgrimes	 * we always execute it, otherwise, check the state of cond.
2981590Srgrimes	 */
2991590Srgrimes
3001590Srgrimes	if ((com->c_argtype & F) == 0)
3011590Srgrimes		if (cond == CRCV && !rcvmode || cond == CSEND && rcvmode)
3021590Srgrimes			return(0);
3031590Srgrimes
3041590Srgrimes	/*
3051590Srgrimes	 * Process the arguments to the command, depending
3061590Srgrimes	 * on the type he expects.  Default to an error.
3071590Srgrimes	 * If we are sourcing an interactive command, it's
3081590Srgrimes	 * an error.
3091590Srgrimes	 */
3101590Srgrimes
3111590Srgrimes	if (!rcvmode && (com->c_argtype & M) == 0) {
3121590Srgrimes		printf("May not execute \"%s\" while sending\n",
3131590Srgrimes		    com->c_name);
3141590Srgrimes		goto out;
3151590Srgrimes	}
3161590Srgrimes	if (sourcing && com->c_argtype & I) {
3171590Srgrimes		printf("May not execute \"%s\" while sourcing\n",
3181590Srgrimes		    com->c_name);
3191590Srgrimes		goto out;
3201590Srgrimes	}
3211590Srgrimes	if (readonly && com->c_argtype & W) {
3221590Srgrimes		printf("May not execute \"%s\" -- message file is read only\n",
3231590Srgrimes		   com->c_name);
3241590Srgrimes		goto out;
3251590Srgrimes	}
3261590Srgrimes	if (contxt && com->c_argtype & R) {
3271590Srgrimes		printf("Cannot recursively invoke \"%s\"\n", com->c_name);
3281590Srgrimes		goto out;
3291590Srgrimes	}
3301590Srgrimes	switch (com->c_argtype & ~(F|P|I|M|T|W|R)) {
3311590Srgrimes	case MSGLIST:
3321590Srgrimes		/*
3331590Srgrimes		 * A message list defaulting to nearest forward
3341590Srgrimes		 * legal message.
3351590Srgrimes		 */
3361590Srgrimes		if (msgvec == 0) {
3371590Srgrimes			printf("Illegal use of \"message list\"\n");
3381590Srgrimes			break;
3391590Srgrimes		}
3401590Srgrimes		if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
3411590Srgrimes			break;
3421590Srgrimes		if (c  == 0) {
3431590Srgrimes			*msgvec = first(com->c_msgflag,
3441590Srgrimes				com->c_msgmask);
34529574Sphk			msgvec[1] = 0;
3461590Srgrimes		}
34729574Sphk		if (*msgvec == 0) {
3481590Srgrimes			printf("No applicable messages\n");
3491590Srgrimes			break;
3501590Srgrimes		}
3511590Srgrimes		e = (*com->c_func)(msgvec);
3521590Srgrimes		break;
3531590Srgrimes
3541590Srgrimes	case NDMLIST:
3551590Srgrimes		/*
3561590Srgrimes		 * A message list with no defaults, but no error
3571590Srgrimes		 * if none exist.
3581590Srgrimes		 */
3591590Srgrimes		if (msgvec == 0) {
3601590Srgrimes			printf("Illegal use of \"message list\"\n");
3611590Srgrimes			break;
3621590Srgrimes		}
3631590Srgrimes		if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
3641590Srgrimes			break;
3651590Srgrimes		e = (*com->c_func)(msgvec);
3661590Srgrimes		break;
3671590Srgrimes
3681590Srgrimes	case STRLIST:
3691590Srgrimes		/*
3701590Srgrimes		 * Just the straight string, with
3711590Srgrimes		 * leading blanks removed.
3721590Srgrimes		 */
3731590Srgrimes		while (isspace(*cp))
3741590Srgrimes			cp++;
3751590Srgrimes		e = (*com->c_func)(cp);
3761590Srgrimes		break;
3771590Srgrimes
3781590Srgrimes	case RAWLIST:
3791590Srgrimes		/*
3801590Srgrimes		 * A vector of strings, in shell style.
3811590Srgrimes		 */
3821590Srgrimes		if ((c = getrawlist(cp, arglist,
3831590Srgrimes				sizeof arglist / sizeof *arglist)) < 0)
3841590Srgrimes			break;
3851590Srgrimes		if (c < com->c_minargs) {
3861590Srgrimes			printf("%s requires at least %d arg(s)\n",
3871590Srgrimes				com->c_name, com->c_minargs);
3881590Srgrimes			break;
3891590Srgrimes		}
3901590Srgrimes		if (c > com->c_maxargs) {
3911590Srgrimes			printf("%s takes no more than %d arg(s)\n",
3921590Srgrimes				com->c_name, com->c_maxargs);
3931590Srgrimes			break;
3941590Srgrimes		}
3951590Srgrimes		e = (*com->c_func)(arglist);
3961590Srgrimes		break;
3971590Srgrimes
3981590Srgrimes	case NOLIST:
3991590Srgrimes		/*
4001590Srgrimes		 * Just the constant zero, for exiting,
4011590Srgrimes		 * eg.
4021590Srgrimes		 */
4031590Srgrimes		e = (*com->c_func)(0);
4041590Srgrimes		break;
4051590Srgrimes
4061590Srgrimes	default:
4071590Srgrimes		panic("Unknown argtype");
4081590Srgrimes	}
4091590Srgrimes
4101590Srgrimesout:
4111590Srgrimes	/*
4121590Srgrimes	 * Exit the current source file on
4131590Srgrimes	 * error.
4141590Srgrimes	 */
4151590Srgrimes	if (e) {
4161590Srgrimes		if (e < 0)
4171590Srgrimes			return 1;
4181590Srgrimes		if (loading)
4191590Srgrimes			return 1;
4201590Srgrimes		if (sourcing)
4211590Srgrimes			unstack();
4221590Srgrimes		return 0;
4231590Srgrimes	}
4241590Srgrimes	if (value("autoprint") != NOSTR && com->c_argtype & P)
4251590Srgrimes		if ((dot->m_flag & MDELETED) == 0) {
4261590Srgrimes			muvec[0] = dot - &message[0] + 1;
4271590Srgrimes			muvec[1] = 0;
4281590Srgrimes			type(muvec);
4291590Srgrimes		}
4301590Srgrimes	if (!sourcing && (com->c_argtype & T) == 0)
4311590Srgrimes		sawcom = 1;
4321590Srgrimes	return(0);
4331590Srgrimes}
4341590Srgrimes
4351590Srgrimes/*
4361590Srgrimes * Set the size of the message vector used to construct argument
4371590Srgrimes * lists to message list functions.
4381590Srgrimes */
4391590Srgrimesvoid
4401590Srgrimessetmsize(sz)
4411590Srgrimes	int sz;
4421590Srgrimes{
4431590Srgrimes
4441590Srgrimes	if (msgvec != 0)
4451590Srgrimes		free((char *) msgvec);
4461590Srgrimes	msgvec = (int *) calloc((unsigned) (sz + 1), sizeof *msgvec);
4471590Srgrimes}
4481590Srgrimes
4491590Srgrimes/*
4501590Srgrimes * Find the correct command in the command table corresponding
4511590Srgrimes * to the passed command "word"
4521590Srgrimes */
4531590Srgrimes
4541590Srgrimesstruct cmd *
4551590Srgrimeslex(word)
4561590Srgrimes	char word[];
4571590Srgrimes{
4581590Srgrimes	register struct cmd *cp;
4591590Srgrimes	extern struct cmd cmdtab[];
4601590Srgrimes
46110067Sjoerg	/*
46210067Sjoerg	 * ignore trailing chars after `#'
46310067Sjoerg	 *
46410067Sjoerg	 * lines with beginning `#' are comments
46510067Sjoerg	 * spaces befor `#' are ignored in execute()
46610067Sjoerg	 */
46710067Sjoerg
46810067Sjoerg	if (*word == '#')
46910067Sjoerg	    *(word+1) = '\0';
47010067Sjoerg
47110067Sjoerg
4721590Srgrimes	for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++)
4731590Srgrimes		if (isprefix(word, cp->c_name))
4741590Srgrimes			return(cp);
4751590Srgrimes	return(NONE);
4761590Srgrimes}
4771590Srgrimes
4781590Srgrimes/*
4791590Srgrimes * Determine if as1 is a valid prefix of as2.
4801590Srgrimes * Return true if yep.
4811590Srgrimes */
4821590Srgrimesint
4831590Srgrimesisprefix(as1, as2)
4841590Srgrimes	char *as1, *as2;
4851590Srgrimes{
4861590Srgrimes	register char *s1, *s2;
4871590Srgrimes
4881590Srgrimes	s1 = as1;
4891590Srgrimes	s2 = as2;
4901590Srgrimes	while (*s1++ == *s2)
4911590Srgrimes		if (*s2++ == '\0')
4921590Srgrimes			return(1);
4931590Srgrimes	return(*--s1 == '\0');
4941590Srgrimes}
4951590Srgrimes
4961590Srgrimes/*
4971590Srgrimes * The following gets called on receipt of an interrupt.  This is
4981590Srgrimes * to abort printout of a command, mainly.
4991590Srgrimes * Dispatching here when command() is inactive crashes rcv.
5001590Srgrimes * Close all open files except 0, 1, 2, and the temporary.
5011590Srgrimes * Also, unstack all source files.
5021590Srgrimes */
5031590Srgrimes
5041590Srgrimesint	inithdr;			/* am printing startup headers */
5051590Srgrimes
5061590Srgrimes/*ARGSUSED*/
5071590Srgrimesvoid
5081590Srgrimesintr(s)
5091590Srgrimes	int s;
5101590Srgrimes{
5111590Srgrimes
5121590Srgrimes	noreset = 0;
5131590Srgrimes	if (!inithdr)
5141590Srgrimes		sawcom++;
5151590Srgrimes	inithdr = 0;
5161590Srgrimes	while (sourcing)
5171590Srgrimes		unstack();
5181590Srgrimes
5191590Srgrimes	close_all_files();
5201590Srgrimes
5211590Srgrimes	if (image >= 0) {
5221590Srgrimes		close(image);
5231590Srgrimes		image = -1;
5241590Srgrimes	}
5251590Srgrimes	fprintf(stderr, "Interrupt\n");
5261590Srgrimes	reset(0);
5271590Srgrimes}
5281590Srgrimes
5291590Srgrimes/*
5301590Srgrimes * When we wake up after ^Z, reprint the prompt.
5311590Srgrimes */
5321590Srgrimesvoid
5331590Srgrimesstop(s)
5341590Srgrimes	int s;
5351590Srgrimes{
5361590Srgrimes	sig_t old_action = signal(s, SIG_DFL);
5371590Srgrimes
5381590Srgrimes	sigsetmask(sigblock(0) & ~sigmask(s));
5391590Srgrimes	kill(0, s);
5401590Srgrimes	sigblock(sigmask(s));
5411590Srgrimes	signal(s, old_action);
5421590Srgrimes	if (reset_on_stop) {
5431590Srgrimes		reset_on_stop = 0;
5441590Srgrimes		reset(0);
5451590Srgrimes	}
5461590Srgrimes}
5471590Srgrimes
5481590Srgrimes/*
5491590Srgrimes * Branch here on hangup signal and simulate "exit".
5501590Srgrimes */
5511590Srgrimes/*ARGSUSED*/
5521590Srgrimesvoid
5531590Srgrimeshangup(s)
5541590Srgrimes	int s;
5551590Srgrimes{
5561590Srgrimes
5571590Srgrimes	/* nothing to do? */
5581590Srgrimes	exit(1);
5591590Srgrimes}
5601590Srgrimes
5611590Srgrimes/*
5621590Srgrimes * Announce the presence of the current Mail version,
5631590Srgrimes * give the message count, and print a header listing.
5641590Srgrimes */
5651590Srgrimesvoid
5661590Srgrimesannounce()
5671590Srgrimes{
5681590Srgrimes	int vec[2], mdot;
5691590Srgrimes
5701590Srgrimes	mdot = newfileinfo();
5711590Srgrimes	vec[0] = mdot;
5721590Srgrimes	vec[1] = 0;
5731590Srgrimes	dot = &message[mdot - 1];
5741590Srgrimes	if (msgCount > 0 && value("noheader") == NOSTR) {
5751590Srgrimes		inithdr++;
5761590Srgrimes		headers(vec);
5771590Srgrimes		inithdr = 0;
5781590Srgrimes	}
5791590Srgrimes}
5801590Srgrimes
5811590Srgrimes/*
5821590Srgrimes * Announce information about the file we are editing.
5831590Srgrimes * Return a likely place to set dot.
5841590Srgrimes */
5851590Srgrimesint
5861590Srgrimesnewfileinfo()
5871590Srgrimes{
5881590Srgrimes	register struct message *mp;
5891590Srgrimes	register int u, n, mdot, d, s;
5901590Srgrimes	char fname[BUFSIZ], zname[BUFSIZ], *ename;
5911590Srgrimes
5921590Srgrimes	for (mp = &message[0]; mp < &message[msgCount]; mp++)
5931590Srgrimes		if (mp->m_flag & MNEW)
5941590Srgrimes			break;
5951590Srgrimes	if (mp >= &message[msgCount])
5961590Srgrimes		for (mp = &message[0]; mp < &message[msgCount]; mp++)
5971590Srgrimes			if ((mp->m_flag & MREAD) == 0)
5981590Srgrimes				break;
5991590Srgrimes	if (mp < &message[msgCount])
6001590Srgrimes		mdot = mp - &message[0] + 1;
6011590Srgrimes	else
6021590Srgrimes		mdot = 1;
6031590Srgrimes	s = d = 0;
6041590Srgrimes	for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) {
6051590Srgrimes		if (mp->m_flag & MNEW)
6061590Srgrimes			n++;
6071590Srgrimes		if ((mp->m_flag & MREAD) == 0)
6081590Srgrimes			u++;
6091590Srgrimes		if (mp->m_flag & MDELETED)
6101590Srgrimes			d++;
6111590Srgrimes		if (mp->m_flag & MSAVED)
6121590Srgrimes			s++;
6131590Srgrimes	}
6141590Srgrimes	ename = mailname;
6151590Srgrimes	if (getfold(fname) >= 0) {
6161590Srgrimes		strcat(fname, "/");
6171590Srgrimes		if (strncmp(fname, mailname, strlen(fname)) == 0) {
6181590Srgrimes			sprintf(zname, "+%s", mailname + strlen(fname));
6191590Srgrimes			ename = zname;
6201590Srgrimes		}
6211590Srgrimes	}
6221590Srgrimes	printf("\"%s\": ", ename);
6231590Srgrimes	if (msgCount == 1)
6241590Srgrimes		printf("1 message");
6251590Srgrimes	else
6261590Srgrimes		printf("%d messages", msgCount);
6271590Srgrimes	if (n > 0)
6281590Srgrimes		printf(" %d new", n);
6291590Srgrimes	if (u-n > 0)
6301590Srgrimes		printf(" %d unread", u);
6311590Srgrimes	if (d > 0)
6321590Srgrimes		printf(" %d deleted", d);
6331590Srgrimes	if (s > 0)
6341590Srgrimes		printf(" %d saved", s);
6351590Srgrimes	if (readonly)
6361590Srgrimes		printf(" [Read only]");
6371590Srgrimes	printf("\n");
6381590Srgrimes	return(mdot);
6391590Srgrimes}
6401590Srgrimes
6411590Srgrimes/*
6421590Srgrimes * Print the current version number.
6431590Srgrimes */
6441590Srgrimes
6451590Srgrimes/*ARGSUSED*/
6461590Srgrimesint
6471590Srgrimespversion(e)
6481590Srgrimes	int e;
6491590Srgrimes{
6501590Srgrimes	extern char *version;
6511590Srgrimes
6521590Srgrimes	printf("Version %s\n", version);
6531590Srgrimes	return(0);
6541590Srgrimes}
6551590Srgrimes
6561590Srgrimes/*
6571590Srgrimes * Load a file of user definitions.
6581590Srgrimes */
6591590Srgrimesvoid
6601590Srgrimesload(name)
6611590Srgrimes	char *name;
6621590Srgrimes{
6631590Srgrimes	register FILE *in, *oldin;
6641590Srgrimes
6651590Srgrimes	if ((in = Fopen(name, "r")) == NULL)
6661590Srgrimes		return;
6671590Srgrimes	oldin = input;
6681590Srgrimes	input = in;
6691590Srgrimes	loading = 1;
6701590Srgrimes	sourcing = 1;
6711590Srgrimes	commands();
6721590Srgrimes	loading = 0;
6731590Srgrimes	sourcing = 0;
6741590Srgrimes	input = oldin;
6751590Srgrimes	Fclose(in);
6761590Srgrimes}
677