tty.c, NCL: fix SIGSEGV, optimize buffer takeover, +..
[s-mailx.git] / lex.c
blob9b3ec1d5e47a946235150d015148f5adf3d3d496
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Lexical processing of commands.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2013 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
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"
42 #include <sys/stat.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <unistd.h>
47 #include "extern.h"
49 static int *_msgvec;
50 static int _reset_on_stop; /* do a reset() if stopped */
51 static sighandler_type _oldpipe;
53 /* Update mailname (if *name* != NULL) and displayname */
54 static void _update_mailname(char const *name);
55 #ifdef HAVE_MBLEN /* TODO unite __narrow_{pre,suf}fix() into one function! */
56 SINLINE size_t __narrow_prefix(char const *cp, size_t maxl);
57 SINLINE size_t __narrow_suffix(char const *cp, size_t cpl, size_t maxl);
58 #endif
60 static const struct cmd *lex(char *Word);
61 static void stop(int s);
62 static void hangup(int s);
64 #ifdef HAVE_MBLEN
65 SINLINE size_t
66 __narrow_prefix(char const *cp, size_t maxl)
68 int err;
69 size_t i, ok;
71 for (err = ok = i = 0; i < maxl;) {
72 int ml = mblen(cp, maxl - i);
73 if (ml < 0) { /* XXX _narrow_prefix(): mblen() error; action? */
74 (void)mblen(NULL, 0);
75 err = 1;
76 ml = 1;
77 } else {
78 if (! err)
79 ok = i;
80 err = 0;
81 if (ml == 0)
82 break;
84 cp += ml;
85 i += ml;
87 return ok;
90 SINLINE size_t
91 __narrow_suffix(char const *cp, size_t cpl, size_t maxl)
93 int err;
94 size_t i, ok;
96 for (err = ok = i = 0; cpl > maxl || err;) {
97 int ml = mblen(cp, cpl);
98 if (ml < 0) { /* XXX _narrow_suffix(): mblen() error; action? */
99 (void)mblen(NULL, 0);
100 err = 1;
101 ml = 1;
102 } else {
103 if (! err)
104 ok = i;
105 err = 0;
106 if (ml == 0)
107 break;
109 cp += ml;
110 i += ml;
111 cpl -= ml;
113 return ok;
115 #endif /* HAVE_MBLEN */
117 static void
118 _update_mailname(char const *name)
120 char tbuf[MAXPATHLEN], *mailp, *dispp;
121 size_t i, j;
123 /* Don't realpath(3) if it's only an update request */
124 if (name != NULL) {
125 #ifdef HAVE_REALPATH
126 enum protocol p = which_protocol(name);
127 if (p == PROTO_FILE || p == PROTO_MAILDIR) {
128 if (realpath(name, mailname) == NULL) {
129 fprintf(stderr, tr(151,
130 "Can't canonicalize `%s'\n"), name);
131 goto jleave;
133 } else
134 #endif
135 (void)n_strlcpy(mailname, name, MAXPATHLEN);
138 mailp = mailname;
139 dispp = displayname;
141 /* Don't display an absolute path but "+FOLDER" if under *folder* */
142 if (getfold(tbuf, sizeof tbuf)) {
143 i = strlen(tbuf);
144 if (i < sizeof(tbuf) - 1)
145 tbuf[i++] = '/';
146 if (strncmp(tbuf, mailp, i) == 0) {
147 mailp += i;
148 *dispp++ = '+';
152 /* We want to see the name of the folder .. on the screen */
153 i = strlen(mailp);
154 if (i < sizeof(displayname) - 1)
155 memcpy(dispp, mailp, i + 1);
156 else {
157 /* Avoid disrupting multibyte sequences (if possible) */
158 #ifndef HAVE_MBLEN
159 j = sizeof(displayname) / 3 - 1;
160 i -= sizeof(displayname) - (1/* + */ + 3) - j;
161 #else
162 j = __narrow_prefix(mailp, sizeof(displayname) / 3);
163 i = j + __narrow_suffix(mailp + j, i - j,
164 sizeof(displayname) - (1/* + */ + 3 + 1) - j);
165 #endif
166 (void)snprintf(dispp, sizeof(displayname), "%.*s...%s",
167 (int)j, mailp, mailp + i);
169 #ifdef HAVE_REALPATH
170 jleave: ;
171 #endif
175 * Set up editing on the given file name.
176 * If the first character of name is %, we are considered to be
177 * editing the file, otherwise we are reading our mail which has
178 * signficance for mbox and so forth.
180 * nmail: Check for new mail in the current folder only.
183 setfile(char const *name, int nmail)
185 FILE *ibuf;
186 int i, compressed = 0;
187 struct stat stb;
188 bool_t isedit;
189 char const *who = name[1] ? name + 1 : myname;
190 static int shudclob;
191 size_t offset;
192 int omsgCount = 0;
193 struct shortcut *sh;
194 struct flock flp;
196 isedit = (*name != '%' && ((sh = get_shortcut(name)) == NULL ||
197 *sh->sh_long != '%'));
198 if ((name = expand(name)) == NULL)
199 return (-1);
201 switch (which_protocol(name)) {
202 case PROTO_FILE:
203 break;
204 case PROTO_MAILDIR:
205 return (maildir_setfile(name, nmail, isedit));
206 #ifdef HAVE_POP3
207 case PROTO_POP3:
208 shudclob = 1;
209 return (pop3_setfile(name, nmail, isedit));
210 #endif
211 #ifdef HAVE_IMAP
212 case PROTO_IMAP:
213 shudclob = 1;
214 if (nmail) {
215 if (mb.mb_type == MB_CACHE)
216 return 1;
218 return imap_setfile(name, nmail, isedit);
219 #endif
220 default:
221 fprintf(stderr, tr(217, "Cannot handle protocol: %s\n"), name);
222 return (-1);
225 if ((ibuf = Zopen(name, "r", &compressed)) == NULL) {
226 if ((!isedit && errno == ENOENT) || nmail) {
227 if (nmail)
228 goto jnonmail;
229 goto nomail;
231 perror(name);
232 return(-1);
235 if (fstat(fileno(ibuf), &stb) < 0) {
236 Fclose(ibuf);
237 if (nmail)
238 goto jnonmail;
239 perror("fstat");
240 return (-1);
243 if (S_ISDIR(stb.st_mode)) {
244 Fclose(ibuf);
245 if (nmail)
246 goto jnonmail;
247 errno = EISDIR;
248 perror(name);
249 return (-1);
250 } else if (S_ISREG(stb.st_mode)) {
251 /*EMPTY*/
252 } else {
253 Fclose(ibuf);
254 if (nmail)
255 goto jnonmail;
256 errno = EINVAL;
257 perror(name);
258 return (-1);
262 * Looks like all will be well. We must now relinquish our
263 * hold on the current set of stuff. Must hold signals
264 * while we are reading the new file, else we will ruin
265 * the message[] data structure.
268 holdsigs(); /* TODO note on this one in quit.c:quit() */
269 if (shudclob && !nmail)
270 quit();
271 #ifdef HAVE_SOCKETS
272 if (!nmail && mb.mb_sock.s_fd >= 0)
273 sclose(&mb.mb_sock);
274 #endif
277 * Copy the messages into /tmp
278 * and set pointers.
281 flp.l_type = F_RDLCK;
282 flp.l_start = 0;
283 flp.l_whence = SEEK_SET;
284 if (!nmail) {
285 mb.mb_type = MB_FILE;
286 mb.mb_perm = (options & OPT_R_FLAG) ? 0 : MB_DELE|MB_EDIT;
287 mb.mb_compressed = compressed;
288 if (compressed) {
289 if (compressed & 0200)
290 mb.mb_perm = 0;
291 } else {
292 if ((i = open(name, O_WRONLY)) < 0)
293 mb.mb_perm = 0;
294 else
295 close(i);
297 if (shudclob) {
298 if (mb.mb_itf) {
299 fclose(mb.mb_itf);
300 mb.mb_itf = NULL;
302 if (mb.mb_otf) {
303 fclose(mb.mb_otf);
304 mb.mb_otf = NULL;
307 shudclob = 1;
308 edit = isedit;
309 initbox(name);
310 offset = 0;
311 flp.l_len = 0;
312 if (!edit && fcntl(fileno(ibuf), F_SETLKW, &flp) < 0) {
313 perror("Unable to lock mailbox");
314 Fclose(ibuf);
315 return -1;
317 } else /* nmail */{
318 fseek(mb.mb_otf, 0L, SEEK_END);
319 fseek(ibuf, mailsize, SEEK_SET);
320 offset = mailsize;
321 omsgCount = msgCount;
322 flp.l_len = offset;
323 if (!edit && fcntl(fileno(ibuf), F_SETLKW, &flp) < 0)
324 goto jnonmail;
326 mailsize = fsize(ibuf);
327 if (nmail && (size_t)mailsize <= offset) {
328 relsesigs();
329 goto jnonmail;
331 setptr(ibuf, offset);
332 setmsize(msgCount);
333 if (nmail && mb.mb_sorted) {
334 mb.mb_threaded = 0;
335 sort((void *)-1);
337 Fclose(ibuf);
338 relsesigs();
339 if (!nmail)
340 sawcom = FAL0;
341 if ((!edit || nmail) && msgCount == 0) {
342 jnonmail:
343 if (!nmail) {
344 if (value("emptystart") == NULL)
345 nomail: fprintf(stderr, catgets(catd, CATSET, 88,
346 "No mail for %s\n"), who);
348 return 1;
350 if (nmail) {
351 newmailinfo(omsgCount);
353 return(0);
357 newmailinfo(int omsgCount)
359 int mdot;
360 int i;
362 for (i = 0; i < omsgCount; i++)
363 message[i].m_flag &= ~MNEWEST;
364 if (msgCount > omsgCount) {
365 for (i = omsgCount; i < msgCount; i++)
366 message[i].m_flag |= MNEWEST;
367 printf(tr(158, "New mail has arrived.\n"));
368 if (msgCount - omsgCount == 1)
369 printf(tr(214, "Loaded 1 new message.\n"));
370 else
371 printf(tr(215, "Loaded %d new messages.\n"),
372 msgCount - omsgCount);
373 } else
374 printf(tr(224, "Loaded %d messages.\n"), msgCount);
375 callhook(mailname, 1);
376 mdot = getmdot(1);
377 if (value("header")) {
378 #ifdef HAVE_IMAP
379 if (mb.mb_type == MB_IMAP)
380 imap_getheaders(omsgCount+1, msgCount);
381 #endif
382 time_current_update(&time_current, FAL0);
383 while (++omsgCount <= msgCount)
384 if (visible(&message[omsgCount-1]))
385 printhead(omsgCount, stdout, 0);
387 return mdot;
391 * Interpret user commands one by one. If standard input is not a tty,
392 * print no prompt.
394 void
395 commands(void)
397 int eofloop = 0, n;
398 char *linebuf = NULL, *av, *nv;
399 size_t linesize = 0;
400 #ifdef HAVE_IMAP
401 int x;
402 #endif
404 if (!sourcing) {
405 if (safe_signal(SIGINT, SIG_IGN) != SIG_IGN)
406 safe_signal(SIGINT, onintr);
407 if (safe_signal(SIGHUP, SIG_IGN) != SIG_IGN)
408 safe_signal(SIGHUP, hangup);
409 /* TODO We do a lot of redundant signal handling, especially
410 * TODO with the line editor(s); try to merge this */
411 safe_signal(SIGTSTP, stop);
412 safe_signal(SIGTTOU, stop);
413 safe_signal(SIGTTIN, stop);
415 _oldpipe = safe_signal(SIGPIPE, SIG_IGN);
416 safe_signal(SIGPIPE, _oldpipe);
417 setexit();
419 for (;;) {
420 interrupts = 0;
421 handlerstacktop = NULL;
423 * Print the prompt, if needed. Clear out
424 * string space, and flush the output.
426 if (! sourcing && (options & OPT_INTERACTIVE)) {
427 av = (av = value("autoinc")) ? savestr(av) : NULL;
428 nv = (nv = value("newmail")) ? savestr(nv) : NULL;
429 if ((options & OPT_TTYIN) &&
430 (av != NULL || nv != NULL ||
431 mb.mb_type == MB_IMAP)) {
432 struct stat st;
434 n = (av && strcmp(av, "noimap") &&
435 strcmp(av, "nopoll")) |
436 (nv && strcmp(nv, "noimap") &&
437 strcmp(nv, "nopoll"));
438 #ifdef HAVE_IMAP
439 x = !(av || nv);
440 #endif
441 if ((mb.mb_type == MB_FILE &&
442 stat(mailname, &st) == 0 &&
443 st.st_size > mailsize) ||
444 #ifdef HAVE_IMAP
445 (mb.mb_type == MB_IMAP &&
446 imap_newmail(n) > x) ||
447 #endif
448 (mb.mb_type == MB_MAILDIR &&
449 n != 0)) {
450 int odot = dot - &message[0];
451 bool_t odid = did_print_dot;
453 setfile(mailname, 1);
454 if (mb.mb_type != MB_IMAP) {
455 dot = &message[odot];
456 did_print_dot = odid;
460 _reset_on_stop = 1;
461 exit_status = 0;
464 if (! sourcing) {
465 sreset();
466 /* TODO Note: this buffer may contain a password
467 * TODO We should redefine the code flow which has
468 * TODO to do that */
469 if ((nv = termios_state.ts_linebuf) != NULL) {
470 termios_state.ts_linebuf = NULL;
471 termios_state.ts_linesize = 0;
472 free(nv); /* TODO pool give-back */
474 /* TODO Due to expand-on-tab of our line editor the
475 * TODO buffer may grow */
476 if (linesize > LINESIZE * 3) {
477 free(linebuf); /* TODO pool! but what? */
478 linebuf = NULL;
479 linesize = 0;
484 * Read a line of commands from the current input
485 * and handle end of file specially.
487 n = readline_input(LNED_LF_ESC | LNED_HIST_ADD, NULL,
488 &linebuf, &linesize);
489 _reset_on_stop = 0;
490 if (n < 0) {
491 /* eof */
492 if (loading)
493 break;
494 if (sourcing) {
495 unstack();
496 continue;
498 if ((options & OPT_INTERACTIVE) &&
499 value("ignoreeof") && ++eofloop < 25) {
500 printf(tr(89, "Use `quit' to quit.\n"));
501 continue;
503 break;
506 eofloop = 0;
507 inhook = 0;
508 if (execute(linebuf, 0, n))
509 break;
510 if (exit_status != EXIT_OK && (options & OPT_BATCH_FLAG) &&
511 boption("batch-exit-on-error"))
512 break;
515 if (linebuf != NULL)
516 free(linebuf);
517 if (sourcing)
518 sreset();
522 * Execute a single command.
523 * Command functions return 0 for success, 1 for error, and -1
524 * for abort. A 1 or -1 aborts a load or source. A -1 aborts
525 * the interactive command loop.
526 * Contxt is non-zero if called while composing mail.
529 execute(char *linebuf, int contxt, size_t linesize)
531 char _wordbuf[2], *arglist[MAXARGC], *cp, *word;
532 struct cmd const *com = (struct cmd*)NULL;
533 int muvec[2], c, e = 1;
535 /* Strip the white space away from the beginning of the command */
536 for (cp = linebuf; whitechar(*cp); ++cp)
539 /* Ignore comments */
540 if (*cp == '#')
541 goto jleave0;
543 /* Handle ! differently to get the correct lexical conventions */
544 if (*cp == '!') {
545 if (sourcing) {
546 fprintf(stderr, tr(90, "Can't `!' while sourcing\n"));
547 goto jleave;
549 shell(++cp);
550 goto jleave0;
553 /* Isolate the actual command; since it may not necessarily be
554 * separated from the arguments (as in `p1') we need to duplicate it to
555 * be able to create a NUL terminated version.
556 * We must be aware of special one letter commands here */
557 arglist[0] = cp;
558 switch (*cp) {
559 case '|':
560 case '~':
561 ++cp;
562 /* FALLTHRU */
563 case '\0':
564 break;
565 default:
566 while (*cp && strchr(" \t0123456789$^.:/-+*'\",;(`",
567 *cp) == NULL)
568 ++cp;
569 break;
571 c = (int)(cp - arglist[0]);
572 linesize -= c;
573 word = (c <= 1) ? _wordbuf : salloc(c + 1);
574 memcpy(word, arglist[0], c);
575 word[c] = '\0';
577 /* Look up the command; if not found, bitch.
578 * Normally, a blank command would map to the first command in the
579 * table; while sourcing, however, we ignore blank lines to eliminate
580 * confusion */
581 if (sourcing && *word == '\0')
582 goto jleave0;
584 com = lex(word);
585 if (com == NULL || com->c_func == &ccmdnotsupp) {
586 fprintf(stderr, tr(91, "Unknown command: `%s'\n"), word);
587 if (com != NULL) {
588 ccmdnotsupp(NULL);
589 com = NULL;
591 goto jleave;
595 * See if we should execute the command -- if a conditional
596 * we always execute it, otherwise, check the state of cond.
598 if ((com->c_argtype & F) == 0) {
599 if ((cond == CRCV && (options & OPT_SENDMODE)) ||
600 (cond == CSEND && ! (options & OPT_SENDMODE)) ||
601 (cond == CTERM && ! (options & OPT_TTYIN)) ||
602 (cond == CNONTERM && (options & OPT_TTYIN)))
603 goto jleave0;
607 * Process the arguments to the command, depending
608 * on the type he expects. Default to an error.
609 * If we are sourcing an interactive command, it's
610 * an error.
612 if ((options & OPT_SENDMODE) && (com->c_argtype & M) == 0) {
613 fprintf(stderr, tr(92,
614 "May not execute `%s' while sending\n"),
615 com->c_name);
616 goto jleave;
618 if (sourcing && com->c_argtype & I) {
619 fprintf(stderr, tr(93,
620 "May not execute `%s' while sourcing\n"),
621 com->c_name);
622 goto jleave;
624 if ((mb.mb_perm & MB_DELE) == 0 && com->c_argtype & W) {
625 fprintf(stderr, tr(94, "May not execute `%s' -- "
626 "message file is read only\n"),
627 com->c_name);
628 goto jleave;
630 if (contxt && com->c_argtype & R) {
631 fprintf(stderr, tr(95,
632 "Cannot recursively invoke `%s'\n"), com->c_name);
633 goto jleave;
635 if (mb.mb_type == MB_VOID && com->c_argtype & A) {
636 fprintf(stderr, tr(257,
637 "Cannot execute `%s' without active mailbox\n"),
638 com->c_name);
639 goto jleave;
642 switch (com->c_argtype & ~(F|P|I|M|T|W|R|A)) {
643 case MSGLIST:
644 /* Message list defaulting to nearest forward legal message */
645 if (_msgvec == 0)
646 goto je96;
647 if ((c = getmsglist(cp, _msgvec, com->c_msgflag)) < 0)
648 break;
649 if (c == 0) {
650 *_msgvec = first(com->c_msgflag, com->c_msgmask);
651 if (*_msgvec != 0)
652 _msgvec[1] = 0;
654 if (*_msgvec == 0) {
655 if (! inhook)
656 printf(tr(97, "No applicable messages\n"));
657 break;
659 e = (*com->c_func)(_msgvec);
660 break;
662 case NDMLIST:
663 /* Message list with no defaults, but no error if none exist */
664 if (_msgvec == 0) {
665 je96:
666 fprintf(stderr, tr(96,
667 "Illegal use of `message list'\n"));
668 break;
670 if ((c = getmsglist(cp, _msgvec, com->c_msgflag)) < 0)
671 break;
672 e = (*com->c_func)(_msgvec);
673 break;
675 case STRLIST:
676 /* Just the straight string, with leading blanks removed */
677 while (whitechar(*cp))
678 cp++;
679 e = (*com->c_func)(cp);
680 break;
682 case RAWLIST:
683 case ECHOLIST:
684 /* A vector of strings, in shell style */
685 if ((c = getrawlist(cp, linesize, arglist,
686 sizeof arglist / sizeof *arglist,
687 (com->c_argtype&~(F|P|I|M|T|W|R|A)) == ECHOLIST)
688 ) < 0)
689 break;
690 if (c < com->c_minargs) {
691 fprintf(stderr, tr(99,
692 "`%s' requires at least %d arg(s)\n"),
693 com->c_name, com->c_minargs);
694 break;
696 if (c > com->c_maxargs) {
697 fprintf(stderr, tr(100,
698 "`%s' takes no more than %d arg(s)\n"),
699 com->c_name, com->c_maxargs);
700 break;
702 e = (*com->c_func)(arglist);
703 break;
705 case NOLIST:
706 /* Just the constant zero, for exiting, eg. */
707 e = (*com->c_func)(0);
708 break;
710 default:
711 panic(tr(101, "Unknown argument type"));
714 jleave:
715 /* Exit the current source file on error */
716 if ((exec_last_comm_error = (e != 0))) {
717 if (e < 0)
718 return 1;
719 if (loading)
720 return 1;
721 if (sourcing)
722 unstack();
723 return 0;
725 if (com == (struct cmd*)NULL )
726 return 0;
727 if (boption("autoprint") && com->c_argtype & P)
728 if (visible(dot)) {
729 muvec[0] = dot - &message[0] + 1;
730 muvec[1] = 0;
731 type(muvec);
733 if (! sourcing && ! inhook && (com->c_argtype & T) == 0)
734 sawcom = TRU1;
735 jleave0:
736 return 0;
740 * Set the size of the message vector used to construct argument
741 * lists to message list functions.
743 void
744 setmsize(int sz)
747 if (_msgvec != 0)
748 free(_msgvec);
749 _msgvec = (int*)scalloc(sz + 1, sizeof *_msgvec);
753 * Find the correct command in the command table corresponding
754 * to the passed command "word"
757 static const struct cmd *
758 lex(char *Word)
760 extern const struct cmd cmdtab[];
761 const struct cmd *cp;
763 for (cp = &cmdtab[0]; cp->c_name != NULL; cp++)
764 if (is_prefix(Word, cp->c_name))
765 return(cp);
766 return(NULL);
770 * The following gets called on receipt of an interrupt. This is
771 * to abort printout of a command, mainly.
772 * Dispatching here when command() is inactive crashes rcv.
773 * Close all open files except 0, 1, 2, and the temporary.
774 * Also, unstack all source files.
777 static int inithdr; /* am printing startup headers */
779 /*ARGSUSED*/
780 void
781 onintr(int s)
783 if (handlerstacktop != NULL) {
784 handlerstacktop(s);
785 return;
787 safe_signal(SIGINT, onintr);
788 noreset = 0;
789 if (!inithdr)
790 sawcom = TRU1;
791 inithdr = 0;
792 while (sourcing)
793 unstack();
795 close_all_files();
797 if (image >= 0) {
798 close(image);
799 image = -1;
801 if (interrupts != 1)
802 fprintf(stderr, catgets(catd, CATSET, 102, "Interrupt\n"));
803 safe_signal(SIGPIPE, _oldpipe);
804 reset(0);
808 * When we wake up after ^Z, reprint the prompt.
810 static void
811 stop(int s)
813 sighandler_type old_action = safe_signal(s, SIG_DFL);
814 sigset_t nset;
816 sigemptyset(&nset);
817 sigaddset(&nset, s);
818 sigprocmask(SIG_UNBLOCK, &nset, (sigset_t *)NULL);
819 kill(0, s);
820 sigprocmask(SIG_BLOCK, &nset, (sigset_t *)NULL);
821 safe_signal(s, old_action);
822 if (_reset_on_stop) {
823 _reset_on_stop = 0;
824 reset(0);
829 * Branch here on hangup signal and simulate "exit".
831 /*ARGSUSED*/
832 static void
833 hangup(int s)
835 (void)s;
836 /* nothing to do? */
837 exit(1);
841 * Announce the presence of the current Mail version,
842 * give the message count, and print a header listing.
844 void
845 announce(int printheaders)
847 int vec[2], mdot;
849 mdot = newfileinfo();
850 vec[0] = mdot;
851 vec[1] = 0;
852 dot = &message[mdot - 1];
853 if (printheaders && msgCount > 0 && value("header") != NULL) {
854 inithdr++;
855 headers(vec);
856 inithdr = 0;
861 * Announce information about the file we are editing.
862 * Return a likely place to set dot.
864 int
865 newfileinfo(void)
867 struct message *mp;
868 int u, n, mdot, d, s, hidden, moved;
870 if (mb.mb_type == MB_VOID)
871 return 1;
872 mdot = getmdot(0);
873 s = d = hidden = moved =0;
874 for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) {
875 if (mp->m_flag & MNEW)
876 n++;
877 if ((mp->m_flag & MREAD) == 0)
878 u++;
879 if ((mp->m_flag & (MDELETED|MSAVED)) == (MDELETED|MSAVED))
880 moved++;
881 if ((mp->m_flag & (MDELETED|MSAVED)) == MDELETED)
882 d++;
883 if ((mp->m_flag & (MDELETED|MSAVED)) == MSAVED)
884 s++;
885 if (mp->m_flag & MHIDDEN)
886 hidden++;
888 _update_mailname(NULL);
889 printf(tr(103, "\"%s\": "), displayname);
890 if (msgCount == 1)
891 printf(tr(104, "1 message"));
892 else
893 printf(tr(105, "%d messages"), msgCount);
894 if (n > 0)
895 printf(tr(106, " %d new"), n);
896 if (u-n > 0)
897 printf(tr(107, " %d unread"), u);
898 if (d > 0)
899 printf(tr(108, " %d deleted"), d);
900 if (s > 0)
901 printf(tr(109, " %d saved"), s);
902 if (moved > 0)
903 printf(tr(136, " %d moved"), moved);
904 if (hidden > 0)
905 printf(tr(139, " %d hidden"), hidden);
906 if (mb.mb_type == MB_CACHE)
907 printf(" [Disconnected]");
908 else if (mb.mb_perm == 0)
909 printf(tr(110, " [Read only]"));
910 printf("\n");
911 return(mdot);
914 int
915 getmdot(int nmail)
917 struct message *mp;
918 char *cp;
919 int mdot;
920 enum mflag avoid = MHIDDEN|MDELETED;
922 if (!nmail) {
923 if (value("autothread"))
924 thread(NULL);
925 else if ((cp = value("autosort")) != NULL) {
926 free(mb.mb_sorted);
927 mb.mb_sorted = sstrdup(cp);
928 sort(NULL);
931 if (mb.mb_type == MB_VOID)
932 return 1;
933 if (nmail)
934 for (mp = &message[0]; mp < &message[msgCount]; mp++)
935 if ((mp->m_flag & (MNEWEST|avoid)) == MNEWEST)
936 break;
937 if (!nmail || mp >= &message[msgCount]) {
938 for (mp = mb.mb_threaded ? threadroot : &message[0];
939 mb.mb_threaded ?
940 mp != NULL : mp < &message[msgCount];
941 mb.mb_threaded ?
942 mp = next_in_thread(mp) : mp++)
943 if ((mp->m_flag & (MNEW|avoid)) == MNEW)
944 break;
946 if (mb.mb_threaded ? mp == NULL : mp >= &message[msgCount])
947 for (mp = mb.mb_threaded ? threadroot : &message[0];
948 mb.mb_threaded ? mp != NULL:
949 mp < &message[msgCount];
950 mb.mb_threaded ? mp = next_in_thread(mp) : mp++)
951 if (mp->m_flag & MFLAGGED)
952 break;
953 if (mb.mb_threaded ? mp == NULL : mp >= &message[msgCount])
954 for (mp = mb.mb_threaded ? threadroot : &message[0];
955 mb.mb_threaded ? mp != NULL:
956 mp < &message[msgCount];
957 mb.mb_threaded ? mp = next_in_thread(mp) : mp++)
958 if ((mp->m_flag & (MREAD|avoid)) == 0)
959 break;
960 if (mb.mb_threaded ? mp != NULL : mp < &message[msgCount])
961 mdot = mp - &message[0] + 1;
962 else if (value("showlast")) {
963 if (mb.mb_threaded) {
964 for (mp = this_in_thread(threadroot, -1); mp;
965 mp = prev_in_thread(mp))
966 if ((mp->m_flag & avoid) == 0)
967 break;
968 mdot = mp ? mp - &message[0] + 1 : msgCount;
969 } else {
970 for (mp = &message[msgCount-1]; mp >= &message[0]; mp--)
971 if ((mp->m_flag & avoid) == 0)
972 break;
973 mdot = mp >= &message[0] ? mp-&message[0]+1 : msgCount;
975 } else if (mb.mb_threaded) {
976 for (mp = threadroot; mp; mp = next_in_thread(mp))
977 if ((mp->m_flag & avoid) == 0)
978 break;
979 mdot = mp ? mp - &message[0] + 1 : 1;
980 } else {
981 for (mp = &message[0]; mp < &message[msgCount]; mp++)
982 if ((mp->m_flag & avoid) == 0)
983 break;
984 mdot = mp < &message[msgCount] ? mp-&message[0]+1 : 1;
986 return mdot;
990 * Print the current version number.
993 /*ARGSUSED*/
994 int
995 pversion(void *v)
997 (void)v;
998 printf(catgets(catd, CATSET, 111, "Version %s\n"), version);
999 return(0);
1002 void
1003 initbox(const char *name)
1005 char *tempMesg;
1006 int dummy;
1008 if (mb.mb_type != MB_VOID)
1009 (void)n_strlcpy(prevfile, mailname, MAXPATHLEN);
1010 _update_mailname(name != mailname ? name : NULL);
1011 if ((mb.mb_otf = Ftemp(&tempMesg, "tmpbox", "w", 0600, 0)) == NULL) {
1012 perror(tr(87, "temporary mail message file"));
1013 exit(1);
1015 (void)fcntl(fileno(mb.mb_otf), F_SETFD, FD_CLOEXEC);
1016 if ((mb.mb_itf = safe_fopen(tempMesg, "r", &dummy)) == NULL) {
1017 perror(tr(87, "temporary mail message file"));
1018 exit(1);
1020 (void)fcntl(fileno(mb.mb_itf), F_SETFD, FD_CLOEXEC);
1021 rm(tempMesg);
1022 Ftfree(&tempMesg);
1023 msgCount = 0;
1024 if (message) {
1025 free(message);
1026 message = NULL;
1027 msgspace = 0;
1029 mb.mb_threaded = 0;
1030 if (mb.mb_sorted != NULL) {
1031 free(mb.mb_sorted);
1032 mb.mb_sorted = NULL;
1034 #ifdef HAVE_IMAP
1035 mb.mb_flags = MB_NOFLAGS;
1036 #endif
1037 prevdot = NULL;
1038 dot = NULL;
1039 did_print_dot = FAL0;