* Implement a different way to delete a password from the cache.
[alpine.git] / alpine / arg.c
blobc6df4249199ce89151edf763cc73dfb77ca1d264
1 /*
2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2008 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 /*======================================================================
16 Command line argument parsing functions
18 ====*/
20 #include "headers.h"
22 #include "../pith/state.h"
23 #include "../pith/init.h"
24 #include "../pith/conf.h"
25 #include "../pith/list.h"
26 #include "../pith/util.h"
27 #include "../pith/help.h"
29 #include "imap.h"
31 #include "arg.h"
32 #include "xoauth2conf.h"
34 int process_debug_str(char *);
35 void args_add_attach(PATMT **, char *, int);
36 int pinerc_cmdline_opt(char *);
37 void display_config_options(char *, char *);
39 extern char configoptions[];
42 * Name started as to invoke function key mode
44 #define ALPINE_FKEY_NAME "alpinef"
47 * Various error and informational strings..
49 /* TRANSLATORS: This is a list of errors printed when something goes wrong
50 early on with the argument list. Be careful not to change literal
51 option names mentioned in the strings. */
52 static char args_err_missing_pinerc[] = N_("missing argument for option \"-pinerc\" (use - for standard out)");
53 #if defined(DOS) || defined(OS2)
54 static char args_err_missing_aux[] = N_("missing argument for option \"-aux\"");
55 #endif
56 #ifdef SMIME
57 static char args_err_missing_smimedir[] = N_("missing argument for option \"-smimedir\"");
58 static char args_err_non_abs_smimedir[] = N_("argument to \"-smimedir\" should be fully-qualified");
59 #endif /* SMIME */
60 #ifdef PASSFILE
61 static char args_err_missing_passfile[] = N_("missing argument for option \"-passfile\"");
62 static char args_err_non_abs_passfile[] = N_("argument to \"-passfile\" should be fully-qualified");
63 #ifdef SMIME
64 static char args_err_missing_pwdcertdir[] = N_("missing argument for option \"-pwdcertdir\"");
65 static char args_err_non_abs_pwdcertdir[] = N_("argument to \"-pwdcertdir\" should be fully-qualified");
66 #endif /* SMIME inside PASSFILE */
67 #endif
68 static char args_err_missing_sort[] = N_("missing argument for option \"-sort\"");
69 static char args_err_missing_flag_arg[] = N_("missing argument for flag \"%c\"");
70 static char args_err_missing_flag_num[] = N_("Non numeric argument for flag \"%c\"");
71 static char args_err_missing_debug_num[] = N_("Non numeric argument for \"%s\"");
72 static char args_err_missing_url[] = N_("missing URL for \"-url\"");
73 static char args_err_missing_attachment[] = N_("missing attachment for \"%s\"");
74 static char args_err_conflict[] = N_("conflicting action: \"%s\"");
75 static char args_err_unknown[] = N_("unknown flag \"%c\"");
76 static char args_err_I_error[] = N_("-I argument \"%s\": %s");
77 static char args_err_d_error[] = N_("-d argument \"%s\": %s");
78 static char args_err_internal[] = "%s";
79 static char args_err_missing_copyprc[] = N_("missing argument for option \"-copy_pinerc\"\nUsage: pine -copy_pinerc <local_pinerc> <remote_pinerc>");
80 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_err_missing_server_name[] = N_("missing server name. Example: -xoauth2-server Gmail");
82 static char args_err_missing_client_id[] = N_("missing client-id name. Example: -xoauth2-client-id 760.someserver.com");
83 static char args_err_missing_client_secret[] = N_("missing client-secret name. Example: -xoauth2-client-secret V56i0fa_");
84 static char args_err_missing_tenant[] = N_("missing tenant value. Example: -xoauth2-tenant common");
85 static char args_err_missing_user[] = N_("missing username value. Example: -xoauth2-user user@example.com");
86 static char args_err_missing_flow[] = N_("missing flow. Example: -xoauth2-client-flow Authorize");
87 static char args_err_missing_xoauth_option[] = N_("you must set all options -xoauth2-server, -xoauth2-client-id and -xoauth2-client-secret if you use any of them");
89 static char *args_pine_args[] = {
90 N_("Possible Starting Arguments for Alpine program:"),
91 "",
92 N_(" Argument\tMeaning"),
93 N_(" <addrs>...\tGo directly into composer sending to given address"),
94 N_("\t\tList multiple addresses with a single space between them."),
95 N_("\t\tStandard input redirection is allowed with addresses."),
96 N_("\t\tNote: Places addresses in the \"To\" field only."),
97 N_(" -attach <file>\tGo directly into composer with given file"),
98 N_(" -attachlist <file-list>"),
99 N_(" -attach_and_delete <file>"),
100 N_("\t\tGo to composer, attach file, delete when finished"),
101 N_("\t\tNote: Attach options can't be used if -f, -F"),
102 N_("\t\tadded to Attachment list. Attachlist must be the last"),
103 N_("\t\toption on the command line"),
104 N_(" -bail\t\tExit if pinerc file doesn't already exist"),
105 #ifdef DEBUG
106 N_(" -d n\t\tDebug - set debug level to 'n', or use the following:"),
107 N_(" -d keywords...\tflush,timestamp,imap=0..4,tcp,http,numfiles=0..31,verbose=0..9"),
108 #endif
109 N_(" -f <folder>\tFolder - give folder name to open"),
110 N_(" -c <number>\tContext - which context to apply to -f arg"),
111 N_(" -F <file>\tFile - give file name to open and page through and"),
112 N_("\t\tforward as email."),
113 N_(" -h \t\tHelp - give this list of options"),
114 N_(" -k \t\tKeys - Force use of function keys"),
115 N_(" -z \t\tSuspend - allow use of ^Z suspension"),
116 N_(" -r \t\tRestricted - can only send mail to oneself"),
117 N_(" -sort <sort>\tSort - Specify sort order of folder:"),
118 N_("\t\t\tarrival, subject, threaded, orderedsubject, date,"),
119 N_("\t\t\tfrom, size, score, to, cc, /reverse"),
120 N_(" -i\t\tIndex - Go directly to index, bypassing main menu"),
121 N_(" -I <keystroke_list> Initial keystrokes to be executed"),
122 N_(" -n <number>\tEntry in index to begin on"),
123 N_(" -o \t\tReadOnly - Open first folder read-only"),
124 N_(" -conf\t\tConfiguration - Print out fresh global configuration. The"),
125 N_("\t\tvalues of your global configuration affect all Alpine users"),
126 N_("\t\ton your system unless they have overridden the values in their"),
127 N_("\t\tpinerc files."),
128 N_(" -pinerc <file>\tConfiguration - Put fresh pinerc configuration in <file>"),
129 N_(" -p <pinerc>\tUse alternate .pinerc file"),
130 #if !defined(DOS) && !defined(OS2)
131 N_(" -P <pine.conf>\tUse alternate pine.conf file"),
132 #else
133 N_(" -aux <aux_files_dir>\tUse this with remote pinerc"),
134 N_(" -P <pine.conf>\tUse pine.conf file for default settings"),
135 N_(" -nosplash \tDisable the PC-Alpine splash screen"),
136 #endif
138 #if defined(APPLEKEYCHAIN) || (WINCRED > 0)
139 N_(" -erase_stored_passwords\tEliminate any stored passwords"),
140 #endif
142 #ifdef SMIME
143 N_(" -smimedir <fully_qualified_path>\tdirectory where smime personal certificates are saved"),
144 #endif /* SMIME */
146 #ifdef PASSFILE
147 N_(" -passfile <fully_qualified_filename>\tSet the password file to something other"),
148 N_("\t\tthan the default"),
149 #ifdef SMIME
150 N_(" -pwdcertdir <fully_qualified_path>\tSet the directory to store a personal"),
151 N_("\t\tkey and certificate to encrypt and decrypt your password file."),
152 #endif /* SMIME inside PASSFILE */
153 #endif /* PASSFILE */
155 #ifdef LOCAL_PASSWD_CACHE
156 N_(" -nowrite_password_cache\tRead from a password cache if there is one, but"),
157 N_("\t\t\t\tnever offer to write a password to the cache"),
158 #endif /* LOCAL_PASSWD_CACHE */
160 N_(" -x <config>\tUse configuration exceptions in <config>."),
161 N_("\t\tExceptions are used to override your default pinerc"),
162 N_("\t\tsettings for a particular platform, can be a local file or"),
163 N_("\t\ta remote folder."),
164 N_(" -v \t\tVersion - show version information"),
165 N_(" -version\tVersion - show version information"),
166 N_(" -supported\tList supported options"),
167 N_(" -url <url>\tOpen the given URL"),
168 N_("\t\tNote: Can't be used if -f, -F"),
169 N_("\t\tStandard input redirection is not allowed with URLs."),
170 N_("\t\tFor mailto URLs, 'body='text should be used in place of"),
171 N_("\t\tinput redirection."),
172 N_(" -copy_pinerc <local_pinerc> <remote_pinerc> copy local pinerc to remote"),
173 N_(" -copy_abook <local_abook> <remote_abook> copy local addressbook to remote"),
174 N_(" -convert_sigs -p <pinerc> convert signatures to literal signatures"),
175 #if defined(_WINDOWS)
176 N_(" -install \tPrompt for some basic setup information"),
177 N_(" -uninstall \tRemove traces of Alpine from Windows system settings"),
178 N_(" -registry <cmd>\tWhere cmd is set,noset,clear,clearsilent,dump"),
179 #endif
180 N_(" -xoauth2-server <value>"),
181 N_(" -xoauth2-client-id <value>"),
182 N_(" -xoauth2-client-secret <value>"),
183 N_("\tNote: All of -xoauth options above must be used, if any of them is used"),
184 N_(" -xoauth2-tenant <value>"),
185 N_(" -xoauth2-user <value>"),
186 N_(" -xoauth2-flow <value>"),
187 N_("\tNote: The previous two options are optional and if not used, Alpine resorts to their default values"),
188 " -<option>=<value> Assign <value> to the pinerc option <option>",
189 "\t\t e.g. -signature-file=sig1",
190 "\t\t e.g. -color-style=no-color",
191 "\t\t e.g. -feature-list=enable-sigdashes",
192 "\t\t Note: feature-list is additive.",
193 "\t\t You may leave off the \"feature-list=\" part of that,",
194 "\t\t e.g. -enable-sigdashes",
195 NULL
198 void
199 display_config_options(char *configopts, char *defprefix)
201 char *copts = configopts ? cpystr(configopts) : NULL;
202 char **options = NULL;
203 char *s, *t, *prefix;
204 int len, nlines, pending;
206 for(nlines = 0, s = copts; s != NULL; nlines++){
207 s = strchr(s, '\'');
208 if(s != NULL) s++;
210 options = fs_get((nlines/2 + 4)*sizeof(char *)); /* 4 in worst case scenario */
211 memset((void *) options, 0, (nlines/2 + 4)*sizeof(char *));
212 for(s = copts, len = 0, nlines = 0; s != NULL; ){
213 pending = 0;
214 prefix = len == 0 ? (nlines == 0 ? defprefix : " ") : " ";
215 if((s = strchr(s, '\'')) != NULL){
216 t = strchr(s+1, '\'');
217 *t = '\0';
218 if(!strncmp(s+1, "CFLAGS=", 7)
219 || !strncmp(s+1, "CC=", 3)
220 || !strncmp(s+1, "LIBS=", 5)
221 || !strncmp(s+1, "CPP=", 4)
222 || !strncmp(s+1, "CPPFLAGS=", 9)
223 || !strncmp(s+1, "LT_SYS_LIBRARY_PATH=", 20)
224 || !strncmp(s+1, "LDFLAGS=", 8)){
225 display_args_err(s+1, NULL, 0);
226 *t++ = '\'';
227 s = t;
228 continue;
230 if(len + strlen(prefix) + strlen(s+1) > 74 ){
231 if(len == 0){
232 options[nlines] = fs_get((strlen(prefix) + strlen(s+1) + 3)*sizeof(char));
233 sprintf(options[nlines++], "%s%s \\", prefix, s+1);
235 else{
236 strcat(options[nlines++], " \\");
237 pending++;
239 len = 0;
241 else{
242 if(len == 0){
243 options[nlines] = fs_get(76*sizeof(char));
244 *options[nlines] = '\0';
246 strcat(options[nlines], prefix);
247 strcat(options[nlines], s+1);
248 len = strlen(options[nlines]);
250 if(t != NULL)
251 *t = '\'';
252 if(!pending)
253 s = t ? t + 1 : NULL;
256 display_args_err(NULL, options, 0);
257 free_list_array(&options);
258 fs_give((void **) &copts);
262 * Parse the command line args.
264 * Args: pine_state -- The pine_state structure to put results in
265 * argc, argv -- The obvious
266 * addrs -- Pointer to address list that we set for caller
268 * Result: command arguments parsed
269 * possible printing of help for command line
270 * various flags in pine_state set
271 * returns the string name of the first folder to open
272 * addrs is set
274 void
275 pine_args(struct pine *pine_state, int argc, char **argv, ARGDATA_S *args)
277 register int ac;
278 register char **av;
279 int c;
280 char *str;
281 char *cmd_list = NULL;
282 char *debug_str = NULL;
283 char *sort = NULL;
284 char *pinerc_file = NULL;
285 char *lc = NULL;
286 XOAUTH2_INFO_S x;
287 int do_help = 0;
288 int do_conf = 0;
289 int usage = 0;
290 int do_use_fk = 0;
291 int do_can_suspend = 0;
292 int do_version = 0;
293 struct variable *vars = pine_state->vars;
295 ac = argc;
296 av = argv;
297 memset(args, 0, sizeof(ARGDATA_S));
298 args->action = aaFolder;
299 memset((void *) &x, 0, sizeof(XOAUTH2_INFO_S));
301 pine_state->pine_name = (lc = last_cmpnt(argv[0])) ? lc : (lc = argv[0]);
302 #ifdef DOS
303 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%.*s", pine_state->pine_name - argv[0], argv[0]);
304 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
305 pine_state->pine_dir = cpystr(tmp_20k_buf);
306 #endif
308 /* while more arguments with leading - */
309 Loop: while(--ac > 0)
310 if(**++av == '-'){
311 /* while more chars in this argument */
312 while(*++*av){
313 /* check for pinerc options */
314 if(pinerc_cmdline_opt(*av)){
315 goto Loop; /* done with this arg, go to next */
317 /* then other multi-char options */
318 else if(strcmp(*av, "conf") == 0){
319 do_conf = 1;
320 goto Loop; /* done with this arg, go to next */
322 else if(strcmp(*av, "pinerc") == 0){
323 if(--ac)
324 pinerc_file = *++av;
325 else{
326 display_args_err(_(args_err_missing_pinerc), NULL, 1);
327 ++usage;
330 goto Loop;
332 #if defined(DOS) || defined(OS2)
333 else if(strcmp(*av, "aux") == 0){
334 if(--ac){
335 if((str = *++av) != NULL)
336 pine_state->aux_files_dir = cpystr(str);
338 else{
339 display_args_err(_(args_err_missing_aux), NULL, 1);
340 ++usage;
343 goto Loop;
345 else if(strcmp(*av, "nosplash") == 0)
346 goto Loop; /* already taken care of in WinMain */
347 #endif
349 #if defined(APPLEKEYCHAIN) || (WINCRED > 0)
350 else if(strcmp(*av, "erase_stored_passwords") == 0){
351 #if (WINCRED > 0)
352 erase_windows_credentials();
353 #else
354 macos_erase_keychain();
355 #endif
356 goto Loop;
358 #endif /* defined(APPLEKEYCHAIN) || (WINCRED > 0) */
360 #ifdef SMIME
361 else if(strcmp(*av, "smimedir") == 0){
362 if(--ac){
363 if((str = *++av) != NULL){
364 if(!is_absolute_path(str)){
365 display_args_err(_(args_err_non_abs_smimedir),
366 NULL, 1);
367 ++usage;
369 else{
370 if(pine_state->smimedir)
371 fs_give((void **)&pine_state->smimedir);
373 pine_state->smimedir = cpystr(str);
377 else{
378 display_args_err(_(args_err_missing_smimedir), NULL, 1);
379 ++usage;
382 goto Loop;
384 #endif /* SMIME */
385 #ifdef PASSFILE
386 else if(strcmp(*av, "passfile") == 0){
387 if(--ac){
388 if((str = *++av) != NULL){
389 if(!is_absolute_path(str)){
390 display_args_err(_(args_err_non_abs_passfile),
391 NULL, 1);
392 ++usage;
394 else{
395 if(pine_state->passfile)
396 fs_give((void **)&pine_state->passfile);
398 pine_state->passfile = cpystr(str);
402 else{
403 display_args_err(_(args_err_missing_passfile), NULL, 1);
404 ++usage;
407 goto Loop;
409 #ifdef SMIME
410 else if(strcmp(*av, "pwdcertdir") == 0){
411 if(--ac){
412 if((str = *++av) != NULL){
413 if(!is_absolute_path(str)){
414 display_args_err(_(args_err_non_abs_pwdcertdir),
415 NULL, 1);
416 ++usage;
418 else{
419 if(pine_state->pwdcertdir)
420 fs_give((void **)&pine_state->pwdcertdir);
422 pine_state->pwdcertdir = cpystr(str);
426 else{
427 display_args_err(_(args_err_missing_pwdcertdir), NULL, 1);
428 ++usage;
431 goto Loop;
433 #endif /* SMIME inside PASSFILE */
434 #endif /* PASSFILE */
436 #ifdef LOCAL_PASSWD_CACHE
437 else if(strcmp(*av, "nowrite_password_cache") == 0){
438 ps_global->nowrite_password_cache = 1;
439 goto Loop;
441 #endif /* LOCAL_PASSWD_CACHE */
443 else if(strcmp(*av, "convert_sigs") == 0){
444 ps_global->convert_sigs = 1;
445 goto Loop;
447 else if(strcmp(*av, "supported") == 0){
448 ps_global->dump_supported_options = 1;
449 goto Loop;
451 else if(strcmp(*av, "copy_pinerc") == 0){
452 if(args->action == aaFolder && !args->data.folder){
453 args->action = aaPrcCopy;
454 if(ac > 2){
455 ac -= 2;
456 args->data.copy.local = *++av;
457 args->data.copy.remote = *++av;
459 else{
460 display_args_err(_(args_err_missing_copyprc), NULL, 1);
461 ++usage;
464 else{
465 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-copy_pinerc");
466 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
467 display_args_err(tmp_20k_buf, NULL, 1);
468 ++usage;
471 goto Loop;
473 else if(strcmp(*av, "copy_abook") == 0){
474 if(args->action == aaFolder && !args->data.folder){
475 args->action = aaAbookCopy;
476 if(ac > 2){
477 ac -= 2;
478 args->data.copy.local = *++av;
479 args->data.copy.remote = *++av;
481 else{
482 display_args_err(_(args_err_missing_copyabook), NULL, 1);
483 ++usage;
486 else{
487 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-copy_abook");
488 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
489 display_args_err(tmp_20k_buf, NULL, 1);
490 ++usage;
493 goto Loop;
495 else if(strcmp(*av, "sort") == 0){
496 if(--ac){
497 sort = *++av;
498 COM_SORT_KEY = cpystr(sort);
500 else{
501 display_args_err(_(args_err_missing_sort), NULL, 1);
502 ++usage;
505 goto Loop;
507 else if(strcmp(*av, "url") == 0){
508 if(args->action == aaFolder && !args->data.folder){
509 args->action = aaURL;
510 if(--ac){
511 args->url = cpystr(*++av);
513 else{
514 display_args_err(_(args_err_missing_url), NULL, 1);
515 ++usage;
518 else{
519 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-url");
520 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
521 display_args_err(tmp_20k_buf, NULL, 1);
522 ++usage;
525 goto Loop;
527 else if(strcmp(*av, "attach") == 0){
528 if((args->action == aaFolder && !args->data.folder)
529 || args->action == aaMail
530 || args->action == aaURL){
531 if(args->action != aaURL)
532 args->action = aaMail;
533 if(--ac){
534 args_add_attach(&args->data.mail.attachlist,
535 *++av, FALSE);
537 else{
538 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_attachment), "-attach");
539 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
540 display_args_err(tmp_20k_buf, NULL, 1);
541 ++usage;
544 else{
545 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-attach");
546 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
547 display_args_err(tmp_20k_buf, NULL, 1);
548 ++usage;
551 goto Loop;
553 else if(strcmp(*av, "attachlist") == 0){
554 if((args->action == aaFolder && !args->data.folder)
555 || args->action == aaMail
556 || args->action == aaURL){
557 if(args->action != aaURL)
558 args->action = aaMail;
559 if(ac - 1){
561 if(can_access(*(av+1), READ_ACCESS) == 0){
562 ac--;
563 args_add_attach(&args->data.mail.attachlist,
564 *++av, FALSE);
566 else
567 break;
569 while(ac);
571 else{
572 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_attachment), "-attachList");
573 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
574 display_args_err(tmp_20k_buf, NULL, 1);
575 ++usage;
578 else{
579 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-attachList");
580 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
581 display_args_err(tmp_20k_buf, NULL, 1);
582 ++usage;
585 goto Loop;
587 else if(strcmp(*av, "attach_and_delete") == 0){
588 if((args->action == aaFolder && !args->data.folder)
589 || args->action == aaMail
590 || args->action == aaURL){
591 if(args->action != aaURL)
592 args->action = aaMail;
593 if(--ac){
594 args_add_attach(&args->data.mail.attachlist,
595 *++av, TRUE);
597 else{
598 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_attachment), "-attach_and_delete");
599 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
600 display_args_err(tmp_20k_buf, NULL, 1);
601 ++usage;
604 else{
605 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-attach_and_delete");
606 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
607 display_args_err(tmp_20k_buf, NULL, 1);
608 ++usage;
611 goto Loop;
613 else if(strcmp(*av, "bail") == 0){
614 pine_state->exit_if_no_pinerc = 1;
615 goto Loop;
617 else if(strcmp(*av, "version") == 0){
618 do_version = 1;
619 goto Loop;
621 else if(strcmp(*av, "xoauth2-server") == 0 || strcmp(*av, "x-s") == 0){
622 if(--ac){
623 if((str = *++av) != NULL){
624 if(x.name)
625 fs_give((void **) &x.name);
626 x.name = (unsigned char *) cpystr(str);
629 else{
630 display_args_err(_(args_err_missing_server_name), NULL, 1);
631 ++usage;
633 goto Loop;
635 else if(strcmp(*av, "xoauth2-client-id") == 0 || strcmp(*av, "x-c-i") == 0){
636 if(--ac){
637 if((str = *++av) != NULL){
638 if(x.client_id)
639 fs_give((void **) &x.client_id);
640 x.client_id = cpystr(str);
643 else{
644 display_args_err(_(args_err_missing_client_id), NULL, 1);
645 ++usage;
647 goto Loop;
649 else if(strcmp(*av, "xoauth2-client-secret") == 0 || strcmp(*av, "x-c-s") == 0){
650 if(--ac){
651 if((str = *++av) != NULL){
652 if(x.client_secret)
653 fs_give((void **) &x.client_secret);
654 x.client_secret = cpystr(str);
657 else{
658 display_args_err(_(args_err_missing_client_secret), NULL, 1);
659 ++usage;
661 goto Loop;
663 else if(strcmp(*av, "xoauth2-tenant") == 0 || strcmp(*av, "x-t") == 0){
664 if(--ac){
665 if((str = *++av) != NULL){
666 if(x.tenant)
667 fs_give((void **) &x.tenant);
668 x.tenant = cpystr(str);
671 else{
672 display_args_err(_(args_err_missing_tenant), NULL, 1);
673 ++usage;
675 goto Loop;
677 else if(strcmp(*av, "xoauth2-user") == 0 || strcmp(*av, "x-u") == 0){
678 if(--ac){
679 if((str = *++av) != NULL){
680 if(x.users)
681 fs_give((void **) &x.users);
682 x.users = cpystr(str);
685 else{
686 display_args_err(_(args_err_missing_user), NULL, 1);
687 ++usage;
689 goto Loop;
691 else if(strcmp(*av, "xoauth2-flow") == 0 || strcmp(*av, "x-f") == 0){
692 if(--ac){
693 if((str = *++av) != NULL){
694 if(x.flow)
695 fs_give((void **) &x.flow);
696 x.flow = cpystr(str);
699 else{
700 display_args_err(_(args_err_missing_flow), NULL, 1);
701 ++usage;
703 goto Loop;
705 #ifdef _WINDOWS
706 else if(strcmp(*av, "install") == 0){
707 pine_state->install_flag = 1;
708 pine_state->update_registry = UREG_ALWAYS_SET;
709 goto Loop;
711 else if(strcmp(*av, "uninstall") == 0){
713 * Blast password cache, clear registry settings
715 #if (WINCRED > 0)
716 erase_windows_credentials();
717 #endif
718 mswin_reg(MSWR_OP_BLAST, MSWR_NULL, NULL, 0);
719 exit(0);
721 else if(strcmp(*av, "registry") == 0){
722 if(--ac){
723 if(!strucmp(*++av, "set")){
724 pine_state->update_registry = UREG_ALWAYS_SET;
726 else if(!strucmp(*av, "noset")){
727 pine_state->update_registry = UREG_NEVER_SET;
729 else if(!strucmp(*av, "clear")){
730 if(!mswin_reg(MSWR_OP_BLAST, MSWR_NULL, NULL, 0))
731 display_args_err(
732 _("Alpine related Registry values removed."),
733 NULL, 0);
734 else
735 display_args_err(
736 _("Not all Alpine related Registry values could be removed"),
737 NULL, 0);
738 exit(0);
740 else if(!strucmp(*av, "clearsilent")){
741 mswin_reg(MSWR_OP_BLAST, MSWR_NULL, NULL, 0);
742 exit(0);
744 else if(!strucmp(*av, "dump")){
745 char **pRegistry = mswin_reg_dump();
747 if(pRegistry){
748 display_args_err(NULL, pRegistry, 0);
749 free_list_array(&pRegistry);
751 exit(0);
753 else{
754 display_args_err(_("unknown registry command"),
755 NULL, 1);
756 ++usage;
759 else{
760 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_flag_arg), c);
761 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
762 display_args_err(tmp_20k_buf, NULL, 1);
763 ++usage;
766 goto Loop;
768 #endif
769 /* single char flags */
770 else{
771 switch(c = **av){
772 case 'h':
773 do_help = 1;
774 break; /* break back to inner-while */
775 case 'k':
776 do_use_fk = 1;
777 break;
778 case 'z':
779 do_can_suspend = 1;
780 break;
781 case 'r':
782 pine_state->restricted = 1;
783 break;
784 case 'o':
785 pine_state->open_readonly_on_startup = 1;
786 break;
787 case 'i':
788 pine_state->start_in_index = 1;
789 break;
790 case 'v':
791 do_version = 1;
792 break; /* break back to inner-while */
793 /* these take arguments */
794 case 'f': case 'F': case 'p': case 'I':
795 case 'c': case 'd': case 'P': case 'x': /* string args */
796 case 'n': /* integer args */
797 if(*++*av)
798 str = *av;
799 else if(--ac)
800 str = *++av;
801 else if(c == 'f' || c == 'F')
802 str = "";
803 else{
804 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_flag_arg), c);
805 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
806 display_args_err(tmp_20k_buf, NULL, 1);
807 ++usage;
808 goto Loop;
811 switch(c){
812 case 'f':
813 if(args->action == aaFolder && !args->data.folder){
814 args->data.folder = cpystr(str);
816 else{
817 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-f");
818 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
819 display_args_err(tmp_20k_buf, NULL, 1);
820 usage++;
823 break;
824 case 'F':
825 if(args->action == aaFolder && !args->data.folder){
826 args->action = aaMore;
827 args->data.file = cpystr(str);
829 else{
830 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-F");
831 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
832 display_args_err(tmp_20k_buf, NULL, 1);
833 usage++;
836 break;
837 case 'd':
838 debug_str = str;
839 break;
840 case 'I':
841 cmd_list = str;
842 break;
843 case 'p':
844 if(str){
845 char path[MAXPATH], dir[MAXPATH];
847 if(IS_REMOTE(str) || is_absolute_path(str)){
848 strncpy(path, str, sizeof(path)-1);
849 path[sizeof(path)-1] = '\0';
851 else{
852 if(getcwd(dir, sizeof(path)) != NULL)
853 build_path(path, dir, str, sizeof(path));
854 else
855 alpine_panic(_("getcwd() call failed while parsing argument (1)"));
859 * Pinerc used to be the name of the pinerc
860 * file. Now, since the pinerc can be remote,
861 * we've replaced the variable pinerc with the
862 * structure prc. Unfortunately, other parts of
863 * pine rely on the fact that pinerc is the
864 * name of the pinerc _file_, and use the
865 * directory that the pinerc file is located
866 * in for their own purposes. We keep that so
867 * things will keep working.
870 if(!IS_REMOTE(path)){
871 if(pine_state->pinerc)
872 fs_give((void **)&pine_state->pinerc);
874 pine_state->pinerc = cpystr(path);
878 * Last one wins. This would be the place where
879 * we put multiple pinercs in a list if we
880 * were to allow that.
882 if(pine_state->prc)
883 free_pinerc_s(&pine_state->prc);
885 pine_state->prc = new_pinerc_s(path);
888 break;
889 case 'P':
890 if(str){
891 char path[MAXPATH], dir[MAXPATH];
893 if(IS_REMOTE(str) || is_absolute_path(str)){
894 strncpy(path, str, sizeof(path)-1);
895 path[sizeof(path)-1] = '\0';
897 else{
898 if(getcwd(dir, sizeof(path)) != NULL)
899 build_path(path, dir, str, sizeof(path));
900 else
901 alpine_panic(_("getcwd() call failed while parsing argument (2)"));
904 if(pine_state->pconf)
905 free_pinerc_s(&pine_state->pconf);
907 pine_state->pconf = new_pinerc_s(path);
910 break;
911 case 'x':
912 if(str)
913 pine_state->exceptions = cpystr(str);
915 break;
916 case 'c':
917 if(!isdigit((unsigned char)str[0])){
918 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
919 _(args_err_missing_flag_num), c);
920 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
921 display_args_err(tmp_20k_buf, NULL, 1);
922 ++usage;
923 break;
926 pine_state->init_context = (short) atoi(str);
927 break;
929 case 'n':
930 if(!isdigit((unsigned char)str[0])){
931 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
932 _(args_err_missing_flag_num), c);
933 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
934 display_args_err(tmp_20k_buf, NULL, 1);
935 ++usage;
936 break;
939 pine_state->start_entry = atoi(str);
940 if(pine_state->start_entry < 1)
941 pine_state->start_entry = 1;
943 break;
946 goto Loop;
948 default:
949 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_unknown), c);
950 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
951 display_args_err(tmp_20k_buf, NULL, 1);
952 ++usage;
953 break;
958 else if(args->action == aaMail
959 || (args->action == aaFolder && !args->data.folder)){
960 STRLIST_S *stp, **slp;
962 args->action = aaMail;
964 stp = new_strlist(*av);
966 for(slp = &args->data.mail.addrlist; *slp; slp = &(*slp)->next)
969 *slp = stp;
971 else{
972 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), *av);
973 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
974 display_args_err(tmp_20k_buf, NULL, 1);
975 usage++;
978 if(cmd_list){
979 int commas = 0;
980 char *p = cmd_list;
981 char *error = NULL;
983 while(*p++)
984 if(*p == ',')
985 ++commas;
987 COM_INIT_CMD_LIST = parse_list(cmd_list, commas+1, 0, &error);
988 if(error){
989 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_I_error), cmd_list, error);
990 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
991 display_args_err(tmp_20k_buf, NULL, 1);
992 exit(-1);
996 #ifdef DEBUG
997 pine_state->debug_nfiles = NUMDEBUGFILES;
998 #endif
999 if(debug_str && process_debug_str(debug_str))
1000 usage++;
1002 if(lc && strncmp(lc, ALPINE_FKEY_NAME, sizeof(ALPINE_FKEY_NAME) - 1) == 0)
1003 do_use_fk = 1;
1005 if(do_use_fk || do_can_suspend){
1006 char list[500];
1007 int commas = 0;
1008 char *p = list;
1009 char *error = NULL;
1011 list[0] = '\0';
1013 if(do_use_fk){
1014 if(list[0]){
1015 strncat(list, ",", sizeof(list)-strlen(list)-1);
1016 list[sizeof(list)-1] = '\0';
1019 strncat(list, "use-function-keys", sizeof(list)-strlen(list)-1);
1020 list[sizeof(list)-1] = '\0';
1023 if(do_can_suspend){
1024 if(list[0]){
1025 strncat(list, ",", sizeof(list)-strlen(list)-1);
1026 list[sizeof(list)-1] = '\0';
1029 strncat(list, "enable-suspend", sizeof(list)-strlen(list)-1);
1030 list[sizeof(list)-1] = '\0';
1033 while(*p++)
1034 if(*p == ',')
1035 ++commas;
1037 pine_state->feat_list_back_compat = parse_list(list,commas+1,0,&error);
1038 if(error){
1039 snprintf(tmp_20k_buf, SIZEOF_20KBUF, args_err_internal, error);
1040 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1041 display_args_err(tmp_20k_buf, NULL, 1);
1042 exit(-1);
1046 if((x.name || x.client_id || x.client_secret)
1047 && !(x.name && x.client_id && x.client_secret)){
1048 display_args_err(_(args_err_missing_xoauth_option), NULL, 1);
1049 ++usage;
1052 if(((do_conf ? 1 : 0)+(pinerc_file ? 1 : 0)) > 1){
1053 display_args_err(_("May only have one of -conf and -pinerc"),
1054 NULL, 1);
1055 exit(-1);
1058 if(do_help || usage)
1059 args_help();
1061 if(usage)
1062 exit(-1);
1064 if(x.name){
1065 char *tmp1, *tmp2;
1066 tmp2 = xoauth_config_line(&x);
1067 if(tmp2){
1068 tmp1 = fs_get((strlen(ps_global->vars[V_XOAUTH2_INFO].name) + strlen(tmp2) + 2)*sizeof(char));
1069 if(tmp1){
1070 sprintf(tmp1,"%s=%s", ps_global->vars[V_XOAUTH2_INFO].name, tmp2);
1071 pinerc_cmdline_opt(tmp1);
1072 set_current_val(&ps_global->vars[V_XOAUTH2_INFO], TRUE, TRUE);
1073 fs_give((void **) &tmp1);
1075 fs_give((void **) &tmp2);
1079 if(do_version){
1080 extern char datestamp[], hoststamp[];
1081 char rev[128];
1083 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Alpine %s (%s %s) built %s on %s",
1084 ALPINE_VERSION,
1085 SYSTYPE ? SYSTYPE : "?",
1086 get_alpine_revision_string(rev, sizeof(rev)),
1087 datestamp, hoststamp);
1088 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1089 display_args_err(tmp_20k_buf, NULL, 0);
1090 if(*configoptions){
1091 display_args_err(_("Alpine was built with the following options:"), NULL, 0);
1092 display_config_options(configoptions, "./configure ");
1094 exit(0);
1097 if(do_conf)
1098 dump_global_conf();
1100 if(pinerc_file)
1101 dump_new_pinerc(pinerc_file);
1104 * Don't NULL out argv[0] or we might crash in unexpected ways. In OS X, we were
1105 * crashing when opening attachments because of this.
1107 if(ac <= 0 && av != argv)
1108 *av = NULL;
1113 * Returns 0 if ok, -1 if error.
1116 process_debug_str(char *debug_str)
1118 int i, usage = 0;
1119 int commas = 0;
1120 int new_style_debug_arg = 0;
1121 char *q = debug_str;
1122 char *error = NULL;
1123 char **list, **p;
1125 #ifdef DEBUG
1126 if(debug_str){
1127 if(!isdigit((unsigned char)debug_str[0]))
1128 new_style_debug_arg++;
1130 if(new_style_debug_arg){
1131 while(*q++)
1132 if(*q == ',')
1133 ++commas;
1135 list = parse_list(debug_str, commas+1, 0, &error);
1136 if(error){
1137 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_d_error), debug_str, error);
1138 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1139 display_args_err(tmp_20k_buf, NULL, 1);
1140 return(-1);
1143 if(list){
1144 for(p = list; *p; p++){
1145 if(struncmp(*p, "timestamp", 9) == 0){
1146 ps_global->debug_timestamp = 1;
1148 else if(struncmp(*p, "imap", 4) == 0){
1149 q = *p + 4;
1150 if(!*q || !*(q+1) || *q != '=' ||
1151 !isdigit((unsigned char)*(q+1))){
1152 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_debug_num), *p);
1153 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1154 display_args_err(tmp_20k_buf, NULL, 1);
1155 usage = -1;
1157 else{
1158 i = atoi(q+1);
1159 ps_global->debug_imap = MIN(5,MAX(0,i));
1162 else if(struncmp(*p, "flush", 5) == 0){
1163 ps_global->debug_flush = 1;
1165 else if(struncmp(*p, "tcp", 3) == 0
1166 || struncmp(*p, "tcpdebug", 8) == 0){
1167 ps_global->debug_tcp = 1;
1169 else if(struncmp(*p, "http", 4) == 0
1170 || struncmp(*p, "httpdebug", 9) == 0){
1171 ps_global->debug_http = 1;
1173 else if(struncmp(*p, "verbose", 7) == 0){
1174 q = *p + 7;
1175 if(!*q || !*(q+1) || *q != '=' ||
1176 !isdigit((unsigned char)*(q+1))){
1177 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_debug_num), *p);
1178 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1179 display_args_err(tmp_20k_buf, NULL, 1);
1180 usage = -1;
1182 else
1183 debug = atoi(q+1);
1185 else if(struncmp(*p, "numfiles", 8) == 0){
1186 q = *p + 8;
1187 if(!*q || !*(q+1) || *q != '=' ||
1188 !isdigit((unsigned char)*(q+1))){
1189 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_debug_num), *p);
1190 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1191 display_args_err(tmp_20k_buf, NULL, 1);
1192 usage = -1;
1194 else{
1195 i = atoi(q+1);
1196 ps_global->debug_nfiles = MIN(31,MAX(0,i));
1199 else if(struncmp(*p, "malloc", 6) == 0){
1200 q = *p + 6;
1201 if(!*q || !*(q+1) || *q != '=' ||
1202 !isdigit((unsigned char)*(q+1))){
1203 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_debug_num), *p);
1204 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1205 display_args_err(tmp_20k_buf, NULL, 1);
1206 usage = -1;
1208 else{
1209 i = atoi(q+1);
1210 ps_global->debug_malloc = MIN(63,MAX(0,i));
1213 #if defined(ENABLE_LDAP) && defined(LDAP_DEBUG)
1214 else if(struncmp(*p, "ldap", 4) == 0){
1215 q = *p + 4;
1216 if(!*q || !*(q+1) || *q != '=' ||
1217 !isdigit((unsigned char)*(q+1))){
1218 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_debug_num), *p);
1219 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1220 display_args_err(tmp_20k_buf, NULL, 1);
1221 usage = -1;
1223 else{
1224 i = atoi(q+1);
1225 ldap_debug = i;
1228 #endif /* LDAP */
1229 else{
1230 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("unknown debug keyword \"%s\""), *p);
1231 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1232 display_args_err(tmp_20k_buf, NULL, 1);
1233 usage = -1;
1237 free_list_array(&list);
1240 else{
1241 debug = atoi(debug_str);
1242 if(debug > 9){
1243 ps_global->debug_imap = 5;
1244 ps_global->debug_http = 1;
1246 else if(debug > 7)
1247 ps_global->debug_imap = 4;
1248 else if(debug > 6)
1249 ps_global->debug_imap = 3;
1250 else if(debug > 4)
1251 ps_global->debug_imap = 2;
1252 else if(debug > 2)
1253 ps_global->debug_imap = 1;
1255 if(debug > 7)
1256 ps_global->debug_timestamp = 1;
1258 if(debug > 8)
1259 ps_global->debug_flush = 1;
1263 if(usage >= 0) ps_global->write_debug_file++;
1264 if(!new_style_debug_arg){
1265 #ifdef CSRIMALLOC
1266 ps_global->debug_malloc =
1267 (debug <= DEFAULT_DEBUG) ? 1 : (debug < 9) ? 2 : 3;
1268 #else /* !CSRIMALLOC */
1269 #ifdef HAVE_SMALLOC
1270 if(debug > 8)
1271 ps_global->debug_malloc = 2;
1272 #else /* !HAVE_SMALLOC */
1273 #ifdef NXT
1274 if(debug > 8)
1275 ps_global->debug_malloc = 32;
1276 #endif /* NXT */
1277 #endif /* HAVE_SMALLOC */
1278 #endif /* CSRIMALLOC */
1281 #else /* !DEBUG */
1283 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("unknown flag \"d\", debugging not compiled in"));
1284 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1285 display_args_err(tmp_20k_buf, NULL, 1);
1286 usage = -1;
1288 #endif /* !DEBUG */
1290 return(usage);
1294 void
1295 args_add_attach(PATMT **alpp, char *s, int deleted)
1297 PATMT *atmp, **atmpp;
1299 atmp = (PATMT *) fs_get(sizeof(PATMT));
1300 memset(atmp, 0, sizeof(PATMT));
1301 atmp->filename = cpystr(s);
1303 #if defined(DOS) || defined(OS2)
1304 (void) removing_quotes(atmp->filename);
1305 #endif
1307 if(deleted)
1308 atmp->flags |= A_TMP;
1310 for(atmpp = alpp; *atmpp; atmpp = &(*atmpp)->next)
1313 *atmpp = atmp;
1317 /*----------------------------------------------------------------------
1318 print a few lines of help for command line arguments
1320 Args: none
1322 Result: prints help messages
1323 ----------------------------------------------------------------------*/
1324 void
1325 args_help(void)
1327 char *pp[2];
1328 #ifndef _WINDOWS
1329 char **a;
1330 #endif
1332 pp[1] = NULL;
1334 /** print out possible starting arguments... **/
1337 * display_args_err expects already translated input
1338 * so we need to translate a line at a time. Since
1339 * the 1st and 3rd args are zero it is ok to call it
1340 * a line at a time.
1342 * Windows expects the full block of text so we'll pass
1343 * it as such.
1345 #ifndef _WINDOWS
1346 for(a=args_pine_args; a && *a; a++){
1347 pp[0] = _(*a);
1348 display_args_err(NULL, pp, 0);
1350 #else
1351 display_args_err(NULL, args_pine_args, 0);
1352 #endif
1354 exit(1);
1358 /*----------------------------------------------------------------------
1359 write argument error to the display...
1361 Args: none
1363 Result: prints help messages
1364 ----------------------------------------------------------------------*/
1365 void
1366 display_args_err(char *s, char **a, int err)
1368 char errstr[256], *errp;
1369 FILE *fp = err ? stderr : stdout;
1372 if(err && s){
1373 snprintf(errp = errstr, sizeof(errstr), "%s: %s", _("Argument Error"), s);
1374 errstr[sizeof(errstr)-1] = '\0';
1376 else
1377 errp = s;
1379 #ifdef _WINDOWS
1380 if(errp)
1381 mswin_messagebox(errp, err);
1383 if(a && *a){
1384 os_argsdialog(a);
1386 #else
1387 if(errp)
1388 fprintf(fp, "%s\n", errp);
1390 while(a && *a)
1391 fprintf(fp, "%s\n", *a++);
1392 #endif
1397 * The argument is an argument from the command line. We check to see
1398 * if it is specifying an alternate value for one of the options that is
1399 * normally set in pinerc. If so, we return 1 and set the appropriate
1400 * values in the variables array.
1401 * The arg can be
1402 * varname=value
1403 * varname=value1,value2,value3
1404 * feature-list=featurename these are just a special
1405 * feature-list=featurename1,featurename2 case of above
1406 * featurename This is equivalent to above
1407 * no-featurename
1410 pinerc_cmdline_opt(char *arg)
1412 struct variable *v;
1413 char *p1 = NULL,
1414 *value,
1415 **oldlvalue = NULL,
1416 **lvalue;
1417 int i, count;
1419 if(!arg || !arg[0])
1420 return 0;
1422 if((value = strchr(arg, '=')) != NULL){
1423 i = value - arg;
1424 arg[i] = '\0';
1426 else
1427 i = -1;
1429 for(v = ps_global->vars; v->name != NULL; v++){
1430 if(v->is_used && strucmp(v->name, arg) == 0){
1431 p1 = arg + strlen(v->name);
1432 if(i > 0) arg[i] = '=';
1434 /*----- Skip to '=' -----*/
1435 while(*p1 && (*p1 == '\t' || *p1 == ' '))
1436 p1++;
1438 if(*p1 != '='){
1439 char buf[MAILTMPLEN];
1441 snprintf(buf, sizeof(buf), _("Missing \"=\" after -%s\n"), v->name);
1442 buf[sizeof(buf)-1] = '\0';
1443 exceptional_exit(buf, -1);
1446 p1++;
1447 break;
1452 if(i > 0) arg[i] = '=';
1454 /* no match, check for a feature name used directly */
1455 if(v->name == NULL){
1456 FEATURE_S *feat;
1457 char *featname;
1459 if(struncmp(arg, "no-", 3) == 0)
1460 featname = arg+3;
1461 else
1462 featname = arg;
1464 for(i = 0; (feat = feature_list(i)) != NULL; i++){
1465 if(strucmp(featname, feat->name) == 0){
1466 v = &(ps_global->vars)[V_FEATURE_LIST];
1467 p1 = arg;
1468 break;
1473 if(!p1)
1474 return 0;
1476 if(v->is_obsolete || !v->is_user){
1477 char buf[MAILTMPLEN];
1479 if(v->is_obsolete)
1480 snprintf(buf, sizeof(buf), _("Option \"%s\" is obsolete\n"), v->name);
1481 else
1482 snprintf(buf, sizeof(buf), _("Option \"%s\" is not user settable\n"), v->name);
1484 exceptional_exit(buf, -1);
1487 /* free mem */
1488 if(v->is_list){
1489 oldlvalue = v->cmdline_val.l;
1490 v->cmdline_val.l = NULL;
1492 else if(v->cmdline_val.p)
1493 fs_give((void **) &(v->cmdline_val.p));
1495 /*----- Matched a variable, get its value ----*/
1496 while(*p1 == ' ' || *p1 == '\t')
1497 p1++;
1498 value = p1;
1500 if(*value == '\0'){
1501 if(v->is_list){
1502 v->cmdline_val.l = (char **)fs_get(2 * sizeof(char *));
1504 * we let people leave off the quotes on command line so that
1505 * they don't have to mess with shell quoting
1507 v->cmdline_val.l[0] = cpystr("");
1508 v->cmdline_val.l[1] = NULL;
1509 if(oldlvalue)
1510 free_list_array(&oldlvalue);
1512 }else{
1513 v->cmdline_val.p = cpystr("");
1515 return 1;
1518 /*--value is non-empty--*/
1519 if(*value == '"' && !v->is_list){
1520 value++;
1521 for(p1 = value; *p1 && *p1 != '"'; p1++)
1523 if(*p1 == '"')
1524 *p1 = '\0';
1525 else
1526 removing_trailing_white_space(value);
1527 }else{
1528 removing_trailing_white_space(value);
1531 if(v->is_list){
1532 int was_quoted = 0;
1533 char *error = NULL;
1535 count = 1;
1536 for(p1=value; *p1; p1++){ /* generous count of list elements */
1537 if(*p1 == '"') /* ignore ',' if quoted */
1538 was_quoted = (was_quoted) ? 0 : 1;
1540 if(*p1 == ',' && !was_quoted)
1541 count++;
1544 lvalue = parse_list(value, count, 0, &error);
1545 if(error){
1546 char buf[MAILTMPLEN];
1548 snprintf(buf, sizeof(buf), "%s in %s = \"%s\"\n", error, v->name, value);
1549 buf[sizeof(buf)-1] = '\0';
1550 exceptional_exit(buf, -1);
1553 * Special case: turn "" strings into empty strings.
1554 * This allows users to turn off default lists. For example,
1555 * if smtp-server is set then a user could override smtp-server
1556 * with smtp-server="".
1558 for(i = 0; lvalue[i]; i++)
1559 if(lvalue[i][0] == '"' &&
1560 lvalue[i][1] == '"' &&
1561 lvalue[i][2] == '\0')
1562 lvalue[i][0] = '\0';
1565 if(v->is_list){
1566 if(oldlvalue){
1567 char **combinedlvalue;
1568 int j;
1570 /* combine old and new cmdline lists */
1571 for(count = 0, i = 0; oldlvalue[i]; i++, count++)
1574 for(i = 0; lvalue && lvalue[i]; i++, count++)
1577 combinedlvalue = (char **) fs_get((count+1) * sizeof(char *));
1578 memset(combinedlvalue, 0, (count+1) * sizeof(*combinedlvalue));
1580 for(i = 0, j = 0; oldlvalue[i]; i++, j++)
1581 combinedlvalue[j] = cpystr(oldlvalue[i]);
1583 for(i = 0; lvalue && lvalue[i]; i++, j++)
1584 combinedlvalue[j] = cpystr(lvalue[i]);
1586 v->cmdline_val.l = combinedlvalue;
1588 fs_give((void **) &oldlvalue);
1589 if(lvalue)
1590 fs_give((void **) &lvalue);
1592 else
1593 v->cmdline_val.l = lvalue;
1595 else
1596 v->cmdline_val.p = cpystr(value);
1598 return 1;