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