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