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.
17 #include <lib-lib/mem.h>
18 #include <lib-lib/macros.h>
24 #include "mutt_curses.h"
30 #include "mutt_crypt.h"
31 #include "mutt_idna.h"
33 #if defined(USE_SSL) || defined(USE_GNUTLS)
37 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
38 #include "mutt_libesmtp.h"
47 #include "lib/debug.h"
53 #include <sys/utsname.h>
60 static const struct mapping_t* get_sortmap (struct option_t* option);
61 static int parse_sort (struct option_t* dst, const char *s,
62 const struct mapping_t *map,
63 char* errbuf, size_t errlen);
65 static HASH *ConfigOptions = NULL;
67 /* for synonym warning reports: synonym found during parsing */
71 struct option_t* n; /* new */
72 struct option_t* o; /* old */
75 /* for synonym warning reports: list of synonyms found */
76 static list2_t* Synonyms;
77 /* for synonym warning reports: current rc file */
78 static const char* CurRCFile = NULL;
79 /* for synonym warning reports: current rc line */
80 static int CurRCLine = 0;
82 /* prototypes for checking for special vars */
83 static int check_dsn_return (const char* option, unsigned long val,
84 char* errbuf, size_t errlen);
85 static int check_dsn_notify (const char* option, unsigned long val,
86 char* errbuf, size_t errlen);
87 static int check_history (const char* option, unsigned long val,
88 char* errbuf, size_t errlen);
89 /* this checks that numbers are >= 0 */
90 static int check_num (const char* option, unsigned long val,
91 char* errbuf, size_t errlen);
93 static int check_debug (const char* option, unsigned long val,
94 char* errbuf, size_t errlen);
97 /* use this to check only */
98 static int check_special (const char* option, unsigned long val,
99 char* errbuf, size_t errlen);
101 /* variable <-> sanity check function mappings
102 * when changing these, make sure the proper _from_string handler
103 * does this checking!
107 int (*check) (const char* option, unsigned long val,
108 char* errbuf, size_t errlen);
110 { "dsn_notify", check_dsn_notify },
111 { "dsn_return", check_dsn_return },
112 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
113 { "smtp_use_tls", mutt_libesmtp_check_usetls },
115 { "history", check_history },
116 { "pager_index_lines", check_num },
118 { "debug_level", check_debug },
124 /* protos for config type handles: convert value to string */
125 static void bool_to_string (char* dst, size_t dstlen, struct option_t* option);
126 static void num_to_string (char* dst, size_t dstlen, struct option_t* option);
127 static void str_to_string (char* dst, size_t dstlen, struct option_t* option);
128 static void quad_to_string (char* dst, size_t dstlen, struct option_t* option);
129 static void sort_to_string (char* dst, size_t dstlen, struct option_t* option);
130 static void rx_to_string (char* dst, size_t dstlen, struct option_t* option);
131 static void magic_to_string (char* dst, size_t dstlen, struct option_t* option);
132 static void addr_to_string (char* dst, size_t dstlen, struct option_t* option);
133 static void user_to_string (char* dst, size_t dstlen, struct option_t* option);
134 static void sys_to_string (char* dst, size_t dstlen, struct option_t* option);
136 /* protos for config type handles: convert to value from string */
137 static int bool_from_string (struct option_t* dst, const char* val,
138 char* errbuf, size_t errlen);
139 static int num_from_string (struct option_t* dst, const char* val,
140 char* errbuf, size_t errlen);
141 static int str_from_string (struct option_t* dst, const char* val,
142 char* errbuf, size_t errlen);
143 static int path_from_string (struct option_t* dst, const char* val,
144 char* errbuf, size_t errlen);
145 static int quad_from_string (struct option_t* dst, const char* val,
146 char* errbuf, size_t errlen);
147 static int sort_from_string (struct option_t* dst, const char* val,
148 char* errbuf, size_t errlen);
149 static int rx_from_string (struct option_t* dst, const char* val,
150 char* errbuf, size_t errlen);
151 static int magic_from_string (struct option_t* dst, const char* val,
152 char* errbuf, size_t errlen);
153 static int addr_from_string (struct option_t* dst, const char* val,
154 char* errbuf, size_t errlen);
155 static int user_from_string (struct option_t* dst, const char* val,
156 char* errbuf, size_t errlen);
160 void (*opt_to_string) (char* dst, size_t dstlen, struct option_t* option);
161 int (*opt_from_string) (struct option_t* dst, const char* val,
162 char* errbuf, size_t errlen);
164 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
165 { DT_BOOL, bool_to_string, bool_from_string },
166 { DT_NUM, num_to_string, num_from_string },
167 { DT_STR, str_to_string, str_from_string },
168 { DT_PATH, str_to_string, path_from_string },
169 { DT_QUAD, quad_to_string, quad_from_string },
170 { DT_SORT, sort_to_string, sort_from_string },
171 { DT_RX, rx_to_string, rx_from_string },
172 { DT_MAGIC, magic_to_string, magic_from_string },
173 /* synonyms should be resolved already so we don't need this
174 * but must define it as DT_ is used for indexing */
175 { DT_SYN, NULL, NULL },
176 { DT_ADDR, addr_to_string, addr_from_string },
177 { DT_USER, user_to_string, user_from_string },
178 { DT_SYS, sys_to_string, NULL },
181 static void bool_to_string (char* dst, size_t dstlen,
182 struct option_t* option) {
183 snprintf (dst, dstlen, "%s=%s", option->option,
184 option (option->data) ? "yes" : "no");
187 static int bool_from_string (struct option_t* dst, const char* val,
188 char* errbuf, size_t errlen) {
193 if (ascii_strncasecmp (val, "yes", 3) == 0)
195 else if (ascii_strncasecmp (val, "no", 2) == 0)
201 set_option (dst->data);
203 unset_option (dst->data);
207 static void num_to_string (char* dst, size_t dstlen,
208 struct option_t* option) {
210 const char* fmt = (str_cmp (option->option, "umask") == 0) ?
212 snprintf (dst, dstlen, fmt, option->option,
213 *((short*) option->data));
216 static int num_from_string (struct option_t* dst, const char* val,
217 char* errbuf, size_t errlen) {
218 int num = 0, old = 0;
224 num = strtol (val, &t, 0);
226 if (!*val || *t || (short) num != num) {
228 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
234 /* just temporarily accept new val so that check_special for
235 * $history already has it when doing history's init() */
236 old = *((short*) dst->data);
237 *((short*) dst->data) = (short) num;
239 if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
240 *((short*) dst->data) = old;
247 static void str_to_string (char* dst, size_t dstlen,
248 struct option_t* option) {
249 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
250 NONULL (*((char**) option->data)));
253 static void user_to_string (char* dst, size_t dstlen,
254 struct option_t* option) {
255 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
256 NONULL (((char*) option->data)));
259 static void sys_to_string (char* dst, size_t dstlen,
260 struct option_t* option) {
261 char *val = NULL, *t = NULL;
264 /* get some $muttng_ values dynamically */
265 if (ascii_strcmp ("muttng_pwd", option->option) == 0) {
266 val = p_new(char, _POSIX_PATH_MAX);
267 val = getcwd (val, _POSIX_PATH_MAX-1);
269 } else if (ascii_strcmp ("muttng_folder_path", option->option) == 0 &&
270 CurrentFolder && *CurrentFolder) {
272 } else if (ascii_strcmp ("muttng_folder_name", option->option) == 0 &&
273 CurrentFolder && *CurrentFolder) {
275 size_t Maildirlength = str_len (Maildir);
278 * if name starts with $folder, just strip it to keep hierarchy
279 * $folder=imap://host, path=imap://host/inbox/b -> inbox/b
281 if (Maildirlength > 0 && str_ncmp (CurrentFolder, Maildir,
282 Maildirlength) == 0 &&
283 str_len (CurrentFolder) > Maildirlength) {
284 val = CurrentFolder + Maildirlength;
285 if (Maildir[strlen(Maildir)-1]!='/')
287 /* if not $folder, just use everything after last / */
288 } else if ((t = strrchr (CurrentFolder, '/')) != NULL)
290 /* default: use as-is */
297 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (val));
302 static int path_from_string (struct option_t* dst, const char* val,
303 char* errbuf, size_t errlen) {
304 char path[_POSIX_PATH_MAX];
310 p_delete((char**) dst->data);
315 strfcpy (path, val, sizeof(path));
316 mutt_expand_path (path, sizeof(path));
317 str_replace ((char **) dst->data, path);
321 static int str_from_string (struct option_t* dst, const char* val,
322 char* errbuf, size_t errlen) {
326 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
329 str_replace ((char**) dst->data, val);
333 static int user_from_string (struct option_t* dst, const char* val,
334 char* errbuf, size_t errlen) {
335 /* if dst == NULL, we may get here in case the user did unset it,
336 * see parse_set() where item is free()'d before coming here; so
337 * just silently ignore it */
340 if (str_len ((char*) dst->data) == 0)
341 dst->data = (unsigned long) str_dup (val);
343 char* s = (char*) dst->data;
344 str_replace (&s, val);
346 if (str_len (dst->init) == 0)
347 dst->init = str_dup ((char*) dst->data);
351 static void quad_to_string (char* dst, size_t dstlen,
352 struct option_t* option) {
353 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
354 snprintf (dst, dstlen, "%s=%s", option->option,
355 vals[quadoption (option->data)]);
358 static int quad_from_string (struct option_t* dst, const char* val,
359 char* errbuf, size_t errlen) {
364 if (ascii_strncasecmp (val, "yes", 3) == 0)
366 else if (ascii_strncasecmp (val, "no", 2) == 0)
368 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
370 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
376 set_quadoption (dst->data, flag);
380 static void sort_to_string (char* dst, size_t dstlen,
381 struct option_t* option) {
382 const struct mapping_t *map = get_sortmap (option);
383 const char *p = NULL;
386 snprintf (dst, sizeof(dst), "%s=unknown", option->option);
390 p = mutt_getnamebyvalue(*((short *)option->data) & SORT_MASK, map);
392 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
393 (*((short *) option->data) & SORT_REVERSE) ?
395 (*((short *) option->data) & SORT_LAST) ? "last-" :
399 static int sort_from_string (struct option_t* dst, const char* val,
400 char* errbuf, size_t errlen) {
401 const struct mapping_t *map = NULL;
402 if (!(map = get_sortmap (dst))) {
404 snprintf (errbuf, errlen, _("%s: Unknown type."),
408 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
413 static void rx_to_string (char* dst, size_t dstlen,
414 struct option_t* option) {
415 rx_t* p = (rx_t*) option->data;
416 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
417 NONULL (p->pattern));
420 static int rx_from_string (struct option_t* dst, const char* val,
421 char* errbuf, size_t errlen) {
424 int flags = 0, e = 0, not = 0;
430 if (option (OPTATTACHMSG) && !str_cmp (dst->option, "reply_regexp")) {
432 snprintf (errbuf, errlen,
433 "Operation not permitted when in attach-message mode.");
437 if (!((rx_t*) dst->data))
438 *((rx_t**) dst->data) = p_new(rx_t, 1);
440 p = (rx_t*) dst->data;
442 /* something to do? */
443 if (!val || !*val || (p->pattern && str_cmp (p->pattern, val) == 0))
446 if (str_cmp (dst->option, "mask") != 0)
447 flags |= mutt_which_case (val);
450 if (str_cmp (dst->option, "mask") == 0 && *s == '!') {
455 rx = p_new(regex_t, 1);
457 if ((e = REGCOMP (rx, s, flags)) != 0) {
458 regerror (e, rx, errbuf, errlen);
469 str_replace (&p->pattern, val);
473 if (str_cmp (dst->option, "reply_regexp") == 0)
474 mutt_adjust_all_subjects ();
479 static void magic_to_string (char* dst, size_t dstlen,
480 struct option_t* option) {
481 const char* s = NULL;
482 switch (option->data) {
483 case M_MBOX: s = "mbox"; break;
484 case M_MMDF: s = "MMDF"; break;
485 case M_MH: s = "MH"; break;
486 case M_MAILDIR: s = "Maildir"; break;
487 default: s = "unknown"; break;
489 snprintf (dst, dstlen, "%s=%s", option->option, s);
492 static int magic_from_string (struct option_t* dst, const char* val,
493 char* errbuf, size_t errlen) {
496 if (!dst || !val || !*val)
498 if (ascii_strncasecmp (val, "mbox", 4) == 0)
500 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
502 else if (ascii_strncasecmp (val, "mh", 2) == 0)
504 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
510 *((short*) dst->data) = flag;
515 static void addr_to_string (char* dst, size_t dstlen,
516 struct option_t* option) {
519 rfc822_write_address (s, sizeof(s), *((ADDRESS**) option->data), 0);
520 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
523 static int addr_from_string (struct option_t* dst, const char* val,
524 char* errbuf, size_t errlen) {
527 rfc822_free_address ((ADDRESS**) dst->data);
529 *((ADDRESS**) dst->data) = rfc822_parse_adrlist (NULL, val);
533 int mutt_option_value (const char* val, char* dst, size_t dstlen) {
534 struct option_t* option = NULL;
535 char* tmp = NULL, *t = NULL;
538 if (!(option = hash_find (ConfigOptions, val))) {
539 debug_print (1, ("var '%s' not found\n", val));
543 tmp = p_new(char, dstlen+1);
544 FuncTable[DTYPE (option->type)].opt_to_string (tmp, dstlen, option);
546 /* as we get things of type $var=value and don't want to bloat the
547 * above "just" for expansion, we do the stripping here */
548 debug_print (1, ("orig == '%s'\n", tmp));
549 t = strchr (tmp, '=');
553 if (t[l-1] == '"' && *t == '"') {
558 memcpy (dst, t, l+1);
560 debug_print (1, ("stripped == '%s'\n", dst));
565 /* for synonym warning reports: adds synonym to end of list */
566 static void syn_add (struct option_t* n, struct option_t* o) {
567 syn_t* tmp = p_new(syn_t, 1);
568 tmp->f = str_dup (CurRCFile);
572 list_push_back (&Synonyms, tmp);
575 /* for synonym warning reports: free single item (for list_del()) */
576 static void syn_del (void** p) {
577 p_delete(&(*(syn_t**) p)->f);
581 void toggle_quadoption (int opt)
584 int b = (opt % 4) * 2;
586 QuadOptions[n] ^= (1 << b);
589 void set_quadoption (int opt, int flag)
592 int b = (opt % 4) * 2;
594 QuadOptions[n] &= ~(0x3 << b);
595 QuadOptions[n] |= (flag & 0x3) << b;
598 int quadoption (int opt)
601 int b = (opt % 4) * 2;
603 return (QuadOptions[n] >> b) & 0x3;
606 int query_quadoption (int opt, const char *prompt)
608 int v = quadoption (opt);
616 v = mutt_yesorno (prompt, (v == M_ASKYES));
617 CLEARLINE (LINES - 1);
624 static void add_to_list (LIST ** list, const char *str)
626 LIST *t, *last = NULL;
628 /* don't add a NULL or empty string to the list */
629 if (!str || *str == '\0')
632 /* check to make sure the item is not already on this list */
633 for (last = *list; last; last = last->next) {
634 if (ascii_strcasecmp (str, last->data) == 0) {
635 /* already on the list, so just ignore it */
643 if (!*list || last) {
645 t->data = str_dup (str);
655 static int add_to_rx_list (list2_t** list, const char *s, int flags,
664 if (!(rx = rx_compile (s, flags))) {
665 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
669 i = rx_lookup ((*list), rx->pattern);
673 list_push_back (list, rx);
677 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
678 const char *templ, BUFFER * err)
680 SPAM_LIST *t = NULL, *last = NULL;
685 if (!pat || !*pat || !templ)
688 if (!(rx = rx_compile (pat, REG_ICASE))) {
689 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
693 /* check to make sure the item is not already on this list */
694 for (last = *list; last; last = last->next) {
695 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
696 /* Already on the list. Formerly we just skipped this case, but
697 * now we're supporting removals, which means we're supporting
698 * re-adds conceptually. So we probably want this to imply a
699 * removal, then do an add. We can achieve the removal by freeing
700 * the template, and leaving t pointed at the current item.
703 p_delete(&t->template);
710 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
711 * update. Otherwise we want to make a new one to link at the list's end.
714 t = mutt_new_spam_list ();
722 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
723 t->template = str_dup (templ);
725 /* Find highest match number in template string */
727 for (p = templ; *p;) {
732 while (*p && isdigit ((int) *p))
738 t->nmatch++; /* match 0 is always the whole expr */
743 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
745 SPAM_LIST *spam, *prev;
748 /* Being first is a special case. */
752 if (spam->rx && !str_cmp (spam->rx->pattern, pat)) {
755 p_delete(&spam->template);
761 for (spam = prev->next; spam;) {
762 if (!str_cmp (spam->rx->pattern, pat)) {
763 prev->next = spam->next;
765 p_delete(&spam->template);
778 static void remove_from_list (LIST ** l, const char *str)
780 LIST *p, *last = NULL;
782 if (str_cmp ("*", str) == 0)
783 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
788 if (ascii_strcasecmp (str, p->data) == 0) {
791 last->next = p->next;
804 static int remove_from_rx_list (list2_t** l, const char *str)
808 if (str_cmp ("*", str) == 0) {
809 list_del (l, (list_del_t*) rx_free);
813 i = rx_lookup ((*l), str);
815 rx_t* r = list_pop_idx ((*l), i);
823 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
828 struct option_t* option = NULL;
830 memset (&token, 0, sizeof(token));
831 mutt_extract_token (tmp, s, 0);
833 /* is the item defined as a variable or a function? */
834 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL)
837 for (i = 0; !res && i < MENU_MAX; i++) {
838 struct binding_t *b = km_get_table (Menus[i].value);
843 for (j = 0; b[j].name; j++)
844 if (!ascii_strncasecmp (tmp->data, b[j].name, str_len (tmp->data))
845 && (str_len (b[j].name) == str_len (tmp->data))) {
851 /* check for feature_* */
852 if (!res && ascii_strncasecmp (tmp->data, "feature_", 8) == 0 &&
853 (j = str_len (tmp->data)) > 8) {
855 while (Features[i]) {
856 if (str_len (Features[i]) == j-8 &&
857 ascii_strncasecmp (Features[i], tmp->data+8, j-8) == 0) {
867 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
869 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
873 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
876 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
877 mutt_error ("Error: %s", err->data);
878 p_delete(&token.data);
881 p_delete(&token.data);
886 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
890 mutt_extract_token (buf, s, 0);
892 /* don't add "*" to the unignore list */
893 if (strcmp (buf->data, "*"))
894 add_to_list (&UnIgnore, buf->data);
896 remove_from_list (&Ignore, buf->data);
898 while (MoreArgs (s));
903 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
907 mutt_extract_token (buf, s, 0);
908 remove_from_list (&UnIgnore, buf->data);
909 add_to_list (&Ignore, buf->data);
911 while (MoreArgs (s));
916 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
920 mutt_extract_token (buf, s, 0);
921 add_to_list ((LIST **) data, buf->data);
923 while (MoreArgs (s));
928 static void _alternates_clean (void)
932 if (Context && Context->msgcount) {
933 for (i = 0; i < Context->msgcount; i++)
934 Context->hdrs[i]->recip_valid = 0;
938 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
941 _alternates_clean ();
943 mutt_extract_token (buf, s, 0);
944 remove_from_rx_list (&UnAlternates, buf->data);
946 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
949 while (MoreArgs (s));
954 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
957 _alternates_clean ();
959 mutt_extract_token (buf, s, 0);
960 remove_from_rx_list (&Alternates, buf->data);
962 if (str_cmp (buf->data, "*") &&
963 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
967 while (MoreArgs (s));
972 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
977 memset (&templ, 0, sizeof(templ));
979 /* Insist on at least one parameter */
982 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
984 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
988 /* Extract the first token, a regexp */
989 mutt_extract_token (buf, s, 0);
991 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
992 if (data == M_SPAM) {
993 /* If there's a second parameter, it's a template for the spam tag. */
995 mutt_extract_token (&templ, s, 0);
997 /* Add to the spam list. */
998 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
999 p_delete(&templ.data);
1002 p_delete(&templ.data);
1005 /* If not, try to remove from the nospam list. */
1007 remove_from_rx_list (&NoSpamList, buf->data);
1013 /* M_NOSPAM is for nospam commands. */
1014 else if (data == M_NOSPAM) {
1015 /* nospam only ever has one parameter. */
1017 /* "*" is a special case. */
1018 if (!str_cmp (buf->data, "*")) {
1019 mutt_free_spam_list (&SpamList);
1020 list_del (&NoSpamList, (list_del_t*) rx_free);
1024 /* If it's on the spam list, just remove it. */
1025 if (remove_from_spam_list (&SpamList, buf->data) != 0)
1028 /* Otherwise, add it to the nospam list. */
1029 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
1035 /* This should not happen. */
1036 strfcpy (err->data, "This is no good at all.", err->dsize);
1040 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
1044 mutt_extract_token (buf, s, 0);
1046 * Check for deletion of entire list
1048 if (str_cmp (buf->data, "*") == 0) {
1049 mutt_free_list ((LIST **) data);
1052 remove_from_list ((LIST **) data, buf->data);
1054 while (MoreArgs (s));
1059 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
1063 mutt_extract_token (buf, s, 0);
1064 remove_from_rx_list (&UnMailLists, buf->data);
1066 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1069 while (MoreArgs (s));
1074 /* always wise to do what someone else did before */
1075 static void _attachments_clean (void) {
1077 if (Context && Context->msgcount) {
1078 for (i = 0; i < Context->msgcount; i++)
1079 Context->hdrs[i]->attach_valid = 0;
1083 static int parse_attach_list (BUFFER *buf, BUFFER *s, LIST **ldata,
1086 LIST *listp, *lastp;
1091 /* Find the last item in the list that data points to. */
1093 debug_print (5, ("parse_attach_list: ldata = %p, *ldata = %p\n",
1095 for (listp = *ldata; listp; listp = listp->next) {
1096 a = (ATTACH_MATCH *)listp->data;
1097 debug_print (5, ("parse_attach_list: skipping %s/%s\n", a->major, a->minor));
1102 mutt_extract_token (buf, s, 0);
1104 if (!buf->data || *buf->data == '\0')
1107 a = p_new(ATTACH_MATCH, 1);
1109 /* some cheap hacks that I expect to remove */
1110 if (!str_casecmp(buf->data, "any"))
1111 a->major = str_dup("*/.*");
1112 else if (!str_casecmp(buf->data, "none"))
1113 a->major = str_dup("cheap_hack/this_should_never_match");
1115 a->major = str_dup(buf->data);
1117 if ((p = strchr(a->major, '/'))) {
1122 a->minor = "unknown";
1125 len = str_len (a->minor);
1126 tmpminor = p_new(char, len + 3);
1127 strcpy(&tmpminor[1], a->minor); /* __STRCPY_CHECKED__ */
1129 tmpminor[len+1] = '$';
1130 tmpminor[len+2] = '\0';
1132 a->major_int = mutt_check_mime_type(a->major);
1133 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
1135 p_delete(&tmpminor);
1137 debug_print (5, ("parse_attach_list: added %s/%s [%d]\n",
1138 a->major, a->minor, a->major_int));
1140 listp = p_new(LIST, 1);
1141 listp->data = (char *)a;
1144 lastp->next = listp;
1150 while (MoreArgs (s));
1152 _attachments_clean();
1156 static int parse_unattach_list (BUFFER *buf, BUFFER *s, LIST **ldata, BUFFER *err) {
1158 LIST *lp, *lastp, *newlp;
1164 mutt_extract_token (buf, s, 0);
1166 if (!str_casecmp(buf->data, "any"))
1167 tmp = str_dup("*/.*");
1168 else if (!str_casecmp(buf->data, "none"))
1169 tmp = str_dup("cheap_hack/this_should_never_match");
1171 tmp = str_dup(buf->data);
1173 if ((minor = strchr(tmp, '/'))) {
1179 major = mutt_check_mime_type(tmp);
1181 /* We must do our own walk here because remove_from_list() will only
1182 * remove the LIST->data, not anything pointed to by the LIST->data. */
1184 for(lp = *ldata; lp; ) {
1185 a = (ATTACH_MATCH *)lp->data;
1186 debug_print(5, ("parse_unattach_list: check %s/%s [%d] : %s/%s [%d]\n",
1187 a->major, a->minor, a->major_int, tmp, minor, major));
1188 if (a->major_int == major && !str_casecmp(minor, a->minor)) {
1189 debug_print(5, ("parse_unattach_list: removed %s/%s [%d]\n",
1190 a->major, a->minor, a->major_int));
1191 regfree(&a->minor_rx);
1192 p_delete(&a->major);
1194 /* Relink backward */
1196 lastp->next = lp->next;
1201 p_delete(&lp->data); /* same as a */
1211 while (MoreArgs (s));
1214 _attachments_clean();
1218 static int print_attach_list (LIST *lp, char op, const char *name) {
1220 printf("attachments %c%s %s/%s\n", op, name,
1221 ((ATTACH_MATCH *)lp->data)->major,
1222 ((ATTACH_MATCH *)lp->data)->minor);
1229 static int parse_attachments (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) {
1233 mutt_extract_token(buf, s, 0);
1234 if (!buf->data || *buf->data == '\0') {
1235 strfcpy(err->data, _("attachments: no disposition"), err->dsize);
1239 category = buf->data;
1245 printf("\nCurrent attachments settings:\n\n");
1246 print_attach_list(AttachAllow, '+', "A");
1247 print_attach_list(AttachExclude, '-', "A");
1248 print_attach_list(InlineAllow, '+', "I");
1249 print_attach_list(InlineExclude, '-', "I");
1250 set_option (OPTFORCEREDRAWINDEX);
1251 set_option (OPTFORCEREDRAWPAGER);
1252 mutt_any_key_to_continue (NULL);
1256 if (op != '+' && op != '-') {
1260 if (!str_ncasecmp(category, "attachment", strlen(category))) {
1262 listp = &AttachAllow;
1264 listp = &AttachExclude;
1266 else if (!str_ncasecmp(category, "inline", strlen(category))) {
1268 listp = &InlineAllow;
1270 listp = &InlineExclude;
1272 strfcpy(err->data, _("attachments: invalid disposition"), err->dsize);
1276 return parse_attach_list(buf, s, listp, err);
1279 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) {
1283 mutt_extract_token(buf, s, 0);
1284 if (!buf->data || *buf->data == '\0') {
1285 strfcpy(err->data, _("unattachments: no disposition"), err->dsize);
1291 if (op != '+' && op != '-') {
1295 if (!str_ncasecmp(p, "attachment", strlen(p))) {
1297 listp = &AttachAllow;
1299 listp = &AttachExclude;
1301 else if (!str_ncasecmp(p, "inline", strlen(p))) {
1303 listp = &InlineAllow;
1305 listp = &InlineExclude;
1308 strfcpy(err->data, _("unattachments: invalid disposition"), err->dsize);
1312 return parse_unattach_list(buf, s, listp, err);
1315 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
1319 mutt_extract_token (buf, s, 0);
1320 remove_from_rx_list (&SubscribedLists, buf->data);
1321 remove_from_rx_list (&MailLists, buf->data);
1323 if (str_cmp (buf->data, "*") &&
1324 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1327 while (MoreArgs (s));
1332 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1336 mutt_extract_token (buf, s, 0);
1337 remove_from_rx_list (&UnMailLists, buf->data);
1338 remove_from_rx_list (&UnSubscribedLists, buf->data);
1340 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1342 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1345 while (MoreArgs (s));
1350 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1354 mutt_extract_token (buf, s, 0);
1355 remove_from_rx_list (&SubscribedLists, buf->data);
1357 if (str_cmp (buf->data, "*") &&
1358 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1361 while (MoreArgs (s));
1366 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
1369 ALIAS *tmp, *last = NULL;
1372 mutt_extract_token (buf, s, 0);
1374 if (str_cmp ("*", buf->data) == 0) {
1375 if (CurrentMenu == MENU_ALIAS) {
1376 for (tmp = Aliases; tmp; tmp = tmp->next)
1378 set_option (OPTFORCEREDRAWINDEX);
1381 mutt_free_alias (&Aliases);
1385 for (tmp = Aliases; tmp; tmp = tmp->next) {
1386 if (str_casecmp (buf->data, tmp->name) == 0) {
1387 if (CurrentMenu == MENU_ALIAS) {
1389 set_option (OPTFORCEREDRAWINDEX);
1394 last->next = tmp->next;
1396 Aliases = tmp->next;
1398 mutt_free_alias (&tmp);
1404 while (MoreArgs (s));
1408 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
1411 ALIAS *tmp = Aliases;
1415 if (!MoreArgs (s)) {
1416 strfcpy (err->data, _("alias: no address"), err->dsize);
1420 mutt_extract_token (buf, s, 0);
1422 debug_print (2, ("first token is '%s'.\n", buf->data));
1424 /* check to see if an alias with this name already exists */
1425 for (; tmp; tmp = tmp->next) {
1426 if (!str_casecmp (tmp->name, buf->data))
1432 /* create a new alias */
1433 tmp = p_new(ALIAS, 1);
1435 tmp->name = str_dup (buf->data);
1436 /* give the main addressbook code a chance */
1437 if (CurrentMenu == MENU_ALIAS)
1438 set_option (OPTMENUCALLER);
1441 /* override the previous value */
1442 rfc822_free_address (&tmp->addr);
1443 if (CurrentMenu == MENU_ALIAS)
1444 set_option (OPTFORCEREDRAWINDEX);
1447 mutt_extract_token (buf, s,
1448 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1449 debug_print (2, ("second token is '%s'.\n", buf->data));
1450 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1455 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1456 snprintf (err->data, err->dsize,
1457 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1461 if (DebugLevel >= 2) {
1464 /* A group is terminated with an empty address, so check a->mailbox */
1465 for (a = tmp->addr; a && a->mailbox; a = a->next) {
1467 debug_print (2, ("%s\n", a->mailbox));
1469 debug_print (2, ("group %s\n", a->mailbox));
1477 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
1480 LIST *tmp = UserHeader;
1485 mutt_extract_token (buf, s, 0);
1486 if (str_cmp ("*", buf->data) == 0)
1487 mutt_free_list (&UserHeader);
1492 l = str_len (buf->data);
1493 if (buf->data[l - 1] == ':')
1497 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1498 && tmp->data[l] == ':') {
1501 last->next = tmp->next;
1503 UserHeader = tmp->next;
1506 mutt_free_list (&ptr);
1515 while (MoreArgs (s));
1519 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
1526 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1527 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1528 strfcpy (err->data, _("invalid header field"), err->dsize);
1531 keylen = p - buf->data + 1;
1534 for (tmp = UserHeader;; tmp = tmp->next) {
1535 /* see if there is already a field by this name */
1536 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1537 /* replace the old value */
1538 p_delete(&tmp->data);
1539 tmp->data = buf->data;
1540 memset (buf, 0, sizeof(BUFFER));
1546 tmp->next = mutt_new_list ();
1550 tmp = mutt_new_list ();
1553 tmp->data = buf->data;
1554 memset (buf, 0, sizeof(BUFFER));
1559 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1560 char* errbuf, size_t errlen) {
1563 if (str_ncmp ("reverse-", s, 8) == 0) {
1565 flags = SORT_REVERSE;
1568 if (str_ncmp ("last-", s, 5) == 0) {
1573 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1575 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1579 *((short*) dst->data) = i | flags;
1583 /* if additional data more == 1, we want to resolve synonyms */
1584 static void mutt_set_default(const char *name, void* p, unsigned long more)
1586 char buf[LONG_STRING];
1587 struct option_t *ptr = p;
1589 if (DTYPE(ptr->type) == DT_SYN) {
1592 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1594 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_from_string)
1597 mutt_option_value(ptr->option, buf, sizeof(buf));
1598 if (str_len(ptr->init) == 0 && buf && *buf)
1599 ptr->init = str_dup(buf);
1602 static struct option_t* add_option (const char* name, const char* init,
1603 short type, short dodup) {
1604 struct option_t* option = p_new(struct option_t, 1);
1606 debug_print (1, ("adding $%s\n", name));
1608 option->option = str_dup (name);
1609 option->type = type;
1611 option->init = dodup ? str_dup (init) : (char*) init;
1615 /* creates new option_t* of type DT_USER for $user_ var */
1616 static struct option_t* add_user_option (const char* name) {
1617 return (add_option (name, NULL, DT_USER, 1));
1620 /* free()'s option_t* */
1621 static void del_option (void* p) {
1622 struct option_t* ptr = (struct option_t*) p;
1623 char* s = (char*) ptr->data;
1624 debug_print (1, ("removing option '%s' from table\n", NONULL (ptr->option)));
1625 p_delete(&ptr->option);
1627 p_delete(&ptr->init);
1631 static int init_expand (char** dst, struct option_t* src) {
1637 if (DTYPE(src->type) == DT_STR ||
1638 DTYPE(src->type) == DT_PATH) {
1639 /* only expand for string as it's the only place where
1640 * we want to expand vars right now */
1641 if (src->init && *src->init) {
1642 memset (&token, 0, sizeof(BUFFER));
1643 memset (&in, 0, sizeof(BUFFER));
1644 len = str_len (src->init) + 2;
1645 in.data = p_new(char, len + 1);
1646 snprintf (in.data, len, "\"%s\"", src->init);
1649 mutt_extract_token (&token, &in, 0);
1650 if (token.data && *token.data)
1651 *dst = str_dup (token.data);
1653 *dst = str_dup ("");
1655 p_delete(&token.data);
1657 *dst = str_dup ("");
1659 /* for non-string: take value as is */
1660 *dst = str_dup (src->init);
1664 /* if additional data more == 1, we want to resolve synonyms */
1665 static void mutt_restore_default (const char* name, void* p,
1666 unsigned long more) {
1667 char errbuf[STRING];
1668 struct option_t* ptr = (struct option_t*) p;
1671 if (DTYPE (ptr->type) == DT_SYN) {
1674 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1678 if (FuncTable[DTYPE (ptr->type)].opt_from_string) {
1679 init_expand (&init, ptr);
1680 if (!FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, init, errbuf,
1682 if (!option (OPTNOCURSES))
1684 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1685 "Please report this error: \"%s\"\n"),
1686 ptr->option, NONULL (init), errbuf);
1692 if (ptr->flags & R_INDEX)
1693 set_option (OPTFORCEREDRAWINDEX);
1694 if (ptr->flags & R_PAGER)
1695 set_option (OPTFORCEREDRAWPAGER);
1696 if (ptr->flags & R_RESORT_SUB)
1697 set_option (OPTSORTSUBTHREADS);
1698 if (ptr->flags & R_RESORT)
1699 set_option (OPTNEEDRESORT);
1700 if (ptr->flags & R_RESORT_INIT)
1701 set_option (OPTRESORTINIT);
1702 if (ptr->flags & R_TREE)
1703 set_option (OPTREDRAWTREE);
1706 /* check whether value for $dsn_return would be valid */
1707 static int check_dsn_return (const char* option, unsigned long p,
1708 char* errbuf, size_t errlen) {
1709 char* val = (char*) p;
1710 if (val && *val && str_ncmp (val, "hdrs", 4) != 0 &&
1711 str_ncmp (val, "full", 4) != 0) {
1713 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1719 /* check whether value for $dsn_notify would be valid */
1720 static int check_dsn_notify (const char* option, unsigned long p,
1721 char* errbuf, size_t errlen) {
1722 list2_t* list = NULL;
1724 char* val = (char*) p;
1728 list = list_from_str (val, ",");
1729 if (list_empty (list))
1732 for (i = 0; i < list->length; i++)
1733 if (str_ncmp (list->data[i], "never", 5) != 0 &&
1734 str_ncmp (list->data[i], "failure", 7) != 0 &&
1735 str_ncmp (list->data[i], "delay", 5) != 0 &&
1736 str_ncmp (list->data[i], "success", 7) != 0) {
1738 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1739 (char*) list->data[i], "dsn_notify");
1743 list_del (&list, (list_del_t*)xmemfree);
1747 static int check_num (const char* option, unsigned long p,
1748 char* errbuf, size_t errlen) {
1751 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1758 static int check_debug (const char* option, unsigned long p,
1759 char* errbuf, size_t errlen) {
1760 if ((int) p <= DEBUG_MAX_LEVEL &&
1761 (int) p >= DEBUG_MIN_LEVEL)
1765 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1770 static int check_history (const char* option, unsigned long p,
1771 char* errbuf, size_t errlen) {
1772 if (!check_num ("history", p, errbuf, errlen))
1774 mutt_init_history ();
1778 static int check_special (const char* name, unsigned long val,
1779 char* errbuf, size_t errlen) {
1782 for (i = 0; SpecialVars[i].name; i++) {
1783 if (str_cmp (SpecialVars[i].name, name) == 0) {
1784 return (SpecialVars[i].check (SpecialVars[i].name,
1785 val, errbuf, errlen));
1791 static const struct mapping_t* get_sortmap (struct option_t* option) {
1792 const struct mapping_t* map = NULL;
1794 switch (option->type & DT_SUBTYPE_MASK) {
1796 map = SortAliasMethods;
1798 case DT_SORT_BROWSER:
1799 map = SortBrowserMethods;
1802 if ((WithCrypto & APPLICATION_PGP))
1803 map = SortKeyMethods;
1806 map = SortAuxMethods;
1815 #define CHECK_PAGER \
1816 if ((CurrentMenu == MENU_PAGER) && \
1817 (!option || (option->flags & R_RESORT))) \
1819 snprintf (err->data, err->dsize, \
1820 _("Not available in this menu.")); \
1824 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1827 int query, unset, inv, reset, r = 0;
1828 struct option_t* option = NULL;
1830 while (MoreArgs (s)) {
1831 /* reset state variables */
1833 unset = data & M_SET_UNSET;
1834 inv = data & M_SET_INV;
1835 reset = data & M_SET_RESET;
1837 if (*s->dptr == '?') {
1841 else if (str_ncmp ("no", s->dptr, 2) == 0) {
1845 else if (str_ncmp ("inv", s->dptr, 3) == 0) {
1849 else if (*s->dptr == '&') {
1854 /* get the variable name */
1855 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1857 /* resolve synonyms */
1858 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1859 DTYPE (option->type == DT_SYN)) {
1860 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1861 syn_add (newopt, option);
1865 /* see if we need to add $user_ var */
1866 if (!option && ascii_strncmp ("user_", tmp->data, 5) == 0) {
1867 /* there's no option named like this yet so only add one
1868 * if the action isn't any of: reset, unset, query */
1869 if (!(reset || unset || query || *s->dptr != '=')) {
1870 debug_print (1, ("adding user option '%s'\n", tmp->data));
1871 option = add_user_option (tmp->data);
1872 hash_insert (ConfigOptions, option->option, option, 0);
1876 if (!option && !(reset && str_cmp ("all", tmp->data) == 0)) {
1877 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1883 if (query || unset || inv) {
1884 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1888 if (s && *s->dptr == '=') {
1889 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1893 if (!str_cmp ("all", tmp->data)) {
1894 if (CurrentMenu == MENU_PAGER) {
1895 snprintf (err->data, err->dsize, _("Not available in this menu."));
1898 hash_map (ConfigOptions, mutt_restore_default, 1);
1899 set_option (OPTFORCEREDRAWINDEX);
1900 set_option (OPTFORCEREDRAWPAGER);
1901 set_option (OPTSORTSUBTHREADS);
1902 set_option (OPTNEEDRESORT);
1903 set_option (OPTRESORTINIT);
1904 set_option (OPTREDRAWTREE);
1907 else if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1908 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1913 mutt_restore_default (NULL, option, 1);
1916 else if (DTYPE (option->type) == DT_BOOL) {
1917 /* XXX this currently ignores the function table
1918 * as we don't get invert and stuff into it */
1919 if (s && *s->dptr == '=') {
1920 if (unset || inv || query) {
1921 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1926 mutt_extract_token (tmp, s, 0);
1927 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1929 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1932 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1938 bool_to_string (err->data, err->dsize, option);
1944 unset_option (option->data);
1946 toggle_option (option->data);
1948 set_option (option->data);
1950 else if (DTYPE (option->type) == DT_STR ||
1951 DTYPE (option->type) == DT_PATH ||
1952 DTYPE (option->type) == DT_ADDR ||
1953 DTYPE (option->type) == DT_MAGIC ||
1954 DTYPE (option->type) == DT_NUM ||
1955 DTYPE (option->type) == DT_SORT ||
1956 DTYPE (option->type) == DT_RX ||
1957 DTYPE (option->type) == DT_USER ||
1958 DTYPE (option->type) == DT_SYS) {
1960 /* XXX maybe we need to get unset into handlers? */
1961 if (DTYPE (option->type) == DT_STR ||
1962 DTYPE (option->type) == DT_PATH ||
1963 DTYPE (option->type) == DT_ADDR ||
1964 DTYPE (option->type) == DT_USER ||
1965 DTYPE (option->type) == DT_SYS) {
1968 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1969 snprintf (err->data, err->dsize, _("$%s is read-only"),
1973 } else if (DTYPE (option->type) == DT_ADDR)
1974 rfc822_free_address ((ADDRESS **) option->data);
1975 else if (DTYPE (option->type) == DT_USER)
1976 /* to unset $user_ means remove */
1977 hash_delete (ConfigOptions, option->option,
1978 option, del_option);
1980 p_delete((void **)&option->data);
1985 if (query || *s->dptr != '=') {
1986 FuncTable[DTYPE (option->type)].opt_to_string
1987 (err->data, err->dsize, option);
1991 /* the $muttng_ variables are read-only */
1992 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1993 snprintf (err->data, err->dsize, _("$%s is read-only"),
2000 mutt_extract_token (tmp, s, 0);
2001 if (!FuncTable[DTYPE (option->type)].opt_from_string
2002 (option, tmp->data, err->data, err->dsize))
2006 else if (DTYPE (option->type) == DT_QUAD) {
2009 quad_to_string (err->data, err->dsize, option);
2013 if (*s->dptr == '=') {
2016 mutt_extract_token (tmp, s, 0);
2017 if (ascii_strcasecmp ("yes", tmp->data) == 0)
2018 set_quadoption (option->data, M_YES);
2019 else if (ascii_strcasecmp ("no", tmp->data) == 0)
2020 set_quadoption (option->data, M_NO);
2021 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
2022 set_quadoption (option->data, M_ASKYES);
2023 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
2024 set_quadoption (option->data, M_ASKNO);
2026 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
2027 tmp->data, option->option);
2034 toggle_quadoption (option->data);
2036 set_quadoption (option->data, M_NO);
2038 set_quadoption (option->data, M_YES);
2042 snprintf (err->data, err->dsize, _("%s: unknown type"),
2048 if (option->flags & R_INDEX)
2049 set_option (OPTFORCEREDRAWINDEX);
2050 if (option->flags & R_PAGER)
2051 set_option (OPTFORCEREDRAWPAGER);
2052 if (option->flags & R_RESORT_SUB)
2053 set_option (OPTSORTSUBTHREADS);
2054 if (option->flags & R_RESORT)
2055 set_option (OPTNEEDRESORT);
2056 if (option->flags & R_RESORT_INIT)
2057 set_option (OPTRESORTINIT);
2058 if (option->flags & R_TREE)
2059 set_option (OPTREDRAWTREE);
2066 /* reads the specified initialization file. returns -1 if errors were found
2067 so that we can pause to let the user know... */
2068 static int source_rc (const char *rcfile, BUFFER * err)
2071 int line = 0, rc = 0, conv = 0;
2073 char *linebuf = NULL;
2074 char *currentline = NULL;
2078 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
2080 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
2081 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
2085 memset (&token, 0, sizeof(token));
2086 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
2087 conv = ConfigCharset && (*ConfigCharset) && Charset;
2089 currentline = str_dup (linebuf);
2092 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
2095 currentline = linebuf;
2100 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
2101 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
2102 if (--rc < -MAXERRS) {
2104 p_delete(¤tline);
2113 p_delete(¤tline);
2115 p_delete(&token.data);
2119 mutt_wait_filter (pid);
2121 /* the muttrc source keyword */
2122 snprintf (err->data, err->dsize,
2123 rc >= -MAXERRS ? _("source: errors in %s")
2124 : _("source: reading aborted due too many errors in %s"),
2133 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
2136 char path[_POSIX_PATH_MAX];
2140 if (mutt_extract_token (tmp, s, 0) != 0) {
2141 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
2145 strfcpy (path, tmp->data, sizeof(path));
2146 mutt_expand_path (path, sizeof(path));
2148 rc += source_rc (path, err);
2150 while (MoreArgs (s));
2152 return ((rc < 0) ? -1 : 0);
2155 /* line command to execute
2157 token scratch buffer to be used by parser. caller should free
2158 token->data when finished. the reason for this variable is
2159 to avoid having to allocate and deallocate a lot of memory
2160 if we are parsing many lines. the caller can pass in the
2161 memory to use, which avoids having to create new space for
2162 every call to this function.
2164 err where to write error messages */
2165 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
2170 memset (&expn, 0, sizeof(expn));
2171 expn.data = expn.dptr = line;
2172 expn.dsize = str_len (line);
2176 debug_print (1, ("expand '%s'\n", line));
2179 while (*expn.dptr) {
2180 if (*expn.dptr == '#')
2181 break; /* rest of line is a comment */
2182 if (*expn.dptr == ';') {
2186 mutt_extract_token (token, &expn, 0);
2187 for (i = 0; Commands[i].name; i++) {
2188 if (!str_cmp (token->data, Commands[i].name)) {
2189 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
2194 if (!Commands[i].name) {
2195 snprintf (err->data, err->dsize, _("%s: unknown command"),
2196 NONULL (token->data));
2203 p_delete(&expn.data);
2208 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
2209 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
2210 /* initial string that starts completion. No telling how much crap
2211 * the user has typed so far. Allocate LONG_STRING just to be sure! */
2212 char User_typed[LONG_STRING] = { 0 };
2214 int Num_matched = 0; /* Number of matches for completion */
2215 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
2216 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
2218 /* helper function for completion. Changes the dest buffer if
2219 necessary/possible to aid completion.
2220 dest == completion result gets here.
2221 src == candidate for completion.
2222 try == user entered data for completion.
2223 len == length of dest buffer.
2225 static void candidate (char *dest, char *try, const char *src, int len)
2229 if (strstr (src, try) == src) {
2230 Matches[Num_matched++] = src;
2232 strfcpy (dest, src, len);
2234 for (l = 0; src[l] && src[l] == dest[l]; l++);
2240 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
2244 int spaces; /* keep track of the number of leading spaces on the line */
2247 spaces = buffer - pt;
2249 pt = buffer + pos - spaces;
2250 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2253 if (pt == buffer) { /* complete cmd */
2254 /* first TAB. Collect all the matches */
2257 strfcpy (User_typed, pt, sizeof(User_typed));
2258 memset (Matches, 0, sizeof(Matches));
2259 memset (Completed, 0, sizeof(Completed));
2260 for (num = 0; Commands[num].name; num++)
2261 candidate (Completed, User_typed, Commands[num].name,
2263 Matches[Num_matched++] = User_typed;
2265 /* All matches are stored. Longest non-ambiguous string is ""
2266 * i.e. dont change 'buffer'. Fake successful return this time */
2267 if (User_typed[0] == 0)
2271 if (Completed[0] == 0 && User_typed[0])
2274 /* Num_matched will _always_ be atleast 1 since the initial
2275 * user-typed string is always stored */
2276 if (numtabs == 1 && Num_matched == 2)
2277 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2278 else if (numtabs > 1 && Num_matched > 2)
2279 /* cycle thru all the matches */
2280 snprintf (Completed, sizeof(Completed), "%s",
2281 Matches[(numtabs - 2) % Num_matched]);
2283 /* return the completed command */
2284 strncpy (buffer, Completed, len - spaces);
2286 else if (!str_ncmp (buffer, "set", 3)
2287 || !str_ncmp (buffer, "unset", 5)
2288 || !str_ncmp (buffer, "reset", 5)
2289 || !str_ncmp (buffer, "toggle", 6)) { /* complete variables */
2290 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
2293 /* loop through all the possible prefixes (no, inv, ...) */
2294 if (!str_ncmp (buffer, "set", 3)) {
2295 for (num = 0; prefixes[num]; num++) {
2296 if (!str_ncmp (pt, prefixes[num], str_len (prefixes[num]))) {
2297 pt += str_len (prefixes[num]);
2303 /* first TAB. Collect all the matches */
2306 strfcpy (User_typed, pt, sizeof(User_typed));
2307 memset (Matches, 0, sizeof(Matches));
2308 memset (Completed, 0, sizeof(Completed));
2309 for (num = 0; MuttVars[num].option; num++)
2310 candidate (Completed, User_typed, MuttVars[num].option,
2312 Matches[Num_matched++] = User_typed;
2314 /* All matches are stored. Longest non-ambiguous string is ""
2315 * i.e. dont change 'buffer'. Fake successful return this time */
2316 if (User_typed[0] == 0)
2320 if (Completed[0] == 0 && User_typed[0])
2323 /* Num_matched will _always_ be atleast 1 since the initial
2324 * user-typed string is always stored */
2325 if (numtabs == 1 && Num_matched == 2)
2326 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2327 else if (numtabs > 1 && Num_matched > 2)
2328 /* cycle thru all the matches */
2329 snprintf (Completed, sizeof(Completed), "%s",
2330 Matches[(numtabs - 2) % Num_matched]);
2332 strncpy (pt, Completed, buffer + len - pt - spaces);
2334 else if (!str_ncmp (buffer, "exec", 4)) {
2335 struct binding_t *menu = km_get_table (CurrentMenu);
2337 if (!menu && CurrentMenu != MENU_PAGER)
2341 /* first TAB. Collect all the matches */
2344 strfcpy (User_typed, pt, sizeof(User_typed));
2345 memset (Matches, 0, sizeof(Matches));
2346 memset (Completed, 0, sizeof(Completed));
2347 for (num = 0; menu[num].name; num++)
2348 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
2349 /* try the generic menu */
2350 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2352 for (num = 0; menu[num].name; num++)
2353 candidate (Completed, User_typed, menu[num].name,
2356 Matches[Num_matched++] = User_typed;
2358 /* All matches are stored. Longest non-ambiguous string is ""
2359 * i.e. dont change 'buffer'. Fake successful return this time */
2360 if (User_typed[0] == 0)
2364 if (Completed[0] == 0 && User_typed[0])
2367 /* Num_matched will _always_ be atleast 1 since the initial
2368 * user-typed string is always stored */
2369 if (numtabs == 1 && Num_matched == 2)
2370 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2371 else if (numtabs > 1 && Num_matched > 2)
2372 /* cycle thru all the matches */
2373 snprintf (Completed, sizeof(Completed), "%s",
2374 Matches[(numtabs - 2) % Num_matched]);
2376 strncpy (pt, Completed, buffer + len - pt - spaces);
2384 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2386 char var[STRING], *pt = buffer;
2388 struct option_t* option = NULL;
2394 spaces = buffer - pt;
2396 pt = buffer + pos - spaces;
2397 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2399 pt++; /* move past the space */
2400 if (*pt == '=') /* abort if no var before the '=' */
2403 if (str_ncmp (buffer, "set", 3) == 0) {
2404 strfcpy (var, pt, sizeof(var));
2405 /* ignore the trailing '=' when comparing */
2406 var[str_len (var) - 1] = 0;
2407 if (!(option = hash_find (ConfigOptions, var)))
2408 return 0; /* no such variable. */
2410 char tmp[LONG_STRING], tmp2[LONG_STRING];
2412 size_t dlen = buffer + len - pt - spaces;
2413 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2417 if ((DTYPE (option->type) == DT_STR) ||
2418 (DTYPE (option->type) == DT_PATH) ||
2419 (DTYPE (option->type) == DT_RX)) {
2420 strfcpy (tmp, NONULL (*((char **) option->data)), sizeof(tmp));
2421 if (DTYPE (option->type) == DT_PATH)
2422 mutt_pretty_mailbox (tmp);
2424 else if (DTYPE (option->type) == DT_ADDR) {
2425 rfc822_write_address (tmp, sizeof(tmp),
2426 *((ADDRESS **) option->data), 0);
2428 else if (DTYPE (option->type) == DT_QUAD)
2429 strfcpy (tmp, vals[quadoption (option->data)], sizeof(tmp));
2430 else if (DTYPE (option->type) == DT_NUM)
2431 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
2432 else if (DTYPE (option->type) == DT_SORT) {
2433 const struct mapping_t *map;
2436 switch (option->type & DT_SUBTYPE_MASK) {
2438 map = SortAliasMethods;
2440 case DT_SORT_BROWSER:
2441 map = SortBrowserMethods;
2444 if ((WithCrypto & APPLICATION_PGP))
2445 map = SortKeyMethods;
2453 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2454 snprintf(tmp, sizeof(tmp), "%s%s%s",
2455 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2456 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2458 else if (DTYPE (option->type) == DT_MAGIC) {
2460 switch (DefaultMagic) {
2476 strfcpy (tmp, p, sizeof(tmp));
2478 else if (DTYPE (option->type) == DT_BOOL)
2479 strfcpy (tmp, option (option->data) ? "yes" : "no",
2484 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof(tmp2) - 2;) {
2485 if (*s == '\\' || *s == '"')
2491 strfcpy (tmp, pt, sizeof(tmp));
2492 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2500 /* Implement the -Q command line flag */
2501 int mutt_query_variables (LIST * queries)
2505 char errbuff[STRING];
2506 char command[STRING];
2510 memset (&err, 0, sizeof(err));
2511 memset (&token, 0, sizeof(token));
2514 err.dsize = sizeof(errbuff);
2516 for (p = queries; p; p = p->next) {
2517 snprintf (command, sizeof(command), "set ?%s\n", p->data);
2518 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2519 fprintf (stderr, "%s\n", err.data);
2520 p_delete(&token.data);
2523 printf ("%s\n", err.data);
2526 p_delete(&token.data);
2530 const char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2534 for (i = 0; map[i].name; i++)
2535 if (map[i].value == val)
2536 return (map[i].name);
2540 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2544 for (i = 0; map[i].name; i++)
2545 if (ascii_strcasecmp (map[i].name, name) == 0)
2546 return (map[i].value);
2550 static int mutt_execute_commands (LIST * p)
2553 char errstr[SHORT_STRING];
2555 memset (&err, 0, sizeof(err));
2557 err.dsize = sizeof(errstr);
2558 memset (&token, 0, sizeof(token));
2559 for (; p; p = p->next) {
2560 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2561 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2562 p_delete(&token.data);
2566 p_delete(&token.data);
2570 void mutt_init (int skip_sys_rc, LIST * commands)
2573 struct utsname utsname;
2575 char buffer[STRING], error[STRING];
2576 int i, default_rc = 0, need_pause = 0;
2579 memset (&err, 0, sizeof(err));
2581 err.dsize = sizeof(error);
2583 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2584 * to have some room for $user_ vars */
2585 ConfigOptions = hash_create (sizeof(MuttVars) * 3);
2586 for (i = 0; MuttVars[i].option; i++) {
2587 if (DTYPE (MuttVars[i].type) != DT_SYS)
2588 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2590 hash_insert (ConfigOptions, MuttVars[i].option,
2591 add_option (MuttVars[i].option, MuttVars[i].init,
2596 * XXX - use something even more difficult to predict?
2598 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2599 "\033]9;%ld\a", (long) time (NULL));
2601 /* on one of the systems I use, getcwd() does not return the same prefix
2602 as is listed in the passwd file */
2603 if ((p = getenv ("HOME")))
2604 Homedir = str_dup (p);
2606 /* Get some information about the user */
2607 if ((pw = getpwuid (getuid ()))) {
2610 Username = str_dup (pw->pw_name);
2612 Homedir = str_dup (pw->pw_dir);
2614 Realname = str_dup (mutt_gecos_name (rnbuf, sizeof(rnbuf), pw));
2615 Shell = str_dup (pw->pw_shell);
2621 fputs (_("unable to determine home directory"), stderr);
2624 if ((p = getenv ("USER")))
2625 Username = str_dup (p);
2628 fputs (_("unable to determine username"), stderr);
2631 Shell = str_dup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2634 debug_start(Homedir);
2636 /* And about the host... */
2638 /* some systems report the FQDN instead of just the hostname */
2639 if ((p = strchr (utsname.nodename, '.'))) {
2640 Hostname = str_substrdup (utsname.nodename, p);
2642 strfcpy (buffer, p, sizeof(buffer)); /* save the domain for below */
2645 Hostname = str_dup (utsname.nodename);
2648 #define DOMAIN buffer
2649 if (!p && getdnsdomainname (buffer, sizeof(buffer)) == -1)
2650 Fqdn = str_dup ("@");
2653 if (*DOMAIN != '@') {
2654 Fqdn = p_new(char, str_len(DOMAIN) + str_len(Hostname) + 2);
2655 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2658 Fqdn = str_dup (NONULL (Hostname));
2665 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2667 fgets (buffer, sizeof(buffer), f);
2671 while (*q && !isspace(*q))
2674 NewsServer = str_dup (p);
2678 if ((p = getenv ("NNTPSERVER")))
2679 NewsServer = str_dup (p);
2682 if ((p = getenv ("MAIL")))
2683 Spoolfile = str_dup (p);
2684 else if ((p = getenv ("MAILDIR")))
2685 Spoolfile = str_dup (p);
2688 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof(buffer));
2690 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof(buffer));
2692 Spoolfile = str_dup (buffer);
2695 if ((p = getenv ("MAILCAPS")))
2696 MailcapPath = str_dup (p);
2698 /* Default search path from RFC1524 */
2700 str_dup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2701 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2704 Tempdir = str_dup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2706 p = getenv ("VISUAL");
2708 p = getenv ("EDITOR");
2712 Editor = str_dup (p);
2713 Visual = str_dup (p);
2715 if ((p = getenv ("REPLYTO")) != NULL) {
2718 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2720 memset (&buf, 0, sizeof(buf));
2721 buf.data = buf.dptr = buffer;
2722 buf.dsize = str_len (buffer);
2724 memset (&token, 0, sizeof(token));
2725 parse_my_hdr (&token, &buf, 0, &err);
2726 p_delete(&token.data);
2729 if ((p = getenv ("EMAIL")) != NULL)
2730 From = rfc822_parse_adrlist (NULL, p);
2732 mutt_set_langinfo_charset ();
2733 mutt_set_charset (Charset);
2736 /* Set standard defaults */
2737 hash_map (ConfigOptions, mutt_set_default, 0);
2738 hash_map (ConfigOptions, mutt_restore_default, 0);
2740 CurrentMenu = MENU_MAIN;
2743 #ifndef LOCALES_HACK
2744 /* Do we have a locale definition? */
2745 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2746 ((p = getenv ("LANG")) != NULL && p[0]) ||
2747 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2748 set_option (OPTLOCALES);
2752 /* Unset suspend by default if we're the session leader */
2753 if (getsid (0) == getpid ())
2754 unset_option (OPTSUSPEND);
2757 mutt_init_history ();
2766 * When changing the code which looks for a configuration file,
2767 * please also change the corresponding code in muttbug.sh.in.
2777 snprintf (buffer, sizeof(buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2779 if (access (buffer, F_OK) == -1)
2781 snprintf (buffer, sizeof(buffer), "%s/.muttngrc", NONULL (Homedir));
2782 if (access (buffer, F_OK) == -1)
2784 snprintf (buffer, sizeof(buffer), "%s/.muttng/muttngrc-%s",
2785 NONULL (Homedir), MUTT_VERSION);
2786 if (access (buffer, F_OK) == -1)
2788 snprintf (buffer, sizeof(buffer), "%s/.muttng/muttngrc",
2792 Muttrc = str_dup (buffer);
2795 strfcpy (buffer, Muttrc, sizeof(buffer));
2797 mutt_expand_path (buffer, sizeof(buffer));
2798 Muttrc = str_dup (buffer);
2800 p_delete(&AliasFile);
2801 AliasFile = str_dup (NONULL (Muttrc));
2803 /* Process the global rc file if it exists and the user hasn't explicity
2804 requested not to via "-n". */
2806 snprintf (buffer, sizeof(buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2808 if (access (buffer, F_OK) == -1)
2809 snprintf (buffer, sizeof(buffer), "%s/Muttngrc", SYSCONFDIR);
2810 if (access (buffer, F_OK) == -1)
2811 snprintf (buffer, sizeof(buffer), "%s/Muttngrc-%s", PKGDATADIR,
2813 if (access (buffer, F_OK) == -1)
2814 snprintf (buffer, sizeof(buffer), "%s/Muttngrc", PKGDATADIR);
2815 if (access (buffer, F_OK) != -1) {
2816 if (source_rc (buffer, &err) != 0) {
2817 fputs (err.data, stderr);
2818 fputc ('\n', stderr);
2824 /* Read the user's initialization file. */
2825 if (access (Muttrc, F_OK) != -1) {
2826 if (!option (OPTNOCURSES))
2828 if (source_rc (Muttrc, &err) != 0) {
2829 fputs (err.data, stderr);
2830 fputc ('\n', stderr);
2834 else if (!default_rc) {
2835 /* file specified by -F does not exist */
2836 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2837 mutt_endwin (buffer);
2841 if (mutt_execute_commands (commands) != 0)
2844 /* warn about synonym variables */
2845 if (!list_empty(Synonyms)) {
2847 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2848 for (i = 0; i < Synonyms->length; i++) {
2849 struct option_t* newopt = NULL, *oldopt = NULL;
2850 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2851 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2852 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2853 oldopt ? NONULL (oldopt->option) : "",
2854 newopt ? NONULL (newopt->option) : "",
2855 NONULL(((syn_t*) Synonyms->data[i])->f),
2856 ((syn_t*) Synonyms->data[i])->l);
2858 fprintf (stderr, _("Warning: synonym variables are scheduled"
2859 " for removal.\n"));
2860 list_del (&Synonyms, syn_del);
2864 if (need_pause && !option (OPTNOCURSES)) {
2865 if (mutt_any_key_to_continue (NULL) == -1)
2870 set_option (OPTWEED); /* turn weeding on by default */
2874 int mutt_get_hook_type (const char *name)
2876 struct command_t *c;
2878 for (c = Commands; c->name; c++)
2879 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2884 /* compare two option_t*'s for sorting -t/-T output */
2885 static int opt_cmp (const void* a, const void* b) {
2886 return (str_cmp ((*(struct option_t**) a)->option,
2887 (*(struct option_t**) b)->option));
2890 /* callback for hash_map() to put all non-synonym vars into list */
2891 static void opt_sel_full (const char* key, void* data,
2892 unsigned long more) {
2893 list2_t** l = (list2_t**) more;
2894 struct option_t* option = (struct option_t*) data;
2896 if (DTYPE (option->type) == DT_SYN)
2898 list_push_back (l, option);
2901 /* callback for hash_map() to put all changed non-synonym vars into list */
2902 static void opt_sel_diff (const char* key, void* data,
2903 unsigned long more) {
2904 list2_t** l = (list2_t**) more;
2905 struct option_t* option = (struct option_t*) data;
2906 char buf[LONG_STRING];
2908 if (DTYPE (option->type) == DT_SYN)
2911 mutt_option_value (option->option, buf, sizeof(buf));
2912 if (str_cmp (buf, option->init) != 0)
2913 list_push_back (l, option);
2916 /* dump out the value of all the variables we have */
2917 int mutt_dump_variables (int full) {
2919 char outbuf[STRING];
2920 list2_t* tmp = NULL;
2921 struct option_t* option = NULL;
2923 /* get all non-synonyms into list... */
2924 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2925 (unsigned long) &tmp);
2927 if (!list_empty(tmp)) {
2928 /* ...and dump list sorted */
2929 qsort (tmp->data, tmp->length, sizeof(void*), opt_cmp);
2930 for (i = 0; i < tmp->length; i++) {
2931 option = (struct option_t*) tmp->data[i];
2932 FuncTable[DTYPE (option->type)].opt_to_string
2933 (outbuf, sizeof(outbuf), option);
2934 printf ("%s\n", outbuf);
2937 list_del (&tmp, NULL);