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 /* always wise to do what someone else did before */
1068 static void _attachments_clean (void) {
1070 if (Context && Context->msgcount) {
1071 for (i = 0; i < Context->msgcount; i++)
1072 Context->hdrs[i]->attach_valid = 0;
1076 static int parse_attach_list (BUFFER *buf, BUFFER *s, LIST **ldata,
1079 LIST *listp, *lastp;
1084 /* Find the last item in the list that data points to. */
1086 debug_print (5, ("parse_attach_list: ldata = %08x, *ldata = %08x\n",
1087 (unsigned int)ldata, (unsigned int)*ldata));
1088 for (listp = *ldata; listp; listp = listp->next) {
1089 a = (ATTACH_MATCH *)listp->data;
1090 debug_print (5, ("parse_attach_list: skipping %s/%s\n", a->major, a->minor));
1095 mutt_extract_token (buf, s, 0);
1097 if (!buf->data || *buf->data == '\0')
1100 a = mem_malloc(sizeof(ATTACH_MATCH));
1102 /* some cheap hacks that I expect to remove */
1103 if (!str_casecmp(buf->data, "any"))
1104 a->major = str_dup("*/.*");
1105 else if (!str_casecmp(buf->data, "none"))
1106 a->major = str_dup("cheap_hack/this_should_never_match");
1108 a->major = str_dup(buf->data);
1110 if ((p = strchr(a->major, '/'))) {
1115 a->minor = "unknown";
1118 len = str_len (a->minor);
1119 tmpminor = mem_malloc(len+3);
1120 strcpy(&tmpminor[1], a->minor); /* __STRCPY_CHECKED__ */
1122 tmpminor[len+1] = '$';
1123 tmpminor[len+2] = '\0';
1125 a->major_int = mutt_check_mime_type(a->major);
1126 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
1128 mem_free (&tmpminor);
1130 debug_print (5, ("parse_attach_list: added %s/%s [%d]\n",
1131 a->major, a->minor, a->major_int));
1133 listp = mem_malloc(sizeof(LIST));
1134 listp->data = (char *)a;
1137 lastp->next = listp;
1143 while (MoreArgs (s));
1145 _attachments_clean();
1149 static int parse_unattach_list (BUFFER *buf, BUFFER *s, LIST **ldata, BUFFER *err) {
1151 LIST *lp, *lastp, *newlp;
1157 mutt_extract_token (buf, s, 0);
1159 if (!str_casecmp(buf->data, "any"))
1160 tmp = str_dup("*/.*");
1161 else if (!str_casecmp(buf->data, "none"))
1162 tmp = str_dup("cheap_hack/this_should_never_match");
1164 tmp = str_dup(buf->data);
1166 if ((minor = strchr(tmp, '/'))) {
1172 major = mutt_check_mime_type(tmp);
1174 /* We must do our own walk here because remove_from_list() will only
1175 * remove the LIST->data, not anything pointed to by the LIST->data. */
1177 for(lp = *ldata; lp; ) {
1178 a = (ATTACH_MATCH *)lp->data;
1179 debug_print(5, ("parse_unattach_list: check %s/%s [%d] : %s/%s [%d]\n",
1180 a->major, a->minor, a->major_int, tmp, minor, major));
1181 if (a->major_int == major && !str_casecmp(minor, a->minor)) {
1182 debug_print(5, ("parse_unattach_list: removed %s/%s [%d]\n",
1183 a->major, a->minor, a->major_int));
1184 regfree(&a->minor_rx);
1185 mem_free(&a->major);
1187 /* Relink backward */
1189 lastp->next = lp->next;
1194 mem_free(&lp->data); /* same as a */
1204 while (MoreArgs (s));
1207 _attachments_clean();
1211 static int print_attach_list (LIST *lp, char op, char *name) {
1213 printf("attachments %c%s %s/%s\n", op, name,
1214 ((ATTACH_MATCH *)lp->data)->major,
1215 ((ATTACH_MATCH *)lp->data)->minor);
1222 static int parse_attachments (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) {
1226 mutt_extract_token(buf, s, 0);
1227 if (!buf->data || *buf->data == '\0') {
1228 strfcpy(err->data, _("attachments: no disposition"), err->dsize);
1232 category = buf->data;
1238 printf("\nCurrent attachments settings:\n\n");
1239 print_attach_list(AttachAllow, '+', "A");
1240 print_attach_list(AttachExclude, '-', "A");
1241 print_attach_list(InlineAllow, '+', "I");
1242 print_attach_list(InlineExclude, '-', "I");
1243 set_option (OPTFORCEREDRAWINDEX);
1244 set_option (OPTFORCEREDRAWPAGER);
1245 mutt_any_key_to_continue (NULL);
1249 if (op != '+' && op != '-') {
1253 if (!str_ncasecmp(category, "attachment", strlen(category))) {
1255 listp = &AttachAllow;
1257 listp = &AttachExclude;
1259 else if (!str_ncasecmp(category, "inline", strlen(category))) {
1261 listp = &InlineAllow;
1263 listp = &InlineExclude;
1265 strfcpy(err->data, _("attachments: invalid disposition"), err->dsize);
1269 return parse_attach_list(buf, s, listp, err);
1272 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) {
1276 mutt_extract_token(buf, s, 0);
1277 if (!buf->data || *buf->data == '\0') {
1278 strfcpy(err->data, _("unattachments: no disposition"), err->dsize);
1284 if (op != '+' && op != '-') {
1288 if (!str_ncasecmp(p, "attachment", strlen(p))) {
1290 listp = &AttachAllow;
1292 listp = &AttachExclude;
1294 else if (!str_ncasecmp(p, "inline", strlen(p))) {
1296 listp = &InlineAllow;
1298 listp = &InlineExclude;
1301 strfcpy(err->data, _("unattachments: invalid disposition"), err->dsize);
1305 return parse_unattach_list(buf, s, listp, err);
1308 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
1312 mutt_extract_token (buf, s, 0);
1313 remove_from_rx_list (&SubscribedLists, buf->data);
1314 remove_from_rx_list (&MailLists, buf->data);
1316 if (str_cmp (buf->data, "*") &&
1317 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1320 while (MoreArgs (s));
1325 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1329 mutt_extract_token (buf, s, 0);
1330 remove_from_rx_list (&UnMailLists, buf->data);
1331 remove_from_rx_list (&UnSubscribedLists, buf->data);
1333 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1335 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1338 while (MoreArgs (s));
1343 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1347 mutt_extract_token (buf, s, 0);
1348 remove_from_rx_list (&SubscribedLists, buf->data);
1350 if (str_cmp (buf->data, "*") &&
1351 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1354 while (MoreArgs (s));
1359 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
1362 ALIAS *tmp, *last = NULL;
1365 mutt_extract_token (buf, s, 0);
1367 if (str_cmp ("*", buf->data) == 0) {
1368 if (CurrentMenu == MENU_ALIAS) {
1369 for (tmp = Aliases; tmp; tmp = tmp->next)
1371 set_option (OPTFORCEREDRAWINDEX);
1374 mutt_free_alias (&Aliases);
1378 for (tmp = Aliases; tmp; tmp = tmp->next) {
1379 if (str_casecmp (buf->data, tmp->name) == 0) {
1380 if (CurrentMenu == MENU_ALIAS) {
1382 set_option (OPTFORCEREDRAWINDEX);
1387 last->next = tmp->next;
1389 Aliases = tmp->next;
1391 mutt_free_alias (&tmp);
1397 while (MoreArgs (s));
1401 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
1404 ALIAS *tmp = Aliases;
1408 if (!MoreArgs (s)) {
1409 strfcpy (err->data, _("alias: no address"), err->dsize);
1413 mutt_extract_token (buf, s, 0);
1415 debug_print (2, ("first token is '%s'.\n", buf->data));
1417 /* check to see if an alias with this name already exists */
1418 for (; tmp; tmp = tmp->next) {
1419 if (!str_casecmp (tmp->name, buf->data))
1425 /* create a new alias */
1426 tmp = (ALIAS *) mem_calloc (1, sizeof (ALIAS));
1428 tmp->name = str_dup (buf->data);
1429 /* give the main addressbook code a chance */
1430 if (CurrentMenu == MENU_ALIAS)
1431 set_option (OPTMENUCALLER);
1434 /* override the previous value */
1435 rfc822_free_address (&tmp->addr);
1436 if (CurrentMenu == MENU_ALIAS)
1437 set_option (OPTFORCEREDRAWINDEX);
1440 mutt_extract_token (buf, s,
1441 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1442 debug_print (2, ("second token is '%s'.\n", buf->data));
1443 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1448 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1449 snprintf (err->data, err->dsize,
1450 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1454 if (DebugLevel >= 2) {
1457 /* A group is terminated with an empty address, so check a->mailbox */
1458 for (a = tmp->addr; a && a->mailbox; a = a->next) {
1460 debug_print (2, ("%s\n", a->mailbox));
1462 debug_print (2, ("group %s\n", a->mailbox));
1470 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
1473 LIST *tmp = UserHeader;
1478 mutt_extract_token (buf, s, 0);
1479 if (str_cmp ("*", buf->data) == 0)
1480 mutt_free_list (&UserHeader);
1485 l = str_len (buf->data);
1486 if (buf->data[l - 1] == ':')
1490 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1491 && tmp->data[l] == ':') {
1494 last->next = tmp->next;
1496 UserHeader = tmp->next;
1499 mutt_free_list (&ptr);
1508 while (MoreArgs (s));
1512 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
1519 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1520 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1521 strfcpy (err->data, _("invalid header field"), err->dsize);
1524 keylen = p - buf->data + 1;
1527 for (tmp = UserHeader;; tmp = tmp->next) {
1528 /* see if there is already a field by this name */
1529 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1530 /* replace the old value */
1531 mem_free (&tmp->data);
1532 tmp->data = buf->data;
1533 memset (buf, 0, sizeof (BUFFER));
1539 tmp->next = mutt_new_list ();
1543 tmp = mutt_new_list ();
1546 tmp->data = buf->data;
1547 memset (buf, 0, sizeof (BUFFER));
1552 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1553 char* errbuf, size_t errlen) {
1556 if (str_ncmp ("reverse-", s, 8) == 0) {
1558 flags = SORT_REVERSE;
1561 if (str_ncmp ("last-", s, 5) == 0) {
1566 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1568 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1572 *((short*) dst->data) = i | flags;
1576 /* if additional data more == 1, we want to resolve synonyms */
1577 static void mutt_set_default (const char* name, void* p, unsigned long more) {
1578 char buf[LONG_STRING];
1579 struct option_t* ptr = (struct option_t*) p;
1581 if (DTYPE (ptr->type) == DT_SYN) {
1584 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1586 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_from_string)
1588 mutt_option_value (ptr->option, buf, sizeof (buf));
1589 if (str_len (ptr->init) == 0 && buf && *buf)
1590 ptr->init = str_dup (buf);
1593 static struct option_t* add_option (const char* name, const char* init,
1594 short type, short dup) {
1595 struct option_t* option = mem_calloc (1, sizeof (struct option_t));
1597 debug_print (1, ("adding $%s\n", name));
1599 option->option = str_dup (name);
1600 option->type = type;
1602 option->init = dup ? str_dup (init) : (char*) init;
1606 /* creates new option_t* of type DT_USER for $user_ var */
1607 static struct option_t* add_user_option (const char* name) {
1608 return (add_option (name, NULL, DT_USER, 1));
1611 /* free()'s option_t* */
1612 static void del_option (void* p) {
1613 struct option_t* ptr = (struct option_t*) p;
1614 char* s = (char*) ptr->data;
1615 debug_print (1, ("removing option '%s' from table\n", NONULL (ptr->option)));
1616 mem_free (&ptr->option);
1618 mem_free (&ptr->init);
1622 static int init_expand (char** dst, struct option_t* src) {
1628 if (DTYPE(src->type) == DT_STR ||
1629 DTYPE(src->type) == DT_PATH) {
1630 /* only expand for string as it's the only place where
1631 * we want to expand vars right now */
1632 if (src->init && *src->init) {
1633 memset (&token, 0, sizeof (BUFFER));
1634 memset (&in, 0, sizeof (BUFFER));
1635 len = str_len (src->init) + 2;
1636 in.data = mem_malloc (len+1);
1637 snprintf (in.data, len, "\"%s\"", src->init);
1640 mutt_extract_token (&token, &in, 0);
1641 if (token.data && *token.data)
1642 *dst = str_dup (token.data);
1644 *dst = str_dup ("");
1645 mem_free (&in.data);
1646 mem_free (&token.data);
1648 *dst = str_dup ("");
1650 /* for non-string: take value as is */
1651 *dst = str_dup (src->init);
1655 /* if additional data more == 1, we want to resolve synonyms */
1656 static void mutt_restore_default (const char* name, void* p,
1657 unsigned long more) {
1658 char errbuf[STRING];
1659 struct option_t* ptr = (struct option_t*) p;
1662 if (DTYPE (ptr->type) == DT_SYN) {
1665 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1669 if (FuncTable[DTYPE (ptr->type)].opt_from_string) {
1670 init_expand (&init, ptr);
1671 if (!FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, init, errbuf,
1673 if (!option (OPTNOCURSES))
1675 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1676 "Please report this error: \"%s\"\n"),
1677 ptr->option, NONULL (init), errbuf);
1683 if (ptr->flags & R_INDEX)
1684 set_option (OPTFORCEREDRAWINDEX);
1685 if (ptr->flags & R_PAGER)
1686 set_option (OPTFORCEREDRAWPAGER);
1687 if (ptr->flags & R_RESORT_SUB)
1688 set_option (OPTSORTSUBTHREADS);
1689 if (ptr->flags & R_RESORT)
1690 set_option (OPTNEEDRESORT);
1691 if (ptr->flags & R_RESORT_INIT)
1692 set_option (OPTRESORTINIT);
1693 if (ptr->flags & R_TREE)
1694 set_option (OPTREDRAWTREE);
1697 /* check whether value for $dsn_return would be valid */
1698 static int check_dsn_return (const char* option, unsigned long p,
1699 char* errbuf, size_t errlen) {
1700 char* val = (char*) p;
1701 if (val && *val && str_ncmp (val, "hdrs", 4) != 0 &&
1702 str_ncmp (val, "full", 4) != 0) {
1704 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1710 /* check whether value for $dsn_notify would be valid */
1711 static int check_dsn_notify (const char* option, unsigned long p,
1712 char* errbuf, size_t errlen) {
1713 list2_t* list = NULL;
1715 char* val = (char*) p;
1719 list = list_from_str (val, ",");
1720 if (list_empty (list))
1723 for (i = 0; i < list->length; i++)
1724 if (str_ncmp (list->data[i], "never", 5) != 0 &&
1725 str_ncmp (list->data[i], "failure", 7) != 0 &&
1726 str_ncmp (list->data[i], "delay", 5) != 0 &&
1727 str_ncmp (list->data[i], "success", 7) != 0) {
1729 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1730 (char*) list->data[i], "dsn_notify");
1734 list_del (&list, (list_del_t*) _mem_free);
1738 static int check_num (const char* option, unsigned long p,
1739 char* errbuf, size_t errlen) {
1742 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1749 static int check_debug (const char* option, unsigned long p,
1750 char* errbuf, size_t errlen) {
1751 if ((int) p <= DEBUG_MAX_LEVEL &&
1752 (int) p >= DEBUG_MIN_LEVEL)
1756 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1761 static int check_history (const char* option, unsigned long p,
1762 char* errbuf, size_t errlen) {
1763 if (!check_num ("history", p, errbuf, errlen))
1765 mutt_init_history ();
1769 static int check_special (const char* name, unsigned long val,
1770 char* errbuf, size_t errlen) {
1773 for (i = 0; SpecialVars[i].name; i++) {
1774 if (str_cmp (SpecialVars[i].name, name) == 0) {
1775 return (SpecialVars[i].check (SpecialVars[i].name,
1776 val, errbuf, errlen));
1782 static const struct mapping_t* get_sortmap (struct option_t* option) {
1783 const struct mapping_t* map = NULL;
1785 switch (option->type & DT_SUBTYPE_MASK) {
1787 map = SortAliasMethods;
1789 case DT_SORT_BROWSER:
1790 map = SortBrowserMethods;
1793 if ((WithCrypto & APPLICATION_PGP))
1794 map = SortKeyMethods;
1797 map = SortAuxMethods;
1806 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1809 int query, unset, inv, reset, r = 0;
1810 struct option_t* option = NULL;
1812 while (MoreArgs (s)) {
1813 /* reset state variables */
1815 unset = data & M_SET_UNSET;
1816 inv = data & M_SET_INV;
1817 reset = data & M_SET_RESET;
1819 if (*s->dptr == '?') {
1823 else if (str_ncmp ("no", s->dptr, 2) == 0) {
1827 else if (str_ncmp ("inv", s->dptr, 3) == 0) {
1831 else if (*s->dptr == '&') {
1836 /* get the variable name */
1837 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1839 /* resolve synonyms */
1840 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1841 DTYPE (option->type == DT_SYN)) {
1842 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1843 syn_add (newopt, option);
1847 /* see if we need to add $user_ var */
1848 if (!option && ascii_strncmp ("user_", tmp->data, 5) == 0) {
1849 /* there's no option named like this yet so only add one
1850 * if the action isn't any of: reset, unset, query */
1851 if (!(reset || unset || query || *s->dptr != '=')) {
1852 debug_print (1, ("adding user option '%s'\n", tmp->data));
1853 option = add_user_option (tmp->data);
1854 hash_insert (ConfigOptions, option->option, option, 0);
1858 if (!option && !(reset && str_cmp ("all", tmp->data) == 0)) {
1859 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1865 if (query || unset || inv) {
1866 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1870 if (s && *s->dptr == '=') {
1871 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1875 if (!str_cmp ("all", tmp->data)) {
1876 if (CurrentMenu == MENU_PAGER) {
1877 snprintf (err->data, err->dsize, _("Not available in this menu."));
1880 hash_map (ConfigOptions, mutt_restore_default, 1);
1881 set_option (OPTFORCEREDRAWINDEX);
1882 set_option (OPTFORCEREDRAWPAGER);
1883 set_option (OPTSORTSUBTHREADS);
1884 set_option (OPTNEEDRESORT);
1885 set_option (OPTRESORTINIT);
1886 set_option (OPTREDRAWTREE);
1889 else if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1890 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1895 mutt_restore_default (NULL, option, 1);
1898 else if (DTYPE (option->type) == DT_BOOL) {
1899 /* XXX this currently ignores the function table
1900 * as we don't get invert and stuff into it */
1901 if (s && *s->dptr == '=') {
1902 if (unset || inv || query) {
1903 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1908 mutt_extract_token (tmp, s, 0);
1909 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1911 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1914 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1920 bool_to_string (err->data, err->dsize, option);
1926 unset_option (option->data);
1928 toggle_option (option->data);
1930 set_option (option->data);
1932 else if (DTYPE (option->type) == DT_STR ||
1933 DTYPE (option->type) == DT_PATH ||
1934 DTYPE (option->type) == DT_ADDR ||
1935 DTYPE (option->type) == DT_MAGIC ||
1936 DTYPE (option->type) == DT_NUM ||
1937 DTYPE (option->type) == DT_SORT ||
1938 DTYPE (option->type) == DT_RX ||
1939 DTYPE (option->type) == DT_USER ||
1940 DTYPE (option->type) == DT_SYS) {
1942 /* XXX maybe we need to get unset into handlers? */
1943 if (DTYPE (option->type) == DT_STR ||
1944 DTYPE (option->type) == DT_PATH ||
1945 DTYPE (option->type) == DT_ADDR ||
1946 DTYPE (option->type) == DT_USER ||
1947 DTYPE (option->type) == DT_SYS) {
1950 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1951 snprintf (err->data, err->dsize, _("$%s is read-only"),
1955 } else if (DTYPE (option->type) == DT_ADDR)
1956 rfc822_free_address ((ADDRESS **) option->data);
1957 else if (DTYPE (option->type) == DT_USER)
1958 /* to unset $user_ means remove */
1959 hash_delete (ConfigOptions, option->option,
1960 option, del_option);
1962 mem_free ((void *) option->data);
1967 if (query || *s->dptr != '=') {
1968 FuncTable[DTYPE (option->type)].opt_to_string
1969 (err->data, err->dsize, option);
1973 /* the $muttng_ variables are read-only */
1974 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1975 snprintf (err->data, err->dsize, _("$%s is read-only"),
1982 mutt_extract_token (tmp, s, 0);
1983 if (!FuncTable[DTYPE (option->type)].opt_from_string
1984 (option, tmp->data, err->data, err->dsize))
1988 else if (DTYPE (option->type) == DT_QUAD) {
1991 quad_to_string (err->data, err->dsize, option);
1995 if (*s->dptr == '=') {
1998 mutt_extract_token (tmp, s, 0);
1999 if (ascii_strcasecmp ("yes", tmp->data) == 0)
2000 set_quadoption (option->data, M_YES);
2001 else if (ascii_strcasecmp ("no", tmp->data) == 0)
2002 set_quadoption (option->data, M_NO);
2003 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
2004 set_quadoption (option->data, M_ASKYES);
2005 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
2006 set_quadoption (option->data, M_ASKNO);
2008 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
2009 tmp->data, option->option);
2016 toggle_quadoption (option->data);
2018 set_quadoption (option->data, M_NO);
2020 set_quadoption (option->data, M_YES);
2024 snprintf (err->data, err->dsize, _("%s: unknown type"),
2030 if (option->flags & R_INDEX)
2031 set_option (OPTFORCEREDRAWINDEX);
2032 if (option->flags & R_PAGER)
2033 set_option (OPTFORCEREDRAWPAGER);
2034 if (option->flags & R_RESORT_SUB)
2035 set_option (OPTSORTSUBTHREADS);
2036 if (option->flags & R_RESORT)
2037 set_option (OPTNEEDRESORT);
2038 if (option->flags & R_RESORT_INIT)
2039 set_option (OPTRESORTINIT);
2040 if (option->flags & R_TREE)
2041 set_option (OPTREDRAWTREE);
2048 /* reads the specified initialization file. returns -1 if errors were found
2049 so that we can pause to let the user know... */
2050 static int source_rc (const char *rcfile, BUFFER * err)
2053 int line = 0, rc = 0, conv = 0;
2055 char *linebuf = NULL;
2056 char *currentline = NULL;
2060 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
2062 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
2063 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
2067 memset (&token, 0, sizeof (token));
2068 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
2069 conv = ConfigCharset && (*ConfigCharset) && Charset;
2071 currentline = str_dup (linebuf);
2074 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
2077 currentline = linebuf;
2082 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
2083 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
2084 if (--rc < -MAXERRS) {
2086 mem_free (¤tline);
2095 mem_free (¤tline);
2097 mem_free (&token.data);
2098 mem_free (&linebuf);
2101 mutt_wait_filter (pid);
2103 /* the muttrc source keyword */
2104 snprintf (err->data, err->dsize,
2105 rc >= -MAXERRS ? _("source: errors in %s")
2106 : _("source: reading aborted due too many errors in %s"),
2115 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
2118 char path[_POSIX_PATH_MAX];
2122 if (mutt_extract_token (tmp, s, 0) != 0) {
2123 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
2127 strfcpy (path, tmp->data, sizeof (path));
2128 mutt_expand_path (path, sizeof (path));
2130 rc += source_rc (path, err);
2132 while (MoreArgs (s));
2134 return ((rc < 0) ? -1 : 0);
2137 /* line command to execute
2139 token scratch buffer to be used by parser. caller should free
2140 token->data when finished. the reason for this variable is
2141 to avoid having to allocate and deallocate a lot of memory
2142 if we are parsing many lines. the caller can pass in the
2143 memory to use, which avoids having to create new space for
2144 every call to this function.
2146 err where to write error messages */
2147 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
2152 memset (&expn, 0, sizeof (expn));
2153 expn.data = expn.dptr = line;
2154 expn.dsize = str_len (line);
2158 debug_print (1, ("expand '%s'\n", line));
2161 while (*expn.dptr) {
2162 if (*expn.dptr == '#')
2163 break; /* rest of line is a comment */
2164 if (*expn.dptr == ';') {
2168 mutt_extract_token (token, &expn, 0);
2169 for (i = 0; Commands[i].name; i++) {
2170 if (!str_cmp (token->data, Commands[i].name)) {
2171 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
2176 if (!Commands[i].name) {
2177 snprintf (err->data, err->dsize, _("%s: unknown command"),
2178 NONULL (token->data));
2185 mem_free (&expn.data);
2190 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
2191 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
2192 /* initial string that starts completion. No telling how much crap
2193 * the user has typed so far. Allocate LONG_STRING just to be sure! */
2194 char User_typed[LONG_STRING] = { 0 };
2196 int Num_matched = 0; /* Number of matches for completion */
2197 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
2198 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
2200 /* helper function for completion. Changes the dest buffer if
2201 necessary/possible to aid completion.
2202 dest == completion result gets here.
2203 src == candidate for completion.
2204 try == user entered data for completion.
2205 len == length of dest buffer.
2207 static void candidate (char *dest, char *try, char *src, int len)
2211 if (strstr (src, try) == src) {
2212 Matches[Num_matched++] = src;
2214 strfcpy (dest, src, len);
2216 for (l = 0; src[l] && src[l] == dest[l]; l++);
2222 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
2226 int spaces; /* keep track of the number of leading spaces on the line */
2229 spaces = buffer - pt;
2231 pt = buffer + pos - spaces;
2232 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2235 if (pt == buffer) { /* complete cmd */
2236 /* first TAB. Collect all the matches */
2239 strfcpy (User_typed, pt, sizeof (User_typed));
2240 memset (Matches, 0, sizeof (Matches));
2241 memset (Completed, 0, sizeof (Completed));
2242 for (num = 0; Commands[num].name; num++)
2243 candidate (Completed, User_typed, Commands[num].name,
2244 sizeof (Completed));
2245 Matches[Num_matched++] = User_typed;
2247 /* All matches are stored. Longest non-ambiguous string is ""
2248 * i.e. dont change 'buffer'. Fake successful return this time */
2249 if (User_typed[0] == 0)
2253 if (Completed[0] == 0 && User_typed[0])
2256 /* Num_matched will _always_ be atleast 1 since the initial
2257 * user-typed string is always stored */
2258 if (numtabs == 1 && Num_matched == 2)
2259 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
2260 else if (numtabs > 1 && Num_matched > 2)
2261 /* cycle thru all the matches */
2262 snprintf (Completed, sizeof (Completed), "%s",
2263 Matches[(numtabs - 2) % Num_matched]);
2265 /* return the completed command */
2266 strncpy (buffer, Completed, len - spaces);
2268 else if (!str_ncmp (buffer, "set", 3)
2269 || !str_ncmp (buffer, "unset", 5)
2270 || !str_ncmp (buffer, "reset", 5)
2271 || !str_ncmp (buffer, "toggle", 6)) { /* complete variables */
2272 char *prefixes[] = { "no", "inv", "?", "&", 0 };
2275 /* loop through all the possible prefixes (no, inv, ...) */
2276 if (!str_ncmp (buffer, "set", 3)) {
2277 for (num = 0; prefixes[num]; num++) {
2278 if (!str_ncmp (pt, prefixes[num], str_len (prefixes[num]))) {
2279 pt += str_len (prefixes[num]);
2285 /* first TAB. Collect all the matches */
2288 strfcpy (User_typed, pt, sizeof (User_typed));
2289 memset (Matches, 0, sizeof (Matches));
2290 memset (Completed, 0, sizeof (Completed));
2291 for (num = 0; MuttVars[num].option; num++)
2292 candidate (Completed, User_typed, MuttVars[num].option,
2293 sizeof (Completed));
2294 Matches[Num_matched++] = User_typed;
2296 /* All matches are stored. Longest non-ambiguous string is ""
2297 * i.e. dont change 'buffer'. Fake successful return this time */
2298 if (User_typed[0] == 0)
2302 if (Completed[0] == 0 && User_typed[0])
2305 /* Num_matched will _always_ be atleast 1 since the initial
2306 * user-typed string is always stored */
2307 if (numtabs == 1 && Num_matched == 2)
2308 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
2309 else if (numtabs > 1 && Num_matched > 2)
2310 /* cycle thru all the matches */
2311 snprintf (Completed, sizeof (Completed), "%s",
2312 Matches[(numtabs - 2) % Num_matched]);
2314 strncpy (pt, Completed, buffer + len - pt - spaces);
2316 else if (!str_ncmp (buffer, "exec", 4)) {
2317 struct binding_t *menu = km_get_table (CurrentMenu);
2319 if (!menu && CurrentMenu != MENU_PAGER)
2323 /* first TAB. Collect all the matches */
2326 strfcpy (User_typed, pt, sizeof (User_typed));
2327 memset (Matches, 0, sizeof (Matches));
2328 memset (Completed, 0, sizeof (Completed));
2329 for (num = 0; menu[num].name; num++)
2330 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
2331 /* try the generic menu */
2332 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2334 for (num = 0; menu[num].name; num++)
2335 candidate (Completed, User_typed, menu[num].name,
2336 sizeof (Completed));
2338 Matches[Num_matched++] = User_typed;
2340 /* All matches are stored. Longest non-ambiguous string is ""
2341 * i.e. dont change 'buffer'. Fake successful return this time */
2342 if (User_typed[0] == 0)
2346 if (Completed[0] == 0 && User_typed[0])
2349 /* Num_matched will _always_ be atleast 1 since the initial
2350 * user-typed string is always stored */
2351 if (numtabs == 1 && Num_matched == 2)
2352 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
2353 else if (numtabs > 1 && Num_matched > 2)
2354 /* cycle thru all the matches */
2355 snprintf (Completed, sizeof (Completed), "%s",
2356 Matches[(numtabs - 2) % Num_matched]);
2358 strncpy (pt, Completed, buffer + len - pt - spaces);
2366 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2368 char var[STRING], *pt = buffer;
2370 struct option_t* option = NULL;
2376 spaces = buffer - pt;
2378 pt = buffer + pos - spaces;
2379 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2381 pt++; /* move past the space */
2382 if (*pt == '=') /* abort if no var before the '=' */
2385 if (str_ncmp (buffer, "set", 3) == 0) {
2386 strfcpy (var, pt, sizeof (var));
2387 /* ignore the trailing '=' when comparing */
2388 var[str_len (var) - 1] = 0;
2389 if (!(option = hash_find (ConfigOptions, var)))
2390 return 0; /* no such variable. */
2392 char tmp[LONG_STRING], tmp2[LONG_STRING];
2394 size_t dlen = buffer + len - pt - spaces;
2395 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2399 if ((DTYPE (option->type) == DT_STR) ||
2400 (DTYPE (option->type) == DT_PATH) ||
2401 (DTYPE (option->type) == DT_RX)) {
2402 strfcpy (tmp, NONULL (*((char **) option->data)), sizeof (tmp));
2403 if (DTYPE (option->type) == DT_PATH)
2404 mutt_pretty_mailbox (tmp);
2406 else if (DTYPE (option->type) == DT_ADDR) {
2407 rfc822_write_address (tmp, sizeof (tmp),
2408 *((ADDRESS **) option->data), 0);
2410 else if (DTYPE (option->type) == DT_QUAD)
2411 strfcpy (tmp, vals[quadoption (option->data)], sizeof (tmp));
2412 else if (DTYPE (option->type) == DT_NUM)
2413 snprintf (tmp, sizeof (tmp), "%d", (*((short *) option->data)));
2414 else if (DTYPE (option->type) == DT_SORT) {
2415 const struct mapping_t *map;
2418 switch (option->type & DT_SUBTYPE_MASK) {
2420 map = SortAliasMethods;
2422 case DT_SORT_BROWSER:
2423 map = SortBrowserMethods;
2426 if ((WithCrypto & APPLICATION_PGP))
2427 map = SortKeyMethods;
2436 mutt_getnamebyvalue (*((short *) option->data) & SORT_MASK,
2438 snprintf (tmp, sizeof (tmp), "%s%s%s",
2439 (*((short *) option->data) & SORT_REVERSE) ?
2441 (*((short *) option->data) & SORT_LAST) ? "last-" :
2444 else if (DTYPE (option->type) == DT_MAGIC) {
2446 switch (DefaultMagic) {
2462 strfcpy (tmp, p, sizeof (tmp));
2464 else if (DTYPE (option->type) == DT_BOOL)
2465 strfcpy (tmp, option (option->data) ? "yes" : "no",
2470 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
2471 if (*s == '\\' || *s == '"')
2477 strfcpy (tmp, pt, sizeof (tmp));
2478 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2486 /* Implement the -Q command line flag */
2487 int mutt_query_variables (LIST * queries)
2491 char errbuff[STRING];
2492 char command[STRING];
2496 memset (&err, 0, sizeof (err));
2497 memset (&token, 0, sizeof (token));
2500 err.dsize = sizeof (errbuff);
2502 for (p = queries; p; p = p->next) {
2503 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2504 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2505 fprintf (stderr, "%s\n", err.data);
2506 mem_free (&token.data);
2509 printf ("%s\n", err.data);
2512 mem_free (&token.data);
2516 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2520 for (i = 0; map[i].name; i++)
2521 if (map[i].value == val)
2522 return (map[i].name);
2526 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2530 for (i = 0; map[i].name; i++)
2531 if (ascii_strcasecmp (map[i].name, name) == 0)
2532 return (map[i].value);
2536 static int mutt_execute_commands (LIST * p)
2539 char errstr[SHORT_STRING];
2541 memset (&err, 0, sizeof (err));
2543 err.dsize = sizeof (errstr);
2544 memset (&token, 0, sizeof (token));
2545 for (; p; p = p->next) {
2546 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2547 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2548 mem_free (&token.data);
2552 mem_free (&token.data);
2556 void mutt_init (int skip_sys_rc, LIST * commands)
2559 struct utsname utsname;
2560 char *p, buffer[STRING], error[STRING];
2561 int i, default_rc = 0, need_pause = 0;
2564 memset (&err, 0, sizeof (err));
2566 err.dsize = sizeof (error);
2568 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2569 * to have some room for $user_ vars */
2570 ConfigOptions = hash_create (sizeof (MuttVars) * 3);
2571 for (i = 0; MuttVars[i].option; i++) {
2572 if (DTYPE (MuttVars[i].type) != DT_SYS)
2573 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2575 hash_insert (ConfigOptions, MuttVars[i].option,
2576 add_option (MuttVars[i].option, MuttVars[i].init,
2581 * XXX - use something even more difficult to predict?
2583 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2584 "\033]9;%ld\a", (long) time (NULL));
2586 /* on one of the systems I use, getcwd() does not return the same prefix
2587 as is listed in the passwd file */
2588 if ((p = getenv ("HOME")))
2589 Homedir = str_dup (p);
2591 /* Get some information about the user */
2592 if ((pw = getpwuid (getuid ()))) {
2595 Username = str_dup (pw->pw_name);
2597 Homedir = str_dup (pw->pw_dir);
2599 Realname = str_dup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2600 Shell = str_dup (pw->pw_shell);
2606 fputs (_("unable to determine home directory"), stderr);
2609 if ((p = getenv ("USER")))
2610 Username = str_dup (p);
2613 fputs (_("unable to determine username"), stderr);
2616 Shell = str_dup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2619 debug_start(Homedir);
2621 /* And about the host... */
2623 /* some systems report the FQDN instead of just the hostname */
2624 if ((p = strchr (utsname.nodename, '.'))) {
2625 Hostname = str_substrdup (utsname.nodename, p);
2627 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2630 Hostname = str_dup (utsname.nodename);
2633 #define DOMAIN buffer
2634 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2635 Fqdn = str_dup ("@");
2638 if (*DOMAIN != '@') {
2639 Fqdn = mem_malloc (str_len (DOMAIN) + str_len (Hostname) + 2);
2640 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2643 Fqdn = str_dup (NONULL (Hostname));
2650 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2652 fgets (buffer, sizeof (buffer), f);
2653 p = (char*) &buffer;
2656 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r')
2660 NewsServer = str_dup (p);
2664 if ((p = getenv ("NNTPSERVER")))
2665 NewsServer = str_dup (p);
2668 if ((p = getenv ("MAIL")))
2669 Spoolfile = str_dup (p);
2670 else if ((p = getenv ("MAILDIR")))
2671 Spoolfile = str_dup (p);
2674 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2676 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
2678 Spoolfile = str_dup (buffer);
2681 if ((p = getenv ("MAILCAPS")))
2682 MailcapPath = str_dup (p);
2684 /* Default search path from RFC1524 */
2686 str_dup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2687 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2690 Tempdir = str_dup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2692 p = getenv ("VISUAL");
2694 p = getenv ("EDITOR");
2698 Editor = str_dup (p);
2699 Visual = str_dup (p);
2701 if ((p = getenv ("REPLYTO")) != NULL) {
2704 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2706 memset (&buf, 0, sizeof (buf));
2707 buf.data = buf.dptr = buffer;
2708 buf.dsize = str_len (buffer);
2710 memset (&token, 0, sizeof (token));
2711 parse_my_hdr (&token, &buf, 0, &err);
2712 mem_free (&token.data);
2715 if ((p = getenv ("EMAIL")) != NULL)
2716 From = rfc822_parse_adrlist (NULL, p);
2718 mutt_set_langinfo_charset ();
2719 mutt_set_charset (Charset);
2722 /* Set standard defaults */
2723 hash_map (ConfigOptions, mutt_set_default, 0);
2724 hash_map (ConfigOptions, mutt_restore_default, 0);
2726 CurrentMenu = MENU_MAIN;
2729 #ifndef LOCALES_HACK
2730 /* Do we have a locale definition? */
2731 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2732 ((p = getenv ("LANG")) != NULL && p[0]) ||
2733 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2734 set_option (OPTLOCALES);
2738 /* Unset suspend by default if we're the session leader */
2739 if (getsid (0) == getpid ())
2740 unset_option (OPTSUSPEND);
2743 mutt_init_history ();
2752 * When changing the code which looks for a configuration file,
2753 * please also change the corresponding code in muttbug.sh.in.
2763 snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2765 if (access (buffer, F_OK) == -1)
2767 snprintf (buffer, sizeof (buffer), "%s/.muttngrc", NONULL (Homedir));
2768 if (access (buffer, F_OK) == -1)
2770 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s",
2771 NONULL (Homedir), MUTT_VERSION);
2772 if (access (buffer, F_OK) == -1)
2774 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc",
2778 Muttrc = str_dup (buffer);
2781 strfcpy (buffer, Muttrc, sizeof (buffer));
2783 mutt_expand_path (buffer, sizeof (buffer));
2784 Muttrc = str_dup (buffer);
2786 mem_free (&AliasFile);
2787 AliasFile = str_dup (NONULL (Muttrc));
2789 /* Process the global rc file if it exists and the user hasn't explicity
2790 requested not to via "-n". */
2792 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2794 if (access (buffer, F_OK) == -1)
2795 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", SYSCONFDIR);
2796 if (access (buffer, F_OK) == -1)
2797 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR,
2799 if (access (buffer, F_OK) == -1)
2800 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2801 if (access (buffer, F_OK) != -1) {
2802 if (source_rc (buffer, &err) != 0) {
2803 fputs (err.data, stderr);
2804 fputc ('\n', stderr);
2810 /* Read the user's initialization file. */
2811 if (access (Muttrc, F_OK) != -1) {
2812 if (!option (OPTNOCURSES))
2814 if (source_rc (Muttrc, &err) != 0) {
2815 fputs (err.data, stderr);
2816 fputc ('\n', stderr);
2820 else if (!default_rc) {
2821 /* file specified by -F does not exist */
2822 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2823 mutt_endwin (buffer);
2827 if (mutt_execute_commands (commands) != 0)
2830 /* warn about synonym variables */
2831 if (!list_empty(Synonyms)) {
2833 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2834 for (i = 0; i < Synonyms->length; i++) {
2835 struct option_t* newopt = NULL, *oldopt = NULL;
2836 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2837 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2838 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2839 oldopt ? NONULL (oldopt->option) : "",
2840 newopt ? NONULL (newopt->option) : "",
2841 NONULL(((syn_t*) Synonyms->data[i])->f),
2842 ((syn_t*) Synonyms->data[i])->l);
2844 fprintf (stderr, _("Warning: synonym variables are scheduled"
2845 " for removal.\n"));
2846 list_del (&Synonyms, syn_del);
2850 if (need_pause && !option (OPTNOCURSES)) {
2851 if (mutt_any_key_to_continue (NULL) == -1)
2856 set_option (OPTWEED); /* turn weeding on by default */
2860 int mutt_get_hook_type (const char *name)
2862 struct command_t *c;
2864 for (c = Commands; c->name; c++)
2865 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2870 /* compare two option_t*'s for sorting -t/-T output */
2871 static int opt_cmp (const void* a, const void* b) {
2872 return (str_cmp ((*(struct option_t**) a)->option,
2873 (*(struct option_t**) b)->option));
2876 /* callback for hash_map() to put all non-synonym vars into list */
2877 static void opt_sel_full (const char* key, void* data,
2878 unsigned long more) {
2879 list2_t** l = (list2_t**) more;
2880 struct option_t* option = (struct option_t*) data;
2882 if (DTYPE (option->type) == DT_SYN)
2884 list_push_back (l, option);
2887 /* callback for hash_map() to put all changed non-synonym vars into list */
2888 static void opt_sel_diff (const char* key, void* data,
2889 unsigned long more) {
2890 list2_t** l = (list2_t**) more;
2891 struct option_t* option = (struct option_t*) data;
2892 char buf[LONG_STRING];
2894 if (DTYPE (option->type) == DT_SYN)
2897 mutt_option_value (option->option, buf, sizeof (buf));
2898 if (str_cmp (buf, option->init) != 0)
2899 list_push_back (l, option);
2902 /* dump out the value of all the variables we have */
2903 int mutt_dump_variables (int full) {
2905 char outbuf[STRING];
2906 list2_t* tmp = NULL;
2907 struct option_t* option = NULL;
2909 /* get all non-synonyms into list... */
2910 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2911 (unsigned long) &tmp);
2913 if (!list_empty(tmp)) {
2914 /* ...and dump list sorted */
2915 qsort (tmp->data, tmp->length, sizeof (void*), opt_cmp);
2916 for (i = 0; i < tmp->length; i++) {
2917 option = (struct option_t*) tmp->data[i];
2918 FuncTable[DTYPE (option->type)].opt_to_string
2919 (outbuf, sizeof (outbuf), option);
2920 printf ("%s\n", outbuf);
2923 list_del (&tmp, NULL);