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/str.h>
19 #include <lib-lib/file.h>
20 #include <lib-lib/ascii.h>
21 #include <lib-lib/macros.h>
22 #include <lib-lib/buffer.h>
23 #include <lib-lib/mapping.h>
26 #include "mutt_curses.h"
32 #include "mutt_crypt.h"
33 #include "mutt_idna.h"
35 #if defined(USE_SSL) || defined(USE_GNUTLS)
39 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
40 #include "mutt_libesmtp.h"
48 #include "lib/debug.h"
54 #include <sys/utsname.h>
61 static const struct mapping_t* get_sortmap (struct option_t* option);
62 static int parse_sort (struct option_t* dst, const char *s,
63 const struct mapping_t *map,
64 char* errbuf, size_t errlen);
66 static HASH *ConfigOptions = NULL;
68 /* for synonym warning reports: synonym found during parsing */
72 struct option_t* n; /* new */
73 struct option_t* o; /* old */
76 /* for synonym warning reports: list of synonyms found */
77 static list2_t* Synonyms;
78 /* for synonym warning reports: current rc file */
79 static const char* CurRCFile = NULL;
80 /* for synonym warning reports: current rc line */
81 static int CurRCLine = 0;
83 /* prototypes for checking for special vars */
84 static int check_dsn_return (const char* option, unsigned long val,
85 char* errbuf, size_t errlen);
86 static int check_dsn_notify (const char* option, unsigned long val,
87 char* errbuf, size_t errlen);
88 static int check_history (const char* option, unsigned long val,
89 char* errbuf, size_t errlen);
90 /* this checks that numbers are >= 0 */
91 static int check_num (const char* option, unsigned long val,
92 char* errbuf, size_t errlen);
94 static int check_debug (const char* option, unsigned long val,
95 char* errbuf, size_t errlen);
98 /* use this to check only */
99 static int check_special (const char* option, unsigned long val,
100 char* errbuf, size_t errlen);
102 /* variable <-> sanity check function mappings
103 * when changing these, make sure the proper _from_string handler
104 * does this checking!
108 int (*check) (const char* option, unsigned long val,
109 char* errbuf, size_t errlen);
111 { "dsn_notify", check_dsn_notify },
112 { "dsn_return", check_dsn_return },
113 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
114 { "smtp_use_tls", mutt_libesmtp_check_usetls },
116 { "history", check_history },
117 { "pager_index_lines", check_num },
119 { "debug_level", check_debug },
125 /* protos for config type handles: convert value to string */
126 static void bool_to_string (char* dst, size_t dstlen, struct option_t* option);
127 static void num_to_string (char* dst, size_t dstlen, struct option_t* option);
128 static void str_to_string (char* dst, size_t dstlen, struct option_t* option);
129 static void quad_to_string (char* dst, size_t dstlen, struct option_t* option);
130 static void sort_to_string (char* dst, size_t dstlen, struct option_t* option);
131 static void rx_to_string (char* dst, size_t dstlen, struct option_t* option);
132 static void magic_to_string (char* dst, size_t dstlen, struct option_t* option);
133 static void addr_to_string (char* dst, size_t dstlen, struct option_t* option);
134 static void user_to_string (char* dst, size_t dstlen, struct option_t* option);
135 static void sys_to_string (char* dst, size_t dstlen, struct option_t* option);
137 /* protos for config type handles: convert to value from string */
138 static int bool_from_string (struct option_t* dst, const char* val,
139 char* errbuf, size_t errlen);
140 static int num_from_string (struct option_t* dst, const char* val,
141 char* errbuf, size_t errlen);
142 static int str_from_string (struct option_t* dst, const char* val,
143 char* errbuf, size_t errlen);
144 static int path_from_string (struct option_t* dst, const char* val,
145 char* errbuf, size_t errlen);
146 static int quad_from_string (struct option_t* dst, const char* val,
147 char* errbuf, size_t errlen);
148 static int sort_from_string (struct option_t* dst, const char* val,
149 char* errbuf, size_t errlen);
150 static int rx_from_string (struct option_t* dst, const char* val,
151 char* errbuf, size_t errlen);
152 static int magic_from_string (struct option_t* dst, const char* val,
153 char* errbuf, size_t errlen);
154 static int addr_from_string (struct option_t* dst, const char* val,
155 char* errbuf, size_t errlen);
156 static int user_from_string (struct option_t* dst, const char* val,
157 char* errbuf, size_t errlen);
161 void (*opt_to_string) (char* dst, size_t dstlen, struct option_t* option);
162 int (*opt_from_string) (struct option_t* dst, const char* val,
163 char* errbuf, size_t errlen);
165 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
166 { DT_BOOL, bool_to_string, bool_from_string },
167 { DT_NUM, num_to_string, num_from_string },
168 { DT_STR, str_to_string, str_from_string },
169 { DT_PATH, str_to_string, path_from_string },
170 { DT_QUAD, quad_to_string, quad_from_string },
171 { DT_SORT, sort_to_string, sort_from_string },
172 { DT_RX, rx_to_string, rx_from_string },
173 { DT_MAGIC, magic_to_string, magic_from_string },
174 /* synonyms should be resolved already so we don't need this
175 * but must define it as DT_ is used for indexing */
176 { DT_SYN, NULL, NULL },
177 { DT_ADDR, addr_to_string, addr_from_string },
178 { DT_USER, user_to_string, user_from_string },
179 { DT_SYS, sys_to_string, NULL },
182 static void bool_to_string (char* dst, size_t dstlen,
183 struct option_t* option) {
184 snprintf (dst, dstlen, "%s=%s", option->option,
185 option (option->data) ? "yes" : "no");
188 static int bool_from_string (struct option_t* dst, const char* val,
189 char* errbuf, size_t errlen) {
194 if (ascii_strncasecmp (val, "yes", 3) == 0)
196 else if (ascii_strncasecmp (val, "no", 2) == 0)
202 set_option (dst->data);
204 unset_option (dst->data);
208 static void num_to_string (char* dst, size_t dstlen,
209 struct option_t* option) {
211 const char* fmt = (m_strcmp(option->option, "umask") == 0) ?
213 snprintf (dst, dstlen, fmt, option->option,
214 *((short*) option->data));
217 static int num_from_string (struct option_t* dst, const char* val,
218 char* errbuf, size_t errlen) {
219 int num = 0, old = 0;
225 num = strtol (val, &t, 0);
227 if (!*val || *t || (short) num != num) {
229 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
235 /* just temporarily accept new val so that check_special for
236 * $history already has it when doing history's init() */
237 old = *((short*) dst->data);
238 *((short*) dst->data) = (short) num;
240 if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
241 *((short*) dst->data) = old;
248 static void str_to_string (char* dst, size_t dstlen,
249 struct option_t* option) {
250 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
251 NONULL (*((char**) option->data)));
254 static void user_to_string (char* dst, size_t dstlen,
255 struct option_t* option) {
256 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
257 NONULL (((char*) option->data)));
260 static void sys_to_string (char* dst, size_t dstlen,
261 struct option_t* option) {
262 char *val = NULL, *t = NULL;
265 /* get some $muttng_ values dynamically */
266 if (ascii_strcmp ("muttng_pwd", option->option) == 0) {
267 val = p_new(char, _POSIX_PATH_MAX);
268 val = getcwd (val, _POSIX_PATH_MAX-1);
270 } else if (ascii_strcmp ("muttng_folder_path", option->option) == 0 &&
271 CurrentFolder && *CurrentFolder) {
273 } else if (ascii_strcmp ("muttng_folder_name", option->option) == 0 &&
274 CurrentFolder && *CurrentFolder) {
276 size_t Maildirlength = m_strlen(Maildir);
279 * if name starts with $folder, just strip it to keep hierarchy
280 * $folder=imap://host, path=imap://host/inbox/b -> inbox/b
282 if (Maildirlength > 0 && m_strncmp(CurrentFolder, Maildir,
283 Maildirlength) == 0 &&
284 m_strlen(CurrentFolder) > Maildirlength) {
285 val = CurrentFolder + Maildirlength;
286 if (Maildir[strlen(Maildir)-1]!='/')
288 /* if not $folder, just use everything after last / */
289 } else if ((t = strrchr (CurrentFolder, '/')) != NULL)
291 /* default: use as-is */
298 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (val));
303 static int path_from_string (struct option_t* dst, const char* val,
304 char* errbuf, size_t errlen) {
305 char path[_POSIX_PATH_MAX];
311 p_delete((char**) dst->data);
316 m_strcpy(path, sizeof(path), val);
317 mutt_expand_path (path, sizeof(path));
318 str_replace ((char **) dst->data, path);
322 static int str_from_string (struct option_t* dst, const char* val,
323 char* errbuf, size_t errlen) {
327 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
330 str_replace ((char**) dst->data, val);
334 static int user_from_string (struct option_t* dst, const char* val,
335 char* errbuf, size_t errlen) {
336 /* if dst == NULL, we may get here in case the user did unset it,
337 * see parse_set() where item is free()'d before coming here; so
338 * just silently ignore it */
341 if (m_strlen((char*) dst->data) == 0)
342 dst->data = (unsigned long) m_strdup(val);
344 char* s = (char*) dst->data;
345 str_replace (&s, val);
347 if (m_strlen(dst->init) == 0)
348 dst->init = m_strdup((char*) dst->data);
352 static void quad_to_string (char* dst, size_t dstlen,
353 struct option_t* option) {
354 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
355 snprintf (dst, dstlen, "%s=%s", option->option,
356 vals[quadoption (option->data)]);
359 static int quad_from_string (struct option_t* dst, const char* val,
360 char* errbuf, size_t errlen) {
365 if (ascii_strncasecmp (val, "yes", 3) == 0)
367 else if (ascii_strncasecmp (val, "no", 2) == 0)
369 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
371 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
377 set_quadoption (dst->data, flag);
381 static void sort_to_string (char* dst, size_t dstlen,
382 struct option_t* option) {
383 const struct mapping_t *map = get_sortmap (option);
384 const char *p = NULL;
387 snprintf (dst, sizeof(dst), "%s=unknown", option->option);
391 p = mutt_getnamebyvalue(*((short *)option->data) & SORT_MASK, map);
393 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
394 (*((short *) option->data) & SORT_REVERSE) ?
396 (*((short *) option->data) & SORT_LAST) ? "last-" :
400 static int sort_from_string (struct option_t* dst, const char* val,
401 char* errbuf, size_t errlen) {
402 const struct mapping_t *map = NULL;
403 if (!(map = get_sortmap (dst))) {
405 snprintf (errbuf, errlen, _("%s: Unknown type."),
409 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
414 static void rx_to_string (char* dst, size_t dstlen,
415 struct option_t* option) {
416 rx_t* p = (rx_t*) option->data;
417 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
418 NONULL (p->pattern));
421 static int rx_from_string (struct option_t* dst, const char* val,
422 char* errbuf, size_t errlen) {
425 int flags = 0, e = 0, not = 0;
431 if (option (OPTATTACHMSG) && !m_strcmp(dst->option, "reply_regexp")) {
433 snprintf (errbuf, errlen,
434 "Operation not permitted when in attach-message mode.");
438 if (!((rx_t*) dst->data))
439 *((rx_t**) dst->data) = p_new(rx_t, 1);
441 p = (rx_t*) dst->data;
443 /* something to do? */
444 if (!val || !*val || (p->pattern && m_strcmp(p->pattern, val) == 0))
447 if (m_strcmp(dst->option, "mask") != 0)
448 flags |= mutt_which_case (val);
451 if (m_strcmp(dst->option, "mask") == 0 && *s == '!') {
456 rx = p_new(regex_t, 1);
458 if ((e = REGCOMP (rx, s, flags)) != 0) {
459 regerror (e, rx, errbuf, errlen);
470 str_replace (&p->pattern, val);
474 if (m_strcmp(dst->option, "reply_regexp") == 0)
475 mutt_adjust_all_subjects ();
480 static void magic_to_string (char* dst, size_t dstlen,
481 struct option_t* option) {
482 const char* s = NULL;
483 switch (option->data) {
484 case M_MBOX: s = "mbox"; break;
485 case M_MMDF: s = "MMDF"; break;
486 case M_MH: s = "MH"; break;
487 case M_MAILDIR: s = "Maildir"; break;
488 default: s = "unknown"; break;
490 snprintf (dst, dstlen, "%s=%s", option->option, s);
493 static int magic_from_string (struct option_t* dst, const char* val,
494 char* errbuf, size_t errlen) {
497 if (!dst || !val || !*val)
499 if (ascii_strncasecmp (val, "mbox", 4) == 0)
501 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
503 else if (ascii_strncasecmp (val, "mh", 2) == 0)
505 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
511 *((short*) dst->data) = flag;
516 static void addr_to_string (char* dst, size_t dstlen,
517 struct option_t* option) {
520 rfc822_write_address (s, sizeof(s), *((address_t**) option->data), 0);
521 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
524 static int addr_from_string (struct option_t* dst, const char* val,
525 char* errbuf, size_t errlen) {
528 address_delete ((address_t**) dst->data);
530 *((address_t**) dst->data) = rfc822_parse_adrlist (NULL, val);
534 int mutt_option_value (const char* val, char* dst, size_t dstlen) {
535 struct option_t* option = NULL;
536 char* tmp = NULL, *t = NULL;
539 if (!(option = hash_find (ConfigOptions, val))) {
540 debug_print (1, ("var '%s' not found\n", val));
544 tmp = p_new(char, dstlen+1);
545 FuncTable[DTYPE (option->type)].opt_to_string (tmp, dstlen, option);
547 /* as we get things of type $var=value and don't want to bloat the
548 * above "just" for expansion, we do the stripping here */
549 debug_print (1, ("orig == '%s'\n", tmp));
550 t = strchr (tmp, '=');
554 if (t[l-1] == '"' && *t == '"') {
559 memcpy (dst, t, l+1);
561 debug_print (1, ("stripped == '%s'\n", dst));
566 /* for synonym warning reports: adds synonym to end of list */
567 static void syn_add (struct option_t* n, struct option_t* o) {
568 syn_t* tmp = p_new(syn_t, 1);
569 tmp->f = m_strdup(CurRCFile);
573 list_push_back (&Synonyms, tmp);
576 /* for synonym warning reports: free single item (for list_del()) */
577 static void syn_del (void** p) {
578 p_delete(&(*(syn_t**) p)->f);
582 void toggle_quadoption (int opt)
585 int b = (opt % 4) * 2;
587 QuadOptions[n] ^= (1 << b);
590 void set_quadoption (int opt, int flag)
593 int b = (opt % 4) * 2;
595 QuadOptions[n] &= ~(0x3 << b);
596 QuadOptions[n] |= (flag & 0x3) << b;
599 int quadoption (int opt)
602 int b = (opt % 4) * 2;
604 return (QuadOptions[n] >> b) & 0x3;
607 int query_quadoption (int opt, const char *prompt)
609 int v = quadoption (opt);
617 v = mutt_yesorno (prompt, (v == M_ASKYES));
618 CLEARLINE (LINES - 1);
625 static void add_to_list (LIST ** list, const char *str)
627 LIST *t, *last = NULL;
629 /* don't add a NULL or empty string to the list */
630 if (!str || *str == '\0')
633 /* check to make sure the item is not already on this list */
634 for (last = *list; last; last = last->next) {
635 if (ascii_strcasecmp (str, last->data) == 0) {
636 /* already on the list, so just ignore it */
644 if (!*list || last) {
646 t->data = m_strdup(str);
656 static int add_to_rx_list (list2_t** list, const char *s, int flags,
665 if (!(rx = rx_compile (s, flags))) {
666 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
670 i = rx_lookup ((*list), rx->pattern);
674 list_push_back (list, rx);
678 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
679 const char *templ, BUFFER * err)
681 SPAM_LIST *t = NULL, *last = NULL;
686 if (!pat || !*pat || !templ)
689 if (!(rx = rx_compile (pat, REG_ICASE))) {
690 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
694 /* check to make sure the item is not already on this list */
695 for (last = *list; last; last = last->next) {
696 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
697 /* Already on the list. Formerly we just skipped this case, but
698 * now we're supporting removals, which means we're supporting
699 * re-adds conceptually. So we probably want this to imply a
700 * removal, then do an add. We can achieve the removal by freeing
701 * the template, and leaving t pointed at the current item.
704 p_delete(&t->template);
711 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
712 * update. Otherwise we want to make a new one to link at the list's end.
715 t = mutt_new_spam_list ();
723 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
724 t->template = m_strdup(templ);
726 /* Find highest match number in template string */
728 for (p = templ; *p;) {
733 while (*p && isdigit ((int) *p))
739 t->nmatch++; /* match 0 is always the whole expr */
744 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
746 SPAM_LIST *spam, *prev;
749 /* Being first is a special case. */
753 if (spam->rx && !m_strcmp(spam->rx->pattern, pat)) {
756 p_delete(&spam->template);
762 for (spam = prev->next; spam;) {
763 if (!m_strcmp(spam->rx->pattern, pat)) {
764 prev->next = spam->next;
766 p_delete(&spam->template);
779 static void remove_from_list (LIST ** l, const char *str)
781 LIST *p, *last = NULL;
783 if (m_strcmp("*", str) == 0)
784 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
789 if (ascii_strcasecmp (str, p->data) == 0) {
792 last->next = p->next;
805 static int remove_from_rx_list (list2_t** l, const char *str)
809 if (m_strcmp("*", str) == 0) {
810 list_del (l, (list_del_t*) rx_free);
814 i = rx_lookup ((*l), str);
816 rx_t* r = list_pop_idx ((*l), i);
824 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
829 struct option_t* option = NULL;
832 mutt_extract_token (tmp, s, 0);
834 /* is the item defined as a variable or a function? */
835 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL)
838 for (i = 0; !res && i < MENU_MAX; i++) {
839 struct binding_t *b = km_get_table (Menus[i].value);
844 for (j = 0; b[j].name; j++)
845 if (!ascii_strncasecmp (tmp->data, b[j].name, m_strlen(tmp->data))
846 && (m_strlen(b[j].name) == m_strlen(tmp->data))) {
852 /* check for feature_* */
853 if (!res && ascii_strncasecmp (tmp->data, "feature_", 8) == 0 &&
854 (j = m_strlen(tmp->data)) > 8) {
856 while (Features[i]) {
857 if (m_strlen(Features[i]) == j-8 &&
858 ascii_strncasecmp (Features[i], tmp->data+8, j-8) == 0) {
868 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
870 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
874 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
877 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
878 mutt_error ("Error: %s", err->data);
879 p_delete(&token.data);
882 p_delete(&token.data);
887 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
891 mutt_extract_token (buf, s, 0);
893 /* don't add "*" to the unignore list */
894 if (strcmp (buf->data, "*"))
895 add_to_list (&UnIgnore, buf->data);
897 remove_from_list (&Ignore, buf->data);
899 while (MoreArgs (s));
904 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
908 mutt_extract_token (buf, s, 0);
909 remove_from_list (&UnIgnore, buf->data);
910 add_to_list (&Ignore, buf->data);
912 while (MoreArgs (s));
917 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
921 mutt_extract_token (buf, s, 0);
922 add_to_list ((LIST **) data, buf->data);
924 while (MoreArgs (s));
929 static void _alternates_clean (void)
933 if (Context && Context->msgcount) {
934 for (i = 0; i < Context->msgcount; i++)
935 Context->hdrs[i]->recip_valid = 0;
939 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
942 _alternates_clean ();
944 mutt_extract_token (buf, s, 0);
945 remove_from_rx_list (&UnAlternates, buf->data);
947 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
950 while (MoreArgs (s));
955 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
958 _alternates_clean ();
960 mutt_extract_token (buf, s, 0);
961 remove_from_rx_list (&Alternates, buf->data);
963 if (m_strcmp(buf->data, "*") &&
964 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
968 while (MoreArgs (s));
973 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
980 /* Insist on at least one parameter */
983 m_strcpy(err->data, err->dsize, _("spam: no matching pattern"));
985 m_strcpy(err->data, err->dsize, _("nospam: no matching pattern"));
989 /* Extract the first token, a regexp */
990 mutt_extract_token (buf, s, 0);
992 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
993 if (data == M_SPAM) {
994 /* If there's a second parameter, it's a template for the spam tag. */
996 mutt_extract_token (&templ, s, 0);
998 /* Add to the spam list. */
999 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
1000 p_delete(&templ.data);
1003 p_delete(&templ.data);
1006 /* If not, try to remove from the nospam list. */
1008 remove_from_rx_list (&NoSpamList, buf->data);
1014 /* M_NOSPAM is for nospam commands. */
1015 else if (data == M_NOSPAM) {
1016 /* nospam only ever has one parameter. */
1018 /* "*" is a special case. */
1019 if (!m_strcmp(buf->data, "*")) {
1020 mutt_free_spam_list (&SpamList);
1021 list_del (&NoSpamList, (list_del_t*) rx_free);
1025 /* If it's on the spam list, just remove it. */
1026 if (remove_from_spam_list (&SpamList, buf->data) != 0)
1029 /* Otherwise, add it to the nospam list. */
1030 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
1036 /* This should not happen. */
1037 m_strcpy(err->data, err->dsize, "This is no good at all.");
1041 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
1045 mutt_extract_token (buf, s, 0);
1047 * Check for deletion of entire list
1049 if (m_strcmp(buf->data, "*") == 0) {
1050 mutt_free_list ((LIST **) data);
1053 remove_from_list ((LIST **) data, buf->data);
1055 while (MoreArgs (s));
1060 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
1064 mutt_extract_token (buf, s, 0);
1065 remove_from_rx_list (&UnMailLists, buf->data);
1067 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1070 while (MoreArgs (s));
1075 /* always wise to do what someone else did before */
1076 static void _attachments_clean (void) {
1078 if (Context && Context->msgcount) {
1079 for (i = 0; i < Context->msgcount; i++)
1080 Context->hdrs[i]->attach_valid = 0;
1084 static int parse_attach_list (BUFFER *buf, BUFFER *s, LIST **ldata,
1087 LIST *listp, *lastp;
1092 /* Find the last item in the list that data points to. */
1094 debug_print (5, ("parse_attach_list: ldata = %p, *ldata = %p\n",
1096 for (listp = *ldata; listp; listp = listp->next) {
1097 a = (ATTACH_MATCH *)listp->data;
1098 debug_print (5, ("parse_attach_list: skipping %s/%s\n", a->major, a->minor));
1103 mutt_extract_token (buf, s, 0);
1105 if (!buf->data || *buf->data == '\0')
1108 a = p_new(ATTACH_MATCH, 1);
1110 /* some cheap hacks that I expect to remove */
1111 if (!m_strcasecmp(buf->data, "any"))
1112 a->major = m_strdup("*/.*");
1113 else if (!m_strcasecmp(buf->data, "none"))
1114 a->major = m_strdup("cheap_hack/this_should_never_match");
1116 a->major = m_strdup(buf->data);
1118 if ((p = strchr(a->major, '/'))) {
1123 a->minor = "unknown";
1126 len = m_strlen(a->minor);
1127 tmpminor = p_new(char, len + 3);
1128 strcpy(&tmpminor[1], a->minor); /* __STRCPY_CHECKED__ */
1130 tmpminor[len+1] = '$';
1131 tmpminor[len+2] = '\0';
1133 a->major_int = mutt_check_mime_type(a->major);
1134 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
1136 p_delete(&tmpminor);
1138 debug_print (5, ("parse_attach_list: added %s/%s [%d]\n",
1139 a->major, a->minor, a->major_int));
1141 listp = p_new(LIST, 1);
1142 listp->data = (char *)a;
1145 lastp->next = listp;
1151 while (MoreArgs (s));
1153 _attachments_clean();
1157 static int parse_unattach_list (BUFFER *buf, BUFFER *s, LIST **ldata, BUFFER *err) {
1159 LIST *lp, *lastp, *newlp;
1165 mutt_extract_token (buf, s, 0);
1167 if (!m_strcasecmp(buf->data, "any"))
1168 tmp = m_strdup("*/.*");
1169 else if (!m_strcasecmp(buf->data, "none"))
1170 tmp = m_strdup("cheap_hack/this_should_never_match");
1172 tmp = m_strdup(buf->data);
1174 if ((minor = strchr(tmp, '/'))) {
1180 major = mutt_check_mime_type(tmp);
1182 /* We must do our own walk here because remove_from_list() will only
1183 * remove the LIST->data, not anything pointed to by the LIST->data. */
1185 for(lp = *ldata; lp; ) {
1186 a = (ATTACH_MATCH *)lp->data;
1187 debug_print(5, ("parse_unattach_list: check %s/%s [%d] : %s/%s [%d]\n",
1188 a->major, a->minor, a->major_int, tmp, minor, major));
1189 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
1190 debug_print(5, ("parse_unattach_list: removed %s/%s [%d]\n",
1191 a->major, a->minor, a->major_int));
1192 regfree(&a->minor_rx);
1193 p_delete(&a->major);
1195 /* Relink backward */
1197 lastp->next = lp->next;
1202 p_delete(&lp->data); /* same as a */
1212 while (MoreArgs (s));
1215 _attachments_clean();
1219 static int print_attach_list (LIST *lp, char op, const char *name) {
1221 printf("attachments %c%s %s/%s\n", op, name,
1222 ((ATTACH_MATCH *)lp->data)->major,
1223 ((ATTACH_MATCH *)lp->data)->minor);
1230 static int parse_attachments (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) {
1234 mutt_extract_token(buf, s, 0);
1235 if (!buf->data || *buf->data == '\0') {
1236 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
1240 category = buf->data;
1246 printf("\nCurrent attachments settings:\n\n");
1247 print_attach_list(AttachAllow, '+', "A");
1248 print_attach_list(AttachExclude, '-', "A");
1249 print_attach_list(InlineAllow, '+', "I");
1250 print_attach_list(InlineExclude, '-', "I");
1251 set_option (OPTFORCEREDRAWINDEX);
1252 set_option (OPTFORCEREDRAWPAGER);
1253 mutt_any_key_to_continue (NULL);
1257 if (op != '+' && op != '-') {
1261 if (!m_strncasecmp(category, "attachment", strlen(category))) {
1263 listp = &AttachAllow;
1265 listp = &AttachExclude;
1267 else if (!m_strncasecmp(category, "inline", strlen(category))) {
1269 listp = &InlineAllow;
1271 listp = &InlineExclude;
1273 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
1277 return parse_attach_list(buf, s, listp, err);
1280 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) {
1284 mutt_extract_token(buf, s, 0);
1285 if (!buf->data || *buf->data == '\0') {
1286 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
1292 if (op != '+' && op != '-') {
1296 if (!m_strncasecmp(p, "attachment", strlen(p))) {
1298 listp = &AttachAllow;
1300 listp = &AttachExclude;
1302 else if (!m_strncasecmp(p, "inline", strlen(p))) {
1304 listp = &InlineAllow;
1306 listp = &InlineExclude;
1309 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
1313 return parse_unattach_list(buf, s, listp, err);
1316 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
1320 mutt_extract_token (buf, s, 0);
1321 remove_from_rx_list (&SubscribedLists, buf->data);
1322 remove_from_rx_list (&MailLists, buf->data);
1324 if (m_strcmp(buf->data, "*") &&
1325 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1328 while (MoreArgs (s));
1333 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1337 mutt_extract_token (buf, s, 0);
1338 remove_from_rx_list (&UnMailLists, buf->data);
1339 remove_from_rx_list (&UnSubscribedLists, buf->data);
1341 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1343 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1346 while (MoreArgs (s));
1351 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1355 mutt_extract_token (buf, s, 0);
1356 remove_from_rx_list (&SubscribedLists, buf->data);
1358 if (m_strcmp(buf->data, "*") &&
1359 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1362 while (MoreArgs (s));
1367 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
1370 ALIAS *tmp, *last = NULL;
1373 mutt_extract_token (buf, s, 0);
1375 if (m_strcmp("*", buf->data) == 0) {
1376 if (CurrentMenu == MENU_ALIAS) {
1377 for (tmp = Aliases; tmp; tmp = tmp->next)
1379 set_option (OPTFORCEREDRAWINDEX);
1382 mutt_free_alias (&Aliases);
1386 for (tmp = Aliases; tmp; tmp = tmp->next) {
1387 if (m_strcasecmp(buf->data, tmp->name) == 0) {
1388 if (CurrentMenu == MENU_ALIAS) {
1390 set_option (OPTFORCEREDRAWINDEX);
1395 last->next = tmp->next;
1397 Aliases = tmp->next;
1399 mutt_free_alias (&tmp);
1405 while (MoreArgs (s));
1409 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
1412 ALIAS *tmp = Aliases;
1416 if (!MoreArgs (s)) {
1417 m_strcpy(err->data, err->dsize, _("alias: no address"));
1421 mutt_extract_token (buf, s, 0);
1423 debug_print (2, ("first token is '%s'.\n", buf->data));
1425 /* check to see if an alias with this name already exists */
1426 for (; tmp; tmp = tmp->next) {
1427 if (!m_strcasecmp(tmp->name, buf->data))
1433 /* create a new alias */
1434 tmp = p_new(ALIAS, 1);
1436 tmp->name = m_strdup(buf->data);
1437 /* give the main addressbook code a chance */
1438 if (CurrentMenu == MENU_ALIAS)
1439 set_option (OPTMENUCALLER);
1442 /* override the previous value */
1443 address_delete (&tmp->addr);
1444 if (CurrentMenu == MENU_ALIAS)
1445 set_option (OPTFORCEREDRAWINDEX);
1448 mutt_extract_token (buf, s,
1449 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1450 debug_print (2, ("second token is '%s'.\n", buf->data));
1451 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1456 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1457 snprintf (err->data, err->dsize,
1458 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1462 if (DebugLevel >= 2) {
1465 /* A group is terminated with an empty address, so check a->mailbox */
1466 for (a = tmp->addr; a && a->mailbox; a = a->next) {
1468 debug_print (2, ("%s\n", a->mailbox));
1470 debug_print (2, ("group %s\n", a->mailbox));
1478 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
1481 LIST *tmp = UserHeader;
1486 mutt_extract_token (buf, s, 0);
1487 if (m_strcmp("*", buf->data) == 0)
1488 mutt_free_list (&UserHeader);
1493 l = m_strlen(buf->data);
1494 if (buf->data[l - 1] == ':')
1498 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1499 && tmp->data[l] == ':') {
1502 last->next = tmp->next;
1504 UserHeader = tmp->next;
1507 mutt_free_list (&ptr);
1516 while (MoreArgs (s));
1520 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
1527 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1528 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1529 m_strcpy(err->data, err->dsize, _("invalid header field"));
1532 keylen = p - buf->data + 1;
1535 for (tmp = UserHeader;; tmp = tmp->next) {
1536 /* see if there is already a field by this name */
1537 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1538 /* replace the old value */
1539 p_delete(&tmp->data);
1540 tmp->data = buf->data;
1547 tmp->next = mutt_new_list ();
1551 tmp = mutt_new_list ();
1554 tmp->data = buf->data;
1560 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1561 char* errbuf, size_t errlen) {
1564 if (m_strncmp("reverse-", s, 8) == 0) {
1566 flags = SORT_REVERSE;
1569 if (m_strncmp("last-", s, 5) == 0) {
1574 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1576 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1580 *((short*) dst->data) = i | flags;
1584 /* if additional data more == 1, we want to resolve synonyms */
1585 static void mutt_set_default(const char *name, void* p, unsigned long more)
1587 char buf[LONG_STRING];
1588 struct option_t *ptr = p;
1590 if (DTYPE(ptr->type) == DT_SYN) {
1593 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1595 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_from_string)
1598 mutt_option_value(ptr->option, buf, sizeof(buf));
1599 if (m_strlen(ptr->init) == 0 && buf && *buf)
1600 ptr->init = m_strdup(buf);
1603 static struct option_t* add_option (const char* name, const char* init,
1604 short type, short dodup) {
1605 struct option_t* option = p_new(struct option_t, 1);
1607 debug_print (1, ("adding $%s\n", name));
1609 option->option = m_strdup(name);
1610 option->type = type;
1612 option->init = dodup ? m_strdup(init) : (char*) init;
1616 /* creates new option_t* of type DT_USER for $user_ var */
1617 static struct option_t* add_user_option (const char* name) {
1618 return (add_option (name, NULL, DT_USER, 1));
1621 /* free()'s option_t* */
1622 static void del_option (void* p) {
1623 struct option_t *ptr = (struct option_t*) p;
1624 char* s = (char*) ptr->data;
1625 debug_print (1, ("removing option '%s' from table\n", NONULL (ptr->option)));
1626 p_delete(&ptr->option);
1628 p_delete(&ptr->init);
1632 static int init_expand (char** dst, struct option_t* src) {
1638 if (DTYPE(src->type) == DT_STR ||
1639 DTYPE(src->type) == DT_PATH) {
1640 /* only expand for string as it's the only place where
1641 * we want to expand vars right now */
1642 if (src->init && *src->init) {
1645 len = m_strlen(src->init) + 2;
1646 in.data = p_new(char, len + 1);
1647 snprintf (in.data, len, "\"%s\"", src->init);
1650 mutt_extract_token (&token, &in, 0);
1651 if (token.data && *token.data)
1652 *dst = m_strdup(token.data);
1654 *dst = m_strdup("");
1656 p_delete(&token.data);
1658 *dst = m_strdup("");
1660 /* for non-string: take value as is */
1661 *dst = m_strdup(src->init);
1665 /* if additional data more == 1, we want to resolve synonyms */
1666 static void mutt_restore_default (const char* name, void* p,
1667 unsigned long more) {
1668 char errbuf[STRING];
1669 struct option_t* ptr = (struct option_t*) p;
1672 if (DTYPE (ptr->type) == DT_SYN) {
1675 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1679 if (FuncTable[DTYPE (ptr->type)].opt_from_string) {
1680 init_expand (&init, ptr);
1681 if (!FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, init, errbuf,
1683 if (!option (OPTNOCURSES))
1685 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1686 "Please report this error: \"%s\"\n"),
1687 ptr->option, NONULL (init), errbuf);
1693 if (ptr->flags & R_INDEX)
1694 set_option (OPTFORCEREDRAWINDEX);
1695 if (ptr->flags & R_PAGER)
1696 set_option (OPTFORCEREDRAWPAGER);
1697 if (ptr->flags & R_RESORT_SUB)
1698 set_option (OPTSORTSUBTHREADS);
1699 if (ptr->flags & R_RESORT)
1700 set_option (OPTNEEDRESORT);
1701 if (ptr->flags & R_RESORT_INIT)
1702 set_option (OPTRESORTINIT);
1703 if (ptr->flags & R_TREE)
1704 set_option (OPTREDRAWTREE);
1707 /* check whether value for $dsn_return would be valid */
1708 static int check_dsn_return (const char* option, unsigned long p,
1709 char* errbuf, size_t errlen) {
1710 char* val = (char*) p;
1711 if (val && *val && m_strncmp(val, "hdrs", 4) != 0 &&
1712 m_strncmp(val, "full", 4) != 0) {
1714 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1720 /* check whether value for $dsn_notify would be valid */
1721 static int check_dsn_notify (const char* option, unsigned long p,
1722 char* errbuf, size_t errlen) {
1723 list2_t* list = NULL;
1725 char* val = (char*) p;
1729 list = list_from_str (val, ",");
1730 if (list_empty (list))
1733 for (i = 0; i < list->length; i++)
1734 if (m_strncmp(list->data[i], "never", 5) != 0 &&
1735 m_strncmp(list->data[i], "failure", 7) != 0 &&
1736 m_strncmp(list->data[i], "delay", 5) != 0 &&
1737 m_strncmp(list->data[i], "success", 7) != 0) {
1739 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1740 (char*) list->data[i], "dsn_notify");
1744 list_del (&list, (list_del_t*)xmemfree);
1748 static int check_num (const char* option, unsigned long p,
1749 char* errbuf, size_t errlen) {
1752 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1759 static int check_debug (const char* option, unsigned long p,
1760 char* errbuf, size_t errlen) {
1761 if ((int) p <= DEBUG_MAX_LEVEL &&
1762 (int) p >= DEBUG_MIN_LEVEL)
1766 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1771 static int check_history (const char* option, unsigned long p,
1772 char* errbuf, size_t errlen) {
1773 if (!check_num ("history", p, errbuf, errlen))
1775 mutt_init_history ();
1779 static int check_special (const char* name, unsigned long val,
1780 char* errbuf, size_t errlen) {
1783 for (i = 0; SpecialVars[i].name; i++) {
1784 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1785 return (SpecialVars[i].check (SpecialVars[i].name,
1786 val, errbuf, errlen));
1792 static const struct mapping_t* get_sortmap (struct option_t* option) {
1793 const struct mapping_t* map = NULL;
1795 switch (option->type & DT_SUBTYPE_MASK) {
1797 map = SortAliasMethods;
1799 case DT_SORT_BROWSER:
1800 map = SortBrowserMethods;
1803 if ((WithCrypto & APPLICATION_PGP))
1804 map = SortKeyMethods;
1807 map = SortAuxMethods;
1816 #define CHECK_PAGER \
1817 if ((CurrentMenu == MENU_PAGER) && \
1818 (!option || (option->flags & R_RESORT))) \
1820 snprintf (err->data, err->dsize, \
1821 _("Not available in this menu.")); \
1825 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1828 int query, unset, inv, reset, r = 0;
1829 struct option_t* option = NULL;
1831 while (MoreArgs (s)) {
1832 /* reset state variables */
1834 unset = data & M_SET_UNSET;
1835 inv = data & M_SET_INV;
1836 reset = data & M_SET_RESET;
1838 if (*s->dptr == '?') {
1842 else if (m_strncmp("no", s->dptr, 2) == 0) {
1846 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1850 else if (*s->dptr == '&') {
1855 /* get the variable name */
1856 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1858 /* resolve synonyms */
1859 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1860 DTYPE (option->type == DT_SYN)) {
1861 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1862 syn_add (newopt, option);
1866 /* see if we need to add $user_ var */
1867 if (!option && ascii_strncmp ("user_", tmp->data, 5) == 0) {
1868 /* there's no option named like this yet so only add one
1869 * if the action isn't any of: reset, unset, query */
1870 if (!(reset || unset || query || *s->dptr != '=')) {
1871 debug_print (1, ("adding user option '%s'\n", tmp->data));
1872 option = add_user_option (tmp->data);
1873 hash_insert (ConfigOptions, option->option, option, 0);
1877 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1878 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1881 s->dptr = vskipspaces(s->dptr);
1884 if (query || unset || inv) {
1885 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1889 if (s && *s->dptr == '=') {
1890 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1894 if (!m_strcmp("all", tmp->data)) {
1895 if (CurrentMenu == MENU_PAGER) {
1896 snprintf (err->data, err->dsize, _("Not available in this menu."));
1899 hash_map (ConfigOptions, mutt_restore_default, 1);
1900 set_option (OPTFORCEREDRAWINDEX);
1901 set_option (OPTFORCEREDRAWPAGER);
1902 set_option (OPTSORTSUBTHREADS);
1903 set_option (OPTNEEDRESORT);
1904 set_option (OPTRESORTINIT);
1905 set_option (OPTREDRAWTREE);
1908 else if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1909 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1914 mutt_restore_default (NULL, option, 1);
1917 else if (DTYPE (option->type) == DT_BOOL) {
1918 /* XXX this currently ignores the function table
1919 * as we don't get invert and stuff into it */
1920 if (s && *s->dptr == '=') {
1921 if (unset || inv || query) {
1922 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1927 mutt_extract_token (tmp, s, 0);
1928 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1930 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1933 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1939 bool_to_string (err->data, err->dsize, option);
1945 unset_option (option->data);
1947 toggle_option (option->data);
1949 set_option (option->data);
1951 else if (DTYPE (option->type) == DT_STR ||
1952 DTYPE (option->type) == DT_PATH ||
1953 DTYPE (option->type) == DT_ADDR ||
1954 DTYPE (option->type) == DT_MAGIC ||
1955 DTYPE (option->type) == DT_NUM ||
1956 DTYPE (option->type) == DT_SORT ||
1957 DTYPE (option->type) == DT_RX ||
1958 DTYPE (option->type) == DT_USER ||
1959 DTYPE (option->type) == DT_SYS) {
1961 /* XXX maybe we need to get unset into handlers? */
1962 if (DTYPE (option->type) == DT_STR ||
1963 DTYPE (option->type) == DT_PATH ||
1964 DTYPE (option->type) == DT_ADDR ||
1965 DTYPE (option->type) == DT_USER ||
1966 DTYPE (option->type) == DT_SYS) {
1969 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1970 snprintf (err->data, err->dsize, _("$%s is read-only"),
1974 } else if (DTYPE (option->type) == DT_ADDR)
1975 address_delete ((address_t **) option->data);
1976 else if (DTYPE (option->type) == DT_USER)
1977 /* to unset $user_ means remove */
1978 hash_delete (ConfigOptions, option->option,
1979 option, del_option);
1981 p_delete((void **)&option->data);
1986 if (query || *s->dptr != '=') {
1987 FuncTable[DTYPE (option->type)].opt_to_string
1988 (err->data, err->dsize, option);
1992 /* the $muttng_ variables are read-only */
1993 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1994 snprintf (err->data, err->dsize, _("$%s is read-only"),
2001 mutt_extract_token (tmp, s, 0);
2002 if (!FuncTable[DTYPE (option->type)].opt_from_string
2003 (option, tmp->data, err->data, err->dsize))
2007 else if (DTYPE (option->type) == DT_QUAD) {
2010 quad_to_string (err->data, err->dsize, option);
2014 if (*s->dptr == '=') {
2017 mutt_extract_token (tmp, s, 0);
2018 if (ascii_strcasecmp ("yes", tmp->data) == 0)
2019 set_quadoption (option->data, M_YES);
2020 else if (ascii_strcasecmp ("no", tmp->data) == 0)
2021 set_quadoption (option->data, M_NO);
2022 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
2023 set_quadoption (option->data, M_ASKYES);
2024 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
2025 set_quadoption (option->data, M_ASKNO);
2027 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
2028 tmp->data, option->option);
2035 toggle_quadoption (option->data);
2037 set_quadoption (option->data, M_NO);
2039 set_quadoption (option->data, M_YES);
2043 snprintf (err->data, err->dsize, _("%s: unknown type"),
2049 if (option->flags & R_INDEX)
2050 set_option (OPTFORCEREDRAWINDEX);
2051 if (option->flags & R_PAGER)
2052 set_option (OPTFORCEREDRAWPAGER);
2053 if (option->flags & R_RESORT_SUB)
2054 set_option (OPTSORTSUBTHREADS);
2055 if (option->flags & R_RESORT)
2056 set_option (OPTNEEDRESORT);
2057 if (option->flags & R_RESORT_INIT)
2058 set_option (OPTRESORTINIT);
2059 if (option->flags & R_TREE)
2060 set_option (OPTREDRAWTREE);
2067 /* reads the specified initialization file. returns -1 if errors were found
2068 so that we can pause to let the user know... */
2069 static int source_rc (const char *rcfile, BUFFER * err)
2072 int line = 0, rc = 0, conv = 0;
2074 char *linebuf = NULL;
2075 char *currentline = NULL;
2079 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
2081 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
2082 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
2087 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
2088 conv = ConfigCharset && (*ConfigCharset) && Charset;
2090 currentline = m_strdup(linebuf);
2093 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
2096 currentline = linebuf;
2101 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
2102 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
2103 if (--rc < -MAXERRS) {
2105 p_delete(¤tline);
2114 p_delete(¤tline);
2116 p_delete(&token.data);
2120 mutt_wait_filter (pid);
2122 /* the muttrc source keyword */
2123 snprintf (err->data, err->dsize,
2124 rc >= -MAXERRS ? _("source: errors in %s")
2125 : _("source: reading aborted due too many errors in %s"),
2134 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
2137 char path[_POSIX_PATH_MAX];
2141 if (mutt_extract_token (tmp, s, 0) != 0) {
2142 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
2146 m_strcpy(path, sizeof(path), tmp->data);
2147 mutt_expand_path (path, sizeof(path));
2149 rc += source_rc (path, err);
2151 while (MoreArgs (s));
2153 return ((rc < 0) ? -1 : 0);
2156 /* line command to execute
2158 token scratch buffer to be used by parser. caller should free
2159 token->data when finished. the reason for this variable is
2160 to avoid having to allocate and deallocate a lot of memory
2161 if we are parsing many lines. the caller can pass in the
2162 memory to use, which avoids having to create new space for
2163 every call to this function.
2165 err where to write error messages */
2166 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
2172 expn.data = expn.dptr = line;
2173 expn.dsize = m_strlen(line);
2177 debug_print (1, ("expand '%s'\n", line));
2179 expn.dptr = vskipspaces(expn.dptr);
2180 while (*expn.dptr) {
2181 if (*expn.dptr == '#')
2182 break; /* rest of line is a comment */
2183 if (*expn.dptr == ';') {
2187 mutt_extract_token (token, &expn, 0);
2188 for (i = 0; Commands[i].name; i++) {
2189 if (!m_strcmp(token->data, Commands[i].name)) {
2190 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
2195 if (!Commands[i].name) {
2196 snprintf (err->data, err->dsize, _("%s: unknown command"),
2197 NONULL (token->data));
2204 p_delete(&expn.data);
2209 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
2210 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
2211 /* initial string that starts completion. No telling how much crap
2212 * the user has typed so far. Allocate LONG_STRING just to be sure! */
2213 char User_typed[LONG_STRING] = { 0 };
2215 int Num_matched = 0; /* Number of matches for completion */
2216 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
2217 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
2219 /* helper function for completion. Changes the dest buffer if
2220 necessary/possible to aid completion.
2221 dest == completion result gets here.
2222 src == candidate for completion.
2223 try == user entered data for completion.
2224 len == length of dest buffer.
2226 static void candidate (char *dest, char *try, const char *src, int len)
2230 if (strstr (src, try) == src) {
2231 Matches[Num_matched++] = src;
2233 m_strcpy(dest, len, src);
2235 for (l = 0; src[l] && src[l] == dest[l]; l++);
2241 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
2245 int spaces; /* keep track of the number of leading spaces on the line */
2247 buffer = vskipspaces(buffer);
2248 spaces = buffer - pt;
2250 pt = buffer + pos - spaces;
2251 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2254 if (pt == buffer) { /* complete cmd */
2255 /* first TAB. Collect all the matches */
2258 m_strcpy(User_typed, sizeof(User_typed), pt);
2259 p_clear(Matches, sizeof(Matches));
2260 p_clear(Completed, sizeof(Completed));
2261 for (num = 0; Commands[num].name; num++)
2262 candidate (Completed, User_typed, Commands[num].name,
2264 Matches[Num_matched++] = User_typed;
2266 /* All matches are stored. Longest non-ambiguous string is ""
2267 * i.e. dont change 'buffer'. Fake successful return this time */
2268 if (User_typed[0] == 0)
2272 if (Completed[0] == 0 && User_typed[0])
2275 /* Num_matched will _always_ be atleast 1 since the initial
2276 * user-typed string is always stored */
2277 if (numtabs == 1 && Num_matched == 2)
2278 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2279 else if (numtabs > 1 && Num_matched > 2)
2280 /* cycle thru all the matches */
2281 snprintf (Completed, sizeof(Completed), "%s",
2282 Matches[(numtabs - 2) % Num_matched]);
2284 /* return the completed command */
2285 m_strcpy(buffer, len - spaces, Completed);
2287 else if (!m_strncmp(buffer, "set", 3)
2288 || !m_strncmp(buffer, "unset", 5)
2289 || !m_strncmp(buffer, "reset", 5)
2290 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
2291 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
2294 /* loop through all the possible prefixes (no, inv, ...) */
2295 if (!m_strncmp(buffer, "set", 3)) {
2296 for (num = 0; prefixes[num]; num++) {
2297 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
2298 pt += m_strlen(prefixes[num]);
2304 /* first TAB. Collect all the matches */
2307 m_strcpy(User_typed, sizeof(User_typed), pt);
2308 p_clear(Matches, sizeof(Matches));
2309 p_clear(Completed, sizeof(Completed));
2310 for (num = 0; MuttVars[num].option; num++)
2311 candidate(Completed, User_typed, MuttVars[num].option,
2313 Matches[Num_matched++] = User_typed;
2315 /* All matches are stored. Longest non-ambiguous string is ""
2316 * i.e. dont change 'buffer'. Fake successful return this time */
2317 if (User_typed[0] == 0)
2321 if (Completed[0] == 0 && User_typed[0])
2324 /* Num_matched will _always_ be atleast 1 since the initial
2325 * user-typed string is always stored */
2326 if (numtabs == 1 && Num_matched == 2)
2327 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2328 else if (numtabs > 1 && Num_matched > 2)
2329 /* cycle thru all the matches */
2330 snprintf (Completed, sizeof(Completed), "%s",
2331 Matches[(numtabs - 2) % Num_matched]);
2333 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2335 else if (!m_strncmp(buffer, "exec", 4)) {
2336 struct binding_t *menu = km_get_table (CurrentMenu);
2338 if (!menu && CurrentMenu != MENU_PAGER)
2342 /* first TAB. Collect all the matches */
2345 m_strcpy(User_typed, sizeof(User_typed), pt);
2346 p_clear(Matches, sizeof(Matches));
2347 p_clear(Completed, sizeof(Completed));
2348 for (num = 0; menu[num].name; num++)
2349 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
2350 /* try the generic menu */
2351 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2353 for (num = 0; menu[num].name; num++)
2354 candidate (Completed, User_typed, menu[num].name,
2357 Matches[Num_matched++] = User_typed;
2359 /* All matches are stored. Longest non-ambiguous string is ""
2360 * i.e. dont change 'buffer'. Fake successful return this time */
2361 if (User_typed[0] == 0)
2365 if (Completed[0] == 0 && User_typed[0])
2368 /* Num_matched will _always_ be atleast 1 since the initial
2369 * user-typed string is always stored */
2370 if (numtabs == 1 && Num_matched == 2)
2371 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2372 else if (numtabs > 1 && Num_matched > 2)
2373 /* cycle thru all the matches */
2374 snprintf (Completed, sizeof(Completed), "%s",
2375 Matches[(numtabs - 2) % Num_matched]);
2377 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2385 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2387 char var[STRING], *pt = buffer;
2389 struct option_t* option = NULL;
2394 buffer = vskipspaces(buffer);
2395 spaces = buffer - pt;
2397 pt = buffer + pos - spaces;
2398 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2400 pt++; /* move past the space */
2401 if (*pt == '=') /* abort if no var before the '=' */
2404 if (m_strncmp(buffer, "set", 3) == 0) {
2405 m_strcpy(var, sizeof(var), pt);
2406 /* ignore the trailing '=' when comparing */
2407 var[m_strlen(var) - 1] = 0;
2408 if (!(option = hash_find (ConfigOptions, var)))
2409 return 0; /* no such variable. */
2411 char tmp[LONG_STRING], tmp2[LONG_STRING];
2413 size_t dlen = buffer + len - pt - spaces;
2414 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2418 if ((DTYPE (option->type) == DT_STR) ||
2419 (DTYPE (option->type) == DT_PATH) ||
2420 (DTYPE (option->type) == DT_RX)) {
2421 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
2422 if (DTYPE (option->type) == DT_PATH)
2423 mutt_pretty_mailbox (tmp);
2425 else if (DTYPE (option->type) == DT_ADDR) {
2426 rfc822_write_address (tmp, sizeof(tmp),
2427 *((address_t **) option->data), 0);
2429 else if (DTYPE (option->type) == DT_QUAD)
2430 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
2431 else if (DTYPE (option->type) == DT_NUM)
2432 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
2433 else if (DTYPE (option->type) == DT_SORT) {
2434 const struct mapping_t *map;
2437 switch (option->type & DT_SUBTYPE_MASK) {
2439 map = SortAliasMethods;
2441 case DT_SORT_BROWSER:
2442 map = SortBrowserMethods;
2445 if ((WithCrypto & APPLICATION_PGP))
2446 map = SortKeyMethods;
2454 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2455 snprintf(tmp, sizeof(tmp), "%s%s%s",
2456 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2457 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2459 else if (DTYPE (option->type) == DT_MAGIC) {
2461 switch (DefaultMagic) {
2477 m_strcpy(tmp, sizeof(tmp), p);
2479 else if (DTYPE (option->type) == DT_BOOL)
2480 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
2484 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof(tmp2) - 2;) {
2485 if (*s == '\\' || *s == '"')
2491 m_strcpy(tmp, sizeof(tmp), pt);
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];
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 static int mutt_execute_commands (LIST * p)
2533 char errstr[SHORT_STRING];
2537 err.dsize = sizeof(errstr);
2539 for (; p; p = p->next) {
2540 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2541 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2542 p_delete(&token.data);
2546 p_delete(&token.data);
2550 void mutt_init (int skip_sys_rc, LIST * commands)
2553 struct utsname utsname;
2555 char buffer[STRING], error[STRING];
2556 int i, default_rc = 0, need_pause = 0;
2561 err.dsize = sizeof(error);
2563 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2564 * to have some room for $user_ vars */
2565 ConfigOptions = hash_create (sizeof(MuttVars) * 3);
2566 for (i = 0; MuttVars[i].option; i++) {
2567 if (DTYPE (MuttVars[i].type) != DT_SYS)
2568 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2570 hash_insert (ConfigOptions, MuttVars[i].option,
2571 add_option (MuttVars[i].option, MuttVars[i].init,
2576 * XXX - use something even more difficult to predict?
2578 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2579 "\033]9;%ld\a", (long) time (NULL));
2581 /* on one of the systems I use, getcwd() does not return the same prefix
2582 as is listed in the passwd file */
2583 if ((p = getenv ("HOME")))
2584 Homedir = m_strdup(p);
2586 /* Get some information about the user */
2587 if ((pw = getpwuid (getuid ()))) {
2590 Username = m_strdup(pw->pw_name);
2592 Homedir = m_strdup(pw->pw_dir);
2594 Realname = m_strdup(mutt_gecos_name (rnbuf, sizeof(rnbuf), pw));
2595 Shell = m_strdup(pw->pw_shell);
2601 fputs (_("unable to determine home directory"), stderr);
2604 if ((p = getenv ("USER")))
2605 Username = m_strdup(p);
2608 fputs (_("unable to determine username"), stderr);
2611 Shell = m_strdup((p = getenv ("SHELL")) ? p : "/bin/sh");
2614 debug_start(Homedir);
2616 /* And about the host... */
2618 /* some systems report the FQDN instead of just the hostname */
2619 if ((p = strchr (utsname.nodename, '.'))) {
2620 Hostname = str_substrdup (utsname.nodename, p);
2622 m_strcpy(buffer, sizeof(buffer), p); /* save the domain for below */
2625 Hostname = m_strdup(utsname.nodename);
2628 #define DOMAIN buffer
2629 if (!p && getdnsdomainname (buffer, sizeof(buffer)) == -1)
2630 Fqdn = m_strdup("@");
2633 if (*DOMAIN != '@') {
2634 Fqdn = p_new(char, m_strlen(DOMAIN) + m_strlen(Hostname) + 2);
2635 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2638 Fqdn = m_strdup(NONULL (Hostname));
2645 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2647 fgets (buffer, sizeof(buffer), f);
2648 p = vskipspaces(buffer);
2650 while (*q && !isspace(*q))
2653 NewsServer = m_strdup(p);
2657 if ((p = getenv ("NNTPSERVER")))
2658 NewsServer = m_strdup(p);
2661 if ((p = getenv ("MAIL")))
2662 Spoolfile = m_strdup(p);
2663 else if ((p = getenv ("MAILDIR")))
2664 Spoolfile = m_strdup(p);
2667 mutt_concat_path(buffer, sizeof(buffer), NONULL(Homedir), MAILPATH);
2669 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(Username));
2671 Spoolfile = m_strdup(buffer);
2674 if ((p = getenv ("MAILCAPS")))
2675 MailcapPath = m_strdup(p);
2677 /* Default search path from RFC1524 */
2679 m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2680 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2683 Tempdir = m_strdup((p = getenv ("TMPDIR")) ? p : "/tmp");
2685 p = getenv ("VISUAL");
2687 p = getenv ("EDITOR");
2691 Editor = m_strdup(p);
2692 Visual = m_strdup(p);
2694 if ((p = getenv ("REPLYTO")) != NULL) {
2697 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2700 buf.data = buf.dptr = buffer;
2701 buf.dsize = m_strlen(buffer);
2704 parse_my_hdr (&token, &buf, 0, &err);
2705 p_delete(&token.data);
2708 if ((p = getenv ("EMAIL")) != NULL)
2709 From = rfc822_parse_adrlist (NULL, p);
2711 mutt_set_langinfo_charset ();
2712 mutt_set_charset (Charset);
2715 /* Set standard defaults */
2716 hash_map (ConfigOptions, mutt_set_default, 0);
2717 hash_map (ConfigOptions, mutt_restore_default, 0);
2719 CurrentMenu = MENU_MAIN;
2722 #ifndef LOCALES_HACK
2723 /* Do we have a locale definition? */
2724 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2725 ((p = getenv ("LANG")) != NULL && p[0]) ||
2726 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2727 set_option (OPTLOCALES);
2731 /* Unset suspend by default if we're the session leader */
2732 if (getsid (0) == getpid ())
2733 unset_option (OPTSUSPEND);
2736 mutt_init_history ();
2745 * When changing the code which looks for a configuration file,
2746 * please also change the corresponding code in muttbug.sh.in.
2756 snprintf (buffer, sizeof(buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2758 if (access (buffer, F_OK) == -1)
2760 snprintf (buffer, sizeof(buffer), "%s/.muttngrc", NONULL (Homedir));
2761 if (access (buffer, F_OK) == -1)
2763 snprintf (buffer, sizeof(buffer), "%s/.muttng/muttngrc-%s",
2764 NONULL (Homedir), MUTT_VERSION);
2765 if (access (buffer, F_OK) == -1)
2767 snprintf (buffer, sizeof(buffer), "%s/.muttng/muttngrc",
2771 Muttrc = m_strdup(buffer);
2774 m_strcpy(buffer, sizeof(buffer), Muttrc);
2776 mutt_expand_path (buffer, sizeof(buffer));
2777 Muttrc = m_strdup(buffer);
2779 p_delete(&AliasFile);
2780 AliasFile = m_strdup(NONULL (Muttrc));
2782 /* Process the global rc file if it exists and the user hasn't explicity
2783 requested not to via "-n". */
2785 snprintf (buffer, sizeof(buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2787 if (access (buffer, F_OK) == -1)
2788 snprintf (buffer, sizeof(buffer), "%s/Muttngrc", SYSCONFDIR);
2789 if (access (buffer, F_OK) == -1)
2790 snprintf (buffer, sizeof(buffer), "%s/Muttngrc-%s", PKGDATADIR,
2792 if (access (buffer, F_OK) == -1)
2793 snprintf (buffer, sizeof(buffer), "%s/Muttngrc", PKGDATADIR);
2794 if (access (buffer, F_OK) != -1) {
2795 if (source_rc (buffer, &err) != 0) {
2796 fputs (err.data, stderr);
2797 fputc ('\n', stderr);
2803 /* Read the user's initialization file. */
2804 if (access (Muttrc, F_OK) != -1) {
2805 if (!option (OPTNOCURSES))
2807 if (source_rc (Muttrc, &err) != 0) {
2808 fputs (err.data, stderr);
2809 fputc ('\n', stderr);
2813 else if (!default_rc) {
2814 /* file specified by -F does not exist */
2815 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2816 mutt_endwin (buffer);
2820 if (mutt_execute_commands (commands) != 0)
2823 /* warn about synonym variables */
2824 if (!list_empty(Synonyms)) {
2826 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2827 for (i = 0; i < Synonyms->length; i++) {
2828 struct option_t* newopt = NULL, *oldopt = NULL;
2829 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2830 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2831 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2832 oldopt ? NONULL (oldopt->option) : "",
2833 newopt ? NONULL (newopt->option) : "",
2834 NONULL(((syn_t*) Synonyms->data[i])->f),
2835 ((syn_t*) Synonyms->data[i])->l);
2837 fprintf (stderr, _("Warning: synonym variables are scheduled"
2838 " for removal.\n"));
2839 list_del (&Synonyms, syn_del);
2843 if (need_pause && !option (OPTNOCURSES)) {
2844 if (mutt_any_key_to_continue (NULL) == -1)
2849 set_option (OPTWEED); /* turn weeding on by default */
2853 int mutt_get_hook_type (const char *name)
2855 struct command_t *c;
2857 for (c = Commands; c->name; c++)
2858 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2863 /* compare two option_t*'s for sorting -t/-T output */
2864 static int opt_cmp (const void* a, const void* b) {
2865 return (m_strcmp((*(struct option_t**) a)->option,
2866 (*(struct option_t**) b)->option));
2869 /* callback for hash_map() to put all non-synonym vars into list */
2870 static void opt_sel_full (const char* key, void* data,
2871 unsigned long more) {
2872 list2_t** l = (list2_t**) more;
2873 struct option_t* option = (struct option_t*) data;
2875 if (DTYPE (option->type) == DT_SYN)
2877 list_push_back (l, option);
2880 /* callback for hash_map() to put all changed non-synonym vars into list */
2881 static void opt_sel_diff (const char* key, void* data,
2882 unsigned long more) {
2883 list2_t** l = (list2_t**) more;
2884 struct option_t* option = (struct option_t*) data;
2885 char buf[LONG_STRING];
2887 if (DTYPE (option->type) == DT_SYN)
2890 mutt_option_value (option->option, buf, sizeof(buf));
2891 if (m_strcmp(buf, option->init) != 0)
2892 list_push_back (l, option);
2895 /* dump out the value of all the variables we have */
2896 int mutt_dump_variables (int full) {
2898 char outbuf[STRING];
2899 list2_t* tmp = NULL;
2900 struct option_t* option = NULL;
2902 /* get all non-synonyms into list... */
2903 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2904 (unsigned long) &tmp);
2906 if (!list_empty(tmp)) {
2907 /* ...and dump list sorted */
2908 qsort (tmp->data, tmp->length, sizeof(void*), opt_cmp);
2909 for (i = 0; i < tmp->length; i++) {
2910 option = (struct option_t*) tmp->data[i];
2911 FuncTable[DTYPE (option->type)].opt_to_string
2912 (outbuf, sizeof(outbuf), option);
2913 printf ("%s\n", outbuf);
2916 list_del (&tmp, NULL);