* S/MIME: validation of signed messages in servers that modify
[alpine.git] / alpine / arg.c
blobb256de6298b1bcf3ae5777ef30ab4d23a271d121
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: arg.c 900 2008-01-05 01:13:26Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2014 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
20 Command line argument parsing functions
22 ====*/
24 #include "headers.h"
26 #include "../pith/state.h"
27 #include "../pith/init.h"
28 #include "../pith/conf.h"
29 #include "../pith/list.h"
30 #include "../pith/util.h"
31 #include "../pith/help.h"
33 #include "imap.h"
35 #include "arg.h"
38 int process_debug_str(char *);
39 void args_add_attach(PATMT **, char *, int);
40 int pinerc_cmdline_opt(char *);
44 * Name started as to invoke function key mode
46 #define ALPINE_FKEY_NAME "alpinef"
49 * Various error and informational strings..
51 /* TRANSLATORS: This is a list of errors printed when something goes wrong
52 early on with the argument list. Be careful not to change literal
53 option names mentioned in the strings. */
54 static char args_err_missing_pinerc[] = N_("missing argument for option \"-pinerc\" (use - for standard out)");
55 #if defined(DOS) || defined(OS2)
56 static char args_err_missing_aux[] = N_("missing argument for option \"-aux\"");
57 #endif
58 #ifdef PASSFILE
59 static char args_err_missing_passfile[] = N_("missing argument for option \"-passfile\"");
60 static char args_err_non_abs_passfile[] = N_("argument to \"-passfile\" should be fully-qualified");
61 #ifdef SMIME
62 static char args_err_missing_pwdcertdir[] = N_("missing argument for option \"-pwdcertdir\"");
63 static char args_err_non_abs_pwdcertdir[] = N_("argument to \"-pwdcertdir\" should be fully-qualified");
64 #endif /* SMIME inside PASSFILE */
65 #endif
66 static char args_err_missing_sort[] = N_("missing argument for option \"-sort\"");
67 static char args_err_missing_flag_arg[] = N_("missing argument for flag \"%c\"");
68 static char args_err_missing_flag_num[] = N_("Non numeric argument for flag \"%c\"");
69 static char args_err_missing_debug_num[] = N_("Non numeric argument for \"%s\"");
70 static char args_err_missing_url[] = N_("missing URL for \"-url\"");
71 static char args_err_missing_attachment[] = N_("missing attachment for \"%s\"");
72 static char args_err_conflict[] = N_("conflicting action: \"%s\"");
73 static char args_err_unknown[] = N_("unknown flag \"%c\"");
74 static char args_err_I_error[] = N_("-I argument \"%s\": %s");
75 static char args_err_d_error[] = N_("-d argument \"%s\": %s");
76 static char args_err_internal[] = "%s";
77 static char args_err_missing_copyprc[] = N_("missing argument for option \"-copy_pinerc\"\nUsage: pine -copy_pinerc <local_pinerc> <remote_pinerc>");
78 static char args_err_missing_copyabook[] = N_("missing argument for option \"-copy_abook\"\nUsage: pine -copy_abook <local_abook> <remote_abook>");
81 static char *args_pine_args[] = {
82 N_("Possible Starting Arguments for Alpine program:"),
83 "",
84 N_(" Argument\tMeaning"),
85 N_(" <addrs>...\tGo directly into composer sending to given address"),
86 N_("\t\tList multiple addresses with a single space between them."),
87 N_("\t\tStandard input redirection is allowed with addresses."),
88 N_("\t\tNote: Places addresses in the \"To\" field only."),
89 N_(" -attach <file>\tGo directly into composer with given file"),
90 N_(" -attachlist <file-list>"),
91 N_(" -attach_and_delete <file>"),
92 N_("\t\tGo to composer, attach file, delete when finished"),
93 N_("\t\tNote: Attach options can't be used if -f, -F"),
94 N_("\t\tadded to Attachment list. Attachlist must be the last"),
95 N_("\t\toption on the command line"),
96 N_(" -bail\t\tExit if pinerc file doesn't already exist"),
97 #ifdef DEBUG
98 N_(" -d n\t\tDebug - set debug level to 'n', or use the following:"),
99 N_(" -d keywords...\tflush,timestamp,imap=0..4,tcp,numfiles=0..31,verbose=0..9"),
100 #endif
101 N_(" -f <folder>\tFolder - give folder name to open"),
102 N_(" -c <number>\tContext - which context to apply to -f arg"),
103 N_(" -F <file>\tFile - give file name to open and page through and"),
104 N_("\t\tforward as email."),
105 N_(" -h \t\tHelp - give this list of options"),
106 N_(" -k \t\tKeys - Force use of function keys"),
107 N_(" -z \t\tSuspend - allow use of ^Z suspension"),
108 N_(" -r \t\tRestricted - can only send mail to oneself"),
109 N_(" -sort <sort>\tSort - Specify sort order of folder:"),
110 N_("\t\t\tarrival, subject, threaded, orderedsubject, date,"),
111 N_("\t\t\tfrom, size, score, to, cc, /reverse"),
112 N_(" -i\t\tIndex - Go directly to index, bypassing main menu"),
113 N_(" -I <keystroke_list> Initial keystrokes to be executed"),
114 N_(" -n <number>\tEntry in index to begin on"),
115 N_(" -o \t\tReadOnly - Open first folder read-only"),
116 N_(" -conf\t\tConfiguration - Print out fresh global configuration. The"),
117 N_("\t\tvalues of your global configuration affect all Alpine users"),
118 N_("\t\ton your system unless they have overridden the values in their"),
119 N_("\t\tpinerc files."),
120 N_(" -pinerc <file>\tConfiguration - Put fresh pinerc configuration in <file>"),
121 N_(" -p <pinerc>\tUse alternate .pinerc file"),
122 #if !defined(DOS) && !defined(OS2)
123 N_(" -P <pine.conf>\tUse alternate pine.conf file"),
124 #else
125 N_(" -aux <aux_files_dir>\tUse this with remote pinerc"),
126 N_(" -P <pine.conf>\tUse pine.conf file for default settings"),
127 N_(" -nosplash \tDisable the PC-Alpine splash screen"),
128 #endif
130 #if defined(APPLEKEYCHAIN) || (WINCRED > 0)
131 N_(" -erase_stored_passwords\tEliminate any stored passwords"),
132 #endif
134 #ifdef PASSFILE
135 N_(" -passfile <fully_qualified_filename>\tSet the password file to something other"),
136 N_("\t\tthan the default"),
137 #ifdef SMIME
138 N_(" -pwdcertdir <fully_qualified_path>\tSet the directory to store a personal"),
139 N_("\t\tkey and certificate to encrypt and decrypt your password file."),
140 #endif /* SMIME inside PASSFILE */
141 #endif /* PASSFILE */
143 #ifdef LOCAL_PASSWD_CACHE
144 N_(" -nowrite_password_cache\tRead from a password cache if there is one, but"),
145 N_("\t\t\t\tnever offer to write a password to the cache"),
146 #endif /* LOCAL_PASSWD_CACHE */
148 N_(" -x <config>\tUse configuration exceptions in <config>."),
149 N_("\t\tExceptions are used to override your default pinerc"),
150 N_("\t\tsettings for a particular platform, can be a local file or"),
151 N_("\t\ta remote folder."),
152 N_(" -v \t\tVersion - show version information"),
153 N_(" -version\tVersion - show version information"),
154 N_(" -supported\tList supported options"),
155 N_(" -url <url>\tOpen the given URL"),
156 N_("\t\tNote: Can't be used if -f, -F"),
157 N_("\t\tStandard input redirection is not allowed with URLs."),
158 N_("\t\tFor mailto URLs, 'body='text should be used in place of"),
159 N_("\t\tinput redirection."),
160 N_(" -copy_pinerc <local_pinerc> <remote_pinerc> copy local pinerc to remote"),
161 N_(" -copy_abook <local_abook> <remote_abook> copy local addressbook to remote"),
162 N_(" -convert_sigs -p <pinerc> convert signatures to literal signatures"),
163 #if defined(_WINDOWS)
164 N_(" -install \tPrompt for some basic setup information"),
165 N_(" -uninstall \tRemove traces of Alpine from Windows system settings"),
166 N_(" -registry <cmd>\tWhere cmd is set,noset,clear,clearsilent,dump"),
167 #endif
168 " -<option>=<value> Assign <value> to the pinerc option <option>",
169 "\t\t e.g. -signature-file=sig1",
170 "\t\t e.g. -color-style=no-color",
171 "\t\t e.g. -feature-list=enable-sigdashes",
172 "\t\t Note: feature-list is additive.",
173 "\t\t You may leave off the \"feature-list=\" part of that,",
174 "\t\t e.g. -enable-sigdashes",
175 NULL
181 * Parse the command line args.
183 * Args: pine_state -- The pine_state structure to put results in
184 * argc, argv -- The obvious
185 * addrs -- Pointer to address list that we set for caller
187 * Result: command arguments parsed
188 * possible printing of help for command line
189 * various flags in pine_state set
190 * returns the string name of the first folder to open
191 * addrs is set
193 void
194 pine_args(struct pine *pine_state, int argc, char **argv, ARGDATA_S *args)
196 register int ac;
197 register char **av;
198 int c;
199 char *str;
200 char *cmd_list = NULL;
201 char *debug_str = NULL;
202 char *sort = NULL;
203 char *pinerc_file = NULL;
204 char *lc = NULL;
205 int do_help = 0;
206 int do_conf = 0;
207 int usage = 0;
208 int do_use_fk = 0;
209 int do_can_suspend = 0;
210 int do_version = 0;
211 struct variable *vars = pine_state->vars;
213 ac = argc;
214 av = argv;
215 memset(args, 0, sizeof(ARGDATA_S));
216 args->action = aaFolder;
218 pine_state->pine_name = (lc = last_cmpnt(argv[0])) ? lc : (lc = argv[0]);
219 #ifdef DOS
220 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%.*s", pine_state->pine_name - argv[0], argv[0]);
221 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
222 pine_state->pine_dir = cpystr(tmp_20k_buf);
223 #endif
225 /* while more arguments with leading - */
226 Loop: while(--ac > 0)
227 if(**++av == '-'){
228 /* while more chars in this argument */
229 while(*++*av){
230 /* check for pinerc options */
231 if(pinerc_cmdline_opt(*av)){
232 goto Loop; /* done with this arg, go to next */
234 /* then other multi-char options */
235 else if(strcmp(*av, "conf") == 0){
236 do_conf = 1;
237 goto Loop; /* done with this arg, go to next */
239 else if(strcmp(*av, "pinerc") == 0){
240 if(--ac)
241 pinerc_file = *++av;
242 else{
243 display_args_err(_(args_err_missing_pinerc), NULL, 1);
244 ++usage;
247 goto Loop;
249 #if defined(DOS) || defined(OS2)
250 else if(strcmp(*av, "aux") == 0){
251 if(--ac){
252 if((str = *++av) != NULL)
253 pine_state->aux_files_dir = cpystr(str);
255 else{
256 display_args_err(_(args_err_missing_aux), NULL, 1);
257 ++usage;
260 goto Loop;
262 else if(strcmp(*av, "nosplash") == 0)
263 goto Loop; /* already taken care of in WinMain */
264 #endif
266 #if defined(APPLEKEYCHAIN) || (WINCRED > 0)
267 else if(strcmp(*av, "erase_stored_passwords") == 0){
268 #if (WINCRED > 0)
269 erase_windows_credentials();
270 #else
271 macos_erase_keychain();
272 #endif
273 goto Loop;
275 #endif /* defined(APPLEKEYCHAIN) || (WINCRED > 0) */
277 #ifdef PASSFILE
278 else if(strcmp(*av, "passfile") == 0){
279 if(--ac){
280 if((str = *++av) != NULL){
281 if(!is_absolute_path(str)){
282 display_args_err(_(args_err_non_abs_passfile),
283 NULL, 1);
284 ++usage;
286 else{
287 if(pine_state->passfile)
288 fs_give((void **)&pine_state->passfile);
290 pine_state->passfile = cpystr(str);
294 else{
295 display_args_err(_(args_err_missing_passfile), NULL, 1);
296 ++usage;
299 goto Loop;
301 #ifdef SMIME
302 else if(strcmp(*av, "pwdcertdir") == 0){
303 if(--ac){
304 if((str = *++av) != NULL){
305 if(!is_absolute_path(str)){
306 display_args_err(_(args_err_non_abs_pwdcertdir),
307 NULL, 1);
308 ++usage;
310 else{
311 if(pine_state->pwdcertdir)
312 fs_give((void **)&pine_state->pwdcertdir);
314 pine_state->pwdcertdir = cpystr(str);
318 else{
319 display_args_err(_(args_err_missing_pwdcertdir), NULL, 1);
320 ++usage;
323 goto Loop;
325 #endif /* SMIME inside PASSFILE */
326 #endif /* PASSFILE */
328 #ifdef LOCAL_PASSWD_CACHE
329 else if(strcmp(*av, "nowrite_password_cache") == 0){
330 ps_global->nowrite_password_cache = 1;
331 goto Loop;
333 #endif /* LOCAL_PASSWD_CACHE */
335 else if(strcmp(*av, "convert_sigs") == 0){
336 ps_global->convert_sigs = 1;
337 goto Loop;
339 else if(strcmp(*av, "supported") == 0){
340 ps_global->dump_supported_options = 1;
341 goto Loop;
343 else if(strcmp(*av, "copy_pinerc") == 0){
344 if(args->action == aaFolder && !args->data.folder){
345 args->action = aaPrcCopy;
346 if(ac > 2){
347 ac -= 2;
348 args->data.copy.local = *++av;
349 args->data.copy.remote = *++av;
351 else{
352 display_args_err(_(args_err_missing_copyprc), NULL, 1);
353 ++usage;
356 else{
357 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-copy_pinerc");
358 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
359 display_args_err(tmp_20k_buf, NULL, 1);
360 ++usage;
363 goto Loop;
365 else if(strcmp(*av, "copy_abook") == 0){
366 if(args->action == aaFolder && !args->data.folder){
367 args->action = aaAbookCopy;
368 if(ac > 2){
369 ac -= 2;
370 args->data.copy.local = *++av;
371 args->data.copy.remote = *++av;
373 else{
374 display_args_err(_(args_err_missing_copyabook), NULL, 1);
375 ++usage;
378 else{
379 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-copy_abook");
380 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
381 display_args_err(tmp_20k_buf, NULL, 1);
382 ++usage;
385 goto Loop;
387 else if(strcmp(*av, "sort") == 0){
388 if(--ac){
389 sort = *++av;
390 COM_SORT_KEY = cpystr(sort);
392 else{
393 display_args_err(_(args_err_missing_sort), NULL, 1);
394 ++usage;
397 goto Loop;
399 else if(strcmp(*av, "url") == 0){
400 if(args->action == aaFolder && !args->data.folder){
401 args->action = aaURL;
402 if(--ac){
403 args->url = cpystr(*++av);
405 else{
406 display_args_err(_(args_err_missing_url), NULL, 1);
407 ++usage;
410 else{
411 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-url");
412 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
413 display_args_err(tmp_20k_buf, NULL, 1);
414 ++usage;
417 goto Loop;
419 else if(strcmp(*av, "attach") == 0){
420 if((args->action == aaFolder && !args->data.folder)
421 || args->action == aaMail
422 || args->action == aaURL){
423 if(args->action != aaURL)
424 args->action = aaMail;
425 if(--ac){
426 args_add_attach(&args->data.mail.attachlist,
427 *++av, FALSE);
429 else{
430 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_attachment), "-attach");
431 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
432 display_args_err(tmp_20k_buf, NULL, 1);
433 ++usage;
436 else{
437 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-attach");
438 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
439 display_args_err(tmp_20k_buf, NULL, 1);
440 ++usage;
443 goto Loop;
445 else if(strcmp(*av, "attachlist") == 0){
446 if((args->action == aaFolder && !args->data.folder)
447 || args->action == aaMail
448 || args->action == aaURL){
449 if(args->action != aaURL)
450 args->action = aaMail;
451 if(ac - 1){
453 if(can_access(*(av+1), READ_ACCESS) == 0){
454 ac--;
455 args_add_attach(&args->data.mail.attachlist,
456 *++av, FALSE);
458 else
459 break;
461 while(ac);
463 else{
464 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_attachment), "-attachList");
465 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
466 display_args_err(tmp_20k_buf, NULL, 1);
467 ++usage;
470 else{
471 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-attachList");
472 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
473 display_args_err(tmp_20k_buf, NULL, 1);
474 ++usage;
477 goto Loop;
479 else if(strcmp(*av, "attach_and_delete") == 0){
480 if((args->action == aaFolder && !args->data.folder)
481 || args->action == aaMail
482 || args->action == aaURL){
483 if(args->action != aaURL)
484 args->action = aaMail;
485 if(--ac){
486 args_add_attach(&args->data.mail.attachlist,
487 *++av, TRUE);
489 else{
490 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_attachment), "-attach_and_delete");
491 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
492 display_args_err(tmp_20k_buf, NULL, 1);
493 ++usage;
496 else{
497 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-attach_and_delete");
498 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
499 display_args_err(tmp_20k_buf, NULL, 1);
500 ++usage;
503 goto Loop;
505 else if(strcmp(*av, "bail") == 0){
506 pine_state->exit_if_no_pinerc = 1;
507 goto Loop;
509 else if(strcmp(*av, "version") == 0){
510 do_version = 1;
511 goto Loop;
513 #ifdef _WINDOWS
514 else if(strcmp(*av, "install") == 0){
515 pine_state->install_flag = 1;
516 pine_state->update_registry = UREG_ALWAYS_SET;
517 goto Loop;
519 else if(strcmp(*av, "uninstall") == 0){
521 * Blast password cache, clear registry settings
523 #if (WINCRED > 0)
524 erase_windows_credentials();
525 #endif
526 mswin_reg(MSWR_OP_BLAST, MSWR_NULL, NULL, 0);
527 exit(0);
529 else if(strcmp(*av, "registry") == 0){
530 if(--ac){
531 if(!strucmp(*++av, "set")){
532 pine_state->update_registry = UREG_ALWAYS_SET;
534 else if(!strucmp(*av, "noset")){
535 pine_state->update_registry = UREG_NEVER_SET;
537 else if(!strucmp(*av, "clear")){
538 if(!mswin_reg(MSWR_OP_BLAST, MSWR_NULL, NULL, 0))
539 display_args_err(
540 _("Alpine related Registry values removed."),
541 NULL, 0);
542 else
543 display_args_err(
544 _("Not all Alpine related Registry values could be removed"),
545 NULL, 0);
546 exit(0);
548 else if(!strucmp(*av, "clearsilent")){
549 mswin_reg(MSWR_OP_BLAST, MSWR_NULL, NULL, 0);
550 exit(0);
552 else if(!strucmp(*av, "dump")){
553 char **pRegistry = mswin_reg_dump();
555 if(pRegistry){
556 display_args_err(NULL, pRegistry, 0);
557 free_list_array(&pRegistry);
559 exit(0);
561 else{
562 display_args_err(_("unknown registry command"),
563 NULL, 1);
564 ++usage;
567 else{
568 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_flag_arg), c);
569 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
570 display_args_err(tmp_20k_buf, NULL, 1);
571 ++usage;
574 goto Loop;
576 #endif
577 /* single char flags */
578 else{
579 switch(c = **av){
580 case 'h':
581 do_help = 1;
582 break; /* break back to inner-while */
583 case 'k':
584 do_use_fk = 1;
585 break;
586 case 'z':
587 do_can_suspend = 1;
588 break;
589 case 'r':
590 pine_state->restricted = 1;
591 break;
592 case 'o':
593 pine_state->open_readonly_on_startup = 1;
594 break;
595 case 'i':
596 pine_state->start_in_index = 1;
597 break;
598 case 'v':
599 do_version = 1;
600 break; /* break back to inner-while */
601 /* these take arguments */
602 case 'f': case 'F': case 'p': case 'I':
603 case 'c': case 'd': case 'P': case 'x': /* string args */
604 case 'n': /* integer args */
605 if(*++*av)
606 str = *av;
607 else if(--ac)
608 str = *++av;
609 else if(c == 'f' || c == 'F')
610 str = "";
611 else{
612 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_flag_arg), c);
613 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
614 display_args_err(tmp_20k_buf, NULL, 1);
615 ++usage;
616 goto Loop;
619 switch(c){
620 case 'f':
621 if(args->action == aaFolder && !args->data.folder){
622 args->data.folder = cpystr(str);
624 else{
625 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-f");
626 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
627 display_args_err(tmp_20k_buf, NULL, 1);
628 usage++;
631 break;
632 case 'F':
633 if(args->action == aaFolder && !args->data.folder){
634 args->action = aaMore;
635 args->data.file = cpystr(str);
637 else{
638 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-F");
639 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
640 display_args_err(tmp_20k_buf, NULL, 1);
641 usage++;
644 break;
645 case 'd':
646 debug_str = str;
647 break;
648 case 'I':
649 cmd_list = str;
650 break;
651 case 'p':
652 if(str){
653 char path[MAXPATH], dir[MAXPATH];
655 if(IS_REMOTE(str) || is_absolute_path(str)){
656 strncpy(path, str, sizeof(path)-1);
657 path[sizeof(path)-1] = '\0';
659 else{
660 getcwd(dir, sizeof(path));
661 build_path(path, dir, str, sizeof(path));
665 * Pinerc used to be the name of the pinerc
666 * file. Now, since the pinerc can be remote,
667 * we've replaced the variable pinerc with the
668 * structure prc. Unfortunately, other parts of
669 * pine rely on the fact that pinerc is the
670 * name of the pinerc _file_, and use the
671 * directory that the pinerc file is located
672 * in for their own purposes. We keep that so
673 * things will keep working.
676 if(!IS_REMOTE(path)){
677 if(pine_state->pinerc)
678 fs_give((void **)&pine_state->pinerc);
680 pine_state->pinerc = cpystr(path);
684 * Last one wins. This would be the place where
685 * we put multiple pinercs in a list if we
686 * were to allow that.
688 if(pine_state->prc)
689 free_pinerc_s(&pine_state->prc);
691 pine_state->prc = new_pinerc_s(path);
694 break;
695 case 'P':
696 if(str){
697 char path[MAXPATH], dir[MAXPATH];
699 if(IS_REMOTE(str) || is_absolute_path(str)){
700 strncpy(path, str, sizeof(path)-1);
701 path[sizeof(path)-1] = '\0';
703 else{
704 getcwd(dir, sizeof(path));
705 build_path(path, dir, str, sizeof(path));
708 if(pine_state->pconf)
709 free_pinerc_s(&pine_state->pconf);
711 pine_state->pconf = new_pinerc_s(path);
714 break;
715 case 'x':
716 if(str)
717 pine_state->exceptions = cpystr(str);
719 break;
720 case 'c':
721 if(!isdigit((unsigned char)str[0])){
722 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
723 _(args_err_missing_flag_num), c);
724 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
725 display_args_err(tmp_20k_buf, NULL, 1);
726 ++usage;
727 break;
730 pine_state->init_context = (short) atoi(str);
731 break;
733 case 'n':
734 if(!isdigit((unsigned char)str[0])){
735 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
736 _(args_err_missing_flag_num), c);
737 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
738 display_args_err(tmp_20k_buf, NULL, 1);
739 ++usage;
740 break;
743 pine_state->start_entry = atoi(str);
744 if(pine_state->start_entry < 1)
745 pine_state->start_entry = 1;
747 break;
750 goto Loop;
752 default:
753 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_unknown), c);
754 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
755 display_args_err(tmp_20k_buf, NULL, 1);
756 ++usage;
757 break;
762 else if(args->action == aaMail
763 || (args->action == aaFolder && !args->data.folder)){
764 STRLIST_S *stp, **slp;
766 args->action = aaMail;
768 stp = new_strlist(*av);
770 for(slp = &args->data.mail.addrlist; *slp; slp = &(*slp)->next)
773 *slp = stp;
775 else{
776 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), *av);
777 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
778 display_args_err(tmp_20k_buf, NULL, 1);
779 usage++;
782 if(cmd_list){
783 int commas = 0;
784 char *p = cmd_list;
785 char *error = NULL;
787 while(*p++)
788 if(*p == ',')
789 ++commas;
791 COM_INIT_CMD_LIST = parse_list(cmd_list, commas+1, 0, &error);
792 if(error){
793 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_I_error), cmd_list, error);
794 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
795 display_args_err(tmp_20k_buf, NULL, 1);
796 exit(-1);
800 #ifdef DEBUG
801 pine_state->debug_nfiles = NUMDEBUGFILES;
802 #endif
803 if(debug_str && process_debug_str(debug_str))
804 usage++;
806 if(lc && strncmp(lc, ALPINE_FKEY_NAME, sizeof(ALPINE_FKEY_NAME) - 1) == 0)
807 do_use_fk = 1;
809 if(do_use_fk || do_can_suspend){
810 char list[500];
811 int commas = 0;
812 char *p = list;
813 char *error = NULL;
815 list[0] = '\0';
817 if(do_use_fk){
818 if(list[0]){
819 strncat(list, ",", sizeof(list)-strlen(list)-1);
820 list[sizeof(list)-1] = '\0';
823 strncat(list, "use-function-keys", sizeof(list)-strlen(list)-1);
824 list[sizeof(list)-1] = '\0';
827 if(do_can_suspend){
828 if(list[0]){
829 strncat(list, ",", sizeof(list)-strlen(list)-1);
830 list[sizeof(list)-1] = '\0';
833 strncat(list, "enable-suspend", sizeof(list)-strlen(list)-1);
834 list[sizeof(list)-1] = '\0';
837 while(*p++)
838 if(*p == ',')
839 ++commas;
841 pine_state->feat_list_back_compat = parse_list(list,commas+1,0,&error);
842 if(error){
843 snprintf(tmp_20k_buf, SIZEOF_20KBUF, args_err_internal, error);
844 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
845 display_args_err(tmp_20k_buf, NULL, 1);
846 exit(-1);
850 if(((do_conf ? 1 : 0)+(pinerc_file ? 1 : 0)) > 1){
851 display_args_err(_("May only have one of -conf and -pinerc"),
852 NULL, 1);
853 exit(-1);
856 if(do_help || usage)
857 args_help();
859 if(usage)
860 exit(-1);
862 if(do_version){
863 extern char datestamp[], hoststamp[];
864 char rev[128];
866 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Alpine %s (%s %s) built %s on %s",
867 ALPINE_VERSION,
868 SYSTYPE ? SYSTYPE : "?",
869 get_alpine_revision_string(rev, sizeof(rev)),
870 datestamp, hoststamp);
871 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
872 display_args_err(tmp_20k_buf, NULL, 0);
873 exit(0);
876 if(do_conf)
877 dump_global_conf();
879 if(pinerc_file)
880 dump_new_pinerc(pinerc_file);
883 * Don't NULL out argv[0] or we might crash in unexpected ways. In OS X, we were
884 * crashing when opening attachments because of this.
886 if(ac <= 0 && av != argv)
887 *av = NULL;
892 * Returns 0 if ok, -1 if error.
895 process_debug_str(char *debug_str)
897 int i, usage = 0;
898 int commas = 0;
899 int new_style_debug_arg = 0;
900 char *q = debug_str;
901 char *error = NULL;
902 char **list, **p;
904 #ifdef DEBUG
905 if(debug_str){
906 if(!isdigit((unsigned char)debug_str[0]))
907 new_style_debug_arg++;
909 if(new_style_debug_arg){
910 while(*q++)
911 if(*q == ',')
912 ++commas;
914 list = parse_list(debug_str, commas+1, 0, &error);
915 if(error){
916 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_d_error), debug_str, error);
917 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
918 display_args_err(tmp_20k_buf, NULL, 1);
919 return(-1);
922 if(list){
923 for(p = list; *p; p++){
924 if(struncmp(*p, "timestamp", 9) == 0){
925 ps_global->debug_timestamp = 1;
927 else if(struncmp(*p, "imap", 4) == 0){
928 q = *p + 4;
929 if(!*q || !*(q+1) || *q != '=' ||
930 !isdigit((unsigned char)*(q+1))){
931 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_debug_num), *p);
932 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
933 display_args_err(tmp_20k_buf, NULL, 1);
934 usage = -1;
936 else{
937 i = atoi(q+1);
938 ps_global->debug_imap = MIN(5,MAX(0,i));
941 else if(struncmp(*p, "flush", 5) == 0){
942 ps_global->debug_flush = 1;
944 else if(struncmp(*p, "tcp", 3) == 0){
945 ps_global->debug_tcp = 1;
947 else if(struncmp(*p, "verbose", 7) == 0){
948 q = *p + 7;
949 if(!*q || !*(q+1) || *q != '=' ||
950 !isdigit((unsigned char)*(q+1))){
951 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_debug_num), *p);
952 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
953 display_args_err(tmp_20k_buf, NULL, 1);
954 usage = -1;
956 else
957 debug = atoi(q+1);
959 else if(struncmp(*p, "numfiles", 8) == 0){
960 q = *p + 8;
961 if(!*q || !*(q+1) || *q != '=' ||
962 !isdigit((unsigned char)*(q+1))){
963 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_debug_num), *p);
964 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
965 display_args_err(tmp_20k_buf, NULL, 1);
966 usage = -1;
968 else{
969 i = atoi(q+1);
970 ps_global->debug_nfiles = MIN(31,MAX(0,i));
973 else if(struncmp(*p, "malloc", 6) == 0){
974 q = *p + 6;
975 if(!*q || !*(q+1) || *q != '=' ||
976 !isdigit((unsigned char)*(q+1))){
977 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_debug_num), *p);
978 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
979 display_args_err(tmp_20k_buf, NULL, 1);
980 usage = -1;
982 else{
983 i = atoi(q+1);
984 ps_global->debug_malloc = MIN(63,MAX(0,i));
987 #if defined(ENABLE_LDAP) && defined(LDAP_DEBUG)
988 else if(struncmp(*p, "ldap", 4) == 0){
989 q = *p + 4;
990 if(!*q || !*(q+1) || *q != '=' ||
991 !isdigit((unsigned char)*(q+1))){
992 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_debug_num), *p);
993 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
994 display_args_err(tmp_20k_buf, NULL, 1);
995 usage = -1;
997 else{
998 i = atoi(q+1);
999 ldap_debug = i;
1002 #endif /* LDAP */
1003 else{
1004 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("unknown debug keyword \"%s\""), *p);
1005 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1006 display_args_err(tmp_20k_buf, NULL, 1);
1007 usage = -1;
1011 free_list_array(&list);
1014 else{
1015 debug = atoi(debug_str);
1016 if(debug > 9)
1017 ps_global->debug_imap = 5;
1018 else if(debug > 7)
1019 ps_global->debug_imap = 4;
1020 else if(debug > 6)
1021 ps_global->debug_imap = 3;
1022 else if(debug > 4)
1023 ps_global->debug_imap = 2;
1024 else if(debug > 2)
1025 ps_global->debug_imap = 1;
1027 if(debug > 7)
1028 ps_global->debug_timestamp = 1;
1030 if(debug > 8)
1031 ps_global->debug_flush = 1;
1035 if(!new_style_debug_arg){
1036 #ifdef CSRIMALLOC
1037 ps_global->debug_malloc =
1038 (debug <= DEFAULT_DEBUG) ? 1 : (debug < 9) ? 2 : 3;
1039 #else /* !CSRIMALLOC */
1040 #ifdef HAVE_SMALLOC
1041 if(debug > 8)
1042 ps_global->debug_malloc = 2;
1043 #else /* !HAVE_SMALLOC */
1044 #ifdef NXT
1045 if(debug > 8)
1046 ps_global->debug_malloc = 32;
1047 #endif /* NXT */
1048 #endif /* HAVE_SMALLOC */
1049 #endif /* CSRIMALLOC */
1052 #else /* !DEBUG */
1054 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("unknown flag \"d\", debugging not compiled in"));
1055 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1056 display_args_err(tmp_20k_buf, NULL, 1);
1057 usage = -1;
1059 #endif /* !DEBUG */
1061 return(usage);
1065 void
1066 args_add_attach(PATMT **alpp, char *s, int deleted)
1068 PATMT *atmp, **atmpp;
1070 atmp = (PATMT *) fs_get(sizeof(PATMT));
1071 memset(atmp, 0, sizeof(PATMT));
1072 atmp->filename = cpystr(s);
1074 #if defined(DOS) || defined(OS2)
1075 (void) removing_quotes(atmp->filename);
1076 #endif
1078 if(deleted)
1079 atmp->flags |= A_TMP;
1081 for(atmpp = alpp; *atmpp; atmpp = &(*atmpp)->next)
1084 *atmpp = atmp;
1088 /*----------------------------------------------------------------------
1089 print a few lines of help for command line arguments
1091 Args: none
1093 Result: prints help messages
1094 ----------------------------------------------------------------------*/
1095 void
1096 args_help(void)
1098 char *pp[2];
1099 #ifndef _WINDOWS
1100 char **a;
1101 #endif
1103 pp[1] = NULL;
1105 /** print out possible starting arguments... **/
1108 * display_args_err expects already translated input
1109 * so we need to translate a line at a time. Since
1110 * the 1st and 3rd args are zero it is ok to call it
1111 * a line at a time.
1113 * Windows expects the full block of text so we'll pass
1114 * it as such.
1116 #ifndef _WINDOWS
1117 for(a=args_pine_args; a && *a; a++){
1118 pp[0] = _(*a);
1119 display_args_err(NULL, pp, 0);
1121 #else
1122 display_args_err(NULL, args_pine_args, 0);
1123 #endif
1125 exit(1);
1129 /*----------------------------------------------------------------------
1130 write argument error to the display...
1132 Args: none
1134 Result: prints help messages
1135 ----------------------------------------------------------------------*/
1136 void
1137 display_args_err(char *s, char **a, int err)
1139 char errstr[256], *errp;
1140 FILE *fp = err ? stderr : stdout;
1143 if(err && s){
1144 snprintf(errp = errstr, sizeof(errstr), "%s: %s", _("Argument Error"), s);
1145 errstr[sizeof(errstr)-1] = '\0';
1147 else
1148 errp = s;
1150 #ifdef _WINDOWS
1151 if(errp)
1152 mswin_messagebox(errp, err);
1154 if(a && *a){
1155 os_argsdialog(a);
1157 #else
1158 if(errp)
1159 fprintf(fp, "%s\n", errp);
1161 while(a && *a)
1162 fprintf(fp, "%s\n", *a++);
1163 #endif
1168 * The argument is an argument from the command line. We check to see
1169 * if it is specifying an alternate value for one of the options that is
1170 * normally set in pinerc. If so, we return 1 and set the appropriate
1171 * values in the variables array.
1172 * The arg can be
1173 * varname=value
1174 * varname=value1,value2,value3
1175 * feature-list=featurename these are just a special
1176 * feature-list=featurename1,featurename2 case of above
1177 * featurename This is equivalent to above
1178 * no-featurename
1181 pinerc_cmdline_opt(char *arg)
1183 struct variable *v;
1184 char *p1 = NULL,
1185 *value,
1186 **oldlvalue = NULL,
1187 **lvalue;
1188 int i, count;
1190 if(!arg || !arg[0])
1191 return 0;
1193 for(v = ps_global->vars; v->name != NULL; v++){
1194 if(v->is_used && struncmp(v->name, arg, strlen(v->name)) == 0){
1195 p1 = arg + strlen(v->name);
1197 /*----- Skip to '=' -----*/
1198 while(*p1 && (*p1 == '\t' || *p1 == ' '))
1199 p1++;
1201 if(*p1 != '='){
1202 char buf[MAILTMPLEN];
1204 snprintf(buf, sizeof(buf), _("Missing \"=\" after -%s\n"), v->name);
1205 buf[sizeof(buf)-1] = '\0';
1206 exceptional_exit(buf, -1);
1209 p1++;
1210 break;
1214 /* no match, check for a feature name used directly */
1215 if(v->name == NULL){
1216 FEATURE_S *feat;
1217 char *featname;
1219 if(struncmp(arg, "no-", 3) == 0)
1220 featname = arg+3;
1221 else
1222 featname = arg;
1224 for(i = 0; (feat = feature_list(i)) != NULL; i++){
1225 if(strucmp(featname, feat->name) == 0){
1226 v = &(ps_global->vars)[V_FEATURE_LIST];
1227 p1 = arg;
1228 break;
1233 if(!p1)
1234 return 0;
1236 if(v->is_obsolete || !v->is_user){
1237 char buf[MAILTMPLEN];
1239 if(v->is_obsolete)
1240 snprintf(buf, sizeof(buf), _("Option \"%s\" is obsolete\n"), v->name);
1241 else
1242 snprintf(buf, sizeof(buf), _("Option \"%s\" is not user settable\n"), v->name);
1244 exceptional_exit(buf, -1);
1247 /* free mem */
1248 if(v->is_list){
1249 oldlvalue = v->cmdline_val.l;
1250 v->cmdline_val.l = NULL;
1252 else if(v->cmdline_val.p)
1253 fs_give((void **) &(v->cmdline_val.p));
1255 /*----- Matched a variable, get its value ----*/
1256 while(*p1 == ' ' || *p1 == '\t')
1257 p1++;
1258 value = p1;
1260 if(*value == '\0'){
1261 if(v->is_list){
1262 v->cmdline_val.l = (char **)fs_get(2 * sizeof(char *));
1264 * we let people leave off the quotes on command line so that
1265 * they don't have to mess with shell quoting
1267 v->cmdline_val.l[0] = cpystr("");
1268 v->cmdline_val.l[1] = NULL;
1269 if(oldlvalue)
1270 free_list_array(&oldlvalue);
1272 }else{
1273 v->cmdline_val.p = cpystr("");
1275 return 1;
1278 /*--value is non-empty--*/
1279 if(*value == '"' && !v->is_list){
1280 value++;
1281 for(p1 = value; *p1 && *p1 != '"'; p1++);
1282 if(*p1 == '"')
1283 *p1 = '\0';
1284 else
1285 removing_trailing_white_space(value);
1286 }else{
1287 removing_trailing_white_space(value);
1290 if(v->is_list){
1291 int was_quoted = 0;
1292 char *error = NULL;
1294 count = 1;
1295 for(p1=value; *p1; p1++){ /* generous count of list elements */
1296 if(*p1 == '"') /* ignore ',' if quoted */
1297 was_quoted = (was_quoted) ? 0 : 1;
1299 if(*p1 == ',' && !was_quoted)
1300 count++;
1303 lvalue = parse_list(value, count, 0, &error);
1304 if(error){
1305 char buf[MAILTMPLEN];
1307 snprintf(buf, sizeof(buf), "%s in %s = \"%s\"\n", error, v->name, value);
1308 buf[sizeof(buf)-1] = '\0';
1309 exceptional_exit(buf, -1);
1312 * Special case: turn "" strings into empty strings.
1313 * This allows users to turn off default lists. For example,
1314 * if smtp-server is set then a user could override smtp-server
1315 * with smtp-server="".
1317 for(i = 0; lvalue[i]; i++)
1318 if(lvalue[i][0] == '"' &&
1319 lvalue[i][1] == '"' &&
1320 lvalue[i][2] == '\0')
1321 lvalue[i][0] = '\0';
1324 if(v->is_list){
1325 if(oldlvalue){
1326 char **combinedlvalue;
1327 int j;
1329 /* combine old and new cmdline lists */
1330 for(count = 0, i = 0; oldlvalue[i]; i++, count++)
1333 for(i = 0; lvalue && lvalue[i]; i++, count++)
1336 combinedlvalue = (char **) fs_get((count+1) * sizeof(char *));
1337 memset(combinedlvalue, 0, (count+1) * sizeof(*combinedlvalue));
1339 for(i = 0, j = 0; oldlvalue[i]; i++, j++)
1340 combinedlvalue[j] = cpystr(oldlvalue[i]);
1342 for(i = 0; lvalue && lvalue[i]; i++, j++)
1343 combinedlvalue[j] = cpystr(lvalue[i]);
1345 v->cmdline_val.l = combinedlvalue;
1347 fs_give((void **) &oldlvalue);
1348 if(lvalue)
1349 fs_give((void **) &lvalue);
1351 else
1352 v->cmdline_val.l = lvalue;
1354 else
1355 v->cmdline_val.p = cpystr(value);
1357 return 1;