lex.c revision 173439
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
351590Srgrimes#if 0
361590Srgrimesstatic char sccsid[] = "@(#)lex.c	8.2 (Berkeley) 4/20/95";
3762833Swsanchez#endif
3862833Swsanchez#endif /* not lint */
391590Srgrimes#include <sys/cdefs.h>
401590Srgrimes__FBSDID("$FreeBSD: head/usr.bin/mail/lex.c 173439 2007-11-08 11:13:03Z dds $");
4162833Swsanchez
4294587Sobrien#include "rcv.h"
431590Srgrimes#include <errno.h>
44141104Sharti#include <fcntl.h>
45141104Sharti#include "extern.h"
461590Srgrimes
47141133Sharti/*
48141104Sharti * Mail -- a mail program
49141104Sharti *
50141104Sharti * Lexical processing of commands.
51146345Sharti */
52146345Sharti
53228992Suqsstatic const char	*prompt = "& ";
54146345Sharti
55146345Shartiextern const struct cmd cmdtab[];
56228992Suqsextern const char *version;
57146345Sharti
58146543Sharti/*
59146345Sharti * Set up editing on the given file name.
60146345Sharti * If the first character of name is %, we are considered to be
615814Sjkh * editing the file, otherwise we are reading our mail which has
62146345Sharti * signficance for mbox and so forth.
63146345Sharti *
64146345Sharti * If the -e option is being passed to mail, this function has a
65146345Sharti * tri-state return code: -1 on error, 0 on no mail, 1 if there is
66146345Sharti * mail.
67146345Sharti */
68146345Shartiint
69146345Shartisetfile(name)
70146345Sharti	char *name;
71146345Sharti{
725814Sjkh	FILE *ibuf;
735814Sjkh	int checkmode, i, fd;
74146345Sharti	struct stat stb;
755814Sjkh	char isedit = *name != '%' || getuserid(myname) != getuid();
76138232Sharti	char *who = name[1] ? name + 1 : myname;
77146345Sharti	char tempname[PATHSIZE];
78146345Sharti	static int shudclob;
79146345Sharti
80146345Sharti	checkmode = value("checkmode") != NULL;
81146345Sharti	if ((name = expand(name)) == NULL)
82146345Sharti		return (-1);
83146345Sharti
84146345Sharti	if ((ibuf = Fopen(name, "r")) == NULL) {
85146345Sharti		if (!isedit && errno == ENOENT)
86146345Sharti			goto nomail;
87146345Sharti		warn("%s", name);
88146345Sharti		return (-1);
89146345Sharti	}
90146345Sharti
91146345Sharti	if (fstat(fileno(ibuf), &stb) < 0) {
92146345Sharti		warn("fstat");
93146345Sharti		(void)Fclose(ibuf);
94146345Sharti		return (-1);
955814Sjkh	}
965814Sjkh
971590Srgrimes	if (S_ISDIR(stb.st_mode) || !S_ISREG(stb.st_mode)) {
981590Srgrimes		(void)Fclose(ibuf);
99138547Sharti		errno = S_ISDIR(stb.st_mode) ? EISDIR : EINVAL;
1001590Srgrimes		warn("%s", name);
1011590Srgrimes		return (-1);
1021590Srgrimes	}
1031590Srgrimes
1041590Srgrimes	/*
105138547Sharti	 * Looks like all will be well.  We must now relinquish our
1061590Srgrimes	 * hold on the current set of stuff.  Must hold signals
10794584Sobrien	 * while we are reading the new file, else we will ruin
10894584Sobrien	 * the message[] data structure.
1091590Srgrimes	 */
1101590Srgrimes
11194638Sobrien	holdsigs();
11294638Sobrien	if (shudclob)
1131590Srgrimes		quit();
1141590Srgrimes
115138264Sharti	/*
1161590Srgrimes	 * Copy the messages into /tmp
1171590Srgrimes	 * and set pointers.
11894638Sobrien	 */
1191590Srgrimes
1201590Srgrimes	readonly = 0;
12194638Sobrien	if ((i = open(name, 1)) < 0)
12294638Sobrien		readonly++;
12394638Sobrien	else
12494638Sobrien		(void)close(i);
12594638Sobrien	if (shudclob) {
12694638Sobrien		(void)fclose(itf);
1271590Srgrimes		(void)fclose(otf);
1281590Srgrimes	}
12994638Sobrien	shudclob = 1;
13094638Sobrien	edit = isedit;
1311590Srgrimes	strlcpy(prevfile, mailname, sizeof(prevfile));
132138232Sharti	if (name != mailname)
1331590Srgrimes		strlcpy(mailname, name, sizeof(mailname));
1341590Srgrimes	mailsize = fsize(ibuf);
135146345Sharti	(void)snprintf(tempname, sizeof(tempname),
136146345Sharti	    "%s/mail.RxXXXXXXXXXX", tmpdir);
137146345Sharti	if ((fd = mkstemp(tempname)) == -1 || (otf = fdopen(fd, "w")) == NULL)
138146345Sharti		err(1, "%s", tempname);
1391590Srgrimes	(void)fcntl(fileno(otf), F_SETFD, 1);
140146345Sharti	if ((itf = fopen(tempname, "r")) == NULL)
141146345Sharti		err(1, "%s", tempname);
1421590Srgrimes	(void)fcntl(fileno(itf), F_SETFD, 1);
143146345Sharti	(void)rm(tempname);
144146345Sharti	setptr(ibuf, 0);
145146345Sharti	setmsize(msgCount);
1461590Srgrimes	/*
1471590Srgrimes	 * New mail may have arrived while we were reading
1481590Srgrimes	 * the mail file, so reset mailsize to be where
1491590Srgrimes	 * we really are in the file...
1501590Srgrimes	 */
151146345Sharti	mailsize = ftello(ibuf);
1521590Srgrimes	(void)Fclose(ibuf);
153209951Semaste	relsesigs();
154146345Sharti	sawcom = 0;
155146345Sharti
156146345Sharti	if ((checkmode || !edit) && msgCount == 0) {
157146345Shartinomail:
158146345Sharti		if (!checkmode) {
1591590Srgrimes			fprintf(stderr, "No mail for %s\n", who);
1601590Srgrimes			return (-1);
1611590Srgrimes		} else
1621590Srgrimes			return (0);
163146345Sharti	}
164146345Sharti	return (checkmode ? 1 : 0);
1651590Srgrimes}
1661590Srgrimes
167146345Sharti/*
168146345Sharti * Incorporate any new mail that has arrived since we first
169146345Sharti * started reading mail.
1701590Srgrimes */
171146345Shartiint
172146345Shartiincfile()
173146345Sharti{
174124966Sru	off_t newsize;
175124966Sru	int omsgCount = msgCount;
176146345Sharti	FILE *ibuf;
177146345Sharti
178146345Sharti	ibuf = Fopen(mailname, "r");
179146345Sharti	if (ibuf == NULL)
180146345Sharti		return (-1);
181146345Sharti	holdsigs();
182146345Sharti	newsize = fsize(ibuf);
183146345Sharti	if (newsize == 0)
184146345Sharti		return (-1);		/* mail box is now empty??? */
185146345Sharti	if (newsize < mailsize)
186124840Sru		return (-1);		/* mail box has shrunk??? */
1871590Srgrimes	if (newsize == mailsize)
1881590Srgrimes		return (0);		/* no new mail */
1895814Sjkh	setptr(ibuf, mailsize);
190146345Sharti	setmsize(msgCount);
191146345Sharti	mailsize = ftello(ibuf);
192146345Sharti	(void)Fclose(ibuf);
193146345Sharti	relsesigs();
1941590Srgrimes	return (msgCount - omsgCount);
195146345Sharti}
196146345Sharti
197146345Shartistatic int	*msgvec;
1981590Srgrimesstatic int	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 */
204146345Shartivoid
205146345Sharticommands()
206146345Sharti{
207146345Sharti	int n, eofloop = 0;
208146345Sharti	char linebuf[LINESIZE];
2095814Sjkh
210146345Sharti	if (!sourcing) {
211146345Sharti		if (signal(SIGINT, SIG_IGN) != SIG_IGN)
212146345Sharti			(void)signal(SIGINT, intr);
213146345Sharti		if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
2141590Srgrimes			(void)signal(SIGHUP, hangup);
215146345Sharti		(void)signal(SIGTSTP, stop);
216146345Sharti		(void)signal(SIGTTOU, stop);
217146345Sharti		(void)signal(SIGTTIN, stop);
218146345Sharti	}
219146345Sharti	setexit();
220146345Sharti	for (;;) {
221146345Sharti		/*
2225814Sjkh		 * Print the prompt, if needed.  Clear out
2235814Sjkh		 * string space, and flush the output.
224146345Sharti		 */
225146345Sharti		if (!sourcing && value("interactive") != NULL) {
226146345Sharti			if ((value("autoinc") != NULL) && (incfile() > 0))
227146345Sharti				printf("New mail has arrived.\n");
228146345Sharti			reset_on_stop = 1;
229146345Sharti			printf("%s", prompt);
230146345Sharti		}
231146345Sharti		(void)fflush(stdout);
232146345Sharti		sreset();
233146345Sharti		/*
234146345Sharti		 * Read a line of commands from the current input
235146345Sharti		 * and handle end of file specially.
236146345Sharti		 */
237146345Sharti		n = 0;
238146345Sharti		for (;;) {
239146345Sharti			if (readline(input, &linebuf[n], LINESIZE - n) < 0) {
240146345Sharti				if (n == 0)
241146345Sharti					n = -1;
242146345Sharti				break;
243146345Sharti			}
244146345Sharti			if ((n = strlen(linebuf)) == 0)
245146345Sharti				break;
246146345Sharti			n--;
247146345Sharti			if (linebuf[n] != '\\')
248146345Sharti				break;
249146345Sharti			linebuf[n++] = ' ';
250146345Sharti		}
251146345Sharti		reset_on_stop = 0;
252146345Sharti		if (n < 0) {
253146345Sharti				/* eof */
254146345Sharti			if (loading)
255146345Sharti				break;
256146345Sharti			if (sourcing) {
257146345Sharti				unstack();
258146345Sharti				continue;
259146345Sharti			}
260146345Sharti			if (value("interactive") != NULL &&
261163171Sru			    value("ignoreeof") != NULL &&
262163171Sru			    ++eofloop < 25) {
263163171Sru				printf("Use \"quit\" to quit.\n");
264163171Sru				continue;
2651590Srgrimes			}
2661590Srgrimes			break;
267104108Sjmallett		}
268146345Sharti		eofloop = 0;
269146345Sharti		if (execute(linebuf, 0))
270146345Sharti			break;
271104108Sjmallett	}
2721590Srgrimes}
273146345Sharti
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
278140870Sharti * the interactive command loop.
279140870Sharti * Contxt is non-zero if called while composing mail.
280140870Sharti */
281140870Shartiint
282140870Shartiexecute(linebuf, contxt)
283140870Sharti	char linebuf[];
284140870Sharti	int contxt;
285140870Sharti{
286140870Sharti	char word[LINESIZE];
287140870Sharti	char *arglist[MAXARGC];
288140870Sharti	const struct cmd *com;
289140870Sharti	char *cp, *cp2;
290140870Sharti	int c, muvec[2];
291140870Sharti	int e = 1;
292140870Sharti
293140870Sharti	/*
294140870Sharti	 * Strip the white space away from the beginning
295140870Sharti	 * of the command, then scan out a word, which
296140870Sharti	 * consists of anything except digits and white space.
297140870Sharti	 *
298140870Sharti	 * Handle ! escapes differently to get the correct
299140870Sharti	 * lexical conventions.
300140870Sharti	 */
301140870Sharti
302140870Sharti	for (cp = linebuf; isspace((unsigned char)*cp); cp++)
303140870Sharti		;
304140870Sharti	if (*cp == '!') {
305140870Sharti		if (sourcing) {
306140870Sharti			printf("Can't \"!\" while sourcing\n");
307140870Sharti			goto out;
308140870Sharti		}
309140870Sharti		shell(cp+1);
310140870Sharti		return (0);
311140870Sharti	}
312140870Sharti	cp2 = word;
313140870Sharti	while (*cp != '\0' && strchr(" \t0123456789$^.:/-+*'\"", *cp) == NULL)
314140870Sharti		*cp2++ = *cp++;
315146345Sharti	*cp2 = '\0';
316146345Sharti
317140870Sharti	/*
318146345Sharti	 * Look up the command; if not found, bitch.
319146345Sharti	 * Normally, a blank command would map to the
320140870Sharti	 * first command in the table; while sourcing,
321146345Sharti	 * however, we ignore blank lines to eliminate
322140870Sharti	 * confusion.
323146345Sharti	 */
324146345Sharti
325146345Sharti	if (sourcing && *word == '\0')
326140870Sharti		return (0);
327140870Sharti	com = lex(word);
328146345Sharti	if (com == NULL) {
329146345Sharti		printf("Unknown command: \"%s\"\n", word);
330146345Sharti		goto out;
331146345Sharti	}
332140870Sharti
333140870Sharti	/*
334140870Sharti	 * See if we should execute the command -- if a conditional
335140870Sharti	 * we always execute it, otherwise, check the state of cond.
336140870Sharti	 */
337140870Sharti
338140870Sharti	if ((com->c_argtype & F) == 0)
339146345Sharti		if ((cond == CRCV && !rcvmode) || (cond == CSEND && rcvmode))
340146345Sharti			return (0);
341146345Sharti
342146345Sharti	/*
343146345Sharti	 * Process the arguments to the command, depending
344146345Sharti	 * on the type he expects.  Default to an error.
345140870Sharti	 * If we are sourcing an interactive command, it's
346146345Sharti	 * an error.
347146345Sharti	 */
348146345Sharti
349146345Sharti	if (!rcvmode && (com->c_argtype & M) == 0) {
350140870Sharti		printf("May not execute \"%s\" while sending\n",
351146345Sharti		    com->c_name);
352146345Sharti		goto out;
353146345Sharti	}
354146345Sharti	if (sourcing && com->c_argtype & I) {
355146345Sharti		printf("May not execute \"%s\" while sourcing\n",
356146345Sharti		    com->c_name);
357146345Sharti		goto out;
358146345Sharti	}
359146345Sharti	if (readonly && com->c_argtype & W) {
360146345Sharti		printf("May not execute \"%s\" -- message file is read only\n",
361140870Sharti		   com->c_name);
362146345Sharti		goto out;
363140870Sharti	}
364140870Sharti	if (contxt && com->c_argtype & R) {
365140870Sharti		printf("Cannot recursively invoke \"%s\"\n", com->c_name);
366140870Sharti		goto out;
367146345Sharti	}
368140870Sharti	switch (com->c_argtype & ~(F|P|I|M|T|W|R)) {
369140870Sharti	case MSGLIST:
370140870Sharti		/*
371146345Sharti		 * A message list defaulting to nearest forward
372146345Sharti		 * legal message.
373140870Sharti		 */
374140870Sharti		if (msgvec == 0) {
375140870Sharti			printf("Illegal use of \"message list\"\n");
376140870Sharti			break;
3771590Srgrimes		}
3788874Srgrimes		if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
3791590Srgrimes			break;
3808874Srgrimes		if (c  == 0) {
3811590Srgrimes			*msgvec = first(com->c_msgflag, com->c_msgmask);
3821590Srgrimes			msgvec[1] = 0;
3831590Srgrimes		}
3848874Srgrimes		if (*msgvec == 0) {
3851590Srgrimes			printf("No applicable messages\n");
3861590Srgrimes			break;
3871590Srgrimes		}
388106106Sjmallett		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;
399138232Sharti		}
4001590Srgrimes		if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
401138232Sharti			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		 */
4101590Srgrimes		while (isspace((unsigned char)*cp))
411138232Sharti			cp++;
4121590Srgrimes		e = (*com->c_func)(cp);
4131590Srgrimes		break;
414138232Sharti
4151590Srgrimes	case RAWLIST:
4161590Srgrimes		/*
417138232Sharti		 * A vector of strings, in shell style.
4181590Srgrimes		 */
4191590Srgrimes		if ((c = getrawlist(cp, arglist,
4201590Srgrimes		    sizeof(arglist) / sizeof(*arglist))) < 0)
4211590Srgrimes			break;
4221590Srgrimes		if (c < com->c_minargs) {
4231590Srgrimes			printf("%s requires at least %d arg(s)\n",
4241590Srgrimes			    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",
4291590Srgrimes			    com->c_name, com->c_maxargs);
4301590Srgrimes			break;
4311590Srgrimes		}
4321590Srgrimes		e = (*com->c_func)(arglist);
4331590Srgrimes		break;
434138232Sharti
4351590Srgrimes	case NOLIST:
4361590Srgrimes		/*
4371590Srgrimes		 * Just the constant zero, for exiting,
4381590Srgrimes		 * eg.
4391590Srgrimes		 */
440138232Sharti		e = (*com->c_func)(0);
4411590Srgrimes		break;
4421590Srgrimes
4431590Srgrimes	default:
4441590Srgrimes		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)
4541590Srgrimes			return (1);
4551590Srgrimes		if (loading)
4561590Srgrimes			return (1);
4571590Srgrimes		if (sourcing)
4581590Srgrimes			unstack();
4591590Srgrimes		return (0);
4601590Srgrimes	}
4611590Srgrimes	if (com == NULL)
462138232Sharti		return (0);
4631590Srgrimes	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		}
469138232Sharti	if (!sourcing && (com->c_argtype & T) == 0)
4701590Srgrimes		sawcom = 1;
4711590Srgrimes	return (0);
4721590Srgrimes}
4731590Srgrimes
4741590Srgrimes/*
4751590Srgrimes * Set the size of the message vector used to construct argument
476144479Sharti * lists to message list functions.
477144479Sharti */
4788874Srgrimesvoid
4798874Srgrimessetmsize(sz)
4801590Srgrimes	int sz;
4811590Srgrimes{
4821590Srgrimes
4831590Srgrimes	if (msgvec != NULL)
484106106Sjmallett		(void)free(msgvec);
485106106Sjmallett	msgvec = calloc((unsigned)(sz + 1), sizeof(*msgvec));
4861590Srgrimes}
487144479Sharti
4881590Srgrimes/*
489144479Sharti * Find the correct command in the command table corresponding
490144479Sharti * to the passed command "word"
491106106Sjmallett */
492144479Sharti
493144479Sharti__const struct cmd *
494144479Shartilex(word)
495144479Sharti	char word[];
496144479Sharti{
49796071Sjmallett	const struct cmd *cp;
498144479Sharti
499144479Sharti	/*
500144479Sharti	 * ignore trailing chars after `#'
501144479Sharti	 *
502144479Sharti	 * lines with beginning `#' are comments
5031590Srgrimes	 * spaces before `#' are ignored in execute()
504144479Sharti	 */
505144479Sharti
506144479Sharti	if (*word == '#')
507144479Sharti	    *(word+1) = '\0';
5081590Srgrimes
509144479Sharti
510144479Sharti	for (cp = &cmdtab[0]; cp->c_name != NULL; cp++)
5111590Srgrimes		if (isprefix(word, cp->c_name))
512144479Sharti			return (cp);
513144479Sharti	return (NULL);
514144479Sharti}
515144479Sharti
516144479Sharti/*
5171590Srgrimes * Determine if as1 is a valid prefix of as2.
5181590Srgrimes * Return true if yep.
519144479Sharti */
5201590Srgrimesint
521144479Shartiisprefix(as1, as2)
522144479Sharti	const char *as1, *as2;
523144479Sharti{
524144479Sharti	const char *s1, *s2;
525144479Sharti
526144479Sharti	s1 = as1;
527144479Sharti	s2 = as2;
5288874Srgrimes	while (*s1++ == *s2)
529144479Sharti		if (*s2++ == '\0')
5301590Srgrimes			return (1);
5311590Srgrimes	return (*--s1 == '\0');
5321590Srgrimes}
533144479Sharti
534144479Sharti/*
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.
5388874Srgrimes * Close all open files except 0, 1, 2, and the temporary.
5391590Srgrimes * Also, unstack all source files.
5401590Srgrimes */
5411590Srgrimes
5421590Srgrimesstatic int	inithdr;		/* am printing startup headers */
543141133Sharti
5441590Srgrimes/*ARGSUSED*/
545144479Shartivoid
5461590Srgrimesintr(s)
547144479Sharti	int s;
548144479Sharti{
549144479Sharti
550144479Sharti	noreset = 0;
551144479Sharti	if (!inithdr)
552144479Sharti		sawcom++;
5531590Srgrimes	inithdr = 0;
554144479Sharti	while (sourcing)
555144479Sharti		unstack();
5561590Srgrimes
557144479Sharti	close_all_files();
558144479Sharti
5591590Srgrimes	if (image >= 0) {
560		(void)close(image);
561		image = -1;
562	}
563	fprintf(stderr, "Interrupt\n");
564	reset(0);
565}
566
567/*
568 * When we wake up after ^Z, reprint the prompt.
569 */
570void
571stop(s)
572	int s;
573{
574	sig_t old_action = signal(s, SIG_DFL);
575	sigset_t nset;
576
577	(void)sigemptyset(&nset);
578	(void)sigaddset(&nset, s);
579	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
580	(void)kill(0, s);
581	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
582	(void)signal(s, old_action);
583	if (reset_on_stop) {
584		reset_on_stop = 0;
585		reset(0);
586	}
587}
588
589/*
590 * Branch here on hangup signal and simulate "exit".
591 */
592/*ARGSUSED*/
593void
594hangup(s)
595	int s;
596{
597
598	/* nothing to do? */
599	exit(1);
600}
601
602/*
603 * Announce the presence of the current Mail version,
604 * give the message count, and print a header listing.
605 */
606void
607announce()
608{
609	int vec[2], mdot;
610
611	mdot = newfileinfo(0);
612	vec[0] = mdot;
613	vec[1] = 0;
614	dot = &message[mdot - 1];
615	if (msgCount > 0 && value("noheader") == NULL) {
616		inithdr++;
617		headers(vec);
618		inithdr = 0;
619	}
620}
621
622/*
623 * Announce information about the file we are editing.
624 * Return a likely place to set dot.
625 */
626int
627newfileinfo(omsgCount)
628	int omsgCount;
629{
630	struct message *mp;
631	int u, n, mdot, d, s;
632	char fname[PATHSIZE+1], zname[PATHSIZE+1], *ename;
633
634	for (mp = &message[omsgCount]; mp < &message[msgCount]; mp++)
635		if (mp->m_flag & MNEW)
636			break;
637	if (mp >= &message[msgCount])
638		for (mp = &message[omsgCount]; mp < &message[msgCount]; mp++)
639			if ((mp->m_flag & MREAD) == 0)
640				break;
641	if (mp < &message[msgCount])
642		mdot = mp - &message[0] + 1;
643	else
644		mdot = omsgCount + 1;
645	s = d = 0;
646	for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) {
647		if (mp->m_flag & MNEW)
648			n++;
649		if ((mp->m_flag & MREAD) == 0)
650			u++;
651		if (mp->m_flag & MDELETED)
652			d++;
653		if (mp->m_flag & MSAVED)
654			s++;
655	}
656	ename = mailname;
657	if (getfold(fname, sizeof(fname) - 1) >= 0) {
658		strcat(fname, "/");
659		if (strncmp(fname, mailname, strlen(fname)) == 0) {
660			(void)snprintf(zname, sizeof(zname), "+%s",
661			    mailname + strlen(fname));
662			ename = zname;
663		}
664	}
665	printf("\"%s\": ", ename);
666	if (msgCount == 1)
667		printf("1 message");
668	else
669		printf("%d messages", msgCount);
670	if (n > 0)
671		printf(" %d new", n);
672	if (u-n > 0)
673		printf(" %d unread", u);
674	if (d > 0)
675		printf(" %d deleted", d);
676	if (s > 0)
677		printf(" %d saved", s);
678	if (readonly)
679		printf(" [Read only]");
680	printf("\n");
681	return (mdot);
682}
683
684/*
685 * Print the current version number.
686 */
687
688/*ARGSUSED*/
689int
690pversion(e)
691	int e;
692{
693
694	printf("Version %s\n", version);
695	return (0);
696}
697
698/*
699 * Load a file of user definitions.
700 */
701void
702load(name)
703	char *name;
704{
705	FILE *in, *oldin;
706
707	if ((in = Fopen(name, "r")) == NULL)
708		return;
709	oldin = input;
710	input = in;
711	loading = 1;
712	sourcing = 1;
713	commands();
714	loading = 0;
715	sourcing = 0;
716	input = oldin;
717	(void)Fclose(in);
718}
719