README: document *next* branch etc.
[s-mailx.git] / lex.c
blobae2afbdb688e4015c6676bfb1a9e871e5c2ac9b1
1 /*
2 * S-nail - 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 #include "rcv.h"
41 #include "extern.h"
42 #include <errno.h>
43 #include <sys/stat.h>
44 #include <fcntl.h>
45 #include <unistd.h>
48 * Mail -- a mail program
50 * Lexical processing of commands.
53 static char *prompt;
54 static sighandler_type oldpipe;
56 static const struct cmd *lex(char *Word);
57 static void stop(int s);
58 static void hangup(int s);
61 * Set up editing on the given file name.
62 * If the first character of name is %, we are considered to be
63 * editing the file, otherwise we are reading our mail which has
64 * signficance for mbox and so forth.
66 * newmail: Check for new mail in the current folder only.
68 int
69 setfile(char *name, int newmail)
71 FILE *ibuf;
72 int i, compressed = 0;
73 struct stat stb;
74 char isedit;
75 char *who = name[1] ? name + 1 : myname;
76 static int shudclob;
77 size_t offset;
78 int omsgCount = 0;
79 struct shortcut *sh;
80 struct flock flp;
82 isedit = *name != '%' && ((sh = get_shortcut(name)) == NULL ||
83 *sh->sh_long != '%');
84 if ((name = expand(name)) == NULL)
85 return -1;
87 switch (which_protocol(name)) {
88 case PROTO_FILE:
89 break;
90 case PROTO_MAILDIR:
91 return maildir_setfile(name, newmail, isedit);
92 case PROTO_POP3:
93 shudclob = 1;
94 return pop3_setfile(name, newmail, isedit);
95 case PROTO_IMAP:
96 shudclob = 1;
97 if (newmail) {
98 if (mb.mb_type == MB_CACHE)
99 return 1;
100 omsgCount = msgCount;
102 return imap_setfile(name, newmail, isedit);
103 case PROTO_UNKNOWN:
104 fprintf(stderr, catgets(catd, CATSET, 217,
105 "Cannot handle protocol: %s\n"), name);
106 return -1;
108 if ((ibuf = Zopen(name, "r", &compressed)) == NULL) {
109 if ((!isedit && errno == ENOENT) || newmail) {
110 if (newmail)
111 goto nonewmail;
112 goto nomail;
114 perror(name);
115 return(-1);
118 if (fstat(fileno(ibuf), &stb) < 0) {
119 Fclose(ibuf);
120 if (newmail)
121 goto nonewmail;
122 perror("fstat");
123 return (-1);
126 if (S_ISDIR(stb.st_mode)) {
127 Fclose(ibuf);
128 if (newmail)
129 goto nonewmail;
130 errno = EISDIR;
131 perror(name);
132 return (-1);
133 } else if (S_ISREG(stb.st_mode)) {
134 /*EMPTY*/
135 } else {
136 Fclose(ibuf);
137 if (newmail)
138 goto nonewmail;
139 errno = EINVAL;
140 perror(name);
141 return (-1);
145 * Looks like all will be well. We must now relinquish our
146 * hold on the current set of stuff. Must hold signals
147 * while we are reading the new file, else we will ruin
148 * the message[] data structure.
151 holdsigs();
152 if (shudclob && !newmail)
153 quit();
155 #ifdef HAVE_SOCKETS
156 if (!newmail && mb.mb_sock.s_fd >= 0)
157 sclose(&mb.mb_sock);
158 #endif /* HAVE_SOCKETS */
161 * Copy the messages into /tmp
162 * and set pointers.
165 flp.l_type = F_RDLCK;
166 flp.l_start = 0;
167 flp.l_whence = SEEK_SET;
168 if (!newmail) {
169 mb.mb_type = MB_FILE;
170 mb.mb_perm = Rflag ? 0 : MB_DELE|MB_EDIT;
171 mb.mb_compressed = compressed;
172 if (compressed) {
173 if (compressed & 0200)
174 mb.mb_perm = 0;
175 } else {
176 if ((i = open(name, O_WRONLY)) < 0)
177 mb.mb_perm = 0;
178 else
179 close(i);
181 if (shudclob) {
182 if (mb.mb_itf) {
183 fclose(mb.mb_itf);
184 mb.mb_itf = NULL;
186 if (mb.mb_otf) {
187 fclose(mb.mb_otf);
188 mb.mb_otf = NULL;
191 shudclob = 1;
192 edit = isedit;
193 initbox(name);
194 offset = 0;
195 flp.l_len = 0;
196 if (!edit && fcntl(fileno(ibuf), F_SETLKW, &flp) < 0) {
197 perror("Unable to lock mailbox");
198 Fclose(ibuf);
199 return -1;
201 } else /* newmail */{
202 fseek(mb.mb_otf, 0L, SEEK_END);
203 fseek(ibuf, mailsize, SEEK_SET);
204 offset = mailsize;
205 omsgCount = msgCount;
206 flp.l_len = offset;
207 if (!edit && fcntl(fileno(ibuf), F_SETLKW, &flp) < 0)
208 goto nonewmail;
210 mailsize = fsize(ibuf);
211 if (newmail && (size_t)mailsize <= offset) {
212 relsesigs();
213 goto nonewmail;
215 setptr(ibuf, offset);
216 setmsize(msgCount);
217 if (newmail && mb.mb_sorted) {
218 mb.mb_threaded = 0;
219 sort((void *)-1);
221 Fclose(ibuf);
222 relsesigs();
223 if (!newmail)
224 sawcom = 0;
225 if ((!edit || newmail) && msgCount == 0) {
226 nonewmail:
227 if (!newmail) {
228 if (value("emptystart") == NULL)
229 nomail: fprintf(stderr, catgets(catd, CATSET, 88,
230 "No mail for %s\n"), who);
232 return 1;
234 if (newmail) {
235 newmailinfo(omsgCount);
237 return(0);
241 int
242 newmailinfo(int omsgCount)
244 int mdot;
245 int i;
247 for (i = 0; i < omsgCount; i++)
248 message[i].m_flag &= ~MNEWEST;
249 if (msgCount > omsgCount) {
250 for (i = omsgCount; i < msgCount; i++)
251 message[i].m_flag |= MNEWEST;
252 printf(catgets(catd, CATSET, 158, "New mail has arrived.\n"));
253 if (msgCount - omsgCount == 1)
254 printf(catgets(catd, CATSET, 214,
255 "Loaded 1 new message\n"));
256 else
257 printf(catgets(catd, CATSET, 215,
258 "Loaded %d new messages\n"),
259 msgCount - omsgCount);
260 } else
261 printf("Loaded %d messages\n", msgCount);
262 callhook(mailname, 1);
263 mdot = getmdot(1);
264 if (value("header")) {
265 if (mb.mb_type == MB_IMAP)
266 imap_getheaders(omsgCount+1, msgCount);
267 while (++omsgCount <= msgCount)
268 if (visible(&message[omsgCount-1]))
269 printhead(omsgCount, stdout, 0);
271 return mdot;
274 static int *msgvec;
275 static int reset_on_stop; /* do a reset() if stopped */
278 * Interpret user commands one by one. If standard input is not a tty,
279 * print no prompt.
281 void
282 commands(void)
284 int eofloop = 0;
285 int n, x;
286 char *linebuf = NULL, *av, *nv;
287 size_t linesize = 0;
289 (void)&eofloop;
290 if (!sourcing) {
291 if (safe_signal(SIGINT, SIG_IGN) != SIG_IGN)
292 safe_signal(SIGINT, onintr);
293 if (safe_signal(SIGHUP, SIG_IGN) != SIG_IGN)
294 safe_signal(SIGHUP, hangup);
295 safe_signal(SIGTSTP, stop);
296 safe_signal(SIGTTOU, stop);
297 safe_signal(SIGTTIN, stop);
299 oldpipe = safe_signal(SIGPIPE, SIG_IGN);
300 safe_signal(SIGPIPE, oldpipe);
301 setexit();
302 for (;;) {
303 interrupts = 0;
304 handlerstacktop = NULL;
306 * Print the prompt, if needed. Clear out
307 * string space, and flush the output.
309 if (!sourcing && value("interactive") != NULL) {
310 av = (av = value("autoinc")) ? savestr(av) : NULL;
311 nv = (nv = value("newmail")) ? savestr(nv) : NULL;
312 if (is_a_tty[0] && (av != NULL || nv != NULL ||
313 mb.mb_type == MB_IMAP)) {
314 struct stat st;
316 n = (av && strcmp(av, "noimap") &&
317 strcmp(av, "nopoll")) |
318 (nv && strcmp(nv, "noimap") &&
319 strcmp(nv, "nopoll"));
320 x = !(av || nv);
321 if ((mb.mb_type == MB_FILE &&
322 stat(mailname, &st) == 0 &&
323 st.st_size > mailsize) ||
324 (mb.mb_type == MB_IMAP &&
325 imap_newmail(n) > x) ||
326 (mb.mb_type == MB_MAILDIR &&
327 n != 0)) {
328 int odot = dot - &message[0];
329 int odid = did_print_dot;
331 setfile(mailname, 1);
332 if (mb.mb_type != MB_IMAP) {
333 dot = &message[odot];
334 did_print_dot = odid;
338 reset_on_stop = 1;
339 if ((prompt = value("prompt")) == NULL)
340 prompt = value("bsdcompat") ? "& " : "? ";
341 printf("%s", prompt);
343 fflush(stdout);
344 sreset();
346 * Read a line of commands from the current input
347 * and handle end of file specially.
349 n = 0;
350 for (;;) {
351 n = readline_restart(input, &linebuf, &linesize, n);
352 if (n < 0)
353 break;
354 if (n == 0 || linebuf[n - 1] != '\\')
355 break;
356 linebuf[n - 1] = ' ';
358 reset_on_stop = 0;
359 if (n < 0) {
360 /* eof */
361 if (loading)
362 break;
363 if (sourcing) {
364 unstack();
365 continue;
367 if (value("interactive") != NULL &&
368 value("ignoreeof") != NULL &&
369 ++eofloop < 25) {
370 printf(catgets(catd, CATSET, 89,
371 "Use \"quit\" to quit.\n"));
372 continue;
374 break;
376 eofloop = 0;
377 inhook = 0;
378 if (execute(linebuf, 0, n))
379 break;
381 if (linebuf)
382 free(linebuf);
386 * Execute a single command.
387 * Command functions return 0 for success, 1 for error, and -1
388 * for abort. A 1 or -1 aborts a load or source. A -1 aborts
389 * the interactive command loop.
390 * Contxt is non-zero if called while composing mail.
393 execute(char *linebuf, int contxt, size_t linesize)
395 char *word;
396 char *arglist[MAXARGC];
397 const struct cmd *com = (struct cmd *)NULL;
398 char *cp, *cp2;
399 int c;
400 int muvec[2];
401 int e = 1;
404 * Strip the white space away from the beginning
405 * of the command, then scan out a word, which
406 * consists of anything except digits and white space.
408 * Handle ! escapes differently to get the correct
409 * lexical conventions.
411 word = ac_alloc(linesize + 1);
412 for (cp = linebuf; whitechar(*cp & 0377); cp++);
413 if (*cp == '!') {
414 if (sourcing) {
415 printf(catgets(catd, CATSET, 90,
416 "Can't \"!\" while sourcing\n"));
417 goto out;
419 shell(cp+1);
420 ac_free(word);
421 return(0);
423 if (*cp == '#') {
424 ac_free(word);
425 return 0;
427 cp2 = word;
428 if (*cp != '|') {
429 while (*cp && strchr(" \t0123456789$^.:/-+*'\",;(`", *cp)
430 == NULL)
431 *cp2++ = *cp++;
432 } else
433 *cp2++ = *cp++;
434 *cp2 = '\0';
437 * Look up the command; if not found, bitch.
438 * Normally, a blank command would map to the
439 * first command in the table; while sourcing,
440 * however, we ignore blank lines to eliminate
441 * confusion.
444 if (sourcing && *word == '\0') {
445 ac_free(word);
446 return(0);
448 com = lex(word);
449 if (com == NULL) {
450 printf(catgets(catd, CATSET, 91,
451 "Unknown command: \"%s\"\n"), word);
452 goto out;
456 * See if we should execute the command -- if a conditional
457 * we always execute it, otherwise, check the state of cond.
460 if ((com->c_argtype & F) == 0) {
461 if ((cond == CRCV && !rcvmode) ||
462 (cond == CSEND && rcvmode) ||
463 (cond == CTERM && !is_a_tty[0]) ||
464 (cond == CNONTERM && is_a_tty[0])) {
465 ac_free(word);
466 return(0);
471 * Process the arguments to the command, depending
472 * on the type he expects. Default to an error.
473 * If we are sourcing an interactive command, it's
474 * an error.
477 if (!rcvmode && (com->c_argtype & M) == 0) {
478 printf(catgets(catd, CATSET, 92,
479 "May not execute \"%s\" while sending\n"), com->c_name);
480 goto out;
482 if (sourcing && com->c_argtype & I) {
483 printf(catgets(catd, CATSET, 93,
484 "May not execute \"%s\" while sourcing\n"),
485 com->c_name);
486 goto out;
488 if ((mb.mb_perm & MB_DELE) == 0 && com->c_argtype & W) {
489 printf(catgets(catd, CATSET, 94,
490 "May not execute \"%s\" -- message file is read only\n"),
491 com->c_name);
492 goto out;
494 if (contxt && com->c_argtype & R) {
495 printf(catgets(catd, CATSET, 95,
496 "Cannot recursively invoke \"%s\"\n"), com->c_name);
497 goto out;
499 if (mb.mb_type == MB_VOID && com->c_argtype & A) {
500 printf(catgets(catd, CATSET, 257,
501 "Cannot execute \"%s\" without active mailbox\n"),
502 com->c_name);
503 goto out;
505 switch (com->c_argtype & ~(F|P|I|M|T|W|R|A)) {
506 case MSGLIST:
508 * A message list defaulting to nearest forward
509 * legal message.
511 if (msgvec == 0) {
512 printf(catgets(catd, CATSET, 96,
513 "Illegal use of \"message list\"\n"));
514 break;
516 if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
517 break;
518 if (c == 0) {
519 if ((*msgvec = first(com->c_msgflag, com->c_msgmask))
520 != 0)
521 msgvec[1] = 0;
523 if (*msgvec == 0) {
524 if (!inhook)
525 printf(catgets(catd, CATSET, 97,
526 "No applicable messages\n"));
527 break;
529 e = (*com->c_func)(msgvec);
530 break;
532 case NDMLIST:
534 * A message list with no defaults, but no error
535 * if none exist.
537 if (msgvec == 0) {
538 printf(catgets(catd, CATSET, 98,
539 "Illegal use of \"message list\"\n"));
540 break;
542 if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
543 break;
544 e = (*com->c_func)(msgvec);
545 break;
547 case STRLIST:
549 * Just the straight string, with
550 * leading blanks removed.
552 while (whitechar(*cp & 0377))
553 cp++;
554 e = (*com->c_func)(cp);
555 break;
557 case RAWLIST:
558 case ECHOLIST:
560 * A vector of strings, in shell style.
562 if ((c = getrawlist(cp, linesize, arglist,
563 sizeof arglist / sizeof *arglist,
564 (com->c_argtype&~(F|P|I|M|T|W|R|A))==ECHOLIST))
565 < 0)
566 break;
567 if (c < com->c_minargs) {
568 printf(catgets(catd, CATSET, 99,
569 "%s requires at least %d arg(s)\n"),
570 com->c_name, com->c_minargs);
571 break;
573 if (c > com->c_maxargs) {
574 printf(catgets(catd, CATSET, 100,
575 "%s takes no more than %d arg(s)\n"),
576 com->c_name, com->c_maxargs);
577 break;
579 e = (*com->c_func)(arglist);
580 break;
582 case NOLIST:
584 * Just the constant zero, for exiting,
585 * eg.
587 e = (*com->c_func)(0);
588 break;
590 default:
591 panic(catgets(catd, CATSET, 101, "Unknown argtype"));
594 out:
595 ac_free(word);
597 * Exit the current source file on
598 * error.
600 if (e) {
601 if (e < 0)
602 return 1;
603 if (loading)
604 return 1;
605 if (sourcing)
606 unstack();
607 return 0;
609 if (com == (struct cmd *)NULL)
610 return(0);
611 if (value("autoprint") != NULL && com->c_argtype & P)
612 if (visible(dot)) {
613 muvec[0] = dot - &message[0] + 1;
614 muvec[1] = 0;
615 type(muvec);
617 if (!sourcing && !inhook && (com->c_argtype & T) == 0)
618 sawcom = 1;
619 return(0);
623 * Set the size of the message vector used to construct argument
624 * lists to message list functions.
626 void
627 setmsize(int sz)
630 if (msgvec != 0)
631 free(msgvec);
632 msgvec = (int *)scalloc((sz + 1), sizeof *msgvec);
636 * Find the correct command in the command table corresponding
637 * to the passed command "word"
640 static const struct cmd *
641 lex(char *Word)
643 extern const struct cmd cmdtab[];
644 const struct cmd *cp;
646 for (cp = &cmdtab[0]; cp->c_name != NULL; cp++)
647 if (is_prefix(Word, cp->c_name))
648 return(cp);
649 return(NULL);
653 * The following gets called on receipt of an interrupt. This is
654 * to abort printout of a command, mainly.
655 * Dispatching here when command() is inactive crashes rcv.
656 * Close all open files except 0, 1, 2, and the temporary.
657 * Also, unstack all source files.
660 static int inithdr; /* am printing startup headers */
662 /*ARGSUSED*/
663 void
664 onintr(int s)
666 if (handlerstacktop != NULL) {
667 handlerstacktop(s);
668 return;
670 safe_signal(SIGINT, onintr);
671 noreset = 0;
672 if (!inithdr)
673 sawcom++;
674 inithdr = 0;
675 while (sourcing)
676 unstack();
678 close_all_files();
680 if (image >= 0) {
681 close(image);
682 image = -1;
684 if (interrupts != 1)
685 fprintf(stderr, catgets(catd, CATSET, 102, "Interrupt\n"));
686 safe_signal(SIGPIPE, oldpipe);
687 reset(0);
691 * When we wake up after ^Z, reprint the prompt.
693 static void
694 stop(int s)
696 sighandler_type old_action = safe_signal(s, SIG_DFL);
697 sigset_t nset;
699 sigemptyset(&nset);
700 sigaddset(&nset, s);
701 sigprocmask(SIG_UNBLOCK, &nset, (sigset_t *)NULL);
702 kill(0, s);
703 sigprocmask(SIG_BLOCK, &nset, (sigset_t *)NULL);
704 safe_signal(s, old_action);
705 if (reset_on_stop) {
706 reset_on_stop = 0;
707 reset(0);
712 * Branch here on hangup signal and simulate "exit".
714 /*ARGSUSED*/
715 static void
716 hangup(int s)
718 (void)s;
719 /* nothing to do? */
720 exit(1);
724 * Announce the presence of the current Mail version,
725 * give the message count, and print a header listing.
727 void
728 announce(int printheaders)
730 int vec[2], mdot;
732 mdot = newfileinfo();
733 vec[0] = mdot;
734 vec[1] = 0;
735 dot = &message[mdot - 1];
736 if (printheaders && msgCount > 0 && value("header") != NULL) {
737 inithdr++;
738 headers(vec);
739 inithdr = 0;
744 * Announce information about the file we are editing.
745 * Return a likely place to set dot.
747 int
748 newfileinfo(void)
750 struct message *mp;
751 int u, n, mdot, d, s, hidden, killed, moved;
752 char fname[PATHSIZE], zname[PATHSIZE], *ename;
754 if (mb.mb_type == MB_VOID)
755 return 1;
756 mdot = getmdot(0);
757 s = d = hidden = killed = moved =0;
758 for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) {
759 if (mp->m_flag & MNEW)
760 n++;
761 if ((mp->m_flag & MREAD) == 0)
762 u++;
763 if ((mp->m_flag & (MDELETED|MSAVED)) == (MDELETED|MSAVED))
764 moved++;
765 if ((mp->m_flag & (MDELETED|MSAVED)) == MDELETED)
766 d++;
767 if ((mp->m_flag & (MDELETED|MSAVED)) == MSAVED)
768 s++;
769 if (mp->m_flag & MHIDDEN)
770 hidden++;
771 if (mp->m_flag & MKILL)
772 killed++;
774 ename = mailname;
775 if (getfold(fname, sizeof fname - 1) >= 0) {
776 strcat(fname, "/");
777 if (which_protocol(fname) != PROTO_IMAP &&
778 strncmp(fname, mailname, strlen(fname)) == 0) {
779 snprintf(zname, sizeof zname, "+%s",
780 mailname + strlen(fname));
781 ename = zname;
784 printf(catgets(catd, CATSET, 103, "\"%s\": "), ename);
785 if (msgCount == 1)
786 printf(catgets(catd, CATSET, 104, "1 message"));
787 else
788 printf(catgets(catd, CATSET, 105, "%d messages"), msgCount);
789 if (n > 0)
790 printf(catgets(catd, CATSET, 106, " %d new"), n);
791 if (u-n > 0)
792 printf(catgets(catd, CATSET, 107, " %d unread"), u);
793 if (d > 0)
794 printf(catgets(catd, CATSET, 108, " %d deleted"), d);
795 if (s > 0)
796 printf(catgets(catd, CATSET, 109, " %d saved"), s);
797 if (moved > 0)
798 printf(catgets(catd, CATSET, 109, " %d moved"), moved);
799 if (hidden > 0)
800 printf(catgets(catd, CATSET, 109, " %d hidden"), hidden);
801 if (killed > 0)
802 printf(catgets(catd, CATSET, 109, " %d killed"), killed);
803 if (mb.mb_type == MB_CACHE)
804 printf(" [Disconnected]");
805 else if (mb.mb_perm == 0)
806 printf(catgets(catd, CATSET, 110, " [Read only]"));
807 printf("\n");
808 return(mdot);
811 int
812 getmdot(int newmail)
814 struct message *mp;
815 char *cp;
816 int mdot;
817 enum mflag avoid = MHIDDEN|MKILL|MDELETED;
819 if (!newmail) {
820 if (value("autothread"))
821 thread(NULL);
822 else if ((cp = value("autosort")) != NULL) {
823 free(mb.mb_sorted);
824 mb.mb_sorted = sstrdup(cp);
825 sort(NULL);
828 if (mb.mb_type == MB_VOID)
829 return 1;
830 if (newmail)
831 for (mp = &message[0]; mp < &message[msgCount]; mp++)
832 if ((mp->m_flag & (MNEWEST|avoid)) == MNEWEST)
833 break;
834 if (!newmail || mp >= &message[msgCount]) {
835 for (mp = mb.mb_threaded ? threadroot : &message[0];
836 mb.mb_threaded ?
837 mp != NULL : mp < &message[msgCount];
838 mb.mb_threaded ?
839 mp = next_in_thread(mp) : mp++)
840 if ((mp->m_flag & (MNEW|avoid)) == MNEW)
841 break;
843 if (mb.mb_threaded ? mp == NULL : mp >= &message[msgCount])
844 for (mp = mb.mb_threaded ? threadroot : &message[0];
845 mb.mb_threaded ? mp != NULL:
846 mp < &message[msgCount];
847 mb.mb_threaded ? mp = next_in_thread(mp) : mp++)
848 if (mp->m_flag & MFLAGGED)
849 break;
850 if (mb.mb_threaded ? mp == NULL : mp >= &message[msgCount])
851 for (mp = mb.mb_threaded ? threadroot : &message[0];
852 mb.mb_threaded ? mp != NULL:
853 mp < &message[msgCount];
854 mb.mb_threaded ? mp = next_in_thread(mp) : mp++)
855 if ((mp->m_flag & (MREAD|avoid)) == 0)
856 break;
857 if (mb.mb_threaded ? mp != NULL : mp < &message[msgCount])
858 mdot = mp - &message[0] + 1;
859 else if (value("showlast")) {
860 if (mb.mb_threaded) {
861 for (mp = this_in_thread(threadroot, -1); mp;
862 mp = prev_in_thread(mp))
863 if ((mp->m_flag & avoid) == 0)
864 break;
865 mdot = mp ? mp - &message[0] + 1 : msgCount;
866 } else {
867 for (mp = &message[msgCount-1]; mp >= &message[0]; mp--)
868 if ((mp->m_flag & avoid) == 0)
869 break;
870 mdot = mp >= &message[0] ? mp-&message[0]+1 : msgCount;
872 } else if (mb.mb_threaded) {
873 for (mp = threadroot; mp; mp = next_in_thread(mp))
874 if ((mp->m_flag & avoid) == 0)
875 break;
876 mdot = mp ? mp - &message[0] + 1 : 1;
877 } else {
878 for (mp = &message[0]; mp < &message[msgCount]; mp++)
879 if ((mp->m_flag & avoid) == 0)
880 break;
881 mdot = mp < &message[msgCount] ? mp-&message[0]+1 : 1;
883 return mdot;
887 * Print the current version number.
890 /*ARGSUSED*/
891 int
892 pversion(void *v)
894 (void)v;
895 printf(catgets(catd, CATSET, 111, "Version %s\n"), version);
896 return(0);
900 * Load a file of user definitions.
902 void
903 load(char *name)
905 FILE *in, *oldin;
907 if ((in = Fopen(name, "r")) == NULL)
908 return;
909 oldin = input;
910 input = in;
911 loading = 1;
912 sourcing = 1;
913 commands();
914 loading = 0;
915 sourcing = 0;
916 input = oldin;
917 Fclose(in);
920 void
921 initbox(const char *name)
923 char *tempMesg;
924 int dummy;
926 if (mb.mb_type != MB_VOID) {
927 strncpy(prevfile, mailname, PATHSIZE);
928 prevfile[PATHSIZE-1]='\0';
930 if (name != mailname) {
931 strncpy(mailname, name, PATHSIZE);
932 mailname[PATHSIZE-1]='\0';
934 if ((mb.mb_otf = Ftemp(&tempMesg, "Rx", "w", 0600, 0)) == NULL) {
935 perror(catgets(catd, CATSET, 87,
936 "temporary mail message file"));
937 exit(1);
939 fcntl(fileno(mb.mb_otf), F_SETFD, FD_CLOEXEC);
940 if ((mb.mb_itf = safe_fopen(tempMesg, "r", &dummy)) == NULL) {
941 perror(tempMesg);
942 exit(1);
944 fcntl(fileno(mb.mb_itf), F_SETFD, FD_CLOEXEC);
945 rm(tempMesg);
946 Ftfree(&tempMesg);
947 msgCount = 0;
948 if (message) {
949 free(message);
950 message = NULL;
951 msgspace = 0;
953 mb.mb_threaded = 0;
954 free(mb.mb_sorted);
955 mb.mb_sorted = NULL;
956 mb.mb_flags = MB_NOFLAGS;
957 prevdot = NULL;
958 dot = NULL;
959 did_print_dot = 0;