1 /*
2  * Heirloom mailx - a mail user agent derived from Berkeley Mail.
3  *
4  * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5  */
6 /*
7  * Copyright (c) 1980, 1993
8  *	The Regents of the University of California.  All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 
39 #ifndef lint
40 #ifdef	DOSCCS
41 static char sccsid[] = "@(#)lex.c	2.86 (gritter) 12/25/06";
42 #endif
43 #endif /* not lint */
44 
45 #include "rcv.h"
46 #include "extern.h"
47 #include <errno.h>
48 #include <sys/stat.h>
49 #include <fcntl.h>
50 #include <unistd.h>
51 
52 /*
53  * Mail -- a mail program
54  *
55  * Lexical processing of commands.
56  */
57 
58 static char	*prompt;
59 static sighandler_type	oldpipe;
60 
61 static const struct cmd *lex(char *Word);
62 static void stop(int s);
63 static void hangup(int s);
64 
65 /*
66  * Set up editing on the given file name.
67  * If the first character of name is %, we are considered to be
68  * editing the file, otherwise we are reading our mail which has
69  * signficance for mbox and so forth.
70  *
71  * newmail: Check for new mail in the current folder only.
72  */
73 int
setfile(char * name,int newmail)74 setfile(char *name, int newmail)
75 {
76 	FILE *ibuf;
77 	int i, compressed = 0;
78 	struct stat stb;
79 	char isedit;
80 	char *who = name[1] ? name + 1 : myname;
81 	static int shudclob;
82 	size_t offset;
83 	int omsgCount = 0;
84 	struct shortcut *sh;
85 	struct flock	flp;
86 
87 	isedit = *name != '%' && ((sh = get_shortcut(name)) == NULL ||
88 			*sh->sh_long != '%');
89 	if ((name = expand(name)) == NULL)
90 		return -1;
91 
92 	switch (which_protocol(name)) {
93 	case PROTO_FILE:
94 		break;
95 	case PROTO_MAILDIR:
96 		return maildir_setfile(name, newmail, isedit);
97 	case PROTO_POP3:
98 		shudclob = 1;
99 		return pop3_setfile(name, newmail, isedit);
100 	case PROTO_IMAP:
101 		shudclob = 1;
102 		if (newmail) {
103 			if (mb.mb_type == MB_CACHE)
104 				return 1;
105 			omsgCount = msgCount;
106 		}
107 		return imap_setfile(name, newmail, isedit);
108 	case PROTO_UNKNOWN:
109 		fprintf(stderr, catgets(catd, CATSET, 217,
110 				"Cannot handle protocol: %s\n"), name);
111 		return -1;
112 	}
113 	if ((ibuf = Zopen(name, "r", &compressed)) == NULL) {
114 		if ((!isedit && errno == ENOENT) || newmail) {
115 			if (newmail)
116 				goto nonewmail;
117 			goto nomail;
118 		}
119 		perror(name);
120 		return(-1);
121 	}
122 
123 	if (fstat(fileno(ibuf), &stb) < 0) {
124 		Fclose(ibuf);
125 		if (newmail)
126 			goto nonewmail;
127 		perror("fstat");
128 		return (-1);
129 	}
130 
131 	if (S_ISDIR(stb.st_mode)) {
132 		Fclose(ibuf);
133 		if (newmail)
134 			goto nonewmail;
135 		errno = EISDIR;
136 		perror(name);
137 		return (-1);
138 	} else if (S_ISREG(stb.st_mode)) {
139 		/*EMPTY*/
140 	} else {
141 		Fclose(ibuf);
142 		if (newmail)
143 			goto nonewmail;
144 		errno = EINVAL;
145 		perror(name);
146 		return (-1);
147 	}
148 
149 	/*
150 	 * Looks like all will be well.  We must now relinquish our
151 	 * hold on the current set of stuff.  Must hold signals
152 	 * while we are reading the new file, else we will ruin
153 	 * the message[] data structure.
154 	 */
155 
156 	holdsigs();
157 	if (shudclob && !newmail)
158 		quit();
159 
160 #ifdef	HAVE_SOCKETS
161 	if (!newmail && mb.mb_sock.s_fd >= 0)
162 		sclose(&mb.mb_sock);
163 #endif	/* HAVE_SOCKETS */
164 
165 	/*
166 	 * Copy the messages into /tmp
167 	 * and set pointers.
168 	 */
169 
170 	flp.l_type = F_RDLCK;
171 	flp.l_start = 0;
172 	flp.l_whence = SEEK_SET;
173 	if (!newmail) {
174 		mb.mb_type = MB_FILE;
175 		mb.mb_perm = Rflag ? 0 : MB_DELE|MB_EDIT;
176 		mb.mb_compressed = compressed;
177 		if (compressed) {
178 			if (compressed & 0200)
179 				mb.mb_perm = 0;
180 		} else {
181 			if ((i = open(name, O_WRONLY)) < 0)
182 				mb.mb_perm = 0;
183 			else
184 				close(i);
185 		}
186 		if (shudclob) {
187 			if (mb.mb_itf) {
188 				fclose(mb.mb_itf);
189 				mb.mb_itf = NULL;
190 			}
191 			if (mb.mb_otf) {
192 				fclose(mb.mb_otf);
193 				mb.mb_otf = NULL;
194 			}
195 		}
196 		shudclob = 1;
197 		edit = isedit;
198 		initbox(name);
199 		offset = 0;
200 		flp.l_len = 0;
201 		if (!edit && fcntl(fileno(ibuf), F_SETLKW, &flp) < 0) {
202 			perror("Unable to lock mailbox");
203 			Fclose(ibuf);
204 			return -1;
205 		}
206 	} else /* newmail */{
207 		fseek(mb.mb_otf, 0L, SEEK_END);
208 		fseek(ibuf, mailsize, SEEK_SET);
209 		offset = mailsize;
210 		omsgCount = msgCount;
211 		flp.l_len = offset;
212 		if (!edit && fcntl(fileno(ibuf), F_SETLKW, &flp) < 0)
213 			goto nonewmail;
214 	}
215 	mailsize = fsize(ibuf);
216 	if (newmail && mailsize <= offset) {
217 		relsesigs();
218 		goto nonewmail;
219 	}
220 	setptr(ibuf, offset);
221 	setmsize(msgCount);
222 	if (newmail && mb.mb_sorted) {
223 		mb.mb_threaded = 0;
224 		sort((void *)-1);
225 	}
226 	Fclose(ibuf);
227 	relsesigs();
228 	if (!newmail)
229 		sawcom = 0;
230 	if ((!edit || newmail) && msgCount == 0) {
231 nonewmail:
232 		if (!newmail) {
233 			if (value("emptystart") == NULL)
234 nomail:				fprintf(stderr, catgets(catd, CATSET, 88,
235 						"No mail for %s\n"), who);
236 		}
237 		return 1;
238 	}
239 	if (newmail) {
240 		newmailinfo(omsgCount);
241 	}
242 	return(0);
243 }
244 
245 
246 int
newmailinfo(int omsgCount)247 newmailinfo(int omsgCount)
248 {
249 	int	mdot;
250 	int	i;
251 
252 	for (i = 0; i < omsgCount; i++)
253 		message[i].m_flag &= ~MNEWEST;
254 	if (msgCount > omsgCount) {
255 		for (i = omsgCount; i < msgCount; i++)
256 			message[i].m_flag |= MNEWEST;
257 		printf(catgets(catd, CATSET, 158, "New mail has arrived.\n"));
258 		if (msgCount - omsgCount == 1)
259 			printf(catgets(catd, CATSET, 214,
260 				"Loaded 1 new message\n"));
261 		else
262 			printf(catgets(catd, CATSET, 215,
263 				"Loaded %d new messages\n"),
264 				msgCount - omsgCount);
265 	} else
266 		printf("Loaded %d messages\n", msgCount);
267 	callhook(mailname, 1);
268 	mdot = getmdot(1);
269 	if (value("header")) {
270 		if (mb.mb_type == MB_IMAP)
271 			imap_getheaders(omsgCount+1, msgCount);
272 		while (++omsgCount <= msgCount)
273 			if (visible(&message[omsgCount-1]))
274 				printhead(omsgCount, stdout, 0);
275 	}
276 	return mdot;
277 }
278 
279 static int	*msgvec;
280 static int	reset_on_stop;			/* do a reset() if stopped */
281 
282 /*
283  * Interpret user commands one by one.  If standard input is not a tty,
284  * print no prompt.
285  */
286 void
commands(void)287 commands(void)
288 {
289 	int eofloop = 0;
290 	int n, x;
291 	char *linebuf = NULL, *av, *nv;
292 	size_t linesize = 0;
293 
294 	(void)&eofloop;
295 	if (!sourcing) {
296 		if (safe_signal(SIGINT, SIG_IGN) != SIG_IGN)
297 			safe_signal(SIGINT, onintr);
298 		if (safe_signal(SIGHUP, SIG_IGN) != SIG_IGN)
299 			safe_signal(SIGHUP, hangup);
300 		safe_signal(SIGTSTP, stop);
301 		safe_signal(SIGTTOU, stop);
302 		safe_signal(SIGTTIN, stop);
303 	}
304 	oldpipe = safe_signal(SIGPIPE, SIG_IGN);
305 	safe_signal(SIGPIPE, oldpipe);
306 	setexit();
307 	for (;;) {
308 		interrupts = 0;
309 		handlerstacktop = NULL;
310 		/*
311 		 * Print the prompt, if needed.  Clear out
312 		 * string space, and flush the output.
313 		 */
314 		if (!sourcing && value("interactive") != NULL) {
315 			av = (av = value("autoinc")) ? savestr(av) : NULL;
316 			nv = (nv = value("newmail")) ? savestr(nv) : NULL;
317 			if (is_a_tty[0] && (av != NULL || nv != NULL ||
318 					mb.mb_type == MB_IMAP)) {
319 				struct stat st;
320 
321 				n = (av && strcmp(av, "noimap") &&
322 						strcmp(av, "nopoll")) |
323 					(nv && strcmp(nv, "noimap") &&
324 					 	strcmp(nv, "nopoll"));
325 				x = !(av || nv);
326 				if ((mb.mb_type == MB_FILE &&
327 						stat(mailname, &st) == 0 &&
328 						st.st_size > mailsize) ||
329 						(mb.mb_type == MB_IMAP &&
330 						imap_newmail(n) > x) ||
331 						(mb.mb_type == MB_MAILDIR &&
332 						n != 0)) {
333 					int odot = dot - &message[0];
334 					int odid = did_print_dot;
335 
336 					setfile(mailname, 1);
337 					if (mb.mb_type != MB_IMAP) {
338 						dot = &message[odot];
339 						did_print_dot = odid;
340 					}
341 				}
342 			}
343 			reset_on_stop = 1;
344 			if ((prompt = value("prompt")) == NULL)
345 				prompt = value("bsdcompat") ? "& " : "? ";
346 			printf("%s", prompt);
347 		}
348 		fflush(stdout);
349 		sreset();
350 		/*
351 		 * Read a line of commands from the current input
352 		 * and handle end of file specially.
353 		 */
354 		n = 0;
355 		for (;;) {
356 			n = readline_restart(input, &linebuf, &linesize, n);
357 			if (n < 0)
358 				break;
359 			if (n == 0 || linebuf[n - 1] != '\\')
360 				break;
361 			linebuf[n - 1] = ' ';
362 		}
363 		reset_on_stop = 0;
364 		if (n < 0) {
365 				/* eof */
366 			if (loading)
367 				break;
368 			if (sourcing) {
369 				unstack();
370 				continue;
371 			}
372 			if (value("interactive") != NULL &&
373 			    value("ignoreeof") != NULL &&
374 			    ++eofloop < 25) {
375 				printf(catgets(catd, CATSET, 89,
376 						"Use \"quit\" to quit.\n"));
377 				continue;
378 			}
379 			break;
380 		}
381 		eofloop = 0;
382 		inhook = 0;
383 		if (execute(linebuf, 0, n))
384 			break;
385 	}
386 	if (linebuf)
387 		free(linebuf);
388 }
389 
390 /*
391  * Execute a single command.
392  * Command functions return 0 for success, 1 for error, and -1
393  * for abort.  A 1 or -1 aborts a load or source.  A -1 aborts
394  * the interactive command loop.
395  * Contxt is non-zero if called while composing mail.
396  */
397 int
execute(char * linebuf,int contxt,size_t linesize)398 execute(char *linebuf, int contxt, size_t linesize)
399 {
400 	char *word;
401 	char *arglist[MAXARGC];
402 	const struct cmd *com = (struct cmd *)NULL;
403 	char *cp, *cp2;
404 	int c;
405 	int muvec[2];
406 	int e = 1;
407 
408 	/*
409 	 * Strip the white space away from the beginning
410 	 * of the command, then scan out a word, which
411 	 * consists of anything except digits and white space.
412 	 *
413 	 * Handle ! escapes differently to get the correct
414 	 * lexical conventions.
415 	 */
416 	word = ac_alloc(linesize + 1);
417 	for (cp = linebuf; whitechar(*cp & 0377); cp++);
418 	if (*cp == '!') {
419 		if (sourcing) {
420 			printf(catgets(catd, CATSET, 90,
421 					"Can't \"!\" while sourcing\n"));
422 			goto out;
423 		}
424 		shell(cp+1);
425 		ac_free(word);
426 		return(0);
427 	}
428 	if (*cp == '#') {
429 		ac_free(word);
430 		return 0;
431 	}
432 	cp2 = word;
433 	if (*cp != '|') {
434 		while (*cp && strchr(" \t0123456789$^.:/-+*'\",;(`", *cp)
435 				== NULL)
436 			*cp2++ = *cp++;
437 	} else
438 		*cp2++ = *cp++;
439 	*cp2 = '\0';
440 
441 	/*
442 	 * Look up the command; if not found, bitch.
443 	 * Normally, a blank command would map to the
444 	 * first command in the table; while sourcing,
445 	 * however, we ignore blank lines to eliminate
446 	 * confusion.
447 	 */
448 
449 	if (sourcing && *word == '\0') {
450 		ac_free(word);
451 		return(0);
452 	}
453 	com = lex(word);
454 	if (com == NULL) {
455 		printf(catgets(catd, CATSET, 91,
456 				"Unknown command: \"%s\"\n"), word);
457 		goto out;
458 	}
459 
460 	/*
461 	 * See if we should execute the command -- if a conditional
462 	 * we always execute it, otherwise, check the state of cond.
463 	 */
464 
465 	if ((com->c_argtype & F) == 0) {
466 		if ((cond == CRCV && !rcvmode) ||
467 				(cond == CSEND && rcvmode) ||
468 				(cond == CTERM && !is_a_tty[0]) ||
469 				(cond == CNONTERM && is_a_tty[0])) {
470 			ac_free(word);
471 			return(0);
472 		}
473 	}
474 
475 	/*
476 	 * Process the arguments to the command, depending
477 	 * on the type he expects.  Default to an error.
478 	 * If we are sourcing an interactive command, it's
479 	 * an error.
480 	 */
481 
482 	if (!rcvmode && (com->c_argtype & M) == 0) {
483 		printf(catgets(catd, CATSET, 92,
484 			"May not execute \"%s\" while sending\n"), com->c_name);
485 		goto out;
486 	}
487 	if (sourcing && com->c_argtype & I) {
488 		printf(catgets(catd, CATSET, 93,
489 			"May not execute \"%s\" while sourcing\n"),
490 				com->c_name);
491 		goto out;
492 	}
493 	if ((mb.mb_perm & MB_DELE) == 0 && com->c_argtype & W) {
494 		printf(catgets(catd, CATSET, 94,
495 		"May not execute \"%s\" -- message file is read only\n"),
496 		   com->c_name);
497 		goto out;
498 	}
499 	if (contxt && com->c_argtype & R) {
500 		printf(catgets(catd, CATSET, 95,
501 			"Cannot recursively invoke \"%s\"\n"), com->c_name);
502 		goto out;
503 	}
504 	if (mb.mb_type == MB_VOID && com->c_argtype & A) {
505 		printf(catgets(catd, CATSET, 257,
506 			"Cannot execute \"%s\" without active mailbox\n"),
507 				com->c_name);
508 		goto out;
509 	}
510 	switch (com->c_argtype & ~(F|P|I|M|T|W|R|A)) {
511 	case MSGLIST:
512 		/*
513 		 * A message list defaulting to nearest forward
514 		 * legal message.
515 		 */
516 		if (msgvec == 0) {
517 			printf(catgets(catd, CATSET, 96,
518 				"Illegal use of \"message list\"\n"));
519 			break;
520 		}
521 		if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
522 			break;
523 		if (c == 0) {
524 			if ((*msgvec = first(com->c_msgflag, com->c_msgmask))
525 					!= 0)
526 				msgvec[1] = 0;
527 		}
528 		if (*msgvec == 0) {
529 			if (!inhook)
530 				printf(catgets(catd, CATSET, 97,
531 					"No applicable messages\n"));
532 			break;
533 		}
534 		e = (*com->c_func)(msgvec);
535 		break;
536 
537 	case NDMLIST:
538 		/*
539 		 * A message list with no defaults, but no error
540 		 * if none exist.
541 		 */
542 		if (msgvec == 0) {
543 			printf(catgets(catd, CATSET, 98,
544 				"Illegal use of \"message list\"\n"));
545 			break;
546 		}
547 		if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
548 			break;
549 		e = (*com->c_func)(msgvec);
550 		break;
551 
552 	case STRLIST:
553 		/*
554 		 * Just the straight string, with
555 		 * leading blanks removed.
556 		 */
557 		while (whitechar(*cp & 0377))
558 			cp++;
559 		e = (*com->c_func)(cp);
560 		break;
561 
562 	case RAWLIST:
563 	case ECHOLIST:
564 		/*
565 		 * A vector of strings, in shell style.
566 		 */
567 		if ((c = getrawlist(cp, linesize, arglist,
568 				sizeof arglist / sizeof *arglist,
569 				(com->c_argtype&~(F|P|I|M|T|W|R|A))==ECHOLIST))
570 					< 0)
571 			break;
572 		if (c < com->c_minargs) {
573 			printf(catgets(catd, CATSET, 99,
574 				"%s requires at least %d arg(s)\n"),
575 				com->c_name, com->c_minargs);
576 			break;
577 		}
578 		if (c > com->c_maxargs) {
579 			printf(catgets(catd, CATSET, 100,
580 				"%s takes no more than %d arg(s)\n"),
581 				com->c_name, com->c_maxargs);
582 			break;
583 		}
584 		e = (*com->c_func)(arglist);
585 		break;
586 
587 	case NOLIST:
588 		/*
589 		 * Just the constant zero, for exiting,
590 		 * eg.
591 		 */
592 		e = (*com->c_func)(0);
593 		break;
594 
595 	default:
596 		panic(catgets(catd, CATSET, 101, "Unknown argtype"));
597 	}
598 
599 out:
600 	ac_free(word);
601 	/*
602 	 * Exit the current source file on
603 	 * error.
604 	 */
605 	if (e) {
606 		if (e < 0)
607 			return 1;
608 		if (loading)
609 			return 1;
610 		if (sourcing)
611 			unstack();
612 		return 0;
613 	}
614 	if (com == (struct cmd *)NULL)
615 		return(0);
616 	if (value("autoprint") != NULL && com->c_argtype & P)
617 		if (visible(dot)) {
618 			muvec[0] = dot - &message[0] + 1;
619 			muvec[1] = 0;
620 			type(muvec);
621 		}
622 	if (!sourcing && !inhook && (com->c_argtype & T) == 0)
623 		sawcom = 1;
624 	return(0);
625 }
626 
627 /*
628  * Set the size of the message vector used to construct argument
629  * lists to message list functions.
630  */
631 void
setmsize(int sz)632 setmsize(int sz)
633 {
634 
635 	if (msgvec != 0)
636 		free(msgvec);
637 	msgvec = (int *)scalloc((sz + 1), sizeof *msgvec);
638 }
639 
640 /*
641  * Find the correct command in the command table corresponding
642  * to the passed command "word"
643  */
644 
645 static const struct cmd *
lex(char * Word)646 lex(char *Word)
647 {
648 	extern const struct cmd cmdtab[];
649 	const struct cmd *cp;
650 
651 	for (cp = &cmdtab[0]; cp->c_name != NULL; cp++)
652 		if (is_prefix(Word, cp->c_name))
653 			return(cp);
654 	return(NULL);
655 }
656 
657 /*
658  * The following gets called on receipt of an interrupt.  This is
659  * to abort printout of a command, mainly.
660  * Dispatching here when command() is inactive crashes rcv.
661  * Close all open files except 0, 1, 2, and the temporary.
662  * Also, unstack all source files.
663  */
664 
665 static int	inithdr;		/* am printing startup headers */
666 
667 /*ARGSUSED*/
668 void
onintr(int s)669 onintr(int s)
670 {
671 	if (handlerstacktop != NULL) {
672 		handlerstacktop(s);
673 		return;
674 	}
675 	safe_signal(SIGINT, onintr);
676 	noreset = 0;
677 	if (!inithdr)
678 		sawcom++;
679 	inithdr = 0;
680 	while (sourcing)
681 		unstack();
682 
683 	close_all_files();
684 
685 	if (image >= 0) {
686 		close(image);
687 		image = -1;
688 	}
689 	if (interrupts != 1)
690 		fprintf(stderr, catgets(catd, CATSET, 102, "Interrupt\n"));
691 	safe_signal(SIGPIPE, oldpipe);
692 	reset(0);
693 }
694 
695 /*
696  * When we wake up after ^Z, reprint the prompt.
697  */
698 static void
stop(int s)699 stop(int s)
700 {
701 	sighandler_type old_action = safe_signal(s, SIG_DFL);
702 	sigset_t nset;
703 
704 	sigemptyset(&nset);
705 	sigaddset(&nset, s);
706 	sigprocmask(SIG_UNBLOCK, &nset, (sigset_t *)NULL);
707 	kill(0, s);
708 	sigprocmask(SIG_BLOCK, &nset, (sigset_t *)NULL);
709 	safe_signal(s, old_action);
710 	if (reset_on_stop) {
711 		reset_on_stop = 0;
712 		reset(0);
713 	}
714 }
715 
716 /*
717  * Branch here on hangup signal and simulate "exit".
718  */
719 /*ARGSUSED*/
720 static void
hangup(int s)721 hangup(int s)
722 {
723 
724 	/* nothing to do? */
725 	exit(1);
726 }
727 
728 /*
729  * Announce the presence of the current Mail version,
730  * give the message count, and print a header listing.
731  */
732 void
announce(int printheaders)733 announce(int printheaders)
734 {
735 	int vec[2], mdot;
736 
737 	mdot = newfileinfo();
738 	vec[0] = mdot;
739 	vec[1] = 0;
740 	dot = &message[mdot - 1];
741 	if (printheaders && msgCount > 0 && value("header") != NULL) {
742 		inithdr++;
743 		headers(vec);
744 		inithdr = 0;
745 	}
746 }
747 
748 /*
749  * Announce information about the file we are editing.
750  * Return a likely place to set dot.
751  */
752 int
newfileinfo(void)753 newfileinfo(void)
754 {
755 	struct message *mp;
756 	int u, n, mdot, d, s, hidden, killed, moved;
757 	char fname[PATHSIZE], zname[PATHSIZE], *ename;
758 
759 	if (mb.mb_type == MB_VOID)
760 		return 1;
761 	mdot = getmdot(0);
762 	s = d = hidden = killed = moved =0;
763 	for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) {
764 		if (mp->m_flag & MNEW)
765 			n++;
766 		if ((mp->m_flag & MREAD) == 0)
767 			u++;
768 		if ((mp->m_flag & (MDELETED|MSAVED)) == (MDELETED|MSAVED))
769 			moved++;
770 		if ((mp->m_flag & (MDELETED|MSAVED)) == MDELETED)
771 			d++;
772 		if ((mp->m_flag & (MDELETED|MSAVED)) == MSAVED)
773 			s++;
774 		if (mp->m_flag & MHIDDEN)
775 			hidden++;
776 		if (mp->m_flag & MKILL)
777 			killed++;
778 	}
779 	ename = mailname;
780 	if (getfold(fname, sizeof fname - 1) >= 0) {
781 		strcat(fname, "/");
782 		if (which_protocol(fname) != PROTO_IMAP &&
783 				strncmp(fname, mailname, strlen(fname)) == 0) {
784 			snprintf(zname, sizeof zname, "+%s",
785 					mailname + strlen(fname));
786 			ename = zname;
787 		}
788 	}
789 	printf(catgets(catd, CATSET, 103, "\"%s\": "), ename);
790 	if (msgCount == 1)
791 		printf(catgets(catd, CATSET, 104, "1 message"));
792 	else
793 		printf(catgets(catd, CATSET, 105, "%d messages"), msgCount);
794 	if (n > 0)
795 		printf(catgets(catd, CATSET, 106, " %d new"), n);
796 	if (u-n > 0)
797 		printf(catgets(catd, CATSET, 107, " %d unread"), u);
798 	if (d > 0)
799 		printf(catgets(catd, CATSET, 108, " %d deleted"), d);
800 	if (s > 0)
801 		printf(catgets(catd, CATSET, 109, " %d saved"), s);
802 	if (moved > 0)
803 		printf(catgets(catd, CATSET, 109, " %d moved"), moved);
804 	if (hidden > 0)
805 		printf(catgets(catd, CATSET, 109, " %d hidden"), hidden);
806 	if (killed > 0)
807 		printf(catgets(catd, CATSET, 109, " %d killed"), killed);
808 	if (mb.mb_type == MB_CACHE)
809 		printf(" [Disconnected]");
810 	else if (mb.mb_perm == 0)
811 		printf(catgets(catd, CATSET, 110, " [Read only]"));
812 	printf("\n");
813 	return(mdot);
814 }
815 
816 int
getmdot(int newmail)817 getmdot(int newmail)
818 {
819 	struct message	*mp;
820 	char	*cp;
821 	int	mdot;
822 	enum mflag	avoid = MHIDDEN|MKILL|MDELETED;
823 
824 	if (!newmail) {
825 		if (value("autothread"))
826 			thread(NULL);
827 		else if ((cp = value("autosort")) != NULL) {
828 			free(mb.mb_sorted);
829 			mb.mb_sorted = sstrdup(cp);
830 			sort(NULL);
831 		}
832 	}
833 	if (mb.mb_type == MB_VOID)
834 		return 1;
835 	if (newmail)
836 		for (mp = &message[0]; mp < &message[msgCount]; mp++)
837 			if ((mp->m_flag & (MNEWEST|avoid)) == MNEWEST)
838 				break;
839 	if (!newmail || mp >= &message[msgCount]) {
840 		for (mp = mb.mb_threaded ? threadroot : &message[0];
841 				mb.mb_threaded ?
842 					mp != NULL : mp < &message[msgCount];
843 				mb.mb_threaded ?
844 					mp = next_in_thread(mp) : mp++)
845 			if ((mp->m_flag & (MNEW|avoid)) == MNEW)
846 				break;
847 	}
848 	if (mb.mb_threaded ? mp == NULL : mp >= &message[msgCount])
849 		for (mp = mb.mb_threaded ? threadroot : &message[0];
850 				mb.mb_threaded ? mp != NULL:
851 					mp < &message[msgCount];
852 				mb.mb_threaded ? mp = next_in_thread(mp) : mp++)
853 			if (mp->m_flag & MFLAGGED)
854 				break;
855 	if (mb.mb_threaded ? mp == NULL : mp >= &message[msgCount])
856 		for (mp = mb.mb_threaded ? threadroot : &message[0];
857 				mb.mb_threaded ? mp != NULL:
858 					mp < &message[msgCount];
859 				mb.mb_threaded ? mp = next_in_thread(mp) : mp++)
860 			if ((mp->m_flag & (MREAD|avoid)) == 0)
861 				break;
862 	if (mb.mb_threaded ? mp != NULL : mp < &message[msgCount])
863 		mdot = mp - &message[0] + 1;
864 	else if (value("showlast")) {
865 		if (mb.mb_threaded) {
866 			for (mp = this_in_thread(threadroot, -1); mp;
867 					mp = prev_in_thread(mp))
868 				if ((mp->m_flag & avoid) == 0)
869 					break;
870 			mdot = mp ? mp - &message[0] + 1 : msgCount;
871 		} else {
872 			for (mp = &message[msgCount-1]; mp >= &message[0]; mp--)
873 				if ((mp->m_flag & avoid) == 0)
874 					break;
875 			mdot = mp >= &message[0] ? mp-&message[0]+1 : msgCount;
876 		}
877 	} else if (mb.mb_threaded) {
878 		for (mp = threadroot; mp; mp = next_in_thread(mp))
879 			if ((mp->m_flag & avoid) == 0)
880 				break;
881 		mdot = mp ? mp - &message[0] + 1 : 1;
882 	} else {
883 		for (mp = &message[0]; mp < &message[msgCount]; mp++)
884 			if ((mp->m_flag & avoid) == 0)
885 				break;
886 		mdot = mp < &message[msgCount] ? mp-&message[0]+1 : 1;
887 	}
888 	return mdot;
889 }
890 
891 /*
892  * Print the current version number.
893  */
894 
895 /*ARGSUSED*/
896 int
pversion(void * v)897 pversion(void *v)
898 {
899 	printf(catgets(catd, CATSET, 111, "Version %s\n"), version);
900 	return(0);
901 }
902 
903 /*
904  * Load a file of user definitions.
905  */
906 void
load(char * name)907 load(char *name)
908 {
909 	FILE *in, *oldin;
910 
911 	if ((in = Fopen(name, "r")) == NULL)
912 		return;
913 	oldin = input;
914 	input = in;
915 	loading = 1;
916 	sourcing = 1;
917 	commands();
918 	loading = 0;
919 	sourcing = 0;
920 	input = oldin;
921 	Fclose(in);
922 }
923 
924 void
initbox(const char * name)925 initbox(const char *name)
926 {
927 	char *tempMesg;
928 	int dummy;
929 
930 	if (mb.mb_type != MB_VOID) {
931 		strncpy(prevfile, mailname, PATHSIZE);
932 		prevfile[PATHSIZE-1]='\0';
933 	}
934 	if (name != mailname) {
935 		strncpy(mailname, name, PATHSIZE);
936 		mailname[PATHSIZE-1]='\0';
937 	}
938 	if ((mb.mb_otf = Ftemp(&tempMesg, "Rx", "w", 0600, 0)) == NULL) {
939 		perror(catgets(catd, CATSET, 87,
940 					"temporary mail message file"));
941 		exit(1);
942 	}
943 	fcntl(fileno(mb.mb_otf), F_SETFD, FD_CLOEXEC);
944 	if ((mb.mb_itf = safe_fopen(tempMesg, "r", &dummy)) == NULL) {
945 		perror(tempMesg);
946 		exit(1);
947 	}
948 	fcntl(fileno(mb.mb_itf), F_SETFD, FD_CLOEXEC);
949 	rm(tempMesg);
950 	Ftfree(&tempMesg);
951 	msgCount = 0;
952 	if (message) {
953 		free(message);
954 		message = NULL;
955 		msgspace = 0;
956 	}
957 	mb.mb_threaded = 0;
958 	free(mb.mb_sorted);
959 	mb.mb_sorted = NULL;
960 	mb.mb_flags = MB_NOFLAGS;
961 	prevdot = NULL;
962 	dot = NULL;
963 	did_print_dot = 0;
964 }
965