expand() filenames given to the ~@ tilde escape..
[s-mailx.git] / lex.c
bloba876ac9a9d6a8f2c6291fa849d7d7085ce367654
1 /*
2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
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.
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.
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.
39 #ifndef lint
40 #ifdef DOSCCS
41 static char sccsid[] = "@(#)lex.c 2.86 (gritter) 12/25/06";
42 #endif
43 #endif /* not lint */
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>
53 * Mail -- a mail program
55 * Lexical processing of commands.
58 static char *prompt;
59 static sighandler_type oldpipe;
61 static const struct cmd *lex(char *Word);
62 static void stop(int s);
63 static void hangup(int s);
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.
71 * newmail: Check for new mail in the current folder only.
73 int
74 setfile(char *name, int newmail)
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;
87 isedit = *name != '%' && ((sh = get_shortcut(name)) == NULL ||
88 *sh->sh_long != '%');
89 if ((name = expand(name)) == NULL)
90 return -1;
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;
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;
113 if ((ibuf = Zopen(name, "r", &compressed)) == NULL) {
114 if ((!isedit && errno == ENOENT) || newmail) {
115 if (newmail)
116 goto nonewmail;
117 goto nomail;
119 perror(name);
120 return(-1);
123 if (fstat(fileno(ibuf), &stb) < 0) {
124 Fclose(ibuf);
125 if (newmail)
126 goto nonewmail;
127 perror("fstat");
128 return (-1);
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);
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.
156 holdsigs();
157 if (shudclob && !newmail)
158 quit();
160 #ifdef HAVE_SOCKETS
161 if (!newmail && mb.mb_sock.s_fd >= 0)
162 sclose(&mb.mb_sock);
163 #endif /* HAVE_SOCKETS */
166 * Copy the messages into /tmp
167 * and set pointers.
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);
186 if (shudclob) {
187 if (mb.mb_itf) {
188 fclose(mb.mb_itf);
189 mb.mb_itf = NULL;
191 if (mb.mb_otf) {
192 fclose(mb.mb_otf);
193 mb.mb_otf = NULL;
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;
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;
215 mailsize = fsize(ibuf);
216 if (newmail && mailsize <= offset) {
217 relsesigs();
218 goto nonewmail;
220 setptr(ibuf, offset);
221 setmsize(msgCount);
222 if (newmail && mb.mb_sorted) {
223 mb.mb_threaded = 0;
224 sort((void *)-1);
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);
237 return 1;
239 if (newmail) {
240 newmailinfo(omsgCount);
242 return(0);
246 int
247 newmailinfo(int omsgCount)
249 int mdot;
250 int i;
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);
276 return mdot;
279 static int *msgvec;
280 static int reset_on_stop; /* do a reset() if stopped */
283 * Interpret user commands one by one. If standard input is not a tty,
284 * print no prompt.
286 void
287 commands(void)
289 int eofloop = 0;
290 int n, x;
291 char *linebuf = NULL, *av, *nv;
292 size_t linesize = 0;
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);
304 oldpipe = safe_signal(SIGPIPE, SIG_IGN);
305 safe_signal(SIGPIPE, oldpipe);
306 setexit();
307 for (;;) {
308 interrupts = 0;
309 handlerstacktop = NULL;
311 * Print the prompt, if needed. Clear out
312 * string space, and flush the output.
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;
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;
336 setfile(mailname, 1);
337 if (mb.mb_type != MB_IMAP) {
338 dot = &message[odot];
339 did_print_dot = odid;
343 reset_on_stop = 1;
344 if ((prompt = value("prompt")) == NULL)
345 prompt = value("bsdcompat") ? "& " : "? ";
346 printf("%s", prompt);
348 fflush(stdout);
349 sreset();
351 * Read a line of commands from the current input
352 * and handle end of file specially.
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] = ' ';
363 reset_on_stop = 0;
364 if (n < 0) {
365 /* eof */
366 if (loading)
367 break;
368 if (sourcing) {
369 unstack();
370 continue;
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;
379 break;
381 eofloop = 0;
382 inhook = 0;
383 if (execute(linebuf, 0, n))
384 break;
386 if (linebuf)
387 free(linebuf);
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.
398 execute(char *linebuf, int contxt, size_t linesize)
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;
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.
413 * Handle ! escapes differently to get the correct
414 * lexical conventions.
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;
424 shell(cp+1);
425 ac_free(word);
426 return(0);
428 if (*cp == '#') {
429 ac_free(word);
430 return 0;
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';
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.
449 if (sourcing && *word == '\0') {
450 ac_free(word);
451 return(0);
453 com = lex(word);
454 if (com == NULL) {
455 printf(catgets(catd, CATSET, 91,
456 "Unknown command: \"%s\"\n"), word);
457 goto out;
461 * See if we should execute the command -- if a conditional
462 * we always execute it, otherwise, check the state of cond.
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);
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.
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;
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;
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;
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;
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;
510 switch (com->c_argtype & ~(F|P|I|M|T|W|R|A)) {
511 case MSGLIST:
513 * A message list defaulting to nearest forward
514 * legal message.
516 if (msgvec == 0) {
517 printf(catgets(catd, CATSET, 96,
518 "Illegal use of \"message list\"\n"));
519 break;
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;
528 if (*msgvec == 0) {
529 if (!inhook)
530 printf(catgets(catd, CATSET, 97,
531 "No applicable messages\n"));
532 break;
534 e = (*com->c_func)(msgvec);
535 break;
537 case NDMLIST:
539 * A message list with no defaults, but no error
540 * if none exist.
542 if (msgvec == 0) {
543 printf(catgets(catd, CATSET, 98,
544 "Illegal use of \"message list\"\n"));
545 break;
547 if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
548 break;
549 e = (*com->c_func)(msgvec);
550 break;
552 case STRLIST:
554 * Just the straight string, with
555 * leading blanks removed.
557 while (whitechar(*cp & 0377))
558 cp++;
559 e = (*com->c_func)(cp);
560 break;
562 case RAWLIST:
563 case ECHOLIST:
565 * A vector of strings, in shell style.
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;
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;
584 e = (*com->c_func)(arglist);
585 break;
587 case NOLIST:
589 * Just the constant zero, for exiting,
590 * eg.
592 e = (*com->c_func)(0);
593 break;
595 default:
596 panic(catgets(catd, CATSET, 101, "Unknown argtype"));
599 out:
600 ac_free(word);
602 * Exit the current source file on
603 * error.
605 if (e) {
606 if (e < 0)
607 return 1;
608 if (loading)
609 return 1;
610 if (sourcing)
611 unstack();
612 return 0;
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);
622 if (!sourcing && !inhook && (com->c_argtype & T) == 0)
623 sawcom = 1;
624 return(0);
628 * Set the size of the message vector used to construct argument
629 * lists to message list functions.
631 void
632 setmsize(int sz)
635 if (msgvec != 0)
636 free(msgvec);
637 msgvec = (int *)scalloc((sz + 1), sizeof *msgvec);
641 * Find the correct command in the command table corresponding
642 * to the passed command "word"
645 static const struct cmd *
646 lex(char *Word)
648 extern const struct cmd cmdtab[];
649 const struct cmd *cp;
651 for (cp = &cmdtab[0]; cp->c_name != NULL; cp++)
652 if (is_prefix(Word, cp->c_name))
653 return(cp);
654 return(NULL);
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.
665 static int inithdr; /* am printing startup headers */
667 /*ARGSUSED*/
668 void
669 onintr(int s)
671 if (handlerstacktop != NULL) {
672 handlerstacktop(s);
673 return;
675 safe_signal(SIGINT, onintr);
676 noreset = 0;
677 if (!inithdr)
678 sawcom++;
679 inithdr = 0;
680 while (sourcing)
681 unstack();
683 close_all_files();
685 if (image >= 0) {
686 close(image);
687 image = -1;
689 if (interrupts != 1)
690 fprintf(stderr, catgets(catd, CATSET, 102, "Interrupt\n"));
691 safe_signal(SIGPIPE, oldpipe);
692 reset(0);
696 * When we wake up after ^Z, reprint the prompt.
698 static void
699 stop(int s)
701 sighandler_type old_action = safe_signal(s, SIG_DFL);
702 sigset_t nset;
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);
717 * Branch here on hangup signal and simulate "exit".
719 /*ARGSUSED*/
720 static void
721 hangup(int s)
724 /* nothing to do? */
725 exit(1);
729 * Announce the presence of the current Mail version,
730 * give the message count, and print a header listing.
732 void
733 announce(int printheaders)
735 int vec[2], mdot;
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;
749 * Announce information about the file we are editing.
750 * Return a likely place to set dot.
752 int
753 newfileinfo(void)
755 struct message *mp;
756 int u, n, mdot, d, s, hidden, killed, moved;
757 char fname[PATHSIZE], zname[PATHSIZE], *ename;
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++;
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;
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);
816 int
817 getmdot(int newmail)
819 struct message *mp;
820 char *cp;
821 int mdot;
822 enum mflag avoid = MHIDDEN|MKILL|MDELETED;
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);
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;
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;
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;
888 return mdot;
892 * Print the current version number.
895 /*ARGSUSED*/
896 int
897 pversion(void *v)
899 printf(catgets(catd, CATSET, 111, "Version %s\n"), version);
900 return(0);
904 * Load a file of user definitions.
906 void
907 load(char *name)
909 FILE *in, *oldin;
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);
924 void
925 initbox(const char *name)
927 char *tempMesg;
928 int dummy;
930 if (mb.mb_type != MB_VOID) {
931 strncpy(prevfile, mailname, PATHSIZE);
932 prevfile[PATHSIZE-1]='\0';
934 if (name != mailname) {
935 strncpy(mailname, name, PATHSIZE);
936 mailname[PATHSIZE-1]='\0';
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);
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);
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;
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;