xref: /illumos-gate/usr/src/cmd/mailx/lex.c (revision fa4a3e77edc40df6d92e8da6fc4961d275e9896d)
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 ((uintptr_t)com->c_func == (uintptr_t)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 ((uintptr_t)com->c_func == (uintptr_t)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 contin(int s __unused)
688 {
689 	if (shudprompt)
690 		printf("%s", prompt);
691 	fflush(stdout);
692 }
693 #endif
694 
695 /*
696  * Branch here on hangup signal and simulate quit.
697  */
698 void
699 hangup(int s __unused)
700 {
701 
702 	holdsigs();
703 #ifdef OLD_BSD_SIGS
704 	sigignore(SIGHUP);
705 #endif
706 	if (edit) {
707 		if (setjmp(srbuf))
708 			exit(rpterr);
709 		edstop(0);
710 	} else {
711 		if (issysmbox)
712 			Verhogen();
713 		if (value("exit") != NOSTR)
714 			exit(1);
715 		else
716 			quit(0);
717 	}
718 	exit(rpterr);
719 }
720 
721 /*
722  * Set the size of the message vector used to construct argument
723  * lists to message list functions.
724  */
725 
726 static void
727 setmsize(int sz)
728 {
729 
730 	if (msgvec != (int *)0)
731 		free(msgvec);
732 	if (sz < 1)
733 		sz = 1; /* need at least one cell for terminating 0 */
734 	if ((msgvec = (int *)
735 	    calloc((unsigned)(sz + 1), sizeof (*msgvec))) == NULL)
736 		panic("Failed to allocate memory for message vector");
737 }
738 
739 /*
740  * Find the correct command in the command table corresponding
741  * to the passed command "word"
742  */
743 
744 static const struct cmd *
745 lex(char word[])
746 {
747 	const struct cmd *cp;
748 
749 	for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++)
750 		if (isprefix(word, cp->c_name))
751 			return (cp);
752 	return (NONE);
753 }
754 
755 /*
756  * Determine if as1 is a valid prefix of as2.
757  */
758 static int
759 isprefix(char *as1, char *as2)
760 {
761 	char *s1, *s2;
762 
763 	s1 = as1;
764 	s2 = as2;
765 	while (*s1++ == *s2)
766 		if (*s2++ == '\0')
767 			return (1);
768 	return (*--s1 == '\0');
769 }
770 
771 /*
772  * The following gets called on receipt of a rubout.  This is
773  * to abort printout of a command, mainly.
774  * Dispatching here when command() is inactive crashes rcv.
775  * Close all open files except 0, 1, 2, and the temporary.
776  * The special call to getuserid() is needed so it won't get
777  * annoyed about losing its open file.
778  * Also, unstack all source files.
779  */
780 
781 static int	inithdr;		/* am printing startup headers */
782 
783 void
784 stop(int s)
785 {
786 	NODE *head;
787 
788 	noreset = 0;
789 	if (!inithdr)
790 		sawcom++;
791 	inithdr = 0;
792 	while (sourcing)
793 		unstack();
794 	(void) getuserid((char *)0);
795 	for (head = fplist; head != (NODE *)NULL; head = head->next) {
796 		if (head->fp == stdin || head->fp == stdout)
797 			continue;
798 		if (head->fp == itf || head->fp == otf)
799 			continue;
800 		if (head->fp == stderr)
801 			continue;
802 		if (head->fp == semfp)
803 			continue;
804 		if (head->fp == pipef) {
805 			npclose(pipef);
806 			pipef = NULL;
807 			continue;
808 		}
809 		fclose(head->fp);
810 	}
811 	if (image >= 0) {
812 		close(image);
813 		image = -1;
814 	}
815 	if (s) {
816 		fprintf(stderr, gettext("Interrupt\n"));
817 		fflush(stderr);
818 #ifdef OLD_BSD_SIGS
819 		sigrelse(s);
820 #endif
821 	}
822 	longjmp(srbuf, 1);
823 }
824 
825 /*
826  * Announce the presence of the current mailx version,
827  * give the message count, and print a header listing.
828  */
829 
830 #define	GREETING	"%s  Type ? for help.\n"
831 
832 void
833 announce(void)
834 {
835 	int vec[2], mdot;
836 	extern const char *const version;
837 
838 	if (!Hflag && value("quiet") == NOSTR)
839 		printf(gettext(GREETING), version);
840 	mdot = newfileinfo(1);
841 	vec[0] = mdot;
842 	vec[1] = 0;
843 	dot = &message[mdot - 1];
844 	if (msgCount > 0 && !noheader) {
845 		inithdr++;
846 		headers(vec);
847 		inithdr = 0;
848 	}
849 }
850 
851 /*
852  * Announce information about the file we are editing.
853  * Return a likely place to set dot.
854  */
855 int
856 newfileinfo(int start)
857 {
858 	struct message *mp;
859 	int u, n, mdot, d, s;
860 	char fname[BUFSIZ], zname[BUFSIZ], *ename;
861 
862 	if (Hflag)
863 		return (1);		/* fake it--return message 1 */
864 	for (mp = &message[start - 1]; mp < &message[msgCount]; mp++)
865 		if ((mp->m_flag & (MNEW|MREAD)) == MNEW)
866 			break;
867 	if (mp >= &message[msgCount])
868 		for (mp = &message[start - 1]; mp < &message[msgCount]; mp++)
869 			if ((mp->m_flag & MREAD) == 0)
870 				break;
871 	if (mp < &message[msgCount])
872 		mdot = mp - &message[0] + 1;
873 	else
874 		mdot = 1;
875 	n = u = d = s = 0;
876 	for (mp = &message[start - 1]; mp < &message[msgCount]; mp++) {
877 		if (mp->m_flag & MNEW)
878 			n++;
879 		if ((mp->m_flag & MREAD) == 0)
880 			u++;
881 		if (mp->m_flag & MDELETED)
882 			d++;
883 		if (mp->m_flag & MSAVED)
884 			s++;
885 	}
886 	ename = origname;
887 	if (getfold(fname) >= 0) {
888 		nstrcat(fname, sizeof (fname), "/");
889 		if (strncmp(fname, editfile, strlen(fname)) == 0) {
890 			snprintf(zname, sizeof (zname),
891 			    "+%s", editfile + strlen(fname));
892 			ename = zname;
893 		}
894 	}
895 	printf("\"%s\": ", ename);
896 	if (msgCount == 1)
897 		printf(gettext("1 message"));
898 	else
899 		printf(gettext("%d messages"), msgCount);
900 	if (n > 0)
901 		printf(gettext(" %d new"), n);
902 	if (u-n > 0)
903 		printf(gettext(" %d unread"), u);
904 	if (d > 0)
905 		printf(gettext(" %d deleted"), d);
906 	if (s > 0)
907 		printf(gettext(" %d saved"), s);
908 	if (readonly)
909 		printf(gettext(" [Read only]"));
910 	printf("\n");
911 	return (mdot);
912 }
913 
914 /*
915  * Print the current version number.
916  */
917 
918 int
919 pversion(char *s __unused)
920 {
921 	printf("%s\n", version);
922 	return (0);
923 }
924 
925 /*
926  * Load a file of user definitions.
927  */
928 void
929 load(char *name)
930 {
931 	FILE *in, *oldin;
932 
933 	if ((in = fopen(name, "r")) == NULL)
934 		return;
935 	oldin = input;
936 	input = in;
937 	loading = 1;
938 	sourcing = 1;
939 	commands();
940 	loading = 0;
941 	sourcing = 0;
942 	input = oldin;
943 	fclose(in);
944 }
945 
946 /*
947  * Incorporate any new mail into the current session.
948  *
949  * XXX - Since autoinc works on "edited" files as well as the
950  * system mailbox, this probably ought to as well.
951  */
952 
953 int
954 inc(void)
955 {
956 	FILE *ibuf;
957 	int mdot;
958 	struct stat stbuf;
959 	int firstnewmsg = msgCount + 1;
960 
961 	if (edit) {
962 		fprintf(stderr, gettext("Not in system mailbox\n"));
963 		return (-1);
964 	}
965 	if (((ibuf = fopen(mailname, "r")) == NULL) ||
966 	    (fstat(fileno(ibuf), &stbuf) < 0) || stbuf.st_size == 0L ||
967 	    stbuf.st_size == mailsize || (stbuf.st_mode&S_IFMT) != S_IFREG) {
968 		if (strrchr(mailname, '/') == NOSTR)
969 			fprintf(stderr, gettext("No new mail.\n"));
970 		else
971 			fprintf(stderr, gettext("No new mail for %s\n"),
972 			    strrchr(mailname, '/')+1);
973 		if (ibuf != NULL)
974 			fclose(ibuf);
975 		return (-1);
976 	}
977 
978 	fseek(otf, 0L, 2);
979 	holdsigs();
980 	if (issysmbox)
981 		lockmail();
982 	fseek(ibuf, mailsize, 0);
983 	mailsize = fsize(ibuf);
984 	setptr(ibuf);
985 	setmsize(msgCount);
986 	fclose(ibuf);
987 	if (issysmbox)
988 		unlockmail();
989 	relsesigs();
990 	mdot = newfileinfo(firstnewmsg);
991 	dot = &message[mdot - 1];
992 	sawcom = 0;
993 	return (0);
994 }
995