xref: /illumos-gate/usr/src/cmd/mailx/lex.c (revision 48bbca816818409505a6e214d0911fda44e622e3)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  * Copyright (c) 2016 by Delphix. All rights reserved.
26  */
27 
28 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
29 /*	All Rights Reserved   */
30 
31 /*
32  * University Copyright- Copyright (c) 1982, 1986, 1988
33  * The Regents of the University of California
34  * All Rights Reserved
35  *
36  * University Acknowledgment- Portions of this document are derived from
37  * software developed by the University of California, Berkeley, and its
38  * contributors.
39  */
40 
41 #pragma ident	"%Z%%M%	%I%	%E% SMI"
42 
43 #include "rcv.h"
44 #include <locale.h>
45 
46 /*
47  * mailx -- a modified version of a University of California at Berkeley
48  *	mail program
49  *
50  * Lexical processing of commands.
51  */
52 
53 #ifdef SIGCONT
54 static void		contin(int);
55 #endif
56 static int		isprefix(char *as1, char *as2);
57 static const struct cmd	*lex(char word[]);
58 static int		Passeren(void);
59 static void		setmsize(int sz);
60 
61 /*
62  * Set up editing on the given file name.
63  * If isedit is true, we are considered to be editing the file,
64  * otherwise we are reading our mail which has signficance for
65  * mbox and so forth.
66  */
67 
68 int
69 setfile(char *name, int isedit)
70 {
71 	FILE *ibuf;
72 	int i;
73 	static int shudclob;
74 	static char efile[PATHSIZE];
75 	char fortest[128];
76 	struct stat stbuf;
77 	int exrc = 1;
78 	int rc = -1;
79 	int fd = -1;
80 
81 	if (!isedit && issysmbox)
82 		lockmail();
83 	if (stat(name, &stbuf) < 0 && errno == EOVERFLOW) {
84 		fprintf(stderr, gettext("mailbox %s is too large to"
85 		    " accept incoming mail\n"), name);
86 		goto doret;
87 	}
88 	if ((ibuf = fopen(name, "r")) == NULL) {
89 		extern int errno;
90 		int sverrno = errno;
91 		int filethere = (access(name, 0) == 0);
92 		errno = sverrno;
93 		if (exitflg)
94 			goto doexit;	/* no mail, return error */
95 		if (isedit || filethere)
96 			perror(name);
97 		else if (!Hflag) {
98 			char *f = strrchr(name, '/');
99 			if (f == NOSTR)
100 				fprintf(stderr, gettext("No mail.\n"));
101 			else
102 				fprintf(stderr, gettext("No mail for %s\n"),
103 f+1);
104 		}
105 		goto doret;
106 	}
107 	fstat(fileno(ibuf), &stbuf);
108 	if (stbuf.st_size == 0L || (stbuf.st_mode&S_IFMT) != S_IFREG) {
109 		if (exitflg)
110 			goto doexit;	/* no mail, return error */
111 		if (isedit)
112 			if (stbuf.st_size == 0L)
113 				fprintf(stderr, gettext("%s: empty file\n"),
114 name);
115 			else
116 				fprintf(stderr,
117 				    gettext("%s: not a regular file\n"), name);
118 		else if (!Hflag) {
119 			if (strrchr(name, '/') == NOSTR)
120 				fprintf(stderr, gettext("No mail.\n"));
121 			else
122 				fprintf(stderr, gettext("No mail for %s\n"),
123 strrchr(name, '/') + 1);
124 		}
125 		fclose(ibuf);
126 		goto doret;
127 	}
128 
129 	if (fgets(fortest, sizeof (fortest), ibuf) == NULL) {
130 		perror(gettext("mailx: Unable to read from mail file"));
131 		goto doexit;
132 	}
133 
134 	fseek(ibuf, (long)(BUFSIZ+1), 0);	/* flush input buffer */
135 	fseek(ibuf, 0L, 0);
136 	if (strncmp(fortest, "Forward to ", 11) == 0) {
137 		if (exitflg)
138 			goto doexit;	/* no mail, return error */
139 		fprintf(stderr, gettext("Your mail is being forwarded to %s"),
140 fortest+11);
141 		fclose(ibuf);
142 		goto doret;
143 	}
144 	if (exitflg) {
145 		exrc = 0;
146 		goto doexit;	/* there is mail, return success */
147 	}
148 
149 	/*
150 	 * Looks like all will be well. Must hold signals
151 	 * while we are reading the new file, else we will ruin
152 	 * the message[] data structure.
153 	 * Copy the messages into /tmp and set pointers.
154 	 */
155 
156 	holdsigs();
157 	if (shudclob) {
158 		/*
159 		 * Now that we know we can switch to the new file
160 		 * it's safe to close out the current file.
161 		 *
162 		 * If we're switching to the file we are currently
163 		 * editing, don't allow it to be removed as a side
164 		 * effect of closing it out.
165 		 */
166 		if (edit)
167 			edstop(strcmp(editfile, name) == 0);
168 		else {
169 			quit(strcmp(editfile, name) == 0);
170 			if (issysmbox)
171 				Verhogen();
172 		}
173 		fflush(stdout);
174 		fclose(itf);
175 		fclose(otf);
176 		free(message);
177 		space = 0;
178 	}
179 	readonly = 0;
180 	if (!isedit && issysmbox && !Hflag)
181 		readonly = Passeren() == -1;
182 	lock(ibuf, "r", 1);
183 	fstat(fileno(ibuf), &stbuf);
184 	utimep->actime = stbuf.st_atime;
185 	utimep->modtime = stbuf.st_mtime;
186 
187 	if (!readonly)
188 		if ((i = open(name, O_WRONLY)) < 0)
189 			readonly++;
190 		else
191 			close(i);
192 	shudclob = 1;
193 	edit = isedit;
194 	nstrcpy(efile, PATHSIZE, name);
195 	editfile = efile;
196 #ifdef notdef
197 	if (name != mailname)
198 		nstrcpy(mailname, PATHSIZE, name);
199 #endif
200 	mailsize = fsize(ibuf);
201 	if ((fd = open(tempMesg, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 ||
202 	(otf = fdopen(fd, "w")) == NULL) {
203 		perror(tempMesg);
204 		if (!edit && issysmbox)
205 			Verhogen();
206 		goto doexit;
207 	}
208 	if ((itf = fopen(tempMesg, "r")) == NULL) {
209 		perror(tempMesg);
210 		if (!edit && issysmbox)
211 			Verhogen();
212 		goto doexit;
213 	}
214 	removefile(tempMesg);
215 	setptr(ibuf);
216 	setmsize(msgCount);
217 	fclose(ibuf);
218 	relsesigs();
219 	sawcom = 0;
220 	rc = 0;
221 
222 doret:
223 	if (!isedit && issysmbox)
224 		unlockmail();
225 	return (rc);
226 
227 doexit:
228 	if (!isedit && issysmbox)
229 		unlockmail();
230 	exit(exrc ? exrc : rpterr);
231 	/* NOTREACHED */
232 }
233 
234 /* global to semaphores */
235 static char semfn[128];
236 static FILE *semfp = NULL;
237 
238 /*
239  *  return -1 if file is already being read, 0 otherwise
240  */
241 static int
242 Passeren(void)
243 {
244 	char *home;
245 
246 	if ((home = getenv("HOME")) == NULL)
247 		return (0);
248 	snprintf(semfn, sizeof (semfn), "%s%s", home, "/.Maillock");
249 	if ((semfp = fopen(semfn, "w")) == NULL) {
250 		fprintf(stderr,
251 	    gettext("WARNING: Can't open mail lock file (%s).\n"), semfn);
252 		fprintf(stderr,
253 	    gettext("\t Assuming you are not already reading mail.\n"));
254 		return (0);
255 	}
256 	if (lock(semfp, "w", 0) < 0) {
257 		if (errno == ENOLCK) {
258 			fprintf(stderr,
259 gettext("WARNING: Unable to acquire mail lock, no record locks available.\n"));
260 			fprintf(stderr,
261 		    gettext("\t Assuming you are not already reading mail.\n"));
262 			return (0);
263 		}
264 		fprintf(stderr,
265 		    gettext("WARNING: You are already reading mail.\n"));
266 		fprintf(stderr,
267 		    gettext("\t This instance of mail is read only.\n"));
268 		fclose(semfp);
269 		semfp = NULL;
270 		return (-1);
271 	}
272 	return (0);
273 }
274 
275 void
276 Verhogen(void)
277 {
278 	if (semfp != NULL) {
279 		unlink(semfn);
280 		fclose(semfp);
281 	}
282 }
283 
284 /*
285  * Interpret user commands one by one.  If standard input is not a tty,
286  * print no prompt.
287  */
288 
289 static int	*msgvec;
290 static int	shudprompt;
291 
292 void
293 commands(void)
294 {
295 	int eofloop;
296 	register int n;
297 	char linebuf[LINESIZE];
298 	char line[LINESIZE];
299 	struct stat minfo;
300 	FILE *ibuf;
301 
302 #ifdef SIGCONT
303 	sigset(SIGCONT, SIG_DFL);
304 #endif
305 	if (rcvmode && !sourcing) {
306 		if (sigset(SIGINT, SIG_IGN) != SIG_IGN)
307 			sigset(SIGINT, stop);
308 		if (sigset(SIGHUP, SIG_IGN) != SIG_IGN)
309 			sigset(SIGHUP, hangup);
310 	}
311 	for (;;) {
312 		setjmp(srbuf);
313 
314 		/*
315 		 * Print the prompt, if needed.  Clear out
316 		 * string space, and flush the output.
317 		 */
318 
319 		if (!rcvmode && !sourcing)
320 			return;
321 		eofloop = 0;
322 top:
323 		if ((shudprompt = (intty && !sourcing)) != 0) {
324 			if (prompt == NOSTR) {
325 				if ((int)value("bsdcompat"))
326 					prompt = "& ";
327 				else
328 					prompt = "";
329 			}
330 #ifdef SIGCONT
331 			sigset(SIGCONT, contin);
332 #endif
333 			if (intty && value("autoinc") &&
334 			    stat(editfile, &minfo) >= 0 &&
335 			    minfo.st_size > mailsize) {
336 				int OmsgCount, i;
337 
338 				OmsgCount = msgCount;
339 				fseek(otf, 0L, 2);
340 				holdsigs();
341 				if (!edit && issysmbox)
342 					lockmail();
343 				if ((ibuf = fopen(editfile, "r")) == NULL) {
344 					fprintf(stderr,
345 					    gettext("Can't reopen %s\n"),
346 					    editfile);
347 					if (!edit && issysmbox)
348 						unlockmail();
349 					exit(1);
350 					/* NOTREACHED */
351 				}
352 				if (edit || !issysmbox)
353 					lock(ibuf, "r", 1);
354 				fseek(ibuf, mailsize, 0);
355 				mailsize = fsize(ibuf);
356 				setptr(ibuf);
357 				setmsize(msgCount);
358 				fclose(ibuf);
359 				if (!edit && issysmbox)
360 					unlockmail();
361 				if (msgCount-OmsgCount > 0) {
362 					printf(gettext(
363 					    "New mail has arrived.\n"));
364 					if (msgCount - OmsgCount == 1)
365 						printf(gettext(
366 						    "Loaded 1 new message\n"));
367 					else
368 						printf(gettext(
369 						    "Loaded %d new messages\n"),
370 						    msgCount-OmsgCount);
371 					if (value("header") != NOSTR)
372 						for (i = OmsgCount+1;
373 						    i <= msgCount; i++) {
374 							printhead(i);
375 							sreset();
376 						}
377 				}
378 				relsesigs();
379 			}
380 			printf("%s", prompt);
381 		}
382 		flush();
383 		sreset();
384 
385 		/*
386 		 * Read a line of commands from the current input
387 		 * and handle end of file specially.
388 		 */
389 
390 		n = 0;
391 		linebuf[0] = '\0';
392 		for (;;) {
393 			if (readline(input, line) <= 0) {
394 				if (n != 0)
395 					break;
396 				if (loading)
397 					return;
398 				if (sourcing) {
399 					unstack();
400 					goto more;
401 				}
402 				if (value("ignoreeof") != NOSTR && shudprompt) {
403 					if (++eofloop < 25) {
404 						printf(gettext(
405 						    "Use \"quit\" to quit.\n"));
406 						goto top;
407 					}
408 				}
409 				return;
410 			}
411 			if ((n = strlen(line)) == 0)
412 				break;
413 			n--;
414 			if (line[n] != '\\')
415 				break;
416 			line[n++] = ' ';
417 			if (n > LINESIZE - (int)strlen(linebuf) - 1)
418 				break;
419 			strcat(linebuf, line);
420 		}
421 		n = LINESIZE - strlen(linebuf) - 1;
422 		if ((int)strlen(line) > n) {
423 			printf(gettext(
424 		"Line plus continuation line too long:\n\t%s\n\nplus\n\t%s\n"),
425 			    linebuf, line);
426 			if (loading)
427 				return;
428 			if (sourcing) {
429 				unstack();
430 				goto more;
431 			}
432 			return;
433 		}
434 		strncat(linebuf, line, n);
435 #ifdef SIGCONT
436 		sigset(SIGCONT, SIG_DFL);
437 #endif
438 		if (execute(linebuf, 0))
439 			return;
440 more:;
441 	}
442 }
443 
444 /*
445  * Execute a single command.  If the command executed
446  * is "quit," then return non-zero so that the caller
447  * will know to return back to main, if it cares.
448  * Contxt is non-zero if called while composing mail.
449  */
450 
451 int
452 execute(char linebuf[], int contxt)
453 {
454 	char word[LINESIZE];
455 	char *arglist[MAXARGC];
456 	const struct cmd *com;
457 	register char *cp, *cp2;
458 	register int c, e;
459 	int muvec[2];
460 
461 	/*
462 	 * Strip the white space away from the beginning
463 	 * of the command, then scan out a word, which
464 	 * consists of anything except digits and white space.
465 	 *
466 	 * Handle |, ! and # differently to get the correct
467 	 * lexical conventions.
468 	 */
469 
470 	cp = linebuf;
471 	while (any(*cp, " \t"))
472 		cp++;
473 	cp2 = word;
474 	if (any(*cp, "!|#"))
475 		*cp2++ = *cp++;
476 	else
477 		while (*cp && !any(*cp, " \t0123456789$^.:/-+*'\""))
478 			*cp2++ = *cp++;
479 	*cp2 = '\0';
480 
481 	/*
482 	 * Look up the command; if not found, complain.
483 	 * Normally, a blank command would map to the
484 	 * first command in the table; while sourcing,
485 	 * however, we ignore blank lines to eliminate
486 	 * confusion.
487 	 */
488 
489 	if (sourcing && equal(word, ""))
490 		return (0);
491 	com = lex(word);
492 	if (com == NONE) {
493 		fprintf(stderr, gettext("Unknown command: \"%s\"\n"), word);
494 		if (loading) {
495 			cond = CANY;
496 			return (1);
497 		}
498 		if (sourcing) {
499 			cond = CANY;
500 			unstack();
501 		}
502 		return (0);
503 	}
504 
505 	/*
506 	 * See if we should execute the command -- if a conditional
507 	 * we always execute it, otherwise, check the state of cond.
508 	 */
509 
510 	if ((com->c_argtype & F) == 0)
511 		if (cond == CRCV && !rcvmode || cond == CSEND && rcvmode ||
512 		    cond == CTTY && !intty || cond == CNOTTY && intty)
513 			return (0);
514 
515 	/*
516 	 * Special case so that quit causes a return to
517 	 * main, who will call the quit code directly.
518 	 * If we are in a source file, just unstack.
519 	 */
520 
521 	if (com->c_func == (int (*)(void *))edstop) {
522 		if (sourcing) {
523 			if (loading)
524 				return (1);
525 			unstack();
526 			return (0);
527 		}
528 		return (1);
529 	}
530 
531 	/*
532 	 * Process the arguments to the command, depending
533 	 * on the type it expects.  Default to an error.
534 	 * If we are sourcing an interactive command, it's
535 	 * an error.
536 	 */
537 
538 	if (!rcvmode && (com->c_argtype & M) == 0) {
539 		fprintf(stderr,
540 		    gettext("May not execute \"%s\" while sending\n"),
541 		    com->c_name);
542 		if (loading)
543 			return (1);
544 		if (sourcing)
545 			unstack();
546 		return (0);
547 	}
548 	if (sourcing && com->c_argtype & I) {
549 		fprintf(stderr,
550 		    gettext("May not execute \"%s\" while sourcing\n"),
551 		    com->c_name);
552 		rpterr = 1;
553 		if (loading)
554 			return (1);
555 		unstack();
556 		return (0);
557 	}
558 	if (readonly && com->c_argtype & W) {
559 		fprintf(stderr, gettext(
560 		    "May not execute \"%s\" -- message file is read only\n"),
561 		    com->c_name);
562 		if (loading)
563 			return (1);
564 		if (sourcing)
565 			unstack();
566 		return (0);
567 	}
568 	if (contxt && com->c_argtype & R) {
569 		fprintf(stderr, gettext("Cannot recursively invoke \"%s\"\n"),
570 		    com->c_name);
571 		return (0);
572 	}
573 	e = 1;
574 	switch (com->c_argtype & ~(F|P|I|M|T|W|R)) {
575 	case MSGLIST:
576 		/*
577 		 * A message list defaulting to nearest forward
578 		 * legal message.
579 		 */
580 		if (msgvec == 0) {
581 			fprintf(stderr,
582 			    gettext("Illegal use of \"message list\"\n"));
583 			return (-1);
584 		}
585 		if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
586 			break;
587 		if (c  == 0)
588 			if (msgCount == 0)
589 				*msgvec = NULL;
590 			else {
591 				*msgvec = first(com->c_msgflag,
592 					com->c_msgmask);
593 				msgvec[1] = NULL;
594 			}
595 		if (*msgvec == NULL) {
596 			fprintf(stderr, gettext("No applicable messages\n"));
597 			break;
598 		}
599 		e = (*com->c_func)(msgvec);
600 		break;
601 
602 	case NDMLIST:
603 		/*
604 		 * A message operand with no defaults, but no error
605 		 * if none exists. There will be an error if the
606 		 * msgvec pointer is of zero value.
607 		 */
608 		if (msgvec == 0) {
609 			fprintf(stderr,
610 			    gettext("Illegal use of \"message operand\"\n"));
611 			return (-1);
612 		}
613 		if (getmessage(cp, msgvec, com->c_msgflag) < 0)
614 			break;
615 		e = (*com->c_func)(msgvec);
616 		break;
617 
618 	case STRLIST:
619 		/*
620 		 * Just the straight string, with
621 		 * leading blanks removed.
622 		 */
623 		while (any(*cp, " \t"))
624 			cp++;
625 		e = (*com->c_func)(cp);
626 		break;
627 
628 	case RAWLIST:
629 		/*
630 		 * A vector of strings, in shell style.
631 		 */
632 		if ((c = getrawlist(cp, arglist,
633 				sizeof (arglist) / sizeof (*arglist))) < 0)
634 			break;
635 		if (c < com->c_minargs) {
636 			fprintf(stderr,
637 			    gettext("%s requires at least %d arg(s)\n"),
638 			    com->c_name, com->c_minargs);
639 			break;
640 		}
641 		if (c > com->c_maxargs) {
642 			fprintf(stderr,
643 			    gettext("%s takes no more than %d arg(s)\n"),
644 			    com->c_name, com->c_maxargs);
645 			break;
646 		}
647 		e = (*com->c_func)(arglist);
648 		break;
649 
650 	case NOLIST:
651 		/*
652 		 * Just the constant zero, for exiting,
653 		 * eg.
654 		 */
655 		e = (*com->c_func)(0);
656 		break;
657 
658 	default:
659 		panic("Unknown argtype");
660 	}
661 
662 	/*
663 	 * Exit the current source file on
664 	 * error.
665 	 */
666 
667 	if (e && loading)
668 		return (1);
669 	if (e && sourcing)
670 		unstack();
671 	if (com->c_func == (int (*)(void *))edstop)
672 		return (1);
673 	if (value("autoprint") != NOSTR && com->c_argtype & P)
674 		if ((dot->m_flag & MDELETED) == 0) {
675 			muvec[0] = dot - &message[0] + 1;
676 			muvec[1] = 0;
677 			type(muvec);
678 		}
679 	if (!sourcing && (com->c_argtype & T) == 0)
680 		sawcom = 1;
681 	return (0);
682 }
683 
684 #ifdef SIGCONT
685 /*
686  * When we wake up after ^Z, reprint the prompt.
687  */
688 static void
689 #ifdef	__cplusplus
690 contin(int)
691 #else
692 /* ARGSUSED */
693 contin(int s)
694 #endif
695 {
696 	if (shudprompt)
697 		printf("%s", prompt);
698 	fflush(stdout);
699 }
700 #endif
701 
702 /*
703  * Branch here on hangup signal and simulate quit.
704  */
705 void
706 #ifdef	__cplusplus
707 hangup(int)
708 #else
709 /* ARGSUSED */
710 hangup(int s)
711 #endif
712 {
713 
714 	holdsigs();
715 #ifdef OLD_BSD_SIGS
716 	sigignore(SIGHUP);
717 #endif
718 	if (edit) {
719 		if (setjmp(srbuf))
720 			exit(rpterr);
721 		edstop(0);
722 	} else {
723 		if (issysmbox)
724 			Verhogen();
725 		if (value("exit") != NOSTR)
726 			exit(1);
727 		else
728 			quit(0);
729 	}
730 	exit(rpterr);
731 }
732 
733 /*
734  * Set the size of the message vector used to construct argument
735  * lists to message list functions.
736  */
737 
738 static void
739 setmsize(int sz)
740 {
741 
742 	if (msgvec != (int *)0)
743 		free(msgvec);
744 	if (sz < 1)
745 		sz = 1; /* need at least one cell for terminating 0 */
746 	if ((msgvec = (int *)
747 	    calloc((unsigned)(sz + 1), sizeof (*msgvec))) == NULL)
748 		panic("Failed to allocate memory for message vector");
749 }
750 
751 /*
752  * Find the correct command in the command table corresponding
753  * to the passed command "word"
754  */
755 
756 static const struct cmd *
757 lex(char word[])
758 {
759 	register const struct cmd *cp;
760 
761 	for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++)
762 		if (isprefix(word, cp->c_name))
763 			return (cp);
764 	return (NONE);
765 }
766 
767 /*
768  * Determine if as1 is a valid prefix of as2.
769  */
770 static int
771 isprefix(char *as1, char *as2)
772 {
773 	register char *s1, *s2;
774 
775 	s1 = as1;
776 	s2 = as2;
777 	while (*s1++ == *s2)
778 		if (*s2++ == '\0')
779 			return (1);
780 	return (*--s1 == '\0');
781 }
782 
783 /*
784  * The following gets called on receipt of a rubout.  This is
785  * to abort printout of a command, mainly.
786  * Dispatching here when command() is inactive crashes rcv.
787  * Close all open files except 0, 1, 2, and the temporary.
788  * The special call to getuserid() is needed so it won't get
789  * annoyed about losing its open file.
790  * Also, unstack all source files.
791  */
792 
793 static int	inithdr;		/* am printing startup headers */
794 
795 void
796 stop(int s)
797 {
798 	register NODE *head;
799 
800 	noreset = 0;
801 	if (!inithdr)
802 		sawcom++;
803 	inithdr = 0;
804 	while (sourcing)
805 		unstack();
806 	(void) getuserid((char *)0);
807 	for (head = fplist; head != (NODE *)NULL; head = head->next) {
808 		if (head->fp == stdin || head->fp == stdout)
809 			continue;
810 		if (head->fp == itf || head->fp == otf)
811 			continue;
812 		if (head->fp == stderr)
813 			continue;
814 		if (head->fp == semfp)
815 			continue;
816 		if (head->fp == pipef) {
817 			npclose(pipef);
818 			pipef = NULL;
819 			continue;
820 		}
821 		fclose(head->fp);
822 	}
823 	if (image >= 0) {
824 		close(image);
825 		image = -1;
826 	}
827 	if (s) {
828 		fprintf(stderr, gettext("Interrupt\n"));
829 		fflush(stderr);
830 #ifdef OLD_BSD_SIGS
831 		sigrelse(s);
832 #endif
833 	}
834 	longjmp(srbuf, 1);
835 }
836 
837 /*
838  * Announce the presence of the current mailx version,
839  * give the message count, and print a header listing.
840  */
841 
842 #define	GREETING	"%s  Type ? for help.\n"
843 
844 void
845 announce(void)
846 {
847 	int vec[2], mdot;
848 	extern const char *const version;
849 
850 	if (!Hflag && value("quiet") == NOSTR)
851 		printf(gettext(GREETING), version);
852 	mdot = newfileinfo(1);
853 	vec[0] = mdot;
854 	vec[1] = 0;
855 	dot = &message[mdot - 1];
856 	if (msgCount > 0 && !noheader) {
857 		inithdr++;
858 		headers(vec);
859 		inithdr = 0;
860 	}
861 }
862 
863 /*
864  * Announce information about the file we are editing.
865  * Return a likely place to set dot.
866  */
867 int
868 newfileinfo(int start)
869 {
870 	register struct message *mp;
871 	register int u, n, mdot, d, s;
872 	char fname[BUFSIZ], zname[BUFSIZ], *ename;
873 
874 	if (Hflag)
875 		return (1);		/* fake it--return message 1 */
876 	for (mp = &message[start - 1]; mp < &message[msgCount]; mp++)
877 		if ((mp->m_flag & (MNEW|MREAD)) == MNEW)
878 			break;
879 	if (mp >= &message[msgCount])
880 		for (mp = &message[start - 1]; mp < &message[msgCount]; mp++)
881 			if ((mp->m_flag & MREAD) == 0)
882 				break;
883 	if (mp < &message[msgCount])
884 		mdot = mp - &message[0] + 1;
885 	else
886 		mdot = 1;
887 	n = u = d = s = 0;
888 	for (mp = &message[start - 1]; mp < &message[msgCount]; mp++) {
889 		if (mp->m_flag & MNEW)
890 			n++;
891 		if ((mp->m_flag & MREAD) == 0)
892 			u++;
893 		if (mp->m_flag & MDELETED)
894 			d++;
895 		if (mp->m_flag & MSAVED)
896 			s++;
897 	}
898 	ename = origname;
899 	if (getfold(fname) >= 0) {
900 		nstrcat(fname, sizeof (fname), "/");
901 		if (strncmp(fname, editfile, strlen(fname)) == 0) {
902 			snprintf(zname, sizeof (zname),
903 				"+%s", editfile + strlen(fname));
904 			ename = zname;
905 		}
906 	}
907 	printf("\"%s\": ", ename);
908 	if (msgCount == 1)
909 		printf(gettext("1 message"));
910 	else
911 		printf(gettext("%d messages"), msgCount);
912 	if (n > 0)
913 		printf(gettext(" %d new"), n);
914 	if (u-n > 0)
915 		printf(gettext(" %d unread"), u);
916 	if (d > 0)
917 		printf(gettext(" %d deleted"), d);
918 	if (s > 0)
919 		printf(gettext(" %d saved"), s);
920 	if (readonly)
921 		printf(gettext(" [Read only]"));
922 	printf("\n");
923 	return (mdot);
924 }
925 
926 /*
927  * Print the current version number.
928  */
929 
930 int
931 #ifdef	__cplusplus
932 pversion(char *)
933 #else
934 /* ARGSUSED */
935 pversion(char *s)
936 #endif
937 {
938 	printf("%s\n", version);
939 	return (0);
940 }
941 
942 /*
943  * Load a file of user definitions.
944  */
945 void
946 load(char *name)
947 {
948 	register FILE *in, *oldin;
949 
950 	if ((in = fopen(name, "r")) == NULL)
951 		return;
952 	oldin = input;
953 	input = in;
954 	loading = 1;
955 	sourcing = 1;
956 	commands();
957 	loading = 0;
958 	sourcing = 0;
959 	input = oldin;
960 	fclose(in);
961 }
962 
963 /*
964  * Incorporate any new mail into the current session.
965  *
966  * XXX - Since autoinc works on "edited" files as well as the
967  * system mailbox, this probably ought to as well.
968  */
969 
970 int
971 inc(void)
972 {
973 	FILE *ibuf;
974 	int mdot;
975 	struct stat stbuf;
976 	int firstnewmsg = msgCount + 1;
977 
978 	if (edit) {
979 		fprintf(stderr, gettext("Not in system mailbox\n"));
980 		return (-1);
981 	}
982 	if (((ibuf = fopen(mailname, "r")) == NULL) ||
983 	    (fstat(fileno(ibuf), &stbuf) < 0) || stbuf.st_size == 0L ||
984 	    stbuf.st_size == mailsize || (stbuf.st_mode&S_IFMT) != S_IFREG) {
985 		if (strrchr(mailname, '/') == NOSTR)
986 			fprintf(stderr, gettext("No new mail.\n"));
987 		else
988 			fprintf(stderr, gettext("No new mail for %s\n"),
989 			    strrchr(mailname, '/')+1);
990 		if (ibuf != NULL)
991 			fclose(ibuf);
992 		return (-1);
993 	}
994 
995 	fseek(otf, 0L, 2);
996 	holdsigs();
997 	if (issysmbox)
998 		lockmail();
999 	fseek(ibuf, mailsize, 0);
1000 	mailsize = fsize(ibuf);
1001 	setptr(ibuf);
1002 	setmsize(msgCount);
1003 	fclose(ibuf);
1004 	if (issysmbox)
1005 		unlockmail();
1006 	relsesigs();
1007 	mdot = newfileinfo(firstnewmsg);
1008 	dot = &message[mdot - 1];
1009 	sawcom = 0;
1010 	return (0);
1011 }
1012