Home | History | Annotate | Line # | Download | only in mail
lex.c revision 1.33.4.2
      1 /*	$NetBSD: lex.c,v 1.33.4.2 2008/01/09 02:00:46 matt Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1980, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. Neither the name of the University nor the names of its contributors
     16  *    may be used to endorse or promote products derived from this software
     17  *    without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29  * SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 #ifndef lint
     34 #if 0
     35 static char sccsid[] = "@(#)lex.c	8.2 (Berkeley) 4/20/95";
     36 #else
     37 __RCSID("$NetBSD: lex.c,v 1.33.4.2 2008/01/09 02:00:46 matt Exp $");
     38 #endif
     39 #endif /* not lint */
     40 
     41 #include <assert.h>
     42 
     43 #include "rcv.h"
     44 #include <util.h>
     45 #include "extern.h"
     46 #ifdef USE_EDITLINE
     47 #include "complete.h"
     48 #endif
     49 #include "format.h"
     50 #include "thread.h"
     51 
     52 /*
     53  * Mail -- a mail program
     54  *
     55  * Lexical processing of commands.
     56  */
     57 
     58 static const char *prompt = DEFAULT_PROMPT;
     59 static int	*msgvec;
     60 static int	reset_on_stop;		/* do a reset() if stopped */
     61 
     62 
     63 /*
     64  * Set the size of the message vector used to construct argument
     65  * lists to message list functions.
     66  */
     67 static void
     68 setmsize(int sz)
     69 {
     70 	if (msgvec != 0)
     71 		free(msgvec);
     72 	msgvec = ecalloc((size_t) (sz + 1), sizeof(*msgvec));
     73 }
     74 
     75 /*
     76  * Set up editing on the given file name.
     77  * If the first character of name is %, we are considered to be
     78  * editing the file, otherwise we are reading our mail which has
     79  * signficance for mbox and so forth.
     80  */
     81 PUBLIC int
     82 setfile(const char *name)
     83 {
     84 	FILE *ibuf;
     85 	int i, fd;
     86 	struct stat stb;
     87 	char isedit = *name != '%' || getuserid(myname) != (int)getuid();
     88 	const char *who = name[1] ? name + 1 : myname;
     89 	static int shudclob;
     90 	char tempname[PATHSIZE];
     91 
     92 	if ((name = expand(name)) == NULL)
     93 		return -1;
     94 
     95 	if ((ibuf = Fopen(name, "r")) == NULL) {
     96 		if (!isedit && errno == ENOENT)
     97 			goto nomail;
     98 		warn("%s", name);
     99 		return -1;
    100 	}
    101 
    102 	if (fstat(fileno(ibuf), &stb) < 0) {
    103 		warn("fstat");
    104 		(void)Fclose(ibuf);
    105 		return -1;
    106 	}
    107 
    108 	switch (stb.st_mode & S_IFMT) {
    109 	case S_IFDIR:
    110 		(void)Fclose(ibuf);
    111 		errno = EISDIR;
    112 		warn("%s", name);
    113 		return -1;
    114 
    115 	case S_IFREG:
    116 		break;
    117 
    118 	default:
    119 		(void)Fclose(ibuf);
    120 		errno = EINVAL;
    121 		warn("%s", name);
    122 		return -1;
    123 	}
    124 
    125 	/*
    126 	 * Looks like all will be well.  We must now relinquish our
    127 	 * hold on the current set of stuff.  Must hold signals
    128 	 * while we are reading the new file, else we will ruin
    129 	 * the message[] data structure.
    130 	 */
    131 
    132 	holdsigs();
    133 	if (shudclob)
    134 		quit();
    135 
    136 	/*
    137 	 * Copy the messages into /tmp
    138 	 * and set pointers.
    139 	 */
    140 
    141 	readonly = 0;
    142 	if ((i = open(name, O_WRONLY)) < 0)
    143 		readonly++;
    144 	else
    145 		(void)close(i);
    146 	if (shudclob) {
    147 		(void)fclose(itf);
    148 		(void)fclose(otf);
    149 	}
    150 	shudclob = 1;
    151 	edit = isedit;
    152 	(void)strcpy(prevfile, mailname);
    153 	if (name != mailname)
    154 		(void)strcpy(mailname, name);
    155 	mailsize = fsize(ibuf);
    156 	(void)snprintf(tempname, sizeof(tempname),
    157 	    "%s/mail.RxXXXXXXXXXX", tmpdir);
    158 	if ((fd = mkstemp(tempname)) == -1 ||
    159 	    (otf = fdopen(fd, "w")) == NULL)
    160 		err(1, "%s", tempname);
    161 	(void)fcntl(fileno(otf), F_SETFD, FD_CLOEXEC);
    162 	if ((itf = fopen(tempname, "r")) == NULL)
    163 		err(1, "%s", tempname);
    164 	(void)fcntl(fileno(itf), F_SETFD, FD_CLOEXEC);
    165 	(void)rm(tempname);
    166 	setptr(ibuf, (off_t)0);
    167 	setmsize(get_abs_msgCount());
    168 	/*
    169 	 * New mail may have arrived while we were reading
    170 	 * the mail file, so reset mailsize to be where
    171 	 * we really are in the file...
    172 	 */
    173 	mailsize = ftell(ibuf);
    174 	(void)Fclose(ibuf);
    175 	relsesigs();
    176 	sawcom = 0;
    177 	if (!edit && get_abs_msgCount() == 0) {
    178 nomail:
    179 		(void)fprintf(stderr, "No mail for %s\n", who);
    180 		return -1;
    181 	}
    182 	return 0;
    183 }
    184 
    185 /*
    186  * Incorporate any new mail that has arrived since we first
    187  * started reading mail.
    188  */
    189 PUBLIC int
    190 incfile(void)
    191 {
    192 	off_t newsize;
    193 	int omsgCount;
    194 	FILE *ibuf;
    195 	int rval;
    196 
    197 	omsgCount = get_abs_msgCount();
    198 
    199 	ibuf = Fopen(mailname, "r");
    200 	if (ibuf == NULL)
    201 		return -1;
    202 	holdsigs();
    203 	newsize = fsize(ibuf);
    204 	if (newsize == 0 ||		/* mail box is now empty??? */
    205 	    newsize < mailsize) {	/* mail box has shrunk??? */
    206 		rval = -1;
    207 		goto done;
    208 	}
    209 	if (newsize == mailsize) {
    210 		rval = 0;               /* no new mail */
    211 		goto done;
    212 	}
    213 	setptr(ibuf, mailsize);		/* read in new mail */
    214 	setmsize(get_abs_msgCount());	/* get the new message count */
    215 	mailsize = ftell(ibuf);
    216 	rval = get_abs_msgCount() - omsgCount;
    217  done:
    218 	(void)Fclose(ibuf);
    219 	relsesigs();
    220 	return rval;
    221 }
    222 
    223 /*
    224  * Return a pointer to the comment character, respecting quoting as
    225  * done in getrawlist().  The comment character is ignored inside
    226  * quotes.
    227  */
    228 static char *
    229 comment_char(char *line)
    230 {
    231 	char *p;
    232 	char quotec;
    233 	quotec = '\0';
    234 	for (p = line; *p; p++) {
    235 		if (quotec != '\0') {
    236 			if (*p == quotec)
    237 				quotec = '\0';
    238 		}
    239 		else if (*p == '"' || *p == '\'')
    240 			quotec = *p;
    241 		else if (*p == COMMENT_CHAR)
    242 			return p;
    243 	}
    244 	return NULL;
    245 }
    246 
    247 /*
    248  * When we wake up after ^Z, reprint the prompt.
    249  */
    250 static void
    251 stop(int s)
    252 {
    253 	sig_t old_action = signal(s, SIG_DFL);
    254 	sigset_t nset;
    255 
    256 	(void)sigemptyset(&nset);
    257 	(void)sigaddset(&nset, s);
    258 	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
    259 	(void)kill(0, s);
    260 	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
    261 	(void)signal(s, old_action);
    262 	if (reset_on_stop) {
    263 		reset_on_stop = 0;
    264 		reset(0);
    265 	}
    266 }
    267 
    268 
    269 
    270 /*
    271  * Signal handler is hooked by setup_piping().
    272  * Respond to a broken pipe signal --
    273  * probably caused by quitting more.
    274  */
    275 static jmp_buf	pipestop;
    276 
    277 /*ARGSUSED*/
    278 static void
    279 brokpipe(int signo __unused)
    280 {
    281 	longjmp(pipestop, 1);
    282 }
    283 
    284 /*
    285  * Check the command line for any requested piping or redirection,
    286  * depending on the value of 'c'.  If "enable-pipes" is set, search
    287  * the command line (cp) for the first occurrence of the character 'c'
    288  * that is not in a quote or (parenthese) group.
    289  */
    290 PUBLIC char *
    291 shellpr(char *cp)
    292 {
    293 	int quotec;
    294 	int level;
    295 
    296 	if (cp == NULL || value(ENAME_ENABLE_PIPES) == NULL)
    297 		return NULL;
    298 
    299 	level = 0;
    300 	quotec = 0;
    301 	for (/*EMPTY*/; *cp != '\0'; cp++) {
    302 		if (quotec) {
    303 			if (*cp == quotec)
    304 				quotec = 0;
    305 			if (*cp == '\\' &&
    306 			    (cp[1] == quotec || cp[1] == '\\'))
    307 				cp++;
    308 		}
    309 		else {
    310 			switch (*cp) {
    311 			case '|':
    312 			case '>':
    313 				if (level == 0)
    314 					return cp;
    315 				break;
    316 			case '(':
    317 				level++;
    318 				break;
    319 			case ')':
    320 				level--;
    321 				break;
    322 			case '"':
    323 			case '\'':
    324 				quotec = *cp;
    325 				break;
    326 			default:
    327 				break;
    328 			}
    329 		}
    330 	}
    331 	return NULL;
    332 }
    333 
    334 /*
    335  * Setup any pipe or redirection that the command line indicates.
    336  * If none, then setup the pager unless "pager-off" is defined.
    337  */
    338 static FILE *fp_stop = NULL;
    339 static int oldfd1 = -1;
    340 static int
    341 setup_piping(char *cmdline, int c_pipe)
    342 {
    343 	FILE *fout;
    344 	FILE *last_file;
    345 	char *cp;
    346 
    347 	last_file = last_registered_file(0);
    348 
    349 	fout = NULL;
    350 	if ((cp = shellpr(cmdline)) != NULL) {
    351 		char c;
    352 		c = *cp;
    353 		*cp = '\0';
    354 		cp++;
    355 
    356 		if (c == '|') {
    357 			if ((fout = Popen(cp, "w")) == NULL) {
    358 				warn("Popen: %s", cp);
    359 				return -1;
    360 			}
    361 		}
    362 		else {
    363 			const char *mode;
    364 			assert(c == '>');
    365 			mode = *cp == '>' ? "a" : "w";
    366 			if (*cp == '>')
    367 				cp++;
    368 
    369 			cp = skip_WSP(cp);
    370 			if ((fout = Fopen(cp, mode)) == NULL) {
    371 				warn("Fopen: %s", cp);
    372 				return -1;
    373 			}
    374 		}
    375 
    376 	}
    377 	else if (value(ENAME_PAGER_OFF) == NULL && (c_pipe & C_PIPE_PAGER ||
    378 		(c_pipe & C_PIPE_CRT && value(ENAME_CRT) != NULL))) {
    379 		const char *pager;
    380 		pager = value(ENAME_PAGER);
    381 		if (pager == NULL || *pager == '\0')
    382 			pager = _PATH_MORE;
    383 
    384 		if ((fout = Popen(pager, "w")) == NULL) {
    385 			warn("Popen: %s", pager);
    386 			return -1;
    387 		}
    388 	}
    389 
    390 	if (fout) {
    391 		(void)signal(SIGPIPE, brokpipe);
    392 		(void)fflush(stdout);
    393 		if ((oldfd1 = dup(STDOUT_FILENO)) == -1)
    394 			err(EXIT_FAILURE, "dup failed");
    395 		if (dup2(fileno(fout), STDOUT_FILENO) == -1)
    396 			err(EXIT_FAILURE, "dup2 failed");
    397 		fp_stop = last_file;
    398 	}
    399 	return 0;
    400 }
    401 
    402 /*
    403  * This will close any piping started by setup_piping().
    404  */
    405 static void
    406 close_piping(void)
    407 {
    408 	if (oldfd1 != -1) {
    409 		(void)fflush(stdout);
    410 		if (fileno(stdout) != oldfd1 &&
    411 		    dup2(oldfd1, STDOUT_FILENO) == -1)
    412 			err(EXIT_FAILURE, "dup2 failed");
    413 
    414 		(void)signal(SIGPIPE, SIG_IGN);
    415 		close_top_files(fp_stop);
    416 		fp_stop = NULL;
    417 		(void)close(oldfd1);
    418 		oldfd1 = -1;
    419 		(void)signal(SIGPIPE, SIG_DFL);
    420 	}
    421 }
    422 
    423 /*
    424  * Determine if as1 is a valid prefix of as2.
    425  * Return true if yep.
    426  */
    427 static int
    428 isprefix(char *as1, const char *as2)
    429 {
    430 	char *s1;
    431 	const char *s2;
    432 
    433 	s1 = as1;
    434 	s2 = as2;
    435 	while (*s1++ == *s2)
    436 		if (*s2++ == '\0')
    437 			return 1;
    438 	return *--s1 == '\0';
    439 }
    440 
    441 /*
    442  * Find the correct command in the command table corresponding
    443  * to the passed command "word"
    444  */
    445 PUBLIC const struct cmd *
    446 lex(char word[])
    447 {
    448 	const struct cmd *cp;
    449 
    450 	for (cp = &cmdtab[0]; cp->c_name != NULL; cp++)
    451 		if (isprefix(word, cp->c_name))
    452 			return cp;
    453 	return NULL;
    454 }
    455 
    456 PUBLIC char *
    457 get_cmdname(char *buf)
    458 {
    459 	char *cp;
    460 	char *cmd;
    461 	size_t len;
    462 
    463 	for (cp = buf; *cp; cp++)
    464 		if (strchr(" \t0123456789$^.:/-+*'\">|", *cp) != NULL)
    465 			break;
    466 	/* XXX - Don't miss the pipe command! */
    467 	if (cp == buf && *cp == '|')
    468 		cp++;
    469 	len = cp - buf + 1;
    470 	cmd = salloc(len);
    471 	(void)strlcpy(cmd, buf, len);
    472 	return cmd;
    473 }
    474 
    475 /*
    476  * Execute a single command.
    477  * Command functions return 0 for success, 1 for error, and -1
    478  * for abort.  A 1 or -1 aborts a load or source.  A -1 aborts
    479  * the interactive command loop.
    480  * execute_contxt_e is in extern.h.
    481  */
    482 PUBLIC int
    483 execute(char linebuf[], enum execute_contxt_e contxt)
    484 {
    485 	char *word;
    486 	char *arglist[MAXARGC];
    487 	const struct cmd *com = NULL;
    488 	char *volatile cp;
    489 	int c;
    490 	int e = 1;
    491 
    492 	/*
    493 	 * Strip the white space away from the beginning
    494 	 * of the command, then scan out a word, which
    495 	 * consists of anything except digits and white space.
    496 	 *
    497 	 * Handle ! escapes differently to get the correct
    498 	 * lexical conventions.
    499 	 */
    500 
    501 	cp = skip_space(linebuf);
    502 	if (*cp == '!') {
    503 		if (sourcing) {
    504 			(void)printf("Can't \"!\" while sourcing\n");
    505 			goto out;
    506 		}
    507 		(void)shell(cp + 1);
    508 		return 0;
    509 	}
    510 
    511 	word = get_cmdname(cp);
    512 	cp += strlen(word);
    513 
    514 	/*
    515 	 * Look up the command; if not found, bitch.
    516 	 * Normally, a blank command would map to the
    517 	 * first command in the table; while sourcing,
    518 	 * however, we ignore blank lines to eliminate
    519 	 * confusion.
    520 	 */
    521 
    522 	if (sourcing && *word == '\0')
    523 		return 0;
    524 	com = lex(word);
    525 	if (com == NULL) {
    526 		(void)printf("Unknown command: \"%s\"\n", word);
    527 		goto out;
    528 	}
    529 
    530 	/*
    531 	 * See if we should execute the command -- if a conditional
    532 	 * we always execute it, otherwise, check the state of cond.
    533 	 */
    534 
    535 	if ((com->c_argtype & F) == 0 && (cond & CSKIP))
    536 		return 0;
    537 
    538 	/*
    539 	 * Process the arguments to the command, depending
    540 	 * on the type he expects.  Default to an error.
    541 	 * If we are sourcing an interactive command, it's
    542 	 * an error.
    543 	 */
    544 
    545 	if (mailmode == mm_sending && (com->c_argtype & M) == 0) {
    546 		(void)printf("May not execute \"%s\" while sending\n",
    547 		    com->c_name);
    548 		goto out;
    549 	}
    550 	if (sourcing && com->c_argtype & I) {
    551 		(void)printf("May not execute \"%s\" while sourcing\n",
    552 		    com->c_name);
    553 		goto out;
    554 	}
    555 	if (readonly && com->c_argtype & W) {
    556 		(void)printf("May not execute \"%s\" -- message file is read only\n",
    557 		   com->c_name);
    558 		goto out;
    559 	}
    560 	if (contxt == ec_composing && com->c_argtype & R) {
    561 		(void)printf("Cannot recursively invoke \"%s\"\n", com->c_name);
    562 		goto out;
    563 	}
    564 
    565 	if (!sourcing && com->c_pipe && value(ENAME_INTERACTIVE) != NULL) {
    566 		if (setjmp(pipestop))
    567 			goto out;
    568 
    569 		if (setup_piping(cp, com->c_pipe) == -1)
    570 			goto out;
    571 	}
    572 	switch (com->c_argtype & ARGTYPE_MASK) {
    573 	case MSGLIST:
    574 		/*
    575 		 * A message list defaulting to nearest forward
    576 		 * legal message.
    577 		 */
    578 		if (msgvec == 0) {
    579 			(void)printf("Illegal use of \"message list\"\n");
    580 			break;
    581 		}
    582 		if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
    583 			break;
    584 		if (c  == 0) {
    585 			*msgvec = first(com->c_msgflag,	com->c_msgmask);
    586 			msgvec[1] = 0;
    587 		}
    588 		if (*msgvec == 0) {
    589 			(void)printf("No applicable messages\n");
    590 			break;
    591 		}
    592 		e = (*com->c_func)(msgvec);
    593 		break;
    594 
    595 	case NDMLIST:
    596 		/*
    597 		 * A message list with no defaults, but no error
    598 		 * if none exist.
    599 		 */
    600 		if (msgvec == 0) {
    601 			(void)printf("Illegal use of \"message list\"\n");
    602 			break;
    603 		}
    604 		if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
    605 			break;
    606 		e = (*com->c_func)(msgvec);
    607 		break;
    608 
    609 	case STRLIST:
    610 		/*
    611 		 * Just the straight string, with
    612 		 * leading blanks removed.
    613 		 */
    614 		cp = skip_space(cp);
    615 		e = (*com->c_func)(cp);
    616 		break;
    617 
    618 	case RAWLIST:
    619 		/*
    620 		 * A vector of strings, in shell style.
    621 		 */
    622 		if ((c = getrawlist(cp, arglist,
    623 				sizeof(arglist) / sizeof(*arglist))) < 0)
    624 			break;
    625 		if (c < com->c_minargs) {
    626 			(void)printf("%s requires at least %d arg(s)\n",
    627 				com->c_name, com->c_minargs);
    628 			break;
    629 		}
    630 		if (c > com->c_maxargs) {
    631 			(void)printf("%s takes no more than %d arg(s)\n",
    632 				com->c_name, com->c_maxargs);
    633 			break;
    634 		}
    635 		e = (*com->c_func)(arglist);
    636 		break;
    637 
    638 	case NOLIST:
    639 		/*
    640 		 * Just the constant zero, for exiting,
    641 		 * eg.
    642 		 */
    643 		e = (*com->c_func)(0);
    644 		break;
    645 
    646 	default:
    647 		errx(1, "Unknown argtype");
    648 	}
    649 
    650 out:
    651 	close_piping();
    652 
    653 	/*
    654 	 * Exit the current source file on
    655 	 * error.
    656 	 */
    657 	if (e) {
    658 		if (e < 0)
    659 			return 1;
    660 		if (loading)
    661 			return 1;
    662 		if (sourcing)
    663 			(void)unstack();
    664 		return 0;
    665 	}
    666 	if (com == NULL)
    667 		return 0;
    668 	if (contxt != ec_autoprint && com->c_argtype & P &&
    669 	    value(ENAME_AUTOPRINT) != NULL && (dot->m_flag & MDELETED) == 0)
    670 		(void)execute(__UNCONST("print ."), ec_autoprint);
    671 	if (!sourcing && (com->c_argtype & T) == 0)
    672 		sawcom = 1;
    673 	return 0;
    674 }
    675 
    676 
    677 /*
    678  * The following gets called on receipt of an interrupt.  This is
    679  * to abort printout of a command, mainly.
    680  * Dispatching here when command() is inactive crashes rcv.
    681  * Close all open files except 0, 1, 2, and the temporary.
    682  * Also, unstack all source files.
    683  */
    684 static int	inithdr;	/* am printing startup headers */
    685 
    686 /*ARGSUSED*/
    687 static void
    688 intr(int s __unused)
    689 {
    690 	noreset = 0;
    691 	if (!inithdr)
    692 		sawcom++;
    693 	inithdr = 0;
    694 	while (sourcing)
    695 		(void)unstack();
    696 
    697 	close_piping();
    698 	close_all_files();
    699 
    700 	if (image >= 0) {
    701 		(void)close(image);
    702 		image = -1;
    703 	}
    704 	(void)fprintf(stderr, "Interrupt\n");
    705 	reset(0);
    706 }
    707 
    708 /*
    709  * Branch here on hangup signal and simulate "exit".
    710  */
    711 /*ARGSUSED*/
    712 static void
    713 hangup(int s __unused)
    714 {
    715 	/* nothing to do? */
    716 	exit(1);
    717 }
    718 
    719 /*
    720  * Interpret user commands one by one.  If standard input is not a tty,
    721  * print no prompt.
    722  */
    723 PUBLIC void
    724 commands(void)
    725 {
    726 	int n;
    727 	char linebuf[LINESIZE];
    728 	int eofloop;
    729 
    730 	if (!sourcing) {
    731 		if (signal(SIGINT, SIG_IGN) != SIG_IGN)
    732 			(void)signal(SIGINT, intr);
    733 		if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
    734 			(void)signal(SIGHUP, hangup);
    735 		(void)signal(SIGTSTP, stop);
    736 		(void)signal(SIGTTOU, stop);
    737 		(void)signal(SIGTTIN, stop);
    738 	}
    739 	setexit();	/* defined as (void)setjmp(srbuf) in def.h */
    740 	eofloop = 0;	/* initialize this after a possible longjmp */
    741 	for (;;) {
    742 		(void)fflush(stdout);
    743 		sreset();
    744 		/*
    745 		 * Print the prompt, if needed.  Clear out
    746 		 * string space, and flush the output.
    747 		 */
    748 		if (!sourcing && value(ENAME_INTERACTIVE) != NULL) {
    749 			if ((prompt = value(ENAME_PROMPT)) == NULL)
    750 				prompt = DEFAULT_PROMPT;
    751 			prompt = smsgprintf(prompt, dot);
    752 			if ((value(ENAME_AUTOINC) != NULL) && (incfile() > 0))
    753 				(void)printf("New mail has arrived.\n");
    754 			reset_on_stop = 1;
    755 #ifndef USE_EDITLINE
    756 			(void)printf("%s", prompt);
    757 #endif
    758 		}
    759 		/*
    760 		 * Read a line of commands from the current input
    761 		 * and handle end of file specially.
    762 		 */
    763 		n = 0;
    764 		for (;;) {
    765 #ifdef USE_EDITLINE
    766 			if (!sourcing) {
    767 				char *line;
    768 				if ((line = my_gets(&elm.command, prompt, NULL)) == NULL) {
    769 					if (n == 0)
    770 						n = -1;
    771 					break;
    772 				}
    773 				(void)strlcpy(linebuf, line, sizeof(linebuf));
    774 				setscreensize();	/* so we can resize a window */
    775 			}
    776 			else {
    777 				if (mail_readline(input, &linebuf[n], LINESIZE - n) < 0) {
    778 					if (n == 0)
    779 						n = -1;
    780 					break;
    781 				}
    782 			}
    783 #else /* USE_EDITLINE */
    784 			if (mail_readline(input, &linebuf[n], LINESIZE - n) < 0) {
    785 				if (n == 0)
    786 					n = -1;
    787 				break;
    788 			}
    789 #endif /* USE_EDITLINE */
    790 
    791 			if (sourcing) {  /* allow comments in source files */
    792 				char *ptr;
    793 				if ((ptr = comment_char(linebuf)) != NULL)
    794 					*ptr = '\0';
    795 			}
    796 			if ((n = strlen(linebuf)) == 0)
    797 				break;
    798 			n--;
    799 			if (linebuf[n] != '\\')
    800 				break;
    801 			linebuf[n++] = ' ';
    802 		}
    803 		reset_on_stop = 0;
    804 		if (n < 0) {
    805 				/* eof */
    806 			if (loading)
    807 				break;
    808 			if (sourcing) {
    809 				(void)unstack();
    810 				continue;
    811 			}
    812 #ifdef USE_EDITLINE
    813 			{
    814 				char *p;
    815 				if (value(ENAME_INTERACTIVE) != NULL &&
    816 				    (p = value(ENAME_IGNOREEOF)) != NULL &&
    817 				    ++eofloop < (*p == '\0' ? 25 : atoi(p))) {
    818 					(void)printf("Use \"quit\" to quit.\n");
    819 					continue;
    820 				}
    821 			}
    822 #else
    823 			if (value(ENAME_INTERACTIVE) != NULL &&
    824 			    value(ENAME_IGNOREEOF) != NULL &&
    825 			    ++eofloop < 25) {
    826 				(void)printf("Use \"quit\" to quit.\n");
    827 				continue;
    828 			}
    829 #endif
    830 			break;
    831 		}
    832 		eofloop = 0;
    833 		if (execute(linebuf, ec_normal))
    834 			break;
    835 	}
    836 }
    837 
    838 /*
    839  * Announce information about the file we are editing.
    840  * Return a likely place to set dot.
    841  */
    842 PUBLIC int
    843 newfileinfo(int omsgCount)
    844 {
    845 	struct message *mp;
    846 	int d, n, s, t, u, mdot;
    847 	char fname[PATHSIZE];
    848 	char *ename;
    849 
    850 	/*
    851 	 * Figure out where to set the 'dot'.  Use the first new or
    852 	 * unread message.
    853 	 */
    854 	for (mp = get_abs_message(omsgCount + 1); mp;
    855 	     mp = next_abs_message(mp))
    856 		if (mp->m_flag & MNEW)
    857 			break;
    858 
    859 	if (mp == NULL)
    860 		for (mp = get_abs_message(omsgCount + 1); mp;
    861 		     mp = next_abs_message(mp))
    862 			if ((mp->m_flag & MREAD) == 0)
    863 				break;
    864 	if (mp != NULL)
    865 		mdot = get_msgnum(mp);
    866 	else
    867 		mdot = omsgCount + 1;
    868 #ifdef THREAD_SUPPORT
    869 	/*
    870 	 * See if the message is in the current thread.
    871 	 */
    872 	if (mp != NULL && get_message(1) != NULL && get_message(mdot) != mp)
    873 		mdot = 0;
    874 #endif
    875 	/*
    876 	 * Scan the message array counting the new, unread, deleted,
    877 	 * and saved messages.
    878 	 */
    879 	d = n = s = t = u = 0;
    880 	for (mp = get_abs_message(1); mp; mp = next_abs_message(mp)) {
    881 		if (mp->m_flag & MNEW)
    882 			n++;
    883 		if ((mp->m_flag & MREAD) == 0)
    884 			u++;
    885 		if (mp->m_flag & MDELETED)
    886 			d++;
    887 		if (mp->m_flag & MSAVED)
    888 			s++;
    889 		if (mp->m_flag & MTAGGED)
    890 			t++;
    891 	}
    892 	ename = mailname;
    893 	if (getfold(fname, sizeof(fname)) >= 0) {
    894 		char zname[PATHSIZE];
    895 		size_t l;
    896 		l = strlen(fname);
    897 		if (l < sizeof(fname) - 1)
    898 			fname[l++] = '/';
    899 		if (strncmp(fname, mailname, l) == 0) {
    900 			(void)snprintf(zname, sizeof(zname), "+%s",
    901 			    mailname + l);
    902 			ename = zname;
    903 		}
    904 	}
    905 	/*
    906 	 * Display the statistics.
    907 	 */
    908 	(void)printf("\"%s\": ", ename);
    909 	{
    910 		int cnt = get_abs_msgCount();
    911 		(void)printf("%d message%s", cnt, cnt == 1 ? "" : "s");
    912 	}
    913 	if (n > 0)
    914 		(void)printf(" %d new", n);
    915 	if (u-n > 0)
    916 		(void)printf(" %d unread", u);
    917 	if (t > 0)
    918 		(void)printf(" %d tagged", t);
    919 	if (d > 0)
    920 		(void)printf(" %d deleted", d);
    921 	if (s > 0)
    922 		(void)printf(" %d saved", s);
    923 	if (readonly)
    924 		(void)printf(" [Read only]");
    925 	(void)printf("\n");
    926 
    927 	return mdot;
    928 }
    929 
    930 /*
    931  * Announce the presence of the current Mail version,
    932  * give the message count, and print a header listing.
    933  */
    934 PUBLIC void
    935 announce(void)
    936 {
    937 	int vec[2], mdot;
    938 
    939 	mdot = newfileinfo(0);
    940 	vec[0] = mdot;
    941 	vec[1] = 0;
    942 	if ((dot = get_message(mdot)) == NULL)
    943 		dot = get_abs_message(1); /* make sure we get something! */
    944 	if (get_abs_msgCount() > 0 && value(ENAME_NOHEADER) == NULL) {
    945 		inithdr++;
    946 		(void)headers(vec);
    947 		inithdr = 0;
    948 	}
    949 }
    950 
    951 /*
    952  * Print the current version number.
    953  */
    954 
    955 /*ARGSUSED*/
    956 PUBLIC int
    957 pversion(void *v __unused)
    958 {
    959 	(void)printf("Version %s\n", version);
    960 	return 0;
    961 }
    962 
    963 /*
    964  * Load a file of user definitions.
    965  */
    966 PUBLIC void
    967 load(const char *name)
    968 {
    969 	FILE *in, *oldin;
    970 
    971 	if ((in = Fopen(name, "r")) == NULL)
    972 		return;
    973 	oldin = input;
    974 	input = in;
    975 	loading = 1;
    976 	sourcing = 1;
    977 	commands();
    978 	loading = 0;
    979 	sourcing = 0;
    980 	input = oldin;
    981 	(void)Fclose(in);
    982 }
    983