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