tty.c: GCC 4.9 warnings
[s-mailx.git] / main.c
blobdd4967e5fb7402d33f93c851aa9dbdf9a06301cc
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Startup -- interface with user.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2014 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.
41 * Most strcpy/sprintf functions have been changed to strncpy/snprintf to
42 * correct several buffer overruns (at least one ot them was exploitable).
43 * Sat Jun 20 04:58:09 CEST 1998 Alvaro Martinez Echevarria <alvaro@lander.es>
44 * ---
45 * Note: We set egid to realgid ... and only if we need the egid we will
46 * switch back temporary. Nevertheless, I do not like seg faults.
47 * Werner Fink, <werner@suse.de>
50 #ifndef HAVE_AMALGAMATION
51 # define _MAIN_SOURCE
52 # include "nail.h"
53 #endif
55 #include <sys/ioctl.h>
57 #include <fcntl.h>
58 #include <pwd.h>
60 #ifdef HAVE_NL_LANGINFO
61 # include <langinfo.h>
62 #endif
63 #ifdef HAVE_SETLOCALE
64 # include <locale.h>
65 #endif
67 #include "version.h"
69 /* Verify that our size_t ZFMT format string has the correct type size */
70 __ZFMT_CTA();
72 struct a_arg {
73 struct a_arg *aa_next;
74 char *aa_file;
77 /* (extern, but not with amalgamation, so define here) */
78 VL char const weekday_names[7 + 1][4] = {
79 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", ""
81 VL char const month_names[12 + 1][4] = {
82 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
83 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", ""
85 VL char const uagent[] = UAGENT;
86 VL char const version[] = VERSION;
87 /*VL char const features[]; The "feature string" comes from config.h */
88 VL uc_it const class_char[] = {
89 /* 000 nul 001 soh 002 stx 003 etx 004 eot 005 enq 006 ack 007 bel */
90 C_CNTRL, C_CNTRL, C_CNTRL, C_CNTRL, C_CNTRL, C_CNTRL, C_CNTRL, C_CNTRL,
91 /* 010 bs 011 ht 012 nl 013 vt 014 np 015 cr 016 so 017 si */
92 C_CNTRL, C_BLANK, C_WHITE, C_SPACE, C_SPACE, C_SPACE, C_CNTRL, C_CNTRL,
93 /* 020 dle 021 dc1 022 dc2 023 dc3 024 dc4 025 nak 026 syn 027 etb */
94 C_CNTRL, C_CNTRL, C_CNTRL, C_CNTRL, C_CNTRL, C_CNTRL, C_CNTRL, C_CNTRL,
95 /* 030 can 031 em 032 sub 033 esc 034 fs 035 gs 036 rs 037 us */
96 C_CNTRL, C_CNTRL, C_CNTRL, C_CNTRL, C_CNTRL, C_CNTRL, C_CNTRL, C_CNTRL,
97 /* 040 sp 041 ! 042 " 043 # 044 $ 045 % 046 & 047 ' */
98 C_BLANK, C_PUNCT, C_PUNCT, C_PUNCT, C_PUNCT, C_PUNCT, C_PUNCT, C_PUNCT,
99 /* 050 ( 051 ) 052 * 053 + 054 , 055 - 056 . 057 / */
100 C_PUNCT, C_PUNCT, C_PUNCT, C_PUNCT, C_PUNCT, C_PUNCT, C_PUNCT, C_PUNCT,
101 /* 060 0 061 1 062 2 063 3 064 4 065 5 066 6 067 7 */
102 C_OCTAL, C_OCTAL, C_OCTAL, C_OCTAL, C_OCTAL, C_OCTAL, C_OCTAL, C_OCTAL,
103 /* 070 8 071 9 072 : 073 ; 074 < 075 = 076 > 077 ? */
104 C_DIGIT, C_DIGIT, C_PUNCT, C_PUNCT, C_PUNCT, C_PUNCT, C_PUNCT, C_PUNCT,
105 /* 100 @ 101 A 102 B 103 C 104 D 105 E 106 F 107 G */
106 C_PUNCT, C_UPPER, C_UPPER, C_UPPER, C_UPPER, C_UPPER, C_UPPER, C_UPPER,
107 /* 110 H 111 I 112 J 113 K 114 L 115 M 116 N 117 O */
108 C_UPPER, C_UPPER, C_UPPER, C_UPPER, C_UPPER, C_UPPER, C_UPPER, C_UPPER,
109 /* 120 P 121 Q 122 R 123 S 124 T 125 U 126 V 127 W */
110 C_UPPER, C_UPPER, C_UPPER, C_UPPER, C_UPPER, C_UPPER, C_UPPER, C_UPPER,
111 /* 130 X 131 Y 132 Z 133 [ 134 \ 135 ] 136 ^ 137 _ */
112 C_UPPER, C_UPPER, C_UPPER, C_PUNCT, C_PUNCT, C_PUNCT, C_PUNCT, C_PUNCT,
113 /* 140 ` 141 a 142 b 143 c 144 d 145 e 146 f 147 g */
114 C_PUNCT, C_LOWER, C_LOWER, C_LOWER, C_LOWER, C_LOWER, C_LOWER, C_LOWER,
115 /* 150 h 151 i 152 j 153 k 154 l 155 m 156 n 157 o */
116 C_LOWER, C_LOWER, C_LOWER, C_LOWER, C_LOWER, C_LOWER, C_LOWER, C_LOWER,
117 /* 160 p 161 q 162 r 163 s 164 t 165 u 166 v 167 w */
118 C_LOWER, C_LOWER, C_LOWER, C_LOWER, C_LOWER, C_LOWER, C_LOWER, C_LOWER,
119 /* 170 x 171 y 172 z 173 { 174 | 175 } 176 ~ 177 del */
120 C_LOWER, C_LOWER, C_LOWER, C_PUNCT, C_PUNCT, C_PUNCT, C_PUNCT, C_CNTRL
123 /* getopt(3) fallback implementation */
124 #ifdef HAVE_GETOPT
125 # define _oarg optarg
126 # define _oind optind
127 /*# define _oerr opterr*/
128 # define _oopt optopt
129 #else
130 static char *_oarg;
131 static int _oind, /*_oerr,*/ _oopt;
132 #endif
134 /* getopt(3) fallback implementation */
135 #ifdef HAVE_GETOPT
136 # define _getopt getopt
137 #else
138 static int _getopt(int argc, char * const argv[], char const *optstring);
139 #endif
141 /* Perform basic startup initialization */
142 static void _startup(void);
144 /* Grow a char** */
145 static size_t _grow_cpp(char const ***cpp, size_t newsize, size_t oldcnt);
147 /* Initialize *tempdir*, *myname*, *homedir* */
148 static void _setup_vars(void);
150 /* We're in an interactive session - compute what the screen size for printing
151 * headers etc. should be; notify tty upon resize if *is_sighdl* is not 0.
152 * We use the following algorithm for the height:
153 * If baud rate < 1200, use 9
154 * If baud rate = 1200, use 14
155 * If baud rate > 1200, use 24 or ws_row
156 * Width is either 80 or ws_col */
157 static void _setscreensize(int is_sighdl);
159 /* Ok, we are reading mail. Decide whether we are editing a mailbox or reading
160 * the system mailbox, and open up the right stuff */
161 static int _rcv_mode(char const *folder, char const *Larg);
163 /* Interrupt printing of the headers */
164 static void _hdrstop(int signo);
166 #ifndef HAVE_GETOPT
167 static int
168 _getopt(int argc, char * const argv[], char const *optstring)
170 static char const *lastp;
172 int rv = -1, colon;
173 char const *curp;
174 NYD_ENTER;
176 if ((colon = (optstring[0] == ':')))
177 ++optstring;
178 if (lastp != NULL) {
179 curp = lastp;
180 lastp = 0;
181 } else {
182 if (_oind >= argc || argv[_oind] == 0 || argv[_oind][0] != '-' ||
183 argv[_oind][1] == '\0')
184 goto jleave;
185 if (argv[_oind][1] == '-' && argv[_oind][2] == '\0') {
186 ++_oind;
187 goto jleave;
189 curp = &argv[_oind][1];
192 _oopt = curp[0];
193 while (optstring[0] != '\0') {
194 if (optstring[0] == ':' || optstring[0] != _oopt) {
195 ++optstring;
196 continue;
198 if (optstring[1] == ':') {
199 if (curp[1] != '\0') {
200 _oarg = UNCONST(curp + 1);
201 ++_oind;
202 } else {
203 if ((_oind += 2) > argc) {
204 if (!colon /*&& _oerr*/) {
205 fprintf(stderr,
206 tr(79, "%s: option requires an argument -- %c\n"),
207 argv[0], (char)_oopt);
209 rv = (colon ? ':' : '?');
210 goto jleave;
212 _oarg = argv[_oind - 1];
214 } else {
215 if (curp[1] != '\0')
216 lastp = curp + 1;
217 else
218 ++_oind;
219 _oarg = NULL;
221 rv = _oopt;
222 goto jleave;
225 if (!colon /*&& opterr*/)
226 fprintf(stderr, tr(78, "%s: illegal option -- %c\n"), argv[0], _oopt);
227 if (curp[1] != '\0')
228 lastp = curp + 1;
229 else
230 ++_oind;
231 _oarg = 0;
232 rv = '?';
233 jleave:
234 NYD_LEAVE;
235 return rv;
237 #endif /* !HAVE_GETOPT */
239 static void
240 _startup(void)
242 char *cp;
243 NYD_ENTER;
245 /* Absolutely the first thing we do is save our egid and set it to the rgid,
246 * so that we can safely run setgid. We use the sgid (saved set-gid) to
247 * allow ourselves to revert to the egid if we want (temporarily) to become
248 * privileged XXX (s-nail-)*dotlock(-program)* [maybe forked<->Unix IPC?] */
249 effectivegid = getegid();
250 realgid = getgid();
251 if (setgid(realgid) == -1) {
252 perror("setgid");
253 exit(1);
256 image = -1;
257 dflpipe = SIG_DFL;
258 #ifndef HAVE_GETOPT
259 _oind = /*_oerr =*/ 1;
260 #endif
262 if ((cp = strrchr(progname, '/')) != NULL)
263 progname = ++cp;
265 /* Set up a reasonable environment */
267 #ifdef HAVE_DEBUG
268 safe_signal(SIGABRT, &_nyd_oncrash);
269 # ifdef SIGBUS
270 safe_signal(SIGBUS, &_nyd_oncrash);
271 # endif
272 safe_signal(SIGFPE, &_nyd_oncrash);
273 safe_signal(SIGILL, &_nyd_oncrash);
274 safe_signal(SIGSEGV, &_nyd_oncrash);
275 #endif
276 command_manager_start();
278 if (isatty(STDIN_FILENO)) /* TODO should be isatty(0) && isatty(2)?? */
279 options |= OPT_TTYIN | OPT_INTERACTIVE;
280 if (isatty(STDOUT_FILENO))
281 options |= OPT_TTYOUT;
282 if (IS_TTY_SESSION())
283 safe_signal(SIGPIPE, dflpipe = SIG_IGN);
285 /* Define defaults for internal variables, based on POSIX 2008/Cor 1-2013 */
286 /* noallnet */
287 /* noappend */
288 ok_bset(asksub, TRU1);
289 /* noaskbcc */
290 /* noaskcc */
291 /* noautoprint */
292 /* nobang */
293 /* nocmd */
294 /* nocrt */
295 /* nodebug */
296 /* nodot */
297 /* ok_vset(escape, ESCAPE *"~"*); TODO non-compliant */
298 /* noflipr */
299 /* nofolder */
300 ok_bset(header, TRU1);
301 /* nohold */
302 /* noignore */
303 /* noignoreeof */
304 /* nokeep */
305 /* nokeepsave */
306 /* nometoo */
307 /* noonehop -- Note: we ignore this one */
308 /* nooutfolder */
309 /* nopage */
310 ok_vset(prompt, "\\& "); /* POSIX "? " unless *bsdcompat*, then "& " */
311 /* noquiet */
312 /* norecord */
313 ok_bset(save, TRU1);
314 /* nosendwait */
315 /* noshowto */
316 /* nosign */
317 /* noSign */
318 /* ok_vset(toplines, "5"); XXX somewhat hmm */
320 #ifdef HAVE_SETLOCALE
321 setlocale(LC_ALL, "");
322 mb_cur_max = MB_CUR_MAX;
323 # ifdef HAVE_NL_LANGINFO
324 if (ok_vlook(ttycharset) == NULL && (cp = nl_langinfo(CODESET)) != NULL)
325 ok_vset(ttycharset, cp);
326 # endif
328 # ifdef HAVE_C90AMEND1
329 if (mb_cur_max > 1) {
330 wchar_t wc;
331 if (mbtowc(&wc, "\303\266", 2) == 2 && wc == 0xF6 &&
332 mbtowc(&wc, "\342\202\254", 3) == 3 && wc == 0x20AC)
333 utf8 = 1;
334 /* Reset possibly messed up state; luckily this also gives us an
335 * indication wether the encoding has locking shift state sequences */
336 /* TODO temporary - use option bits! */
337 enc_has_state = mbtowc(&wc, NULL, mb_cur_max);
339 # endif
340 #else
341 mb_cur_max = 1;
342 #endif
344 #ifdef HAVE_CATGETS
345 # ifdef NL_CAT_LOCALE
346 catd = catopen(CATNAME, NL_CAT_LOCALE);
347 # else
348 catd = catopen(CATNAME, 0);
349 # endif
350 #endif
352 #ifdef HAVE_ICONV
353 iconvd = (iconv_t)-1;
354 #endif
355 NYD_LEAVE;
358 static size_t
359 _grow_cpp(char const ***cpp, size_t newsize, size_t oldcnt)
361 /* Before spreserve(): use our string pool instead of LibC heap */
362 char const **newcpp;
363 NYD_ENTER;
365 newcpp = salloc(sizeof(char*) * newsize);
367 if (oldcnt > 0)
368 memcpy(newcpp, *cpp, oldcnt * sizeof(char*));
369 *cpp = newcpp;
370 NYD_LEAVE;
371 return newsize;
374 static void
375 _setup_vars(void)
377 /* Before spreserve(): use our string pool instead of LibC heap */
378 /* XXX further check paths? */
379 char const *cp;
380 uid_t uid;
381 struct passwd *pwuid, *pw;
382 NYD_ENTER;
384 tempdir = ((cp = getenv("TMPDIR")) != NULL) ? savestr(cp) : TMPDIR_FALLBACK;
386 cp = (myname == NULL) ? getenv("USER") : myname;
387 uid = getuid();
388 if ((pwuid = getpwuid(uid)) == NULL)
389 panic(tr(201, "Cannot associate a name with uid %lu"), (ul_it)uid);
390 if (cp == NULL)
391 myname = pwuid->pw_name;
392 else if ((pw = getpwnam(cp)) == NULL)
393 panic(tr(236, "`%s' is not a user of this system"), cp);
394 else {
395 myname = pw->pw_name;
396 if (pw->pw_uid != uid)
397 options |= OPT_u_FLAG;
399 myname = savestr(myname);
400 /* XXX myfullname = pw->pw_gecos[OPTIONAL!] -> GUT THAT; TODO pw_shell */
402 if ((cp = getenv("HOME")) == NULL)
403 cp = "."; /* XXX User and Login objects; Login: pw->pw_dir */
404 homedir = savestr(cp);
405 NYD_LEAVE;
408 static void
409 _setscreensize(int is_sighdl)
411 struct termios tbuf;
412 #ifdef TIOCGWINSZ
413 struct winsize ws;
414 #elif defined TIOCGSIZE
415 struct ttysize ts;
416 #endif
417 NYD_ENTER;
419 scrnheight = realscreenheight = scrnwidth = 0;
421 /* (Also) POSIX: LINES and COLUMNS always override. Adjust this
422 * a little bit to be able to honour resizes during our lifetime and
423 * only honour it upon first run; abuse *is_sighdl* as an indicator */
424 if (!is_sighdl) {
425 char *cp;
426 long i;
428 if ((cp = getenv("LINES")) != NULL && (i = strtol(cp, NULL, 10)) > 0)
429 scrnheight = realscreenheight = (int)i;
430 if ((cp = getenv("COLUMNS")) != NULL && (i = strtol(cp, NULL, 10)) > 0)
431 scrnwidth = (int)i;
433 if (scrnwidth != 0 && scrnheight != 0)
434 goto jleave;
437 #ifdef TIOCGWINSZ
438 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1)
439 ws.ws_col = ws.ws_row = 0;
440 #elif defined TIOCGSIZE
441 if (ioctl(STDOUT_FILENO, TIOCGSIZE, &ws) == -1)
442 ts.ts_lines = ts.ts_cols = 0;
443 #endif
445 if (scrnheight == 0) {
446 speed_t ospeed = ((tcgetattr(STDOUT_FILENO, &tbuf) == -1)
447 ? B9600 : cfgetospeed(&tbuf));
449 if (ospeed < B1200)
450 scrnheight = 9;
451 else if (ospeed == B1200)
452 scrnheight = 14;
453 #ifdef TIOCGWINSZ
454 else if (ws.ws_row != 0)
455 scrnheight = ws.ws_row;
456 #elif defined TIOCGSIZE
457 else if (ts.ts_lines != 0)
458 scrnheight = ts.ts_lines;
459 #endif
460 else
461 scrnheight = 24;
463 #if defined TIOCGWINSZ || defined TIOCGSIZE
464 if (0 ==
465 # ifdef TIOCGWINSZ
466 (realscreenheight = ws.ws_row)
467 # else
468 (realscreenheight = ts.ts_lines)
469 # endif
471 realscreenheight = 24;
472 #endif
475 if (scrnwidth == 0 && 0 ==
476 #ifdef TIOCGWINSZ
477 (scrnwidth = ws.ws_col)
478 #elif defined TIOCGSIZE
479 (scrnwidth = ts.ts_cols)
480 #endif
482 scrnwidth = 80;
484 jleave:
485 #ifdef SIGWINCH
486 if (is_sighdl && IS_TTY_SESSION())
487 tty_signal(SIGWINCH);
488 #endif
489 NYD_LEAVE;
492 static sigjmp_buf __hdrjmp; /* XXX */
494 static int
495 _rcv_mode(char const *folder, char const *Larg)
497 char *cp;
498 int i;
499 sighandler_type prevint;
500 NYD_ENTER;
502 if (folder == NULL)
503 folder = "%";
504 else if (*folder == '@') {
505 /* This must be treated specially to make possible invocation like
506 * -A imap -f @mailbox */
507 if ((cp = ok_vlook(folder)) != NULL && which_protocol(cp) == PROTO_IMAP)
508 n_strlcpy(mailname, cp, PATH_MAX);
511 i = setfile(folder, 0);
512 if (i < 0) {
513 exit_status = EXIT_ERR; /* error already reported */
514 goto jleave;
516 if (options & OPT_EXISTONLY) {
517 exit_status = i;
518 goto jleave;
520 if (options & (OPT_HEADERSONLY | OPT_HEADERLIST)) {
521 if ((exit_status = i) == EXIT_OK)
522 print_header_summary(Larg);
523 goto jleave;
526 callhook(mailname, 0);
527 if (i > 0 && !ok_blook(emptystart)) {
528 exit_status = EXIT_ERR;
529 goto jleave;
532 if (sigsetjmp(__hdrjmp, 1) == 0) {
533 if ((prevint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
534 safe_signal(SIGINT, _hdrstop);
535 if (!(options & OPT_N_FLAG)) {
536 if (!ok_blook(quiet))
537 printf(tr(140, "%s version %s. Type ? for help.\n"),
538 (ok_blook(bsdcompat) ? "Mail" : uagent), version);
539 announce(1);
540 fflush(stdout);
542 safe_signal(SIGINT, prevint);
545 /* Enter the command loop */
546 if (options & OPT_INTERACTIVE)
547 tty_init();
548 commands();
549 if (options & OPT_INTERACTIVE)
550 tty_destroy();
552 if (mb.mb_type == MB_FILE || mb.mb_type == MB_MAILDIR) {
553 safe_signal(SIGHUP, SIG_IGN);
554 safe_signal(SIGINT, SIG_IGN);
555 safe_signal(SIGQUIT, SIG_IGN);
557 save_mbox_for_possible_quitstuff();
558 quit();
559 jleave:
560 NYD_LEAVE;
561 return exit_status;
564 static void
565 _hdrstop(int signo)
567 NYD_X; /* Signal handler */
568 UNUSED(signo);
570 fflush(stdout);
571 fprintf(stderr, tr(141, "\nInterrupt\n"));
572 siglongjmp(__hdrjmp, 1);
576 main(int argc, char *argv[])
578 static char const optstr[] = "A:a:Bb:c:DdEeFfHiL:NnO:q:Rr:S:s:tu:Vv~#",
579 usagestr[] =
580 "Synopsis:\n"
581 " %s [-BDdEFintv~] [-A acc] [-a attachment] "
582 "[-b bcc-addr] [-c cc-addr]\n"
583 "\t [-O mtaopt [-O mtaopt-arg]] [-q file] [-r from-addr] "
584 "[-S var[=value]]\n"
585 "\t [-s subject] to-addr...\n"
586 " %s [-BDdEeHiNnRv~#] [-A acc] [-L spec-list] [-S var[=value]] "
587 "-f [file]\n"
588 " %s [-BDdEeHiNnRv~#] [-A acc] [-L spec-list] [-S var[=value]] "
589 "[-u user]\n";
591 struct a_arg *a_head = NULL, *a_curr = /* silence CC */ NULL;
592 struct name *to = NULL, *cc = NULL, *bcc = NULL;
593 struct attachment *attach = NULL;
594 char *cp = NULL, *subject = NULL, *qf = NULL, *Aarg = NULL, *Larg = NULL;
595 char const *okey, **oargs = NULL, *folder = NULL;
596 size_t oargs_size = 0, oargs_count = 0, smopts_size = 0;
597 int i;
598 NYD_ENTER;
601 * Start our lengthy setup
604 starting =
605 var_clear_allow_undefined = TRU1;
607 progname = argv[0];
608 _startup();
610 /* Command line parsing */
611 while ((i = _getopt(argc, argv, optstr)) >= 0) {
612 switch (i) {
613 case 'A':
614 /* Execute an account command later on */
615 Aarg = _oarg;
616 break;
617 case 'a':
618 { struct a_arg *nap = ac_alloc(sizeof(struct a_arg));
619 if (a_head == NULL)
620 a_head = nap;
621 else
622 a_curr->aa_next = nap;
623 nap->aa_next = NULL;
624 nap->aa_file = _oarg;
625 a_curr = nap;
627 options |= OPT_SENDMODE;
628 break;
629 case 'B':
630 /* Make 0/1 line buffered */
631 setvbuf(stdin, NULL, _IOLBF, 0);
632 setvbuf(stdout, NULL, _IOLBF, 0);
633 break;
634 case 'b':
635 /* Get Blind Carbon Copy Recipient list */
636 bcc = cat(bcc, checkaddrs(lextract(_oarg, GBCC | GFULL)));
637 options |= OPT_SENDMODE;
638 break;
639 case 'c':
640 /* Get Carbon Copy Recipient list */
641 cc = cat(cc, checkaddrs(lextract(_oarg, GCC | GFULL)));
642 options |= OPT_SENDMODE;
643 break;
644 case 'D':
645 #ifdef HAVE_IMAP
646 okey = "disconnected";
647 goto joarg;
648 #else
649 break;
650 #endif
651 case 'd':
652 okey = "debug";
653 #ifdef HAVE_DEBUG
654 ok_bset(debug, TRU1);
655 #endif
656 goto joarg;
657 case 'E':
658 okey = "skipemptybody";
659 goto joarg;
660 case 'e':
661 options |= OPT_EXISTONLY;
662 break;
663 case 'F':
664 options |= OPT_F_FLAG | OPT_SENDMODE;
665 break;
666 case 'f':
667 /* User is specifying file to "edit" with Mail, as opposed to reading
668 * system mailbox. If no argument is given, we read his mbox file.
669 * Check for remaining arguments later */
670 folder = "&";
671 break;
672 case 'H':
673 options |= OPT_HEADERSONLY;
674 break;
675 case 'i':
676 /* Ignore interrupts */
677 okey = "ignore";
678 goto joarg;
679 case 'L':
680 Larg = _oarg;
681 if (*Larg == '"' || *Larg == '\'') { /* TODO list.c:listspec_check() */
682 size_t j = strlen(++Larg);
683 if (j > 0)
684 Larg[j - 1] = '\0';
686 options |= OPT_HEADERLIST;
687 break;
688 case 'N':
689 /* Avoid initial header printing */
690 okey = "noheader";
691 goto joarg;
692 case 'n':
693 /* Don't source "unspecified system start-up file" */
694 options |= OPT_NOSRC;
695 break;
696 case 'O':
697 /* Additional options to pass-through to MTA */
698 if (smopts_count == (size_t)smopts_size)
699 smopts_size = _grow_cpp(&smopts, smopts_size + 8, smopts_count);
700 smopts[smopts_count++] = skin(_oarg);
701 break;
702 case 'q':
703 /* Quote file TODO drop? -Q with real quote?? what ? */
704 if (*_oarg != '-')
705 qf = _oarg;
706 options |= OPT_SENDMODE;
707 break;
708 case 'R':
709 /* Open folders read-only */
710 options |= OPT_R_FLAG;
711 break;
712 case 'r':
713 /* Set From address. */
714 options |= OPT_r_FLAG;
715 if ((option_r_arg = _oarg)[0] != '\0') {
716 struct name *fa = nalloc(_oarg, GFULL);
718 if (is_addr_invalid(fa, 1) || is_fileorpipe_addr(fa)) {
719 fprintf(stderr, tr(271, "Invalid address argument with -r\n"));
720 goto jusage;
722 option_r_arg = fa->n_name;
723 /* TODO -r options is set in smopts, but may
724 * TODO be overwritten by setting from= in
725 * TODO an interactive session!
726 * TODO Maybe disable setting of from?
727 * TODO Warn user? Update manual!! */
728 okey = savecat("from=", fa->n_fullname);
729 goto joarg;
730 /* ..and fa goes even though it is ready :/ */
732 break;
733 case 'S':
734 /* Set variable. We need to do this twice, since the user surely
735 * wants the setting to take effect immediately, but also doesn't want
736 * it to be overwritten from within resource files */
737 { char *a[2];
738 okey = a[0] = _oarg;
739 a[1] = NULL;
740 c_set(a);
742 joarg:
743 if (oargs_count == oargs_size)
744 oargs_size = _grow_cpp(&oargs, oargs_size + 8, oargs_count);
745 oargs[oargs_count++] = okey;
746 break;
747 case 's':
748 /* Subject: */
749 subject = _oarg;
750 options |= OPT_SENDMODE;
751 break;
752 case 't':
753 /* Read defined set of headers from mail to be send */
754 options |= OPT_SENDMODE | OPT_t_FLAG;
755 break;
756 case 'u':
757 /* Set user name to pretend to be; don't set OPT_u_FLAG yet, this is
758 * done as necessary in _setup_vars() above */
759 myname = _oarg;
760 break;
761 case 'V':
762 puts(version);
763 exit(0);
764 /* NOTREACHED */
765 case 'v':
766 /* Be verbose */
767 okey = "verbose";
768 #ifdef HAVE_DEBUG
769 ok_bset(verbose, TRU1);
770 #endif
771 goto joarg;
772 case '~':
773 /* Enable tilde escapes even in non-interactive mode */
774 options |= OPT_TILDE_FLAG;
775 break;
776 case '#':
777 /* Work in batch mode, even if non-interactive */
778 if (oargs_count + 6 >= oargs_size)
779 oargs_size = _grow_cpp(&oargs, oargs_size + 8, oargs_count);
780 oargs[oargs_count + 0] = "dot";
781 oargs[oargs_count + 1] = "emptystart";
782 oargs[oargs_count + 2] = "noheader";
783 oargs[oargs_count + 3] = "quiet";
784 oargs[oargs_count + 4] = "sendwait";
785 oargs[oargs_count + 5] = "MBOX=/dev/null";
786 oargs_count += 6;
787 folder = "/dev/null";
788 options |= OPT_TILDE_FLAG | OPT_BATCH_FLAG;
789 break;
790 case '?':
791 jusage:
792 fprintf(stderr, tr(135, usagestr), progname, progname, progname);
793 exit_status = EXIT_USE;
794 goto jleave;
798 /* OPT_BATCH_FLAG sets to /dev/null, but -f can still be used and sets & */
799 if (folder != NULL && folder[1] == '\0') {
800 if (_oind < argc) {
801 if (_oind + 1 < argc) {
802 fprintf(stderr, tr(205, "More than one file given with -f\n"));
803 goto jusage;
805 folder = argv[_oind];
807 } else {
808 for (i = _oind; argv[i]; ++i)
809 to = cat(to, checkaddrs(lextract(argv[i], GTO | GFULL)));
810 if (to != NULL)
811 options |= OPT_SENDMODE;
814 /* Check for inconsistent arguments */
815 if (options & OPT_SENDMODE) {
816 if (folder != NULL && !(options & OPT_BATCH_FLAG)) {
817 fprintf(stderr, tr(137, "Cannot give -f and people to send to.\n"));
818 goto jusage;
820 if (myname != NULL) {
821 fprintf(stderr, tr(568,
822 "The -u option cannot be used in send mode\n"));
823 goto jusage;
825 if (!(options & OPT_t_FLAG) && to == NULL) {
826 fprintf(stderr, tr(138,
827 "Send options without primary recipient specified.\n"));
828 goto jusage;
830 if (options & (OPT_HEADERSONLY | OPT_HEADERLIST)) {
831 fprintf(stderr, tr(45,
832 "The -H and -L options cannot be used in send mode.\n"));
833 goto jusage;
835 if (options & OPT_R_FLAG) {
836 fprintf(stderr,
837 tr(235, "The -R option is meaningless in send mode.\n"));
838 goto jusage;
840 } else {
841 if (folder != NULL && myname != NULL) {
842 fprintf(stderr, tr(569,
843 "The options -f and -u are mutually exclusive\n"));
844 goto jusage;
849 * Likely to go, perform more setup
852 _setup_vars();
854 if (options & OPT_INTERACTIVE) {
855 _setscreensize(0);
856 #ifdef SIGWINCH
857 # ifndef TTY_WANTS_SIGWINCH
858 if (safe_signal(SIGWINCH, SIG_IGN) != SIG_IGN)
859 # endif
860 safe_signal(SIGWINCH, _setscreensize);
861 #endif
862 } else
863 scrnheight = realscreenheight = 24, scrnwidth = 80;
865 /* Snapshot our string pools. Memory is auto-reclaimed from now on */
866 spreserve();
868 if (!(options & OPT_NOSRC) && getenv("NAIL_NO_SYSTEM_RC") == NULL)
869 load(SYSCONFRC);
870 /* *expand() returns a savestr(), but load only uses the file name for
871 * fopen(), so it's safe to do this */
872 if ((cp = getenv("MAILRC")) == NULL && (cp = getenv("NAILRC")) == NULL)
873 cp = UNCONST(MAILRC);
874 load(file_expand(cp));
875 if (getenv("NAIL_EXTRA_RC") == NULL &&
876 (cp = ok_vlook(NAIL_EXTRA_RC)) != NULL)
877 load(file_expand(cp));
879 /* Now we can set the account */
880 if (Aarg != NULL) {
881 char *a[2];
882 a[0] = Aarg;
883 a[1] = NULL;
884 c_account(a);
887 /* Ensure the -S and other command line options take precedence over
888 * anything that may have been placed in resource files */
889 for (i = 0; UICMP(z, i, <, oargs_count); ++i) {
890 char const *a[2];
891 a[0] = oargs[i];
892 a[1] = NULL;
893 c_set(a);
897 * We're finally completely setup and ready to go
900 starting =
901 var_clear_allow_undefined = FAL0;
903 if (options & OPT_DEBUG)
904 fprintf(stderr, tr(199, "user = %s, homedir = %s\n"), myname, homedir);
906 if (!(options & OPT_SENDMODE)) {
907 exit_status = _rcv_mode(folder, Larg);
908 goto jleave;
911 /* Now that full mailx(1)-style file expansion is possible handle the
912 * attachments which we had delayed due to this.
913 * This may use savestr(), but since we won't enter the command loop we
914 * don't need to care about that */
915 while (a_head != NULL) {
916 attach = add_attachment(attach, a_head->aa_file, NULL);
917 if (attach != NULL) {
918 a_curr = a_head;
919 a_head = a_head->aa_next;
920 ac_free(a_curr);
921 } else {
922 perror(a_head->aa_file);
923 exit_status = EXIT_ERR;
924 goto jleave;
928 /* xxx exit_status = EXIT_OK; */
929 if (options & OPT_INTERACTIVE)
930 tty_init();
931 mail(to, cc, bcc, subject, attach, qf, ((options & OPT_F_FLAG) != 0));
932 if (options & OPT_INTERACTIVE)
933 tty_destroy();
934 jleave:
935 NYD_LEAVE;
936 return exit_status;
939 /* vim:set fenc=utf-8:s-it-mode */