2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
5 * Parts were written/modified by:
6 * Rocco Rutte <pdmef@cs.tu-berlin.de>
8 * This file is part of mutt-ng, see http://www.muttng.org/.
9 * It's licensed under the GNU General Public License,
10 * please see the file GPL in the top level source directory.
21 #include "mutt_curses.h"
27 #include "mutt_crypt.h"
28 #include "mutt_idna.h"
30 #if defined(USE_SSL) || defined(USE_GNUTLS)
34 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
35 #include "mutt_libesmtp.h"
46 #include "lib/debug.h"
52 #include <sys/utsname.h>
57 if ((CurrentMenu == MENU_PAGER) && \
58 (!option || (option->flags & R_RESORT))) \
60 snprintf (err->data, err->dsize, \
61 _("Not available in this menu.")); \
68 static const struct mapping_t* get_sortmap (struct option_t* option);
69 static int parse_sort (struct option_t* dst, const char *s,
70 const struct mapping_t *map,
71 char* errbuf, size_t errlen);
73 static HASH* ConfigOptions = NULL;
75 /* for synonym warning reports: synonym found during parsing */
79 struct option_t* n; /* new */
80 struct option_t* o; /* old */
83 /* for synonym warning reports: list of synonyms found */
84 static list2_t* Synonyms;
85 /* for synonym warning reports: current rc file */
86 static const char* CurRCFile = NULL;
87 /* for synonym warning reports: current rc line */
88 static int CurRCLine = 0;
90 /* prototypes for checking for special vars */
91 static int check_dsn_return (const char* option, unsigned long val,
92 char* errbuf, size_t errlen);
93 static int check_dsn_notify (const char* option, unsigned long val,
94 char* errbuf, size_t errlen);
95 static int check_history (const char* option, unsigned long val,
96 char* errbuf, size_t errlen);
97 /* this checks that numbers are >= 0 */
98 static int check_num (const char* option, unsigned long val,
99 char* errbuf, size_t errlen);
101 static int check_debug (const char* option, unsigned long val,
102 char* errbuf, size_t errlen);
105 /* use this to check only */
106 static int check_special (const char* option, unsigned long val,
107 char* errbuf, size_t errlen);
109 /* variable <-> sanity check function mappings
110 * when changing these, make sure the proper _from_string handler
111 * does this checking!
115 int (*check) (const char* option, unsigned long val,
116 char* errbuf, size_t errlen);
118 { "dsn_notify", check_dsn_notify },
119 { "dsn_return", check_dsn_return },
120 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
121 { "smtp_use_tls", mutt_libesmtp_check_usetls },
123 { "history", check_history },
124 { "pager_index_lines", check_num },
126 { "debug_level", check_debug },
132 /* protos for config type handles: convert value to string */
133 static void bool_to_string (char* dst, size_t dstlen, struct option_t* option);
134 static void num_to_string (char* dst, size_t dstlen, struct option_t* option);
135 static void str_to_string (char* dst, size_t dstlen, struct option_t* option);
136 static void quad_to_string (char* dst, size_t dstlen, struct option_t* option);
137 static void sort_to_string (char* dst, size_t dstlen, struct option_t* option);
138 static void rx_to_string (char* dst, size_t dstlen, struct option_t* option);
139 static void magic_to_string (char* dst, size_t dstlen, struct option_t* option);
140 static void addr_to_string (char* dst, size_t dstlen, struct option_t* option);
141 static void user_to_string (char* dst, size_t dstlen, struct option_t* option);
142 static void sys_to_string (char* dst, size_t dstlen, struct option_t* option);
144 /* protos for config type handles: convert to value from string */
145 static int bool_from_string (struct option_t* dst, const char* val,
146 char* errbuf, size_t errlen);
147 static int num_from_string (struct option_t* dst, const char* val,
148 char* errbuf, size_t errlen);
149 static int str_from_string (struct option_t* dst, const char* val,
150 char* errbuf, size_t errlen);
151 static int path_from_string (struct option_t* dst, const char* val,
152 char* errbuf, size_t errlen);
153 static int quad_from_string (struct option_t* dst, const char* val,
154 char* errbuf, size_t errlen);
155 static int sort_from_string (struct option_t* dst, const char* val,
156 char* errbuf, size_t errlen);
157 static int rx_from_string (struct option_t* dst, const char* val,
158 char* errbuf, size_t errlen);
159 static int magic_from_string (struct option_t* dst, const char* val,
160 char* errbuf, size_t errlen);
161 static int addr_from_string (struct option_t* dst, const char* val,
162 char* errbuf, size_t errlen);
163 static int user_from_string (struct option_t* dst, const char* val,
164 char* errbuf, size_t errlen);
168 void (*opt_to_string) (char* dst, size_t dstlen, struct option_t* option);
169 int (*opt_from_string) (struct option_t* dst, const char* val,
170 char* errbuf, size_t errlen);
172 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
173 { DT_BOOL, bool_to_string, bool_from_string },
174 { DT_NUM, num_to_string, num_from_string },
175 { DT_STR, str_to_string, str_from_string },
176 { DT_PATH, str_to_string, path_from_string },
177 { DT_QUAD, quad_to_string, quad_from_string },
178 { DT_SORT, sort_to_string, sort_from_string },
179 { DT_RX, rx_to_string, rx_from_string },
180 { DT_MAGIC, magic_to_string, magic_from_string },
181 /* synonyms should be resolved already so we don't need this
182 * but must define it as DT_ is used for indexing */
183 { DT_SYN, NULL, NULL },
184 { DT_ADDR, addr_to_string, addr_from_string },
185 { DT_USER, user_to_string, user_from_string },
186 { DT_SYS, sys_to_string, NULL },
189 static void bool_to_string (char* dst, size_t dstlen,
190 struct option_t* option) {
191 snprintf (dst, dstlen, "%s=%s", option->option,
192 option (option->data) ? "yes" : "no");
195 static int bool_from_string (struct option_t* dst, const char* val,
196 char* errbuf, size_t errlen) {
201 if (ascii_strncasecmp (val, "yes", 3) == 0)
203 else if (ascii_strncasecmp (val, "no", 2) == 0)
209 set_option (dst->data);
211 unset_option (dst->data);
215 static void num_to_string (char* dst, size_t dstlen,
216 struct option_t* option) {
218 const char* fmt = (str_cmp (option->option, "umask") == 0) ?
220 snprintf (dst, dstlen, fmt, option->option,
221 *((short*) option->data));
224 static int num_from_string (struct option_t* dst, const char* val,
225 char* errbuf, size_t errlen) {
226 int num = 0, old = 0;
232 num = strtol (val, &t, 0);
234 if (!*val || *t || (short) num != num) {
236 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
242 /* just temporarily accept new val so that check_special for
243 * $history already has it when doing history's init() */
244 old = *((short*) dst->data);
245 *((short*) dst->data) = (short) num;
247 if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
248 *((short*) dst->data) = old;
255 static void str_to_string (char* dst, size_t dstlen,
256 struct option_t* option) {
257 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
258 NONULL (*((char**) option->data)));
261 static void user_to_string (char* dst, size_t dstlen,
262 struct option_t* option) {
263 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
264 NONULL (((char*) option->data)));
267 static void sys_to_string (char* dst, size_t dstlen,
268 struct option_t* option) {
269 char* val = NULL, *t = NULL;
272 /* get some $muttng_ values dynamically */
273 if (ascii_strcmp ("muttng_pwd", option->option) == 0) {
274 val = mem_malloc (_POSIX_PATH_MAX);
275 val = getcwd (val, _POSIX_PATH_MAX-1);
277 } else if (ascii_strcmp ("muttng_folder_path", option->option) == 0 &&
278 CurrentFolder && *CurrentFolder) {
280 } else if (ascii_strcmp ("muttng_folder_name", option->option) == 0 &&
281 CurrentFolder && *CurrentFolder) {
282 if ((t = strrchr (CurrentFolder, '/')) != NULL)
289 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (val));
294 static int path_from_string (struct option_t* dst, const char* val,
295 char* errbuf, size_t errlen) {
296 char path[_POSIX_PATH_MAX];
302 mem_free ((char**) dst->data);
307 strfcpy (path, val, sizeof (path));
308 mutt_expand_path (path, sizeof (path));
309 str_replace ((char **) dst->data, path);
313 static int str_from_string (struct option_t* dst, const char* val,
314 char* errbuf, size_t errlen) {
318 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
321 str_replace ((char**) dst->data, val);
325 static int user_from_string (struct option_t* dst, const char* val,
326 char* errbuf, size_t errlen) {
327 /* if dst == NULL, we may get here in case the user did unset it,
328 * see parse_set() where item is free()'d before coming here; so
329 * just silently ignore it */
332 if (str_len ((char*) dst->data) == 0)
333 dst->data = (unsigned long) str_dup (val);
335 char* s = (char*) dst->data;
336 str_replace (&s, val);
338 if (str_len (dst->init) == 0)
339 dst->init = str_dup ((char*) dst->data);
343 static void quad_to_string (char* dst, size_t dstlen,
344 struct option_t* option) {
345 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
346 snprintf (dst, dstlen, "%s=%s", option->option,
347 vals[quadoption (option->data)]);
350 static int quad_from_string (struct option_t* dst, const char* val,
351 char* errbuf, size_t errlen) {
356 if (ascii_strncasecmp (val, "yes", 3) == 0)
358 else if (ascii_strncasecmp (val, "no", 2) == 0)
360 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
362 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
368 set_quadoption (dst->data, flag);
372 static void sort_to_string (char* dst, size_t dstlen,
373 struct option_t* option) {
374 const struct mapping_t *map = get_sortmap (option);
378 snprintf (dst, sizeof (dst), "%s=unknown", option->option);
382 p = mutt_getnamebyvalue (*((short *) option->data) & SORT_MASK,
385 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
386 (*((short *) option->data) & SORT_REVERSE) ?
388 (*((short *) option->data) & SORT_LAST) ? "last-" :
392 static int sort_from_string (struct option_t* dst, const char* val,
393 char* errbuf, size_t errlen) {
394 const struct mapping_t *map = NULL;
395 if (!(map = get_sortmap (dst))) {
397 snprintf (errbuf, errlen, _("%s: Unknown type."),
401 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
406 static void rx_to_string (char* dst, size_t dstlen,
407 struct option_t* option) {
408 rx_t* p = (rx_t*) option->data;
409 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
410 NONULL (p->pattern));
413 static int rx_from_string (struct option_t* dst, const char* val,
414 char* errbuf, size_t errlen) {
417 int flags = 0, e = 0, not = 0;
423 if (option (OPTATTACHMSG) && !str_cmp (dst->option, "reply_regexp")) {
425 snprintf (errbuf, errlen,
426 "Operation not permitted when in attach-message mode.");
430 if (!((rx_t*) dst->data))
431 *((rx_t**) dst->data) = mem_calloc (1, sizeof (rx_t));
433 p = (rx_t*) dst->data;
435 /* something to do? */
436 if (!val || !*val || (p->pattern && str_cmp (p->pattern, val) == 0))
439 if (str_cmp (dst->option, "mask") != 0)
440 flags |= mutt_which_case (val);
443 if (str_cmp (dst->option, "mask") == 0 && *s == '!') {
448 rx = mem_malloc (sizeof (regex_t));
450 if ((e = REGCOMP (rx, s, flags)) != 0) {
451 regerror (e, rx, errbuf, errlen);
462 str_replace (&p->pattern, val);
466 if (str_cmp (dst->option, "reply_regexp") == 0)
467 mutt_adjust_all_subjects ();
472 static void magic_to_string (char* dst, size_t dstlen,
473 struct option_t* option) {
474 const char* s = NULL;
475 switch (option->data) {
476 case M_MBOX: s = "mbox"; break;
477 case M_MMDF: s = "MMDF"; break;
478 case M_MH: s = "MH"; break;
479 case M_MAILDIR: s = "Maildir"; break;
480 default: s = "unknown"; break;
482 snprintf (dst, dstlen, "%s=%s", option->option, s);
485 static int magic_from_string (struct option_t* dst, const char* val,
486 char* errbuf, size_t errlen) {
489 if (!dst || !val || !*val)
491 if (ascii_strncasecmp (val, "mbox", 4) == 0)
493 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
495 else if (ascii_strncasecmp (val, "mh", 2) == 0)
497 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
503 *((short*) dst->data) = flag;
508 static void addr_to_string (char* dst, size_t dstlen,
509 struct option_t* option) {
512 rfc822_write_address (s, sizeof (s), *((ADDRESS**) option->data), 0);
513 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
516 static int addr_from_string (struct option_t* dst, const char* val,
517 char* errbuf, size_t errlen) {
520 rfc822_free_address ((ADDRESS**) dst->data);
522 *((ADDRESS**) dst->data) = rfc822_parse_adrlist (NULL, val);
526 int mutt_option_value (const char* val, char* dst, size_t dstlen) {
527 struct option_t* option = NULL;
528 char* tmp = NULL, *t = NULL;
531 if (!(option = hash_find (ConfigOptions, val))) {
532 debug_print (1, ("var '%s' not found\n", val));
536 tmp = mem_malloc (dstlen+1);
537 FuncTable[DTYPE (option->type)].opt_to_string (tmp, dstlen, option);
539 /* as we get things of type $var=value and don't want to bloat the
540 * above "just" for expansion, we do the stripping here */
541 debug_print (1, ("orig == '%s'\n", tmp));
542 t = strchr (tmp, '=');
546 if (t[l-1] == '"' && *t == '"') {
551 memcpy (dst, t, l+1);
553 debug_print (1, ("stripped == '%s'\n", dst));
558 /* for synonym warning reports: adds synonym to end of list */
559 static void syn_add (struct option_t* n, struct option_t* o) {
560 syn_t* tmp = mem_malloc (sizeof (syn_t));
561 tmp->f = str_dup (CurRCFile);
565 list_push_back (&Synonyms, tmp);
568 /* for synonym warning reports: free single item (for list_del()) */
569 static void syn_del (void** p) {
570 mem_free(&(*(syn_t**) p)->f);
574 void toggle_quadoption (int opt)
577 int b = (opt % 4) * 2;
579 QuadOptions[n] ^= (1 << b);
582 void set_quadoption (int opt, int flag)
585 int b = (opt % 4) * 2;
587 QuadOptions[n] &= ~(0x3 << b);
588 QuadOptions[n] |= (flag & 0x3) << b;
591 int quadoption (int opt)
594 int b = (opt % 4) * 2;
596 return (QuadOptions[n] >> b) & 0x3;
599 int query_quadoption (int opt, const char *prompt)
601 int v = quadoption (opt);
609 v = mutt_yesorno (prompt, (v == M_ASKYES));
610 CLEARLINE (LINES - 1);
617 static void add_to_list (LIST ** list, const char *str)
619 LIST *t, *last = NULL;
621 /* don't add a NULL or empty string to the list */
622 if (!str || *str == '\0')
625 /* check to make sure the item is not already on this list */
626 for (last = *list; last; last = last->next) {
627 if (ascii_strcasecmp (str, last->data) == 0) {
628 /* already on the list, so just ignore it */
636 if (!*list || last) {
637 t = (LIST *) mem_calloc (1, sizeof (LIST));
638 t->data = str_dup (str);
648 static int add_to_rx_list (list2_t** list, const char *s, int flags,
657 if (!(rx = rx_compile (s, flags))) {
658 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
662 i = rx_lookup ((*list), rx->pattern);
666 list_push_back (list, rx);
670 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
671 const char *templ, BUFFER * err)
673 SPAM_LIST *t = NULL, *last = NULL;
678 if (!pat || !*pat || !templ)
681 if (!(rx = rx_compile (pat, REG_ICASE))) {
682 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
686 /* check to make sure the item is not already on this list */
687 for (last = *list; last; last = last->next) {
688 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
689 /* Already on the list. Formerly we just skipped this case, but
690 * now we're supporting removals, which means we're supporting
691 * re-adds conceptually. So we probably want this to imply a
692 * removal, then do an add. We can achieve the removal by freeing
693 * the template, and leaving t pointed at the current item.
696 mem_free(t->template);
703 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
704 * update. Otherwise we want to make a new one to link at the list's end.
707 t = mutt_new_spam_list ();
715 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
716 t->template = str_dup (templ);
718 /* Find highest match number in template string */
720 for (p = templ; *p;) {
725 while (*p && isdigit ((int) *p))
731 t->nmatch++; /* match 0 is always the whole expr */
736 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
738 SPAM_LIST *spam, *prev;
741 /* Being first is a special case. */
745 if (spam->rx && !str_cmp (spam->rx->pattern, pat)) {
748 mem_free(&spam->template);
754 for (spam = prev->next; spam;) {
755 if (!str_cmp (spam->rx->pattern, pat)) {
756 prev->next = spam->next;
758 mem_free(spam->template);
771 static void remove_from_list (LIST ** l, const char *str)
773 LIST *p, *last = NULL;
775 if (str_cmp ("*", str) == 0)
776 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
781 if (ascii_strcasecmp (str, p->data) == 0) {
784 last->next = p->next;
797 static int remove_from_rx_list (list2_t** l, const char *str)
801 if (str_cmp ("*", str) == 0) {
802 list_del (l, (list_del_t*) rx_free);
806 i = rx_lookup ((*l), str);
808 rx_t* r = list_pop_idx ((*l), i);
816 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
821 struct option_t* option = NULL;
823 memset (&token, 0, sizeof (token));
824 mutt_extract_token (tmp, s, 0);
826 /* is the item defined as a variable or a function? */
827 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL)
830 for (i = 0; !res && i < MENU_MAX; i++) {
831 struct binding_t *b = km_get_table (Menus[i].value);
836 for (j = 0; b[j].name; j++)
837 if (!ascii_strncasecmp (tmp->data, b[j].name, str_len (tmp->data))
838 && (str_len (b[j].name) == str_len (tmp->data))) {
844 /* check for feature_* */
845 if (!res && ascii_strncasecmp (tmp->data, "feature_", 8) == 0 &&
846 (j = str_len (tmp->data)) > 8) {
848 while (Features[i]) {
849 if (str_len (Features[i]) == j-8 &&
850 ascii_strncasecmp (Features[i], tmp->data+8, j-8) == 0) {
860 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
862 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
866 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
869 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
870 mutt_error ("Error: %s", err->data);
871 mem_free (&token.data);
874 mem_free (&token.data);
879 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
883 mutt_extract_token (buf, s, 0);
885 /* don't add "*" to the unignore list */
886 if (strcmp (buf->data, "*"))
887 add_to_list (&UnIgnore, buf->data);
889 remove_from_list (&Ignore, buf->data);
891 while (MoreArgs (s));
896 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
900 mutt_extract_token (buf, s, 0);
901 remove_from_list (&UnIgnore, buf->data);
902 add_to_list (&Ignore, buf->data);
904 while (MoreArgs (s));
909 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
913 mutt_extract_token (buf, s, 0);
914 add_to_list ((LIST **) data, buf->data);
916 while (MoreArgs (s));
921 static void _alternates_clean (void)
925 if (Context && Context->msgcount) {
926 for (i = 0; i < Context->msgcount; i++)
927 Context->hdrs[i]->recip_valid = 0;
931 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
934 _alternates_clean ();
936 mutt_extract_token (buf, s, 0);
937 remove_from_rx_list (&UnAlternates, buf->data);
939 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
942 while (MoreArgs (s));
947 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
950 _alternates_clean ();
952 mutt_extract_token (buf, s, 0);
953 remove_from_rx_list (&Alternates, buf->data);
955 if (str_cmp (buf->data, "*") &&
956 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
960 while (MoreArgs (s));
965 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
970 memset (&templ, 0, sizeof (templ));
972 /* Insist on at least one parameter */
975 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
977 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
981 /* Extract the first token, a regexp */
982 mutt_extract_token (buf, s, 0);
984 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
985 if (data == M_SPAM) {
986 /* If there's a second parameter, it's a template for the spam tag. */
988 mutt_extract_token (&templ, s, 0);
990 /* Add to the spam list. */
991 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
992 mem_free (&templ.data);
995 mem_free (&templ.data);
998 /* If not, try to remove from the nospam list. */
1000 remove_from_rx_list (&NoSpamList, buf->data);
1006 /* M_NOSPAM is for nospam commands. */
1007 else if (data == M_NOSPAM) {
1008 /* nospam only ever has one parameter. */
1010 /* "*" is a special case. */
1011 if (!str_cmp (buf->data, "*")) {
1012 mutt_free_spam_list (&SpamList);
1013 list_del (&NoSpamList, (list_del_t*) rx_free);
1017 /* If it's on the spam list, just remove it. */
1018 if (remove_from_spam_list (&SpamList, buf->data) != 0)
1021 /* Otherwise, add it to the nospam list. */
1022 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
1028 /* This should not happen. */
1029 strfcpy (err->data, "This is no good at all.", err->dsize);
1033 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
1037 mutt_extract_token (buf, s, 0);
1039 * Check for deletion of entire list
1041 if (str_cmp (buf->data, "*") == 0) {
1042 mutt_free_list ((LIST **) data);
1045 remove_from_list ((LIST **) data, buf->data);
1047 while (MoreArgs (s));
1052 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
1056 mutt_extract_token (buf, s, 0);
1057 remove_from_rx_list (&UnMailLists, buf->data);
1059 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1062 while (MoreArgs (s));
1067 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
1071 mutt_extract_token (buf, s, 0);
1072 remove_from_rx_list (&SubscribedLists, buf->data);
1073 remove_from_rx_list (&MailLists, buf->data);
1075 if (str_cmp (buf->data, "*") &&
1076 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1079 while (MoreArgs (s));
1084 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1088 mutt_extract_token (buf, s, 0);
1089 remove_from_rx_list (&UnMailLists, buf->data);
1090 remove_from_rx_list (&UnSubscribedLists, buf->data);
1092 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1094 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1097 while (MoreArgs (s));
1102 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1106 mutt_extract_token (buf, s, 0);
1107 remove_from_rx_list (&SubscribedLists, buf->data);
1109 if (str_cmp (buf->data, "*") &&
1110 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1113 while (MoreArgs (s));
1118 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
1121 ALIAS *tmp, *last = NULL;
1124 mutt_extract_token (buf, s, 0);
1126 if (str_cmp ("*", buf->data) == 0) {
1127 if (CurrentMenu == MENU_ALIAS) {
1128 for (tmp = Aliases; tmp; tmp = tmp->next)
1130 set_option (OPTFORCEREDRAWINDEX);
1133 mutt_free_alias (&Aliases);
1137 for (tmp = Aliases; tmp; tmp = tmp->next) {
1138 if (str_casecmp (buf->data, tmp->name) == 0) {
1139 if (CurrentMenu == MENU_ALIAS) {
1141 set_option (OPTFORCEREDRAWINDEX);
1146 last->next = tmp->next;
1148 Aliases = tmp->next;
1150 mutt_free_alias (&tmp);
1156 while (MoreArgs (s));
1160 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
1163 ALIAS *tmp = Aliases;
1167 if (!MoreArgs (s)) {
1168 strfcpy (err->data, _("alias: no address"), err->dsize);
1172 mutt_extract_token (buf, s, 0);
1174 debug_print (2, ("first token is '%s'.\n", buf->data));
1176 /* check to see if an alias with this name already exists */
1177 for (; tmp; tmp = tmp->next) {
1178 if (!str_casecmp (tmp->name, buf->data))
1184 /* create a new alias */
1185 tmp = (ALIAS *) mem_calloc (1, sizeof (ALIAS));
1187 tmp->name = str_dup (buf->data);
1188 /* give the main addressbook code a chance */
1189 if (CurrentMenu == MENU_ALIAS)
1190 set_option (OPTMENUCALLER);
1193 /* override the previous value */
1194 rfc822_free_address (&tmp->addr);
1195 if (CurrentMenu == MENU_ALIAS)
1196 set_option (OPTFORCEREDRAWINDEX);
1199 mutt_extract_token (buf, s,
1200 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1201 debug_print (2, ("second token is '%s'.\n", buf->data));
1202 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1207 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1208 snprintf (err->data, err->dsize,
1209 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1213 if (DebugLevel >= 2) {
1216 /* A group is terminated with an empty address, so check a->mailbox */
1217 for (a = tmp->addr; a && a->mailbox; a = a->next) {
1219 debug_print (2, ("%s\n", a->mailbox));
1221 debug_print (2, ("group %s\n", a->mailbox));
1229 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
1232 LIST *tmp = UserHeader;
1237 mutt_extract_token (buf, s, 0);
1238 if (str_cmp ("*", buf->data) == 0)
1239 mutt_free_list (&UserHeader);
1244 l = str_len (buf->data);
1245 if (buf->data[l - 1] == ':')
1249 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1250 && tmp->data[l] == ':') {
1253 last->next = tmp->next;
1255 UserHeader = tmp->next;
1258 mutt_free_list (&ptr);
1267 while (MoreArgs (s));
1271 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
1278 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1279 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1280 strfcpy (err->data, _("invalid header field"), err->dsize);
1283 keylen = p - buf->data + 1;
1286 for (tmp = UserHeader;; tmp = tmp->next) {
1287 /* see if there is already a field by this name */
1288 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1289 /* replace the old value */
1290 mem_free (&tmp->data);
1291 tmp->data = buf->data;
1292 memset (buf, 0, sizeof (BUFFER));
1298 tmp->next = mutt_new_list ();
1302 tmp = mutt_new_list ();
1305 tmp->data = buf->data;
1306 memset (buf, 0, sizeof (BUFFER));
1311 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1312 char* errbuf, size_t errlen) {
1315 if (str_ncmp ("reverse-", s, 8) == 0) {
1317 flags = SORT_REVERSE;
1320 if (str_ncmp ("last-", s, 5) == 0) {
1325 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1327 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1331 *((short*) dst->data) = i | flags;
1335 /* if additional data more == 1, we want to resolve synonyms */
1336 static void mutt_set_default (const char* name, void* p, unsigned long more) {
1337 char buf[LONG_STRING];
1338 struct option_t* ptr = (struct option_t*) p;
1340 if (DTYPE (ptr->type) == DT_SYN) {
1343 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1345 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_from_string)
1347 mutt_option_value (ptr->option, buf, sizeof (buf));
1348 if (str_len (ptr->init) == 0 && buf && *buf)
1349 ptr->init = str_dup (buf);
1352 static struct option_t* add_option (const char* name, const char* init,
1353 short type, short dup) {
1354 struct option_t* option = mem_calloc (1, sizeof (struct option_t));
1356 debug_print (1, ("adding $%s\n", name));
1358 option->option = str_dup (name);
1359 option->type = type;
1361 option->init = dup ? str_dup (init) : (char*) init;
1365 /* creates new option_t* of type DT_USER for $user_ var */
1366 static struct option_t* add_user_option (const char* name) {
1367 return (add_option (name, NULL, DT_USER, 1));
1370 /* free()'s option_t* */
1371 static void del_option (void* p) {
1372 struct option_t* ptr = (struct option_t*) p;
1373 char* s = (char*) ptr->data;
1374 debug_print (1, ("removing option '%s' from table\n", NONULL (ptr->option)));
1375 mem_free (&ptr->option);
1377 mem_free (&ptr->init);
1381 static int init_expand (char** dst, struct option_t* src) {
1387 if (DTYPE(src->type) == DT_STR ||
1388 DTYPE(src->type) == DT_PATH) {
1389 /* only expand for string as it's the only place where
1390 * we want to expand vars right now */
1391 if (src->init && *src->init) {
1392 memset (&token, 0, sizeof (BUFFER));
1393 memset (&in, 0, sizeof (BUFFER));
1394 len = str_len (src->init) + 2;
1395 in.data = mem_malloc (len+1);
1396 snprintf (in.data, len, "\"%s\"", src->init);
1399 mutt_extract_token (&token, &in, 0);
1400 if (token.data && *token.data)
1401 *dst = str_dup (token.data);
1403 *dst = str_dup ("");
1404 mem_free (&in.data);
1405 mem_free (&token.data);
1407 *dst = str_dup ("");
1409 /* for non-string: take value as is */
1410 *dst = str_dup (src->init);
1414 /* if additional data more == 1, we want to resolve synonyms */
1415 static void mutt_restore_default (const char* name, void* p,
1416 unsigned long more) {
1417 char errbuf[STRING];
1418 struct option_t* ptr = (struct option_t*) p;
1421 if (DTYPE (ptr->type) == DT_SYN) {
1424 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1428 if (FuncTable[DTYPE (ptr->type)].opt_from_string) {
1429 init_expand (&init, ptr);
1430 if (!FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, init, errbuf,
1432 if (!option (OPTNOCURSES))
1434 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1435 "Please report this error: \"%s\"\n"),
1436 ptr->option, NONULL (init), errbuf);
1442 if (ptr->flags & R_INDEX)
1443 set_option (OPTFORCEREDRAWINDEX);
1444 if (ptr->flags & R_PAGER)
1445 set_option (OPTFORCEREDRAWPAGER);
1446 if (ptr->flags & R_RESORT_SUB)
1447 set_option (OPTSORTSUBTHREADS);
1448 if (ptr->flags & R_RESORT)
1449 set_option (OPTNEEDRESORT);
1450 if (ptr->flags & R_RESORT_INIT)
1451 set_option (OPTRESORTINIT);
1452 if (ptr->flags & R_TREE)
1453 set_option (OPTREDRAWTREE);
1456 /* check whether value for $dsn_return would be valid */
1457 static int check_dsn_return (const char* option, unsigned long p,
1458 char* errbuf, size_t errlen) {
1459 char* val = (char*) p;
1460 if (val && *val && str_ncmp (val, "hdrs", 4) != 0 &&
1461 str_ncmp (val, "full", 4) != 0) {
1463 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1469 /* check whether value for $dsn_notify would be valid */
1470 static int check_dsn_notify (const char* option, unsigned long p,
1471 char* errbuf, size_t errlen) {
1472 list2_t* list = NULL;
1474 char* val = (char*) p;
1478 list = list_from_str (val, ",");
1479 if (list_empty (list))
1482 for (i = 0; i < list->length; i++)
1483 if (str_ncmp (list->data[i], "never", 5) != 0 &&
1484 str_ncmp (list->data[i], "failure", 7) != 0 &&
1485 str_ncmp (list->data[i], "delay", 5) != 0 &&
1486 str_ncmp (list->data[i], "success", 7) != 0) {
1488 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1489 (char*) list->data[i], "dsn_notify");
1493 list_del (&list, (list_del_t*) _mem_free);
1497 static int check_num (const char* option, unsigned long p,
1498 char* errbuf, size_t errlen) {
1501 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1508 static int check_debug (const char* option, unsigned long p,
1509 char* errbuf, size_t errlen) {
1510 if ((int) p <= DEBUG_MAX_LEVEL &&
1511 (int) p >= DEBUG_MIN_LEVEL)
1515 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1520 static int check_history (const char* option, unsigned long p,
1521 char* errbuf, size_t errlen) {
1522 if (!check_num ("history", p, errbuf, errlen))
1524 mutt_init_history ();
1528 static int check_special (const char* name, unsigned long val,
1529 char* errbuf, size_t errlen) {
1532 for (i = 0; SpecialVars[i].name; i++) {
1533 if (str_cmp (SpecialVars[i].name, name) == 0) {
1534 return (SpecialVars[i].check (SpecialVars[i].name,
1535 val, errbuf, errlen));
1541 static const struct mapping_t* get_sortmap (struct option_t* option) {
1542 const struct mapping_t* map = NULL;
1544 switch (option->type & DT_SUBTYPE_MASK) {
1546 map = SortAliasMethods;
1548 case DT_SORT_BROWSER:
1549 map = SortBrowserMethods;
1552 if ((WithCrypto & APPLICATION_PGP))
1553 map = SortKeyMethods;
1556 map = SortAuxMethods;
1565 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1568 int query, unset, inv, reset, r = 0;
1569 struct option_t* option = NULL;
1571 while (MoreArgs (s)) {
1572 /* reset state variables */
1574 unset = data & M_SET_UNSET;
1575 inv = data & M_SET_INV;
1576 reset = data & M_SET_RESET;
1578 if (*s->dptr == '?') {
1582 else if (str_ncmp ("no", s->dptr, 2) == 0) {
1586 else if (str_ncmp ("inv", s->dptr, 3) == 0) {
1590 else if (*s->dptr == '&') {
1595 /* get the variable name */
1596 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1598 /* resolve synonyms */
1599 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1600 DTYPE (option->type == DT_SYN)) {
1601 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1602 syn_add (newopt, option);
1606 /* see if we need to add $user_ var */
1607 if (!option && ascii_strncmp ("user_", tmp->data, 5) == 0) {
1608 /* there's no option named like this yet so only add one
1609 * if the action isn't any of: reset, unset, query */
1610 if (!(reset || unset || query || *s->dptr != '=')) {
1611 debug_print (1, ("adding user option '%s'\n", tmp->data));
1612 option = add_user_option (tmp->data);
1613 hash_insert (ConfigOptions, option->option, option, 0);
1617 if (!option && !(reset && str_cmp ("all", tmp->data) == 0)) {
1618 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1624 if (query || unset || inv) {
1625 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1629 if (s && *s->dptr == '=') {
1630 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1634 if (!str_cmp ("all", tmp->data)) {
1635 if (CurrentMenu == MENU_PAGER) {
1636 snprintf (err->data, err->dsize, _("Not available in this menu."));
1639 hash_map (ConfigOptions, mutt_restore_default, 1);
1640 set_option (OPTFORCEREDRAWINDEX);
1641 set_option (OPTFORCEREDRAWPAGER);
1642 set_option (OPTSORTSUBTHREADS);
1643 set_option (OPTNEEDRESORT);
1644 set_option (OPTRESORTINIT);
1645 set_option (OPTREDRAWTREE);
1648 else if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1649 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1654 mutt_restore_default (NULL, option, 1);
1657 else if (DTYPE (option->type) == DT_BOOL) {
1658 /* XXX this currently ignores the function table
1659 * as we don't get invert and stuff into it */
1660 if (s && *s->dptr == '=') {
1661 if (unset || inv || query) {
1662 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1667 mutt_extract_token (tmp, s, 0);
1668 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1670 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1673 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1679 bool_to_string (err->data, err->dsize, option);
1685 unset_option (option->data);
1687 toggle_option (option->data);
1689 set_option (option->data);
1691 else if (DTYPE (option->type) == DT_STR ||
1692 DTYPE (option->type) == DT_PATH ||
1693 DTYPE (option->type) == DT_ADDR ||
1694 DTYPE (option->type) == DT_MAGIC ||
1695 DTYPE (option->type) == DT_NUM ||
1696 DTYPE (option->type) == DT_SORT ||
1697 DTYPE (option->type) == DT_RX ||
1698 DTYPE (option->type) == DT_USER ||
1699 DTYPE (option->type) == DT_SYS) {
1701 /* XXX maybe we need to get unset into handlers? */
1702 if (DTYPE (option->type) == DT_STR ||
1703 DTYPE (option->type) == DT_PATH ||
1704 DTYPE (option->type) == DT_ADDR ||
1705 DTYPE (option->type) == DT_USER ||
1706 DTYPE (option->type) == DT_SYS) {
1709 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1710 snprintf (err->data, err->dsize, _("$%s is read-only"),
1714 } else if (DTYPE (option->type) == DT_ADDR)
1715 rfc822_free_address ((ADDRESS **) option->data);
1716 else if (DTYPE (option->type) == DT_USER)
1717 /* to unset $user_ means remove */
1718 hash_delete (ConfigOptions, option->option,
1719 option, del_option);
1721 mem_free ((void *) option->data);
1726 if (query || *s->dptr != '=') {
1727 FuncTable[DTYPE (option->type)].opt_to_string
1728 (err->data, err->dsize, option);
1732 /* the $muttng_ variables are read-only */
1733 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1734 snprintf (err->data, err->dsize, _("$%s is read-only"),
1741 mutt_extract_token (tmp, s, 0);
1742 if (!FuncTable[DTYPE (option->type)].opt_from_string
1743 (option, tmp->data, err->data, err->dsize))
1747 else if (DTYPE (option->type) == DT_QUAD) {
1750 quad_to_string (err->data, err->dsize, option);
1754 if (*s->dptr == '=') {
1757 mutt_extract_token (tmp, s, 0);
1758 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1759 set_quadoption (option->data, M_YES);
1760 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1761 set_quadoption (option->data, M_NO);
1762 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1763 set_quadoption (option->data, M_ASKYES);
1764 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1765 set_quadoption (option->data, M_ASKNO);
1767 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1768 tmp->data, option->option);
1775 toggle_quadoption (option->data);
1777 set_quadoption (option->data, M_NO);
1779 set_quadoption (option->data, M_YES);
1783 snprintf (err->data, err->dsize, _("%s: unknown type"),
1789 if (option->flags & R_INDEX)
1790 set_option (OPTFORCEREDRAWINDEX);
1791 if (option->flags & R_PAGER)
1792 set_option (OPTFORCEREDRAWPAGER);
1793 if (option->flags & R_RESORT_SUB)
1794 set_option (OPTSORTSUBTHREADS);
1795 if (option->flags & R_RESORT)
1796 set_option (OPTNEEDRESORT);
1797 if (option->flags & R_RESORT_INIT)
1798 set_option (OPTRESORTINIT);
1799 if (option->flags & R_TREE)
1800 set_option (OPTREDRAWTREE);
1807 /* reads the specified initialization file. returns -1 if errors were found
1808 so that we can pause to let the user know... */
1809 static int source_rc (const char *rcfile, BUFFER * err)
1812 int line = 0, rc = 0, conv = 0;
1814 char *linebuf = NULL;
1815 char *currentline = NULL;
1819 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
1821 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1822 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1826 memset (&token, 0, sizeof (token));
1827 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
1828 conv = ConfigCharset && (*ConfigCharset) && Charset;
1830 currentline = str_dup (linebuf);
1833 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1836 currentline = linebuf;
1841 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1842 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1843 if (--rc < -MAXERRS) {
1845 mem_free (¤tline);
1854 mem_free (¤tline);
1856 mem_free (&token.data);
1857 mem_free (&linebuf);
1860 mutt_wait_filter (pid);
1862 /* the muttrc source keyword */
1863 snprintf (err->data, err->dsize,
1864 rc >= -MAXERRS ? _("source: errors in %s")
1865 : _("source: reading aborted due too many errors in %s"),
1874 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
1877 char path[_POSIX_PATH_MAX];
1881 if (mutt_extract_token (tmp, s, 0) != 0) {
1882 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1886 strfcpy (path, tmp->data, sizeof (path));
1887 mutt_expand_path (path, sizeof (path));
1889 rc += source_rc (path, err);
1891 while (MoreArgs (s));
1893 return ((rc < 0) ? -1 : 0);
1896 /* line command to execute
1898 token scratch buffer to be used by parser. caller should free
1899 token->data when finished. the reason for this variable is
1900 to avoid having to allocate and deallocate a lot of memory
1901 if we are parsing many lines. the caller can pass in the
1902 memory to use, which avoids having to create new space for
1903 every call to this function.
1905 err where to write error messages */
1906 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1911 memset (&expn, 0, sizeof (expn));
1912 expn.data = expn.dptr = line;
1913 expn.dsize = str_len (line);
1917 debug_print (1, ("expand '%s'\n", line));
1920 while (*expn.dptr) {
1921 if (*expn.dptr == '#')
1922 break; /* rest of line is a comment */
1923 if (*expn.dptr == ';') {
1927 mutt_extract_token (token, &expn, 0);
1928 for (i = 0; Commands[i].name; i++) {
1929 if (!str_cmp (token->data, Commands[i].name)) {
1930 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1935 if (!Commands[i].name) {
1936 snprintf (err->data, err->dsize, _("%s: unknown command"),
1937 NONULL (token->data));
1944 mem_free (&expn.data);
1949 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1950 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1951 /* initial string that starts completion. No telling how much crap
1952 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1953 char User_typed[LONG_STRING] = { 0 };
1955 int Num_matched = 0; /* Number of matches for completion */
1956 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1957 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1959 /* helper function for completion. Changes the dest buffer if
1960 necessary/possible to aid completion.
1961 dest == completion result gets here.
1962 src == candidate for completion.
1963 try == user entered data for completion.
1964 len == length of dest buffer.
1966 static void candidate (char *dest, char *try, char *src, int len)
1970 if (strstr (src, try) == src) {
1971 Matches[Num_matched++] = src;
1973 strfcpy (dest, src, len);
1975 for (l = 0; src[l] && src[l] == dest[l]; l++);
1981 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1985 int spaces; /* keep track of the number of leading spaces on the line */
1988 spaces = buffer - pt;
1990 pt = buffer + pos - spaces;
1991 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1994 if (pt == buffer) { /* complete cmd */
1995 /* first TAB. Collect all the matches */
1998 strfcpy (User_typed, pt, sizeof (User_typed));
1999 memset (Matches, 0, sizeof (Matches));
2000 memset (Completed, 0, sizeof (Completed));
2001 for (num = 0; Commands[num].name; num++)
2002 candidate (Completed, User_typed, Commands[num].name,
2003 sizeof (Completed));
2004 Matches[Num_matched++] = User_typed;
2006 /* All matches are stored. Longest non-ambiguous string is ""
2007 * i.e. dont change 'buffer'. Fake successful return this time */
2008 if (User_typed[0] == 0)
2012 if (Completed[0] == 0 && User_typed[0])
2015 /* Num_matched will _always_ be atleast 1 since the initial
2016 * user-typed string is always stored */
2017 if (numtabs == 1 && Num_matched == 2)
2018 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
2019 else if (numtabs > 1 && Num_matched > 2)
2020 /* cycle thru all the matches */
2021 snprintf (Completed, sizeof (Completed), "%s",
2022 Matches[(numtabs - 2) % Num_matched]);
2024 /* return the completed command */
2025 strncpy (buffer, Completed, len - spaces);
2027 else if (!str_ncmp (buffer, "set", 3)
2028 || !str_ncmp (buffer, "unset", 5)
2029 || !str_ncmp (buffer, "reset", 5)
2030 || !str_ncmp (buffer, "toggle", 6)) { /* complete variables */
2031 char *prefixes[] = { "no", "inv", "?", "&", 0 };
2034 /* loop through all the possible prefixes (no, inv, ...) */
2035 if (!str_ncmp (buffer, "set", 3)) {
2036 for (num = 0; prefixes[num]; num++) {
2037 if (!str_ncmp (pt, prefixes[num], str_len (prefixes[num]))) {
2038 pt += str_len (prefixes[num]);
2044 /* first TAB. Collect all the matches */
2047 strfcpy (User_typed, pt, sizeof (User_typed));
2048 memset (Matches, 0, sizeof (Matches));
2049 memset (Completed, 0, sizeof (Completed));
2050 for (num = 0; MuttVars[num].option; num++)
2051 candidate (Completed, User_typed, MuttVars[num].option,
2052 sizeof (Completed));
2053 Matches[Num_matched++] = User_typed;
2055 /* All matches are stored. Longest non-ambiguous string is ""
2056 * i.e. dont change 'buffer'. Fake successful return this time */
2057 if (User_typed[0] == 0)
2061 if (Completed[0] == 0 && User_typed[0])
2064 /* Num_matched will _always_ be atleast 1 since the initial
2065 * user-typed string is always stored */
2066 if (numtabs == 1 && Num_matched == 2)
2067 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
2068 else if (numtabs > 1 && Num_matched > 2)
2069 /* cycle thru all the matches */
2070 snprintf (Completed, sizeof (Completed), "%s",
2071 Matches[(numtabs - 2) % Num_matched]);
2073 strncpy (pt, Completed, buffer + len - pt - spaces);
2075 else if (!str_ncmp (buffer, "exec", 4)) {
2076 struct binding_t *menu = km_get_table (CurrentMenu);
2078 if (!menu && CurrentMenu != MENU_PAGER)
2082 /* first TAB. Collect all the matches */
2085 strfcpy (User_typed, pt, sizeof (User_typed));
2086 memset (Matches, 0, sizeof (Matches));
2087 memset (Completed, 0, sizeof (Completed));
2088 for (num = 0; menu[num].name; num++)
2089 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
2090 /* try the generic menu */
2091 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2093 for (num = 0; menu[num].name; num++)
2094 candidate (Completed, User_typed, menu[num].name,
2095 sizeof (Completed));
2097 Matches[Num_matched++] = User_typed;
2099 /* All matches are stored. Longest non-ambiguous string is ""
2100 * i.e. dont change 'buffer'. Fake successful return this time */
2101 if (User_typed[0] == 0)
2105 if (Completed[0] == 0 && User_typed[0])
2108 /* Num_matched will _always_ be atleast 1 since the initial
2109 * user-typed string is always stored */
2110 if (numtabs == 1 && Num_matched == 2)
2111 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
2112 else if (numtabs > 1 && Num_matched > 2)
2113 /* cycle thru all the matches */
2114 snprintf (Completed, sizeof (Completed), "%s",
2115 Matches[(numtabs - 2) % Num_matched]);
2117 strncpy (pt, Completed, buffer + len - pt - spaces);
2125 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2127 char var[STRING], *pt = buffer;
2129 struct option_t* option = NULL;
2135 spaces = buffer - pt;
2137 pt = buffer + pos - spaces;
2138 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2140 pt++; /* move past the space */
2141 if (*pt == '=') /* abort if no var before the '=' */
2144 if (str_ncmp (buffer, "set", 3) == 0) {
2145 strfcpy (var, pt, sizeof (var));
2146 /* ignore the trailing '=' when comparing */
2147 var[str_len (var) - 1] = 0;
2148 if (!(option = hash_find (ConfigOptions, var)))
2149 return 0; /* no such variable. */
2151 char tmp[LONG_STRING], tmp2[LONG_STRING];
2153 size_t dlen = buffer + len - pt - spaces;
2154 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2158 if ((DTYPE (option->type) == DT_STR) ||
2159 (DTYPE (option->type) == DT_PATH) ||
2160 (DTYPE (option->type) == DT_RX)) {
2161 strfcpy (tmp, NONULL (*((char **) option->data)), sizeof (tmp));
2162 if (DTYPE (option->type) == DT_PATH)
2163 mutt_pretty_mailbox (tmp);
2165 else if (DTYPE (option->type) == DT_ADDR) {
2166 rfc822_write_address (tmp, sizeof (tmp),
2167 *((ADDRESS **) option->data), 0);
2169 else if (DTYPE (option->type) == DT_QUAD)
2170 strfcpy (tmp, vals[quadoption (option->data)], sizeof (tmp));
2171 else if (DTYPE (option->type) == DT_NUM)
2172 snprintf (tmp, sizeof (tmp), "%d", (*((short *) option->data)));
2173 else if (DTYPE (option->type) == DT_SORT) {
2174 const struct mapping_t *map;
2177 switch (option->type & DT_SUBTYPE_MASK) {
2179 map = SortAliasMethods;
2181 case DT_SORT_BROWSER:
2182 map = SortBrowserMethods;
2185 if ((WithCrypto & APPLICATION_PGP))
2186 map = SortKeyMethods;
2195 mutt_getnamebyvalue (*((short *) option->data) & SORT_MASK,
2197 snprintf (tmp, sizeof (tmp), "%s%s%s",
2198 (*((short *) option->data) & SORT_REVERSE) ?
2200 (*((short *) option->data) & SORT_LAST) ? "last-" :
2203 else if (DTYPE (option->type) == DT_MAGIC) {
2205 switch (DefaultMagic) {
2221 strfcpy (tmp, p, sizeof (tmp));
2223 else if (DTYPE (option->type) == DT_BOOL)
2224 strfcpy (tmp, option (option->data) ? "yes" : "no",
2229 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
2230 if (*s == '\\' || *s == '"')
2236 strfcpy (tmp, pt, sizeof (tmp));
2237 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2245 /* Implement the -Q command line flag */
2246 int mutt_query_variables (LIST * queries)
2250 char errbuff[STRING];
2251 char command[STRING];
2255 memset (&err, 0, sizeof (err));
2256 memset (&token, 0, sizeof (token));
2259 err.dsize = sizeof (errbuff);
2261 for (p = queries; p; p = p->next) {
2262 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2263 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2264 fprintf (stderr, "%s\n", err.data);
2265 mem_free (&token.data);
2268 printf ("%s\n", err.data);
2271 mem_free (&token.data);
2275 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2279 for (i = 0; map[i].name; i++)
2280 if (map[i].value == val)
2281 return (map[i].name);
2285 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2289 for (i = 0; map[i].name; i++)
2290 if (ascii_strcasecmp (map[i].name, name) == 0)
2291 return (map[i].value);
2295 static int mutt_execute_commands (LIST * p)
2298 char errstr[SHORT_STRING];
2300 memset (&err, 0, sizeof (err));
2302 err.dsize = sizeof (errstr);
2303 memset (&token, 0, sizeof (token));
2304 for (; p; p = p->next) {
2305 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2306 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2307 mem_free (&token.data);
2311 mem_free (&token.data);
2315 void mutt_init (int skip_sys_rc, LIST * commands)
2318 struct utsname utsname;
2319 char *p, buffer[STRING], error[STRING];
2320 int i, default_rc = 0, need_pause = 0;
2323 memset (&err, 0, sizeof (err));
2325 err.dsize = sizeof (error);
2327 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2328 * to have some room for $user_ vars */
2329 ConfigOptions = hash_create (sizeof (MuttVars) * 3);
2330 for (i = 0; MuttVars[i].option; i++) {
2331 if (DTYPE (MuttVars[i].type) != DT_SYS)
2332 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2334 hash_insert (ConfigOptions, MuttVars[i].option,
2335 add_option (MuttVars[i].option, MuttVars[i].init,
2340 * XXX - use something even more difficult to predict?
2342 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2343 "\033]9;%ld\a", (long) time (NULL));
2345 /* on one of the systems I use, getcwd() does not return the same prefix
2346 as is listed in the passwd file */
2347 if ((p = getenv ("HOME")))
2348 Homedir = str_dup (p);
2350 /* Get some information about the user */
2351 if ((pw = getpwuid (getuid ()))) {
2354 Username = str_dup (pw->pw_name);
2356 Homedir = str_dup (pw->pw_dir);
2358 Realname = str_dup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2359 Shell = str_dup (pw->pw_shell);
2365 fputs (_("unable to determine home directory"), stderr);
2368 if ((p = getenv ("USER")))
2369 Username = str_dup (p);
2372 fputs (_("unable to determine username"), stderr);
2375 Shell = str_dup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2378 debug_start(Homedir);
2380 /* And about the host... */
2382 /* some systems report the FQDN instead of just the hostname */
2383 if ((p = strchr (utsname.nodename, '.'))) {
2384 Hostname = str_substrdup (utsname.nodename, p);
2386 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2389 Hostname = str_dup (utsname.nodename);
2392 #define DOMAIN buffer
2393 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2394 Fqdn = str_dup ("@");
2397 if (*DOMAIN != '@') {
2398 Fqdn = mem_malloc (str_len (DOMAIN) + str_len (Hostname) + 2);
2399 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2402 Fqdn = str_dup (NONULL (Hostname));
2409 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2411 fgets (buffer, sizeof (buffer), f);
2412 p = (char*) &buffer;
2415 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r')
2419 NewsServer = str_dup (p);
2423 if ((p = getenv ("NNTPSERVER")))
2424 NewsServer = str_dup (p);
2427 if ((p = getenv ("MAIL")))
2428 Spoolfile = str_dup (p);
2429 else if ((p = getenv ("MAILDIR")))
2430 Spoolfile = str_dup (p);
2433 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2435 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
2437 Spoolfile = str_dup (buffer);
2440 if ((p = getenv ("MAILCAPS")))
2441 MailcapPath = str_dup (p);
2443 /* Default search path from RFC1524 */
2445 str_dup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2446 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2449 Tempdir = str_dup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2451 p = getenv ("VISUAL");
2453 p = getenv ("EDITOR");
2457 Editor = str_dup (p);
2458 Visual = str_dup (p);
2460 if ((p = getenv ("REPLYTO")) != NULL) {
2463 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2465 memset (&buf, 0, sizeof (buf));
2466 buf.data = buf.dptr = buffer;
2467 buf.dsize = str_len (buffer);
2469 memset (&token, 0, sizeof (token));
2470 parse_my_hdr (&token, &buf, 0, &err);
2471 mem_free (&token.data);
2474 if ((p = getenv ("EMAIL")) != NULL)
2475 From = rfc822_parse_adrlist (NULL, p);
2477 mutt_set_langinfo_charset ();
2478 mutt_set_charset (Charset);
2481 /* Set standard defaults */
2482 hash_map (ConfigOptions, mutt_set_default, 0);
2483 hash_map (ConfigOptions, mutt_restore_default, 0);
2485 CurrentMenu = MENU_MAIN;
2488 #ifndef LOCALES_HACK
2489 /* Do we have a locale definition? */
2490 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2491 ((p = getenv ("LANG")) != NULL && p[0]) ||
2492 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2493 set_option (OPTLOCALES);
2497 /* Unset suspend by default if we're the session leader */
2498 if (getsid (0) == getpid ())
2499 unset_option (OPTSUSPEND);
2502 mutt_init_history ();
2511 * When changing the code which looks for a configuration file,
2512 * please also change the corresponding code in muttbug.sh.in.
2522 snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2524 if (access (buffer, F_OK) == -1)
2526 snprintf (buffer, sizeof (buffer), "%s/.muttngrc", NONULL (Homedir));
2527 if (access (buffer, F_OK) == -1)
2529 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s",
2530 NONULL (Homedir), MUTT_VERSION);
2531 if (access (buffer, F_OK) == -1)
2533 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc",
2537 Muttrc = str_dup (buffer);
2540 strfcpy (buffer, Muttrc, sizeof (buffer));
2542 mutt_expand_path (buffer, sizeof (buffer));
2543 Muttrc = str_dup (buffer);
2545 mem_free (&AliasFile);
2546 AliasFile = str_dup (NONULL (Muttrc));
2548 /* Process the global rc file if it exists and the user hasn't explicity
2549 requested not to via "-n". */
2551 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2553 if (access (buffer, F_OK) == -1)
2554 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", SYSCONFDIR);
2555 if (access (buffer, F_OK) == -1)
2556 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR,
2558 if (access (buffer, F_OK) == -1)
2559 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2560 if (access (buffer, F_OK) != -1) {
2561 if (source_rc (buffer, &err) != 0) {
2562 fputs (err.data, stderr);
2563 fputc ('\n', stderr);
2569 /* Read the user's initialization file. */
2570 if (access (Muttrc, F_OK) != -1) {
2571 if (!option (OPTNOCURSES))
2573 if (source_rc (Muttrc, &err) != 0) {
2574 fputs (err.data, stderr);
2575 fputc ('\n', stderr);
2579 else if (!default_rc) {
2580 /* file specified by -F does not exist */
2581 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2582 mutt_endwin (buffer);
2586 if (mutt_execute_commands (commands) != 0)
2589 /* warn about synonym variables */
2590 if (!list_empty(Synonyms)) {
2592 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2593 for (i = 0; i < Synonyms->length; i++) {
2594 struct option_t* newopt = NULL, *oldopt = NULL;
2595 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2596 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2597 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2598 oldopt ? NONULL (oldopt->option) : "",
2599 newopt ? NONULL (newopt->option) : "",
2600 NONULL(((syn_t*) Synonyms->data[i])->f),
2601 ((syn_t*) Synonyms->data[i])->l);
2603 fprintf (stderr, _("Warning: synonym variables are scheduled"
2604 " for removal.\n"));
2605 list_del (&Synonyms, syn_del);
2609 if (need_pause && !option (OPTNOCURSES)) {
2610 if (mutt_any_key_to_continue (NULL) == -1)
2615 set_option (OPTWEED); /* turn weeding on by default */
2619 int mutt_get_hook_type (const char *name)
2621 struct command_t *c;
2623 for (c = Commands; c->name; c++)
2624 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2629 /* compare two option_t*'s for sorting -t/-T output */
2630 static int opt_cmp (const void* a, const void* b) {
2631 return (str_cmp ((*(struct option_t**) a)->option,
2632 (*(struct option_t**) b)->option));
2635 /* callback for hash_map() to put all non-synonym vars into list */
2636 static void opt_sel_full (const char* key, void* data,
2637 unsigned long more) {
2638 list2_t** l = (list2_t**) more;
2639 struct option_t* option = (struct option_t*) data;
2641 if (DTYPE (option->type) == DT_SYN)
2643 list_push_back (l, option);
2646 /* callback for hash_map() to put all changed non-synonym vars into list */
2647 static void opt_sel_diff (const char* key, void* data,
2648 unsigned long more) {
2649 list2_t** l = (list2_t**) more;
2650 struct option_t* option = (struct option_t*) data;
2651 char buf[LONG_STRING];
2653 if (DTYPE (option->type) == DT_SYN)
2656 mutt_option_value (option->option, buf, sizeof (buf));
2657 if (str_cmp (buf, option->init) != 0)
2658 list_push_back (l, option);
2661 /* dump out the value of all the variables we have */
2662 int mutt_dump_variables (int full) {
2664 char outbuf[STRING];
2665 list2_t* tmp = NULL;
2666 struct option_t* option = NULL;
2668 /* get all non-synonyms into list... */
2669 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2670 (unsigned long) &tmp);
2672 if (!list_empty(tmp)) {
2673 /* ...and dump list sorted */
2674 qsort (tmp->data, tmp->length, sizeof (void*), opt_cmp);
2675 for (i = 0; i < tmp->length; i++) {
2676 option = (struct option_t*) tmp->data[i];
2677 FuncTable[DTYPE (option->type)].opt_to_string
2678 (outbuf, sizeof (outbuf), option);
2679 printf ("%s\n", outbuf);
2682 list_del (&tmp, NULL);