* New version 2.21.999
[alpine.git] / alpine / arg.c
blobb350b8fc14a5314a606d0358b73600f5cde95c88
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 2013-2018 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
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 SMIME
59 static char args_err_missing_smimedir[] = N_("missing argument for option \"-smimedir\"");
60 static char args_err_non_abs_smimedir[] = N_("argument to \"-smimedir\" should be fully-qualified");
61 #endif /* SMIME */
62 #ifdef PASSFILE
63 static char args_err_missing_passfile[] = N_("missing argument for option \"-passfile\"");
64 static char args_err_non_abs_passfile[] = N_("argument to \"-passfile\" should be fully-qualified");
65 #ifdef SMIME
66 static char args_err_missing_pwdcertdir[] = N_("missing argument for option \"-pwdcertdir\"");
67 static char args_err_non_abs_pwdcertdir[] = N_("argument to \"-pwdcertdir\" should be fully-qualified");
68 #endif /* SMIME inside PASSFILE */
69 #endif
70 static char args_err_missing_sort[] = N_("missing argument for option \"-sort\"");
71 static char args_err_missing_flag_arg[] = N_("missing argument for flag \"%c\"");
72 static char args_err_missing_flag_num[] = N_("Non numeric argument for flag \"%c\"");
73 static char args_err_missing_debug_num[] = N_("Non numeric argument for \"%s\"");
74 static char args_err_missing_url[] = N_("missing URL for \"-url\"");
75 static char args_err_missing_attachment[] = N_("missing attachment for \"%s\"");
76 static char args_err_conflict[] = N_("conflicting action: \"%s\"");
77 static char args_err_unknown[] = N_("unknown flag \"%c\"");
78 static char args_err_I_error[] = N_("-I argument \"%s\": %s");
79 static char args_err_d_error[] = N_("-d argument \"%s\": %s");
80 static char args_err_internal[] = "%s";
81 static char args_err_missing_copyprc[] = N_("missing argument for option \"-copy_pinerc\"\nUsage: pine -copy_pinerc <local_pinerc> <remote_pinerc>");
82 static char args_err_missing_copyabook[] = N_("missing argument for option \"-copy_abook\"\nUsage: pine -copy_abook <local_abook> <remote_abook>");
85 static char *args_pine_args[] = {
86 N_("Possible Starting Arguments for Alpine program:"),
87 "",
88 N_(" Argument\tMeaning"),
89 N_(" <addrs>...\tGo directly into composer sending to given address"),
90 N_("\t\tList multiple addresses with a single space between them."),
91 N_("\t\tStandard input redirection is allowed with addresses."),
92 N_("\t\tNote: Places addresses in the \"To\" field only."),
93 N_(" -attach <file>\tGo directly into composer with given file"),
94 N_(" -attachlist <file-list>"),
95 N_(" -attach_and_delete <file>"),
96 N_("\t\tGo to composer, attach file, delete when finished"),
97 N_("\t\tNote: Attach options can't be used if -f, -F"),
98 N_("\t\tadded to Attachment list. Attachlist must be the last"),
99 N_("\t\toption on the command line"),
100 N_(" -bail\t\tExit if pinerc file doesn't already exist"),
101 #ifdef DEBUG
102 N_(" -d n\t\tDebug - set debug level to 'n', or use the following:"),
103 N_(" -d keywords...\tflush,timestamp,imap=0..4,tcp,numfiles=0..31,verbose=0..9"),
104 #endif
105 N_(" -f <folder>\tFolder - give folder name to open"),
106 N_(" -c <number>\tContext - which context to apply to -f arg"),
107 N_(" -F <file>\tFile - give file name to open and page through and"),
108 N_("\t\tforward as email."),
109 N_(" -h \t\tHelp - give this list of options"),
110 N_(" -k \t\tKeys - Force use of function keys"),
111 N_(" -z \t\tSuspend - allow use of ^Z suspension"),
112 N_(" -r \t\tRestricted - can only send mail to oneself"),
113 N_(" -sort <sort>\tSort - Specify sort order of folder:"),
114 N_("\t\t\tarrival, subject, threaded, orderedsubject, date,"),
115 N_("\t\t\tfrom, size, score, to, cc, /reverse"),
116 N_(" -i\t\tIndex - Go directly to index, bypassing main menu"),
117 N_(" -I <keystroke_list> Initial keystrokes to be executed"),
118 N_(" -n <number>\tEntry in index to begin on"),
119 N_(" -o \t\tReadOnly - Open first folder read-only"),
120 N_(" -conf\t\tConfiguration - Print out fresh global configuration. The"),
121 N_("\t\tvalues of your global configuration affect all Alpine users"),
122 N_("\t\ton your system unless they have overridden the values in their"),
123 N_("\t\tpinerc files."),
124 N_(" -pinerc <file>\tConfiguration - Put fresh pinerc configuration in <file>"),
125 N_(" -p <pinerc>\tUse alternate .pinerc file"),
126 #if !defined(DOS) && !defined(OS2)
127 N_(" -P <pine.conf>\tUse alternate pine.conf file"),
128 #else
129 N_(" -aux <aux_files_dir>\tUse this with remote pinerc"),
130 N_(" -P <pine.conf>\tUse pine.conf file for default settings"),
131 N_(" -nosplash \tDisable the PC-Alpine splash screen"),
132 #endif
134 #if defined(APPLEKEYCHAIN) || (WINCRED > 0)
135 N_(" -erase_stored_passwords\tEliminate any stored passwords"),
136 #endif
138 #ifdef SMIME
139 N_(" -smimedir <fully_qualified_path>\tdirectory where smime personal certificates are saved"),
140 #endif /* SMIME */
142 #ifdef PASSFILE
143 N_(" -passfile <fully_qualified_filename>\tSet the password file to something other"),
144 N_("\t\tthan the default"),
145 #ifdef SMIME
146 N_(" -pwdcertdir <fully_qualified_path>\tSet the directory to store a personal"),
147 N_("\t\tkey and certificate to encrypt and decrypt your password file."),
148 #endif /* SMIME inside PASSFILE */
149 #endif /* PASSFILE */
151 #ifdef LOCAL_PASSWD_CACHE
152 N_(" -nowrite_password_cache\tRead from a password cache if there is one, but"),
153 N_("\t\t\t\tnever offer to write a password to the cache"),
154 #endif /* LOCAL_PASSWD_CACHE */
156 N_(" -x <config>\tUse configuration exceptions in <config>."),
157 N_("\t\tExceptions are used to override your default pinerc"),
158 N_("\t\tsettings for a particular platform, can be a local file or"),
159 N_("\t\ta remote folder."),
160 N_(" -v \t\tVersion - show version information"),
161 N_(" -version\tVersion - show version information"),
162 N_(" -supported\tList supported options"),
163 N_(" -url <url>\tOpen the given URL"),
164 N_("\t\tNote: Can't be used if -f, -F"),
165 N_("\t\tStandard input redirection is not allowed with URLs."),
166 N_("\t\tFor mailto URLs, 'body='text should be used in place of"),
167 N_("\t\tinput redirection."),
168 N_(" -copy_pinerc <local_pinerc> <remote_pinerc> copy local pinerc to remote"),
169 N_(" -copy_abook <local_abook> <remote_abook> copy local addressbook to remote"),
170 N_(" -convert_sigs -p <pinerc> convert signatures to literal signatures"),
171 #if defined(_WINDOWS)
172 N_(" -install \tPrompt for some basic setup information"),
173 N_(" -uninstall \tRemove traces of Alpine from Windows system settings"),
174 N_(" -registry <cmd>\tWhere cmd is set,noset,clear,clearsilent,dump"),
175 #endif
176 " -<option>=<value> Assign <value> to the pinerc option <option>",
177 "\t\t e.g. -signature-file=sig1",
178 "\t\t e.g. -color-style=no-color",
179 "\t\t e.g. -feature-list=enable-sigdashes",
180 "\t\t Note: feature-list is additive.",
181 "\t\t You may leave off the \"feature-list=\" part of that,",
182 "\t\t e.g. -enable-sigdashes",
183 NULL
189 * Parse the command line args.
191 * Args: pine_state -- The pine_state structure to put results in
192 * argc, argv -- The obvious
193 * addrs -- Pointer to address list that we set for caller
195 * Result: command arguments parsed
196 * possible printing of help for command line
197 * various flags in pine_state set
198 * returns the string name of the first folder to open
199 * addrs is set
201 void
202 pine_args(struct pine *pine_state, int argc, char **argv, ARGDATA_S *args)
204 register int ac;
205 register char **av;
206 int c;
207 char *str;
208 char *cmd_list = NULL;
209 char *debug_str = NULL;
210 char *sort = NULL;
211 char *pinerc_file = NULL;
212 char *lc = NULL;
213 int do_help = 0;
214 int do_conf = 0;
215 int usage = 0;
216 int do_use_fk = 0;
217 int do_can_suspend = 0;
218 int do_version = 0;
219 struct variable *vars = pine_state->vars;
221 ac = argc;
222 av = argv;
223 memset(args, 0, sizeof(ARGDATA_S));
224 args->action = aaFolder;
226 pine_state->pine_name = (lc = last_cmpnt(argv[0])) ? lc : (lc = argv[0]);
227 #ifdef DOS
228 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%.*s", pine_state->pine_name - argv[0], argv[0]);
229 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
230 pine_state->pine_dir = cpystr(tmp_20k_buf);
231 #endif
233 /* while more arguments with leading - */
234 Loop: while(--ac > 0)
235 if(**++av == '-'){
236 /* while more chars in this argument */
237 while(*++*av){
238 /* check for pinerc options */
239 if(pinerc_cmdline_opt(*av)){
240 goto Loop; /* done with this arg, go to next */
242 /* then other multi-char options */
243 else if(strcmp(*av, "conf") == 0){
244 do_conf = 1;
245 goto Loop; /* done with this arg, go to next */
247 else if(strcmp(*av, "pinerc") == 0){
248 if(--ac)
249 pinerc_file = *++av;
250 else{
251 display_args_err(_(args_err_missing_pinerc), NULL, 1);
252 ++usage;
255 goto Loop;
257 #if defined(DOS) || defined(OS2)
258 else if(strcmp(*av, "aux") == 0){
259 if(--ac){
260 if((str = *++av) != NULL)
261 pine_state->aux_files_dir = cpystr(str);
263 else{
264 display_args_err(_(args_err_missing_aux), NULL, 1);
265 ++usage;
268 goto Loop;
270 else if(strcmp(*av, "nosplash") == 0)
271 goto Loop; /* already taken care of in WinMain */
272 #endif
274 #if defined(APPLEKEYCHAIN) || (WINCRED > 0)
275 else if(strcmp(*av, "erase_stored_passwords") == 0){
276 #if (WINCRED > 0)
277 erase_windows_credentials();
278 #else
279 macos_erase_keychain();
280 #endif
281 goto Loop;
283 #endif /* defined(APPLEKEYCHAIN) || (WINCRED > 0) */
285 #ifdef SMIME
286 else if(strcmp(*av, "smimedir") == 0){
287 if(--ac){
288 if((str = *++av) != NULL){
289 if(!is_absolute_path(str)){
290 display_args_err(_(args_err_non_abs_smimedir),
291 NULL, 1);
292 ++usage;
294 else{
295 if(pine_state->smimedir)
296 fs_give((void **)&pine_state->smimedir);
298 pine_state->smimedir = cpystr(str);
302 else{
303 display_args_err(_(args_err_missing_smimedir), NULL, 1);
304 ++usage;
307 goto Loop;
309 #endif /* SMIME */
310 #ifdef PASSFILE
311 else if(strcmp(*av, "passfile") == 0){
312 if(--ac){
313 if((str = *++av) != NULL){
314 if(!is_absolute_path(str)){
315 display_args_err(_(args_err_non_abs_passfile),
316 NULL, 1);
317 ++usage;
319 else{
320 if(pine_state->passfile)
321 fs_give((void **)&pine_state->passfile);
323 pine_state->passfile = cpystr(str);
327 else{
328 display_args_err(_(args_err_missing_passfile), NULL, 1);
329 ++usage;
332 goto Loop;
334 #ifdef SMIME
335 else if(strcmp(*av, "pwdcertdir") == 0){
336 if(--ac){
337 if((str = *++av) != NULL){
338 if(!is_absolute_path(str)){
339 display_args_err(_(args_err_non_abs_pwdcertdir),
340 NULL, 1);
341 ++usage;
343 else{
344 if(pine_state->pwdcertdir)
345 fs_give((void **)&pine_state->pwdcertdir);
347 pine_state->pwdcertdir = cpystr(str);
351 else{
352 display_args_err(_(args_err_missing_pwdcertdir), NULL, 1);
353 ++usage;
356 goto Loop;
358 #endif /* SMIME inside PASSFILE */
359 #endif /* PASSFILE */
361 #ifdef LOCAL_PASSWD_CACHE
362 else if(strcmp(*av, "nowrite_password_cache") == 0){
363 ps_global->nowrite_password_cache = 1;
364 goto Loop;
366 #endif /* LOCAL_PASSWD_CACHE */
368 else if(strcmp(*av, "convert_sigs") == 0){
369 ps_global->convert_sigs = 1;
370 goto Loop;
372 else if(strcmp(*av, "supported") == 0){
373 ps_global->dump_supported_options = 1;
374 goto Loop;
376 else if(strcmp(*av, "copy_pinerc") == 0){
377 if(args->action == aaFolder && !args->data.folder){
378 args->action = aaPrcCopy;
379 if(ac > 2){
380 ac -= 2;
381 args->data.copy.local = *++av;
382 args->data.copy.remote = *++av;
384 else{
385 display_args_err(_(args_err_missing_copyprc), NULL, 1);
386 ++usage;
389 else{
390 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-copy_pinerc");
391 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
392 display_args_err(tmp_20k_buf, NULL, 1);
393 ++usage;
396 goto Loop;
398 else if(strcmp(*av, "copy_abook") == 0){
399 if(args->action == aaFolder && !args->data.folder){
400 args->action = aaAbookCopy;
401 if(ac > 2){
402 ac -= 2;
403 args->data.copy.local = *++av;
404 args->data.copy.remote = *++av;
406 else{
407 display_args_err(_(args_err_missing_copyabook), NULL, 1);
408 ++usage;
411 else{
412 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-copy_abook");
413 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
414 display_args_err(tmp_20k_buf, NULL, 1);
415 ++usage;
418 goto Loop;
420 else if(strcmp(*av, "sort") == 0){
421 if(--ac){
422 sort = *++av;
423 COM_SORT_KEY = cpystr(sort);
425 else{
426 display_args_err(_(args_err_missing_sort), NULL, 1);
427 ++usage;
430 goto Loop;
432 else if(strcmp(*av, "url") == 0){
433 if(args->action == aaFolder && !args->data.folder){
434 args->action = aaURL;
435 if(--ac){
436 args->url = cpystr(*++av);
438 else{
439 display_args_err(_(args_err_missing_url), NULL, 1);
440 ++usage;
443 else{
444 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-url");
445 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
446 display_args_err(tmp_20k_buf, NULL, 1);
447 ++usage;
450 goto Loop;
452 else if(strcmp(*av, "attach") == 0){
453 if((args->action == aaFolder && !args->data.folder)
454 || args->action == aaMail
455 || args->action == aaURL){
456 if(args->action != aaURL)
457 args->action = aaMail;
458 if(--ac){
459 args_add_attach(&args->data.mail.attachlist,
460 *++av, FALSE);
462 else{
463 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_attachment), "-attach");
464 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
465 display_args_err(tmp_20k_buf, NULL, 1);
466 ++usage;
469 else{
470 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-attach");
471 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
472 display_args_err(tmp_20k_buf, NULL, 1);
473 ++usage;
476 goto Loop;
478 else if(strcmp(*av, "attachlist") == 0){
479 if((args->action == aaFolder && !args->data.folder)
480 || args->action == aaMail
481 || args->action == aaURL){
482 if(args->action != aaURL)
483 args->action = aaMail;
484 if(ac - 1){
486 if(can_access(*(av+1), READ_ACCESS) == 0){
487 ac--;
488 args_add_attach(&args->data.mail.attachlist,
489 *++av, FALSE);
491 else
492 break;
494 while(ac);
496 else{
497 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_attachment), "-attachList");
498 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
499 display_args_err(tmp_20k_buf, NULL, 1);
500 ++usage;
503 else{
504 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-attachList");
505 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
506 display_args_err(tmp_20k_buf, NULL, 1);
507 ++usage;
510 goto Loop;
512 else if(strcmp(*av, "attach_and_delete") == 0){
513 if((args->action == aaFolder && !args->data.folder)
514 || args->action == aaMail
515 || args->action == aaURL){
516 if(args->action != aaURL)
517 args->action = aaMail;
518 if(--ac){
519 args_add_attach(&args->data.mail.attachlist,
520 *++av, TRUE);
522 else{
523 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_attachment), "-attach_and_delete");
524 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
525 display_args_err(tmp_20k_buf, NULL, 1);
526 ++usage;
529 else{
530 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-attach_and_delete");
531 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
532 display_args_err(tmp_20k_buf, NULL, 1);
533 ++usage;
536 goto Loop;
538 else if(strcmp(*av, "bail") == 0){
539 pine_state->exit_if_no_pinerc = 1;
540 goto Loop;
542 else if(strcmp(*av, "version") == 0){
543 do_version = 1;
544 goto Loop;
546 #ifdef _WINDOWS
547 else if(strcmp(*av, "install") == 0){
548 pine_state->install_flag = 1;
549 pine_state->update_registry = UREG_ALWAYS_SET;
550 goto Loop;
552 else if(strcmp(*av, "uninstall") == 0){
554 * Blast password cache, clear registry settings
556 #if (WINCRED > 0)
557 erase_windows_credentials();
558 #endif
559 mswin_reg(MSWR_OP_BLAST, MSWR_NULL, NULL, 0);
560 exit(0);
562 else if(strcmp(*av, "registry") == 0){
563 if(--ac){
564 if(!strucmp(*++av, "set")){
565 pine_state->update_registry = UREG_ALWAYS_SET;
567 else if(!strucmp(*av, "noset")){
568 pine_state->update_registry = UREG_NEVER_SET;
570 else if(!strucmp(*av, "clear")){
571 if(!mswin_reg(MSWR_OP_BLAST, MSWR_NULL, NULL, 0))
572 display_args_err(
573 _("Alpine related Registry values removed."),
574 NULL, 0);
575 else
576 display_args_err(
577 _("Not all Alpine related Registry values could be removed"),
578 NULL, 0);
579 exit(0);
581 else if(!strucmp(*av, "clearsilent")){
582 mswin_reg(MSWR_OP_BLAST, MSWR_NULL, NULL, 0);
583 exit(0);
585 else if(!strucmp(*av, "dump")){
586 char **pRegistry = mswin_reg_dump();
588 if(pRegistry){
589 display_args_err(NULL, pRegistry, 0);
590 free_list_array(&pRegistry);
592 exit(0);
594 else{
595 display_args_err(_("unknown registry command"),
596 NULL, 1);
597 ++usage;
600 else{
601 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_flag_arg), c);
602 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
603 display_args_err(tmp_20k_buf, NULL, 1);
604 ++usage;
607 goto Loop;
609 #endif
610 /* single char flags */
611 else{
612 switch(c = **av){
613 case 'h':
614 do_help = 1;
615 break; /* break back to inner-while */
616 case 'k':
617 do_use_fk = 1;
618 break;
619 case 'z':
620 do_can_suspend = 1;
621 break;
622 case 'r':
623 pine_state->restricted = 1;
624 break;
625 case 'o':
626 pine_state->open_readonly_on_startup = 1;
627 break;
628 case 'i':
629 pine_state->start_in_index = 1;
630 break;
631 case 'v':
632 do_version = 1;
633 break; /* break back to inner-while */
634 /* these take arguments */
635 case 'f': case 'F': case 'p': case 'I':
636 case 'c': case 'd': case 'P': case 'x': /* string args */
637 case 'n': /* integer args */
638 if(*++*av)
639 str = *av;
640 else if(--ac)
641 str = *++av;
642 else if(c == 'f' || c == 'F')
643 str = "";
644 else{
645 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_flag_arg), c);
646 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
647 display_args_err(tmp_20k_buf, NULL, 1);
648 ++usage;
649 goto Loop;
652 switch(c){
653 case 'f':
654 if(args->action == aaFolder && !args->data.folder){
655 args->data.folder = cpystr(str);
657 else{
658 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-f");
659 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
660 display_args_err(tmp_20k_buf, NULL, 1);
661 usage++;
664 break;
665 case 'F':
666 if(args->action == aaFolder && !args->data.folder){
667 args->action = aaMore;
668 args->data.file = cpystr(str);
670 else{
671 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-F");
672 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
673 display_args_err(tmp_20k_buf, NULL, 1);
674 usage++;
677 break;
678 case 'd':
679 debug_str = str;
680 break;
681 case 'I':
682 cmd_list = str;
683 break;
684 case 'p':
685 if(str){
686 char path[MAXPATH], dir[MAXPATH];
688 if(IS_REMOTE(str) || is_absolute_path(str)){
689 strncpy(path, str, sizeof(path)-1);
690 path[sizeof(path)-1] = '\0';
692 else{
693 if(getcwd(dir, sizeof(path)) != NULL)
694 build_path(path, dir, str, sizeof(path));
695 else
696 alpine_panic(_("getcwd() call failed while parsing argument (1)"));
700 * Pinerc used to be the name of the pinerc
701 * file. Now, since the pinerc can be remote,
702 * we've replaced the variable pinerc with the
703 * structure prc. Unfortunately, other parts of
704 * pine rely on the fact that pinerc is the
705 * name of the pinerc _file_, and use the
706 * directory that the pinerc file is located
707 * in for their own purposes. We keep that so
708 * things will keep working.
711 if(!IS_REMOTE(path)){
712 if(pine_state->pinerc)
713 fs_give((void **)&pine_state->pinerc);
715 pine_state->pinerc = cpystr(path);
719 * Last one wins. This would be the place where
720 * we put multiple pinercs in a list if we
721 * were to allow that.
723 if(pine_state->prc)
724 free_pinerc_s(&pine_state->prc);
726 pine_state->prc = new_pinerc_s(path);
729 break;
730 case 'P':
731 if(str){
732 char path[MAXPATH], dir[MAXPATH];
734 if(IS_REMOTE(str) || is_absolute_path(str)){
735 strncpy(path, str, sizeof(path)-1);
736 path[sizeof(path)-1] = '\0';
738 else{
739 if(getcwd(dir, sizeof(path)) != NULL)
740 build_path(path, dir, str, sizeof(path));
741 else
742 alpine_panic(_("getcwd() call failed while parsing argument (2)"));
745 if(pine_state->pconf)
746 free_pinerc_s(&pine_state->pconf);
748 pine_state->pconf = new_pinerc_s(path);
751 break;
752 case 'x':
753 if(str)
754 pine_state->exceptions = cpystr(str);
756 break;
757 case 'c':
758 if(!isdigit((unsigned char)str[0])){
759 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
760 _(args_err_missing_flag_num), c);
761 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
762 display_args_err(tmp_20k_buf, NULL, 1);
763 ++usage;
764 break;
767 pine_state->init_context = (short) atoi(str);
768 break;
770 case 'n':
771 if(!isdigit((unsigned char)str[0])){
772 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
773 _(args_err_missing_flag_num), c);
774 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
775 display_args_err(tmp_20k_buf, NULL, 1);
776 ++usage;
777 break;
780 pine_state->start_entry = atoi(str);
781 if(pine_state->start_entry < 1)
782 pine_state->start_entry = 1;
784 break;
787 goto Loop;
789 default:
790 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_unknown), c);
791 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
792 display_args_err(tmp_20k_buf, NULL, 1);
793 ++usage;
794 break;
799 else if(args->action == aaMail
800 || (args->action == aaFolder && !args->data.folder)){
801 STRLIST_S *stp, **slp;
803 args->action = aaMail;
805 stp = new_strlist(*av);
807 for(slp = &args->data.mail.addrlist; *slp; slp = &(*slp)->next)
810 *slp = stp;
812 else{
813 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), *av);
814 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
815 display_args_err(tmp_20k_buf, NULL, 1);
816 usage++;
819 if(cmd_list){
820 int commas = 0;
821 char *p = cmd_list;
822 char *error = NULL;
824 while(*p++)
825 if(*p == ',')
826 ++commas;
828 COM_INIT_CMD_LIST = parse_list(cmd_list, commas+1, 0, &error);
829 if(error){
830 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_I_error), cmd_list, error);
831 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
832 display_args_err(tmp_20k_buf, NULL, 1);
833 exit(-1);
837 #ifdef DEBUG
838 pine_state->debug_nfiles = NUMDEBUGFILES;
839 #endif
840 if(debug_str && process_debug_str(debug_str))
841 usage++;
843 if(lc && strncmp(lc, ALPINE_FKEY_NAME, sizeof(ALPINE_FKEY_NAME) - 1) == 0)
844 do_use_fk = 1;
846 if(do_use_fk || do_can_suspend){
847 char list[500];
848 int commas = 0;
849 char *p = list;
850 char *error = NULL;
852 list[0] = '\0';
854 if(do_use_fk){
855 if(list[0]){
856 strncat(list, ",", sizeof(list)-strlen(list)-1);
857 list[sizeof(list)-1] = '\0';
860 strncat(list, "use-function-keys", sizeof(list)-strlen(list)-1);
861 list[sizeof(list)-1] = '\0';
864 if(do_can_suspend){
865 if(list[0]){
866 strncat(list, ",", sizeof(list)-strlen(list)-1);
867 list[sizeof(list)-1] = '\0';
870 strncat(list, "enable-suspend", sizeof(list)-strlen(list)-1);
871 list[sizeof(list)-1] = '\0';
874 while(*p++)
875 if(*p == ',')
876 ++commas;
878 pine_state->feat_list_back_compat = parse_list(list,commas+1,0,&error);
879 if(error){
880 snprintf(tmp_20k_buf, SIZEOF_20KBUF, args_err_internal, error);
881 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
882 display_args_err(tmp_20k_buf, NULL, 1);
883 exit(-1);
887 if(((do_conf ? 1 : 0)+(pinerc_file ? 1 : 0)) > 1){
888 display_args_err(_("May only have one of -conf and -pinerc"),
889 NULL, 1);
890 exit(-1);
893 if(do_help || usage)
894 args_help();
896 if(usage)
897 exit(-1);
899 if(do_version){
900 extern char datestamp[], hoststamp[];
901 char rev[128];
903 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Alpine %s (%s %s) built %s on %s",
904 ALPINE_VERSION,
905 SYSTYPE ? SYSTYPE : "?",
906 get_alpine_revision_string(rev, sizeof(rev)),
907 datestamp, hoststamp);
908 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
909 display_args_err(tmp_20k_buf, NULL, 0);
910 exit(0);
913 if(do_conf)
914 dump_global_conf();
916 if(pinerc_file)
917 dump_new_pinerc(pinerc_file);
920 * Don't NULL out argv[0] or we might crash in unexpected ways. In OS X, we were
921 * crashing when opening attachments because of this.
923 if(ac <= 0 && av != argv)
924 *av = NULL;
929 * Returns 0 if ok, -1 if error.
932 process_debug_str(char *debug_str)
934 int i, usage = 0;
935 int commas = 0;
936 int new_style_debug_arg = 0;
937 char *q = debug_str;
938 char *error = NULL;
939 char **list, **p;
941 #ifdef DEBUG
942 if(debug_str){
943 if(!isdigit((unsigned char)debug_str[0]))
944 new_style_debug_arg++;
946 if(new_style_debug_arg){
947 while(*q++)
948 if(*q == ',')
949 ++commas;
951 list = parse_list(debug_str, commas+1, 0, &error);
952 if(error){
953 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_d_error), debug_str, error);
954 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
955 display_args_err(tmp_20k_buf, NULL, 1);
956 return(-1);
959 if(list){
960 for(p = list; *p; p++){
961 if(struncmp(*p, "timestamp", 9) == 0){
962 ps_global->debug_timestamp = 1;
964 else if(struncmp(*p, "imap", 4) == 0){
965 q = *p + 4;
966 if(!*q || !*(q+1) || *q != '=' ||
967 !isdigit((unsigned char)*(q+1))){
968 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_debug_num), *p);
969 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
970 display_args_err(tmp_20k_buf, NULL, 1);
971 usage = -1;
973 else{
974 i = atoi(q+1);
975 ps_global->debug_imap = MIN(5,MAX(0,i));
978 else if(struncmp(*p, "flush", 5) == 0){
979 ps_global->debug_flush = 1;
981 else if(struncmp(*p, "tcp", 3) == 0
982 || struncmp(*p, "tcpdebug", 8) == 0){
983 ps_global->debug_tcp = 1;
985 else if(struncmp(*p, "verbose", 7) == 0){
986 q = *p + 7;
987 if(!*q || !*(q+1) || *q != '=' ||
988 !isdigit((unsigned char)*(q+1))){
989 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_debug_num), *p);
990 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
991 display_args_err(tmp_20k_buf, NULL, 1);
992 usage = -1;
994 else
995 debug = atoi(q+1);
997 else if(struncmp(*p, "numfiles", 8) == 0){
998 q = *p + 8;
999 if(!*q || !*(q+1) || *q != '=' ||
1000 !isdigit((unsigned char)*(q+1))){
1001 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_debug_num), *p);
1002 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1003 display_args_err(tmp_20k_buf, NULL, 1);
1004 usage = -1;
1006 else{
1007 i = atoi(q+1);
1008 ps_global->debug_nfiles = MIN(31,MAX(0,i));
1011 else if(struncmp(*p, "malloc", 6) == 0){
1012 q = *p + 6;
1013 if(!*q || !*(q+1) || *q != '=' ||
1014 !isdigit((unsigned char)*(q+1))){
1015 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_debug_num), *p);
1016 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1017 display_args_err(tmp_20k_buf, NULL, 1);
1018 usage = -1;
1020 else{
1021 i = atoi(q+1);
1022 ps_global->debug_malloc = MIN(63,MAX(0,i));
1025 #if defined(ENABLE_LDAP) && defined(LDAP_DEBUG)
1026 else if(struncmp(*p, "ldap", 4) == 0){
1027 q = *p + 4;
1028 if(!*q || !*(q+1) || *q != '=' ||
1029 !isdigit((unsigned char)*(q+1))){
1030 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_debug_num), *p);
1031 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1032 display_args_err(tmp_20k_buf, NULL, 1);
1033 usage = -1;
1035 else{
1036 i = atoi(q+1);
1037 ldap_debug = i;
1040 #endif /* LDAP */
1041 else{
1042 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("unknown debug keyword \"%s\""), *p);
1043 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1044 display_args_err(tmp_20k_buf, NULL, 1);
1045 usage = -1;
1049 free_list_array(&list);
1052 else{
1053 debug = atoi(debug_str);
1054 if(debug > 9)
1055 ps_global->debug_imap = 5;
1056 else if(debug > 7)
1057 ps_global->debug_imap = 4;
1058 else if(debug > 6)
1059 ps_global->debug_imap = 3;
1060 else if(debug > 4)
1061 ps_global->debug_imap = 2;
1062 else if(debug > 2)
1063 ps_global->debug_imap = 1;
1065 if(debug > 7)
1066 ps_global->debug_timestamp = 1;
1068 if(debug > 8)
1069 ps_global->debug_flush = 1;
1073 if(!new_style_debug_arg){
1074 #ifdef CSRIMALLOC
1075 ps_global->debug_malloc =
1076 (debug <= DEFAULT_DEBUG) ? 1 : (debug < 9) ? 2 : 3;
1077 #else /* !CSRIMALLOC */
1078 #ifdef HAVE_SMALLOC
1079 if(debug > 8)
1080 ps_global->debug_malloc = 2;
1081 #else /* !HAVE_SMALLOC */
1082 #ifdef NXT
1083 if(debug > 8)
1084 ps_global->debug_malloc = 32;
1085 #endif /* NXT */
1086 #endif /* HAVE_SMALLOC */
1087 #endif /* CSRIMALLOC */
1090 #else /* !DEBUG */
1092 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("unknown flag \"d\", debugging not compiled in"));
1093 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1094 display_args_err(tmp_20k_buf, NULL, 1);
1095 usage = -1;
1097 #endif /* !DEBUG */
1099 return(usage);
1103 void
1104 args_add_attach(PATMT **alpp, char *s, int deleted)
1106 PATMT *atmp, **atmpp;
1108 atmp = (PATMT *) fs_get(sizeof(PATMT));
1109 memset(atmp, 0, sizeof(PATMT));
1110 atmp->filename = cpystr(s);
1112 #if defined(DOS) || defined(OS2)
1113 (void) removing_quotes(atmp->filename);
1114 #endif
1116 if(deleted)
1117 atmp->flags |= A_TMP;
1119 for(atmpp = alpp; *atmpp; atmpp = &(*atmpp)->next)
1122 *atmpp = atmp;
1126 /*----------------------------------------------------------------------
1127 print a few lines of help for command line arguments
1129 Args: none
1131 Result: prints help messages
1132 ----------------------------------------------------------------------*/
1133 void
1134 args_help(void)
1136 char *pp[2];
1137 #ifndef _WINDOWS
1138 char **a;
1139 #endif
1141 pp[1] = NULL;
1143 /** print out possible starting arguments... **/
1146 * display_args_err expects already translated input
1147 * so we need to translate a line at a time. Since
1148 * the 1st and 3rd args are zero it is ok to call it
1149 * a line at a time.
1151 * Windows expects the full block of text so we'll pass
1152 * it as such.
1154 #ifndef _WINDOWS
1155 for(a=args_pine_args; a && *a; a++){
1156 pp[0] = _(*a);
1157 display_args_err(NULL, pp, 0);
1159 #else
1160 display_args_err(NULL, args_pine_args, 0);
1161 #endif
1163 exit(1);
1167 /*----------------------------------------------------------------------
1168 write argument error to the display...
1170 Args: none
1172 Result: prints help messages
1173 ----------------------------------------------------------------------*/
1174 void
1175 display_args_err(char *s, char **a, int err)
1177 char errstr[256], *errp;
1178 FILE *fp = err ? stderr : stdout;
1181 if(err && s){
1182 snprintf(errp = errstr, sizeof(errstr), "%s: %s", _("Argument Error"), s);
1183 errstr[sizeof(errstr)-1] = '\0';
1185 else
1186 errp = s;
1188 #ifdef _WINDOWS
1189 if(errp)
1190 mswin_messagebox(errp, err);
1192 if(a && *a){
1193 os_argsdialog(a);
1195 #else
1196 if(errp)
1197 fprintf(fp, "%s\n", errp);
1199 while(a && *a)
1200 fprintf(fp, "%s\n", *a++);
1201 #endif
1206 * The argument is an argument from the command line. We check to see
1207 * if it is specifying an alternate value for one of the options that is
1208 * normally set in pinerc. If so, we return 1 and set the appropriate
1209 * values in the variables array.
1210 * The arg can be
1211 * varname=value
1212 * varname=value1,value2,value3
1213 * feature-list=featurename these are just a special
1214 * feature-list=featurename1,featurename2 case of above
1215 * featurename This is equivalent to above
1216 * no-featurename
1219 pinerc_cmdline_opt(char *arg)
1221 struct variable *v;
1222 char *p1 = NULL,
1223 *value,
1224 **oldlvalue = NULL,
1225 **lvalue;
1226 int i, count;
1228 if(!arg || !arg[0])
1229 return 0;
1231 if((value = strchr(arg, '=')) != NULL){
1232 i = value - arg;
1233 arg[i] = '\0';
1235 else
1236 i = -1;
1238 for(v = ps_global->vars; v->name != NULL; v++){
1239 if(v->is_used && strucmp(v->name, arg) == 0){
1240 p1 = arg + strlen(v->name);
1241 if(i > 0) arg[i] = '=';
1243 /*----- Skip to '=' -----*/
1244 while(*p1 && (*p1 == '\t' || *p1 == ' '))
1245 p1++;
1247 if(*p1 != '='){
1248 char buf[MAILTMPLEN];
1250 snprintf(buf, sizeof(buf), _("Missing \"=\" after -%s\n"), v->name);
1251 buf[sizeof(buf)-1] = '\0';
1252 exceptional_exit(buf, -1);
1255 p1++;
1256 break;
1261 if(i > 0) arg[i] = '=';
1263 /* no match, check for a feature name used directly */
1264 if(v->name == NULL){
1265 FEATURE_S *feat;
1266 char *featname;
1268 if(struncmp(arg, "no-", 3) == 0)
1269 featname = arg+3;
1270 else
1271 featname = arg;
1273 for(i = 0; (feat = feature_list(i)) != NULL; i++){
1274 if(strucmp(featname, feat->name) == 0){
1275 v = &(ps_global->vars)[V_FEATURE_LIST];
1276 p1 = arg;
1277 break;
1282 if(!p1)
1283 return 0;
1285 if(v->is_obsolete || !v->is_user){
1286 char buf[MAILTMPLEN];
1288 if(v->is_obsolete)
1289 snprintf(buf, sizeof(buf), _("Option \"%s\" is obsolete\n"), v->name);
1290 else
1291 snprintf(buf, sizeof(buf), _("Option \"%s\" is not user settable\n"), v->name);
1293 exceptional_exit(buf, -1);
1296 /* free mem */
1297 if(v->is_list){
1298 oldlvalue = v->cmdline_val.l;
1299 v->cmdline_val.l = NULL;
1301 else if(v->cmdline_val.p)
1302 fs_give((void **) &(v->cmdline_val.p));
1304 /*----- Matched a variable, get its value ----*/
1305 while(*p1 == ' ' || *p1 == '\t')
1306 p1++;
1307 value = p1;
1309 if(*value == '\0'){
1310 if(v->is_list){
1311 v->cmdline_val.l = (char **)fs_get(2 * sizeof(char *));
1313 * we let people leave off the quotes on command line so that
1314 * they don't have to mess with shell quoting
1316 v->cmdline_val.l[0] = cpystr("");
1317 v->cmdline_val.l[1] = NULL;
1318 if(oldlvalue)
1319 free_list_array(&oldlvalue);
1321 }else{
1322 v->cmdline_val.p = cpystr("");
1324 return 1;
1327 /*--value is non-empty--*/
1328 if(*value == '"' && !v->is_list){
1329 value++;
1330 for(p1 = value; *p1 && *p1 != '"'; p1++)
1332 if(*p1 == '"')
1333 *p1 = '\0';
1334 else
1335 removing_trailing_white_space(value);
1336 }else{
1337 removing_trailing_white_space(value);
1340 if(v->is_list){
1341 int was_quoted = 0;
1342 char *error = NULL;
1344 count = 1;
1345 for(p1=value; *p1; p1++){ /* generous count of list elements */
1346 if(*p1 == '"') /* ignore ',' if quoted */
1347 was_quoted = (was_quoted) ? 0 : 1;
1349 if(*p1 == ',' && !was_quoted)
1350 count++;
1353 lvalue = parse_list(value, count, 0, &error);
1354 if(error){
1355 char buf[MAILTMPLEN];
1357 snprintf(buf, sizeof(buf), "%s in %s = \"%s\"\n", error, v->name, value);
1358 buf[sizeof(buf)-1] = '\0';
1359 exceptional_exit(buf, -1);
1362 * Special case: turn "" strings into empty strings.
1363 * This allows users to turn off default lists. For example,
1364 * if smtp-server is set then a user could override smtp-server
1365 * with smtp-server="".
1367 for(i = 0; lvalue[i]; i++)
1368 if(lvalue[i][0] == '"' &&
1369 lvalue[i][1] == '"' &&
1370 lvalue[i][2] == '\0')
1371 lvalue[i][0] = '\0';
1374 if(v->is_list){
1375 if(oldlvalue){
1376 char **combinedlvalue;
1377 int j;
1379 /* combine old and new cmdline lists */
1380 for(count = 0, i = 0; oldlvalue[i]; i++, count++)
1383 for(i = 0; lvalue && lvalue[i]; i++, count++)
1386 combinedlvalue = (char **) fs_get((count+1) * sizeof(char *));
1387 memset(combinedlvalue, 0, (count+1) * sizeof(*combinedlvalue));
1389 for(i = 0, j = 0; oldlvalue[i]; i++, j++)
1390 combinedlvalue[j] = cpystr(oldlvalue[i]);
1392 for(i = 0; lvalue && lvalue[i]; i++, j++)
1393 combinedlvalue[j] = cpystr(lvalue[i]);
1395 v->cmdline_val.l = combinedlvalue;
1397 fs_give((void **) &oldlvalue);
1398 if(lvalue)
1399 fs_give((void **) &lvalue);
1401 else
1402 v->cmdline_val.l = lvalue;
1404 else
1405 v->cmdline_val.p = cpystr(value);
1407 return 1;