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.
13 #include <lib-lib/lib-lib.h>
14 #include <sys/utsname.h>
16 #include <lib-sys/unix.h>
17 #include <lib-sys/mutt_ssl.h>
19 #include <lib-ui/curses.h>
20 #include <lib-ui/history.h>
26 #include <lib-crypt/crypt.h>
27 #include "mutt_idna.h"
29 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
30 #include "mutt_libesmtp.h"
42 static const struct mapping_t* get_sortmap (struct option_t* option);
43 static int parse_sort (struct option_t* dst, const char *s,
44 const struct mapping_t *map,
45 char* errbuf, ssize_t errlen);
47 static HASH *ConfigOptions = NULL;
49 /* for synonym warning reports: synonym found during parsing */
53 struct option_t* n; /* new */
54 struct option_t* o; /* old */
57 /* for synonym warning reports: list of synonyms found */
58 static list2_t* Synonyms;
59 /* for synonym warning reports: current rc file */
60 static const char* CurRCFile = NULL;
61 /* for synonym warning reports: current rc line */
62 static int CurRCLine = 0;
64 /* prototypes for checking for special vars */
65 static int check_dsn_return (const char* option, unsigned long val,
66 char* errbuf, ssize_t errlen);
67 static int check_dsn_notify (const char* option, unsigned long val,
68 char* errbuf, ssize_t errlen);
69 static int check_history (const char* option, unsigned long val,
70 char* errbuf, ssize_t errlen);
71 /* this checks that numbers are >= 0 */
72 static int check_num (const char* option, unsigned long val,
73 char* errbuf, ssize_t errlen);
75 /* use this to check only */
76 static int check_special (const char* option, unsigned long val,
77 char* errbuf, ssize_t errlen);
79 /* variable <-> sanity check function mappings
80 * when changing these, make sure the proper _from_string handler
85 int (*check) (const char* option, unsigned long val,
86 char* errbuf, ssize_t errlen);
88 { "dsn_notify", check_dsn_notify },
89 { "dsn_return", check_dsn_return },
90 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
91 { "smtp_use_tls", mutt_libesmtp_check_usetls },
93 { "history", check_history },
94 { "pager_index_lines", check_num },
99 /* protos for config type handles: convert value to string */
100 static void bool_to_string (char* dst, ssize_t dstlen, struct option_t* option);
101 static void num_to_string (char* dst, ssize_t dstlen, struct option_t* option);
102 static void str_to_string (char* dst, ssize_t dstlen, struct option_t* option);
103 static void quad_to_string (char* dst, ssize_t dstlen, struct option_t* option);
104 static void sort_to_string (char* dst, ssize_t dstlen, struct option_t* option);
105 static void rx_to_string (char* dst, ssize_t dstlen, struct option_t* option);
106 static void magic_to_string (char* dst, ssize_t dstlen, struct option_t* option);
107 static void addr_to_string (char* dst, ssize_t dstlen, struct option_t* option);
108 static void user_to_string (char* dst, ssize_t dstlen, struct option_t* option);
109 static void sys_to_string (char* dst, ssize_t dstlen, struct option_t* option);
111 /* protos for config type handles: convert to value from string */
112 static int bool_from_string (struct option_t* dst, const char* val,
113 char* errbuf, ssize_t errlen);
114 static int num_from_string (struct option_t* dst, const char* val,
115 char* errbuf, ssize_t errlen);
116 static int str_from_string (struct option_t* dst, const char* val,
117 char* errbuf, ssize_t errlen);
118 static int path_from_string (struct option_t* dst, const char* val,
119 char* errbuf, ssize_t errlen);
120 static int quad_from_string (struct option_t* dst, const char* val,
121 char* errbuf, ssize_t errlen);
122 static int sort_from_string (struct option_t* dst, const char* val,
123 char* errbuf, ssize_t errlen);
124 static int rx_from_string (struct option_t* dst, const char* val,
125 char* errbuf, ssize_t errlen);
126 static int magic_from_string (struct option_t* dst, const char* val,
127 char* errbuf, ssize_t errlen);
128 static int addr_from_string (struct option_t* dst, const char* val,
129 char* errbuf, ssize_t errlen);
130 static int user_from_string (struct option_t* dst, const char* val,
131 char* errbuf, ssize_t errlen);
135 void (*opt_to_string) (char* dst, ssize_t dstlen, struct option_t* option);
136 int (*opt_from_string) (struct option_t* dst, const char* val,
137 char* errbuf, ssize_t errlen);
139 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
140 { DT_BOOL, bool_to_string, bool_from_string },
141 { DT_NUM, num_to_string, num_from_string },
142 { DT_STR, str_to_string, str_from_string },
143 { DT_PATH, str_to_string, path_from_string },
144 { DT_QUAD, quad_to_string, quad_from_string },
145 { DT_SORT, sort_to_string, sort_from_string },
146 { DT_RX, rx_to_string, rx_from_string },
147 { DT_MAGIC, magic_to_string, magic_from_string },
148 /* synonyms should be resolved already so we don't need this
149 * but must define it as DT_ is used for indexing */
150 { DT_SYN, NULL, NULL },
151 { DT_ADDR, addr_to_string, addr_from_string },
152 { DT_USER, user_to_string, user_from_string },
153 { DT_SYS, sys_to_string, NULL },
156 static void bool_to_string (char* dst, ssize_t dstlen,
157 struct option_t* option) {
158 snprintf (dst, dstlen, "%s=%s", option->option,
159 option (option->data) ? "yes" : "no");
162 static int bool_from_string (struct option_t* dst, const char* val,
163 char* errbuf __attribute__ ((unused)),
164 ssize_t errlen __attribute__ ((unused))) {
169 if (ascii_strncasecmp (val, "yes", 3) == 0)
171 else if (ascii_strncasecmp (val, "no", 2) == 0)
177 set_option (dst->data);
179 unset_option (dst->data);
183 static void num_to_string (char* dst, ssize_t dstlen,
184 struct option_t* option) {
186 const char* fmt = (m_strcmp(option->option, "umask") == 0) ?
188 snprintf (dst, dstlen, fmt, option->option,
189 *((short*) option->data));
192 static int num_from_string (struct option_t* dst, const char* val,
193 char* errbuf, ssize_t errlen) {
194 int num = 0, old = 0;
200 num = strtol (val, &t, 0);
202 if (!*val || *t || (short) num != num) {
204 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
210 /* just temporarily accept new val so that check_special for
211 * $history already has it when doing history's init() */
212 old = *((short*) dst->data);
213 *((short*) dst->data) = (short) num;
215 if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
216 *((short*) dst->data) = old;
223 static void str_to_string (char* dst, ssize_t dstlen,
224 struct option_t* option) {
225 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
226 NONULL (*((char**) option->data)));
229 static void user_to_string (char* dst, ssize_t dstlen,
230 struct option_t* option) {
231 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
232 NONULL (((char*) option->data)));
235 static void sys_to_string (char* dst, ssize_t dstlen,
236 struct option_t* option) {
237 char *val = NULL, *t = NULL;
240 /* get some $madmutt_ values dynamically */
241 if (m_strcmp("madmutt_pwd", option->option) == 0) {
242 val = p_new(char, _POSIX_PATH_MAX);
243 val = getcwd (val, _POSIX_PATH_MAX-1);
245 } else if (m_strcmp("madmutt_folder_path", option->option) == 0 &&
246 CurrentFolder && *CurrentFolder) {
248 } else if (m_strcmp("madmutt_folder_name", option->option) == 0 &&
249 CurrentFolder && *CurrentFolder) {
251 ssize_t Maildirlength = m_strlen(Maildir);
254 * if name starts with $folder, just strip it to keep hierarchy
255 * $folder=imap://host, path=imap://host/inbox/b -> inbox/b
257 if (Maildirlength > 0 && m_strncmp(CurrentFolder, Maildir,
258 Maildirlength) == 0 &&
259 m_strlen(CurrentFolder) > Maildirlength) {
260 val = CurrentFolder + Maildirlength;
261 if (Maildir[strlen(Maildir)-1]!='/')
263 /* if not $folder, just use everything after last / */
264 } else if ((t = strrchr (CurrentFolder, '/')) != NULL)
266 /* default: use as-is */
268 val = (char *) CurrentFolder;
271 val = (char *) option->init;
273 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (val));
278 static int path_from_string (struct option_t* dst, const char* val,
279 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
280 char path[_POSIX_PATH_MAX];
286 p_delete((char**) dst->data);
291 m_strcpy(path, sizeof(path), val);
292 mutt_expand_path (path, sizeof(path));
293 m_strreplace((char **) dst->data, path);
297 static int str_from_string (struct option_t* dst, const char* val,
298 char* errbuf, ssize_t errlen) {
302 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
305 m_strreplace((char**) dst->data, val);
309 static int user_from_string (struct option_t* dst, const char* val,
310 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
311 /* if dst == NULL, we may get here in case the user did unset it,
312 * see parse_set() where item is free()'d before coming here; so
313 * just silently ignore it */
316 if (m_strlen((char*) dst->data) == 0)
317 dst->data = (unsigned long) m_strdup(val);
319 char* s = (char*) dst->data;
320 m_strreplace(&s, val);
322 if (m_strlen(dst->init) == 0)
323 dst->init = m_strdup((char*) dst->data);
327 static void quad_to_string (char* dst, ssize_t dstlen,
328 struct option_t* option) {
329 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
330 snprintf (dst, dstlen, "%s=%s", option->option,
331 vals[quadoption (option->data)]);
334 static int quad_from_string (struct option_t* dst, const char* val,
335 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
340 if (ascii_strncasecmp (val, "yes", 3) == 0)
342 else if (ascii_strncasecmp (val, "no", 2) == 0)
344 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
346 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
352 set_quadoption (dst->data, flag);
356 static void sort_to_string (char* dst, ssize_t dstlen,
357 struct option_t* option) {
358 const struct mapping_t *map = get_sortmap (option);
359 const char *p = NULL;
362 snprintf (dst, sizeof(dst), "%s=unknown", option->option);
366 p = mutt_getnamebyvalue(*((short *)option->data) & SORT_MASK, map);
368 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
369 (*((short *) option->data) & SORT_REVERSE) ?
371 (*((short *) option->data) & SORT_LAST) ? "last-" :
375 static int sort_from_string (struct option_t* dst, const char* val,
376 char* errbuf, ssize_t errlen) {
377 const struct mapping_t *map = NULL;
378 if (!(map = get_sortmap (dst))) {
380 snprintf (errbuf, errlen, _("%s: Unknown type."),
384 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
389 static void rx_to_string (char* dst, ssize_t dstlen,
390 struct option_t* option) {
391 rx_t* p = (rx_t*) option->data;
392 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
393 NONULL (p->pattern));
396 static int rx_from_string (struct option_t* dst, const char* val,
397 char* errbuf, ssize_t errlen) {
400 int flags = 0, e = 0, not = 0;
406 if (option (OPTATTACHMSG) && !m_strcmp(dst->option, "reply_regexp")) {
408 snprintf (errbuf, errlen,
409 "Operation not permitted when in attach-message mode.");
413 if (!((rx_t*) dst->data))
414 *((rx_t**) dst->data) = p_new(rx_t, 1);
416 p = (rx_t*) dst->data;
418 /* something to do? */
419 if (!val || !*val || (p->pattern && m_strcmp(p->pattern, val) == 0))
422 if (m_strcmp(dst->option, "mask") != 0)
423 flags |= mutt_which_case (val);
426 if (m_strcmp(dst->option, "mask") == 0 && *s == '!') {
431 rx = p_new(regex_t, 1);
433 if ((e = REGCOMP (rx, s, flags)) != 0) {
434 regerror (e, rx, errbuf, errlen);
445 m_strreplace(&p->pattern, val);
449 if (m_strcmp(dst->option, "reply_regexp") == 0)
450 mutt_adjust_all_subjects ();
455 static void magic_to_string (char* dst, ssize_t dstlen,
456 struct option_t* option) {
457 const char* s = NULL;
458 switch (option->data) {
459 case M_MBOX: s = "mbox"; break;
460 case M_MMDF: s = "MMDF"; break;
461 case M_MH: s = "MH"; break;
462 case M_MAILDIR: s = "Maildir"; break;
463 default: s = "unknown"; break;
465 snprintf (dst, dstlen, "%s=%s", option->option, s);
468 static int magic_from_string (struct option_t* dst, const char* val,
469 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
472 if (!dst || !val || !*val)
474 if (ascii_strncasecmp (val, "mbox", 4) == 0)
476 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
478 else if (ascii_strncasecmp (val, "mh", 2) == 0)
480 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
486 *((short*) dst->data) = flag;
491 static void addr_to_string (char* dst, ssize_t dstlen,
492 struct option_t* option) {
495 rfc822_write_address (s, sizeof(s), *((address_t**) option->data), 0);
496 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
499 static int addr_from_string (struct option_t* dst, const char* val,
500 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
503 address_list_wipe((address_t**) dst->data);
505 *((address_t**) dst->data) = rfc822_parse_adrlist (NULL, val);
509 int mutt_option_value (const char* val, char* dst, ssize_t dstlen) {
510 struct option_t* option = NULL;
511 char* tmp = NULL, *t = NULL;
514 if (!(option = hash_find (ConfigOptions, val))) {
518 tmp = p_new(char, dstlen+1);
519 FuncTable[DTYPE (option->type)].opt_to_string (tmp, dstlen, option);
521 /* as we get things of type $var=value and don't want to bloat the
522 * above "just" for expansion, we do the stripping here */
523 t = strchr (tmp, '=');
527 if (t[l-1] == '"' && *t == '"') {
532 memcpy (dst, t, l+1);
538 /* for synonym warning reports: adds synonym to end of list */
539 static void syn_add (struct option_t* n, struct option_t* o) {
540 syn_t* tmp = p_new(syn_t, 1);
541 tmp->f = m_strdup(CurRCFile);
545 list_push_back (&Synonyms, tmp);
548 /* for synonym warning reports: free single item (for list_del()) */
549 static void syn_del (void** p) {
550 p_delete(&(*(syn_t**) p)->f);
554 static void toggle_quadoption (int opt)
557 int b = (opt % 4) * 2;
559 QuadOptions[n] ^= (1 << b);
562 void set_quadoption (int opt, int flag)
565 int b = (opt % 4) * 2;
567 QuadOptions[n] &= ~(0x3 << b);
568 QuadOptions[n] |= (flag & 0x3) << b;
571 int quadoption (int opt)
574 int b = (opt % 4) * 2;
576 return (QuadOptions[n] >> b) & 0x3;
579 int query_quadoption (int opt, const char *prompt)
581 int v = quadoption (opt);
589 v = mutt_yesorno (prompt, (v == M_ASKYES));
590 CLEARLINE (LINES - 1);
597 static void add_to_list (string_list_t ** list, const char *str)
599 string_list_t *t, *last = NULL;
601 /* don't add a NULL or empty string to the list */
602 if (!str || *str == '\0')
605 /* check to make sure the item is not already on this list */
606 for (last = *list; last; last = last->next) {
607 if (ascii_strcasecmp (str, last->data) == 0) {
608 /* already on the list, so just ignore it */
616 if (!*list || last) {
617 t = p_new(string_list_t, 1);
618 t->data = m_strdup(str);
629 add_to_rx_list(rx_t **list, const char *s, int flags, BUFFER *err)
636 if (rx_lookup(list, s))
639 rx = rx_compile(s, flags);
641 snprintf(err->data, err->dsize, "Bad regexp: %s\n", s);
645 rx_list_append(list, rx);
649 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
650 const char *templ, BUFFER * err)
652 SPAM_LIST *t = NULL, *last = NULL;
657 if (!pat || !*pat || !templ)
660 if (!(rx = rx_compile (pat, REG_ICASE))) {
661 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
665 /* check to make sure the item is not already on this list */
666 for (last = *list; last; last = last->next) {
667 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
668 /* Already on the list. Formerly we just skipped this case, but
669 * now we're supporting removals, which means we're supporting
670 * re-adds conceptually. So we probably want this to imply a
671 * removal, then do an add. We can achieve the removal by freeing
672 * the template, and leaving t pointed at the current item.
675 p_delete(&t->template);
682 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
683 * update. Otherwise we want to make a new one to link at the list's end.
686 t = mutt_new_spam_list ();
694 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
695 t->template = m_strdup(templ);
697 /* Find highest match number in template string */
699 for (p = templ; *p;) {
704 while (*p && isdigit ((int) *p))
710 t->nmatch++; /* match 0 is always the whole expr */
715 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
717 SPAM_LIST *spam, *prev;
720 /* Being first is a special case. */
724 if (spam->rx && !m_strcmp(spam->rx->pattern, pat)) {
726 rx_delete(&spam->rx);
727 p_delete(&spam->template);
733 for (spam = prev->next; spam;) {
734 if (!m_strcmp(spam->rx->pattern, pat)) {
735 prev->next = spam->next;
736 rx_delete(&spam->rx);
737 p_delete(&spam->template);
750 static void remove_from_list (string_list_t ** l, const char *str)
752 string_list_t *p, *last = NULL;
754 if (m_strcmp("*", str) == 0)
755 string_list_wipe(l); /* ``unCMD *'' means delete all current entries */
760 if (ascii_strcasecmp (str, p->data) == 0) {
763 last->next = p->next;
776 static int remove_from_rx_list(rx_t **l, const char *str)
778 if (m_strcmp("*", str) == 0) {
783 l = rx_lookup(l, str);
785 rx_t *r = rx_list_pop(l);
793 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
797 unsigned long res = 0;
799 struct option_t* option = NULL;
802 mutt_extract_token (tmp, s, 0);
804 /* is the item defined as a variable or a function? */
805 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL)
808 for (i = 0; !res && i < MENU_MAX; i++) {
809 struct binding_t *b = km_get_table (Menus[i].value);
814 for (j = 0; b[j].name; j++)
815 if (!ascii_strncasecmp (tmp->data, b[j].name, m_strlen(tmp->data))
816 && (m_strlen(b[j].name) == m_strlen(tmp->data))) {
822 /* check for feature_* */
823 if (!res && ascii_strncasecmp (tmp->data, "feature_", 8) == 0 &&
824 (j = m_strlen(tmp->data)) > 8) {
826 while (Features[i]) {
827 if (m_strlen(Features[i]) == j-8 &&
828 ascii_strncasecmp (Features[i], tmp->data+8, j-8) == 0) {
838 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
840 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
844 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
847 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
848 mutt_error ("Error: %s", err->data);
849 p_delete(&token.data);
852 p_delete(&token.data);
857 static int parse_unignore (BUFFER * buf, BUFFER * s,
858 unsigned long data __attribute__ ((unused)),
859 BUFFER * err __attribute__ ((unused)))
862 mutt_extract_token (buf, s, 0);
864 /* don't add "*" to the unignore list */
865 if (strcmp (buf->data, "*"))
866 add_to_list (&UnIgnore, buf->data);
868 remove_from_list (&Ignore, buf->data);
870 while (MoreArgs (s));
875 static int parse_ignore (BUFFER * buf, BUFFER * s,
876 unsigned long data __attribute__ ((unused)),
877 BUFFER * err __attribute__ ((unused)))
880 mutt_extract_token (buf, s, 0);
881 remove_from_list (&UnIgnore, buf->data);
882 add_to_list (&Ignore, buf->data);
884 while (MoreArgs (s));
889 static int parse_list (BUFFER * buf, BUFFER * s,
890 unsigned long data __attribute__ ((unused)),
891 BUFFER * err __attribute__ ((unused)))
894 mutt_extract_token (buf, s, 0);
895 add_to_list ((string_list_t **) data, buf->data);
897 while (MoreArgs (s));
902 static void _alternates_clean (void)
906 if (Context && Context->msgcount) {
907 for (i = 0; i < Context->msgcount; i++)
908 Context->hdrs[i]->recip_valid = 0;
912 static int parse_alternates (BUFFER * buf, BUFFER * s,
913 unsigned long data __attribute__ ((unused)),
914 BUFFER * err __attribute__ ((unused)))
916 _alternates_clean ();
918 mutt_extract_token (buf, s, 0);
919 remove_from_rx_list (&UnAlternates, buf->data);
921 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
924 while (MoreArgs (s));
929 static int parse_unalternates (BUFFER * buf, BUFFER * s,
930 unsigned long data __attribute__ ((unused)),
931 BUFFER * err __attribute__ ((unused)))
933 _alternates_clean ();
935 mutt_extract_token (buf, s, 0);
936 remove_from_rx_list (&Alternates, buf->data);
938 if (m_strcmp(buf->data, "*") &&
939 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
943 while (MoreArgs (s));
948 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
955 /* Insist on at least one parameter */
958 m_strcpy(err->data, err->dsize, _("spam: no matching pattern"));
960 m_strcpy(err->data, err->dsize, _("nospam: no matching pattern"));
964 /* Extract the first token, a regexp */
965 mutt_extract_token (buf, s, 0);
967 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
968 if (data == M_SPAM) {
969 /* If there's a second parameter, it's a template for the spam tag. */
971 mutt_extract_token (&templ, s, 0);
973 /* Add to the spam list. */
974 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
975 p_delete(&templ.data);
978 p_delete(&templ.data);
981 /* If not, try to remove from the nospam list. */
983 remove_from_rx_list (&NoSpamList, buf->data);
989 /* M_NOSPAM is for nospam commands. */
990 else if (data == M_NOSPAM) {
991 /* nospam only ever has one parameter. */
993 /* "*" is a special case. */
994 if (!m_strcmp(buf->data, "*")) {
995 mutt_free_spam_list (&SpamList);
996 rx_list_wipe(&NoSpamList);
1000 /* If it's on the spam list, just remove it. */
1001 if (remove_from_spam_list (&SpamList, buf->data) != 0)
1004 /* Otherwise, add it to the nospam list. */
1005 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
1011 /* This should not happen. */
1012 m_strcpy(err->data, err->dsize, "This is no good at all.");
1016 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
1017 BUFFER * err __attribute__ ((unused)))
1020 mutt_extract_token (buf, s, 0);
1022 * Check for deletion of entire list
1024 if (m_strcmp(buf->data, "*") == 0) {
1025 string_list_wipe((string_list_t **) data);
1028 remove_from_list ((string_list_t **) data, buf->data);
1030 while (MoreArgs (s));
1035 static int parse_lists (BUFFER * buf, BUFFER * s,
1036 unsigned long data __attribute__ ((unused)),
1040 mutt_extract_token (buf, s, 0);
1041 remove_from_rx_list (&UnMailLists, buf->data);
1043 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1046 while (MoreArgs (s));
1051 /* always wise to do what someone else did before */
1052 static void _attachments_clean (void) {
1054 if (Context && Context->msgcount) {
1055 for (i = 0; i < Context->msgcount; i++)
1056 Context->hdrs[i]->attach_valid = 0;
1060 static int parse_attach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
1061 BUFFER *err __attribute__ ((unused))) {
1063 string_list_t *listp, *lastp;
1068 /* Find the last item in the list that data points to. */
1070 for (listp = *ldata; listp; listp = listp->next) {
1071 a = (ATTACH_MATCH *)listp->data;
1076 mutt_extract_token (buf, s, 0);
1078 if (!buf->data || *buf->data == '\0')
1081 a = p_new(ATTACH_MATCH, 1);
1083 /* some cheap hacks that I expect to remove */
1084 if (!m_strcasecmp(buf->data, "any"))
1085 a->major = m_strdup("*/.*");
1086 else if (!m_strcasecmp(buf->data, "none"))
1087 a->major = m_strdup("cheap_hack/this_should_never_match");
1089 a->major = m_strdup(buf->data);
1091 if ((p = strchr(a->major, '/'))) {
1096 a->minor = "unknown";
1099 len = m_strlen(a->minor);
1100 tmpminor = p_new(char, len + 3);
1101 strcpy(&tmpminor[1], a->minor); /* __STRCPY_CHECKED__ */
1103 tmpminor[len+1] = '$';
1104 tmpminor[len+2] = '\0';
1106 a->major_int = mutt_check_mime_type(a->major);
1107 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
1109 p_delete(&tmpminor);
1111 listp = p_new(string_list_t, 1);
1112 listp->data = (char *)a;
1115 lastp->next = listp;
1121 while (MoreArgs (s));
1123 _attachments_clean();
1127 static int parse_unattach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
1128 BUFFER *err __attribute__ ((unused))) {
1130 string_list_t *lp, *lastp, *newlp;
1136 mutt_extract_token (buf, s, 0);
1138 if (!m_strcasecmp(buf->data, "any"))
1139 tmp = m_strdup("*/.*");
1140 else if (!m_strcasecmp(buf->data, "none"))
1141 tmp = m_strdup("cheap_hack/this_should_never_match");
1143 tmp = m_strdup(buf->data);
1145 if ((minor = strchr(tmp, '/'))) {
1149 minor = m_strdup("unknown");
1151 major = mutt_check_mime_type(tmp);
1153 /* We must do our own walk here because remove_from_list() will only
1154 * remove the string_list_t->data, not anything pointed to by the string_list_t->data. */
1156 for(lp = *ldata; lp; ) {
1157 a = (ATTACH_MATCH *)lp->data;
1158 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
1159 regfree(&a->minor_rx);
1160 p_delete(&a->major);
1162 /* Relink backward */
1164 lastp->next = lp->next;
1169 p_delete(&lp->data); /* same as a */
1179 while (MoreArgs (s));
1182 _attachments_clean();
1186 static int print_attach_list (string_list_t *lp, char op, const char *name) {
1188 printf("attachments %c%s %s/%s\n", op, name,
1189 ((ATTACH_MATCH *)lp->data)->major,
1190 ((ATTACH_MATCH *)lp->data)->minor);
1197 static int parse_attachments (BUFFER *buf, BUFFER *s,
1198 unsigned long data __attribute__ ((unused)),
1201 string_list_t **listp;
1203 mutt_extract_token(buf, s, 0);
1204 if (!buf->data || *buf->data == '\0') {
1205 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
1209 category = buf->data;
1215 printf("\nCurrent attachments settings:\n\n");
1216 print_attach_list(AttachAllow, '+', "A");
1217 print_attach_list(AttachExclude, '-', "A");
1218 print_attach_list(InlineAllow, '+', "I");
1219 print_attach_list(InlineExclude, '-', "I");
1220 set_option (OPTFORCEREDRAWINDEX);
1221 set_option (OPTFORCEREDRAWPAGER);
1222 mutt_any_key_to_continue (NULL);
1226 if (op != '+' && op != '-') {
1230 if (!m_strncasecmp(category, "attachment", strlen(category))) {
1232 listp = &AttachAllow;
1234 listp = &AttachExclude;
1236 else if (!m_strncasecmp(category, "inline", strlen(category))) {
1238 listp = &InlineAllow;
1240 listp = &InlineExclude;
1242 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
1246 return parse_attach_list(buf, s, listp, err);
1249 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
1251 string_list_t **listp;
1253 mutt_extract_token(buf, s, 0);
1254 if (!buf->data || *buf->data == '\0') {
1255 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
1261 if (op != '+' && op != '-') {
1265 if (!m_strncasecmp(p, "attachment", strlen(p))) {
1267 listp = &AttachAllow;
1269 listp = &AttachExclude;
1271 else if (!m_strncasecmp(p, "inline", strlen(p))) {
1273 listp = &InlineAllow;
1275 listp = &InlineExclude;
1278 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
1282 return parse_unattach_list(buf, s, listp, err);
1285 static int parse_unlists (BUFFER * buf, BUFFER * s,
1286 unsigned long data __attribute__ ((unused)),
1287 BUFFER * err __attribute__ ((unused)))
1290 mutt_extract_token (buf, s, 0);
1291 remove_from_rx_list (&SubscribedLists, buf->data);
1292 remove_from_rx_list (&MailLists, buf->data);
1294 if (m_strcmp(buf->data, "*") &&
1295 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1298 while (MoreArgs (s));
1303 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1307 mutt_extract_token (buf, s, 0);
1308 remove_from_rx_list (&UnMailLists, buf->data);
1309 remove_from_rx_list (&UnSubscribedLists, buf->data);
1311 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1313 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1316 while (MoreArgs (s));
1321 static int parse_unsubscribe (BUFFER * buf, BUFFER * s,
1322 unsigned long data __attribute__ ((unused)),
1323 BUFFER * err __attribute__ ((unused)))
1326 mutt_extract_token (buf, s, 0);
1327 remove_from_rx_list (&SubscribedLists, buf->data);
1329 if (m_strcmp(buf->data, "*") &&
1330 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1333 while (MoreArgs (s));
1338 static int parse_unalias (BUFFER * buf, BUFFER * s,
1339 unsigned long data __attribute__ ((unused)),
1340 BUFFER * err __attribute__ ((unused)))
1342 alias_t *tmp, *last = NULL;
1345 mutt_extract_token (buf, s, 0);
1347 if (m_strcmp("*", buf->data) == 0) {
1348 if (CurrentMenu == MENU_ALIAS) {
1349 for (tmp = Aliases; tmp; tmp = tmp->next)
1351 set_option (OPTFORCEREDRAWINDEX);
1354 alias_list_wipe(&Aliases);
1358 for (tmp = Aliases; tmp; tmp = tmp->next) {
1359 if (m_strcasecmp(buf->data, tmp->name) == 0) {
1360 if (CurrentMenu == MENU_ALIAS) {
1362 set_option (OPTFORCEREDRAWINDEX);
1367 last->next = tmp->next;
1369 Aliases = tmp->next;
1371 alias_list_wipe(&tmp);
1377 while (MoreArgs (s));
1381 static int parse_alias (BUFFER * buf, BUFFER * s,
1382 unsigned long data __attribute__ ((unused)),
1385 alias_t *tmp = Aliases;
1386 alias_t *last = NULL;
1389 if (!MoreArgs (s)) {
1390 m_strcpy(err->data, err->dsize, _("alias: no address"));
1394 mutt_extract_token (buf, s, 0);
1396 /* check to see if an alias with this name already exists */
1397 for (; tmp; tmp = tmp->next) {
1398 if (!m_strcasecmp(tmp->name, buf->data))
1404 /* create a new alias */
1406 tmp->name = m_strdup(buf->data);
1407 /* give the main addressbook code a chance */
1408 if (CurrentMenu == MENU_ALIAS)
1409 set_option (OPTMENUCALLER);
1412 /* override the previous value */
1413 address_list_wipe(&tmp->addr);
1414 if (CurrentMenu == MENU_ALIAS)
1415 set_option (OPTFORCEREDRAWINDEX);
1418 mutt_extract_token (buf, s,
1419 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1420 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1425 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1426 snprintf (err->data, err->dsize,
1427 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1436 parse_unmy_hdr (BUFFER * buf, BUFFER * s,
1437 unsigned long data __attribute__ ((unused)),
1438 BUFFER * err __attribute__ ((unused)))
1440 string_list_t *last = NULL;
1441 string_list_t *tmp = UserHeader;
1446 mutt_extract_token (buf, s, 0);
1447 if (m_strcmp("*", buf->data) == 0)
1448 string_list_wipe(&UserHeader);
1453 l = m_strlen(buf->data);
1454 if (buf->data[l - 1] == ':')
1458 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1459 && tmp->data[l] == ':') {
1462 last->next = tmp->next;
1464 UserHeader = tmp->next;
1467 string_list_wipe(&ptr);
1476 while (MoreArgs (s));
1480 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1487 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1488 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1489 m_strcpy(err->data, err->dsize, _("invalid header field"));
1492 keylen = p - buf->data + 1;
1495 for (tmp = UserHeader;; tmp = tmp->next) {
1496 /* see if there is already a field by this name */
1497 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1498 /* replace the old value */
1499 p_delete(&tmp->data);
1500 tmp->data = buf->data;
1507 tmp->next = string_item_new();
1511 tmp = string_item_new();
1514 tmp->data = buf->data;
1520 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1521 char* errbuf, ssize_t errlen) {
1524 if (m_strncmp("reverse-", s, 8) == 0) {
1526 flags = SORT_REVERSE;
1529 if (m_strncmp("last-", s, 5) == 0) {
1534 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1536 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1540 *((short*) dst->data) = i | flags;
1544 /* if additional data more == 1, we want to resolve synonyms */
1545 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
1547 char buf[LONG_STRING];
1548 struct option_t *ptr = p;
1550 if (DTYPE(ptr->type) == DT_SYN) {
1553 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1555 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_from_string)
1558 mutt_option_value(ptr->option, buf, sizeof(buf));
1559 if (m_strlen(ptr->init) == 0 && buf && *buf)
1560 ptr->init = m_strdup(buf);
1563 static struct option_t* add_option (const char* name, const char* init,
1564 short type, short dodup) {
1565 struct option_t* option = p_new(struct option_t, 1);
1567 option->option = m_strdup(name);
1568 option->type = type;
1570 option->init = dodup ? m_strdup(init) : (char*) init;
1574 /* creates new option_t* of type DT_USER for $user_ var */
1575 static struct option_t* add_user_option (const char* name) {
1576 return (add_option (name, NULL, DT_USER, 1));
1579 /* free()'s option_t* */
1580 static void del_option (void* p) {
1581 struct option_t *ptr = (struct option_t*) p;
1582 char* s = (char*) ptr->data;
1583 p_delete(&ptr->option);
1585 p_delete(&ptr->init);
1589 static int init_expand (char** dst, struct option_t* src) {
1595 if (DTYPE(src->type) == DT_STR ||
1596 DTYPE(src->type) == DT_PATH) {
1597 /* only expand for string as it's the only place where
1598 * we want to expand vars right now */
1599 if (src->init && *src->init) {
1602 len = m_strlen(src->init) + 2;
1603 in.data = p_new(char, len + 1);
1604 snprintf (in.data, len, "\"%s\"", src->init);
1607 mutt_extract_token (&token, &in, 0);
1608 if (token.data && *token.data)
1609 *dst = m_strdup(token.data);
1611 *dst = m_strdup("");
1613 p_delete(&token.data);
1615 *dst = m_strdup("");
1617 /* for non-string: take value as is */
1618 *dst = m_strdup(src->init);
1622 /* if additional data more == 1, we want to resolve synonyms */
1623 static void mutt_restore_default (const char* name __attribute__ ((unused)),
1624 void* p, unsigned long more) {
1625 char errbuf[STRING];
1626 struct option_t* ptr = (struct option_t*) p;
1629 if (DTYPE (ptr->type) == DT_SYN) {
1632 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1636 if (FuncTable[DTYPE (ptr->type)].opt_from_string) {
1637 init_expand (&init, ptr);
1638 if (!FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, init, errbuf,
1640 if (!option (OPTNOCURSES))
1642 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1643 "Please report this error: \"%s\"\n"),
1644 ptr->option, NONULL (init), errbuf);
1650 if (ptr->flags & R_INDEX)
1651 set_option (OPTFORCEREDRAWINDEX);
1652 if (ptr->flags & R_PAGER)
1653 set_option (OPTFORCEREDRAWPAGER);
1654 if (ptr->flags & R_RESORT_SUB)
1655 set_option (OPTSORTSUBTHREADS);
1656 if (ptr->flags & R_RESORT)
1657 set_option (OPTNEEDRESORT);
1658 if (ptr->flags & R_RESORT_INIT)
1659 set_option (OPTRESORTINIT);
1660 if (ptr->flags & R_TREE)
1661 set_option (OPTREDRAWTREE);
1664 /* check whether value for $dsn_return would be valid */
1665 static int check_dsn_return (const char* option __attribute__ ((unused)), unsigned long p,
1666 char* errbuf, ssize_t errlen) {
1667 char* val = (char*) p;
1668 if (val && *val && m_strncmp(val, "hdrs", 4) != 0 &&
1669 m_strncmp(val, "full", 4) != 0) {
1671 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1677 /* check whether value for $dsn_notify would be valid */
1678 static int check_dsn_notify (const char* option __attribute__ ((unused)), unsigned long p,
1679 char* errbuf, ssize_t errlen) {
1680 list2_t* list = NULL;
1683 char* val = (char*) p;
1687 list = list_from_str (val, ",");
1688 if (list_empty (list))
1691 for (i = 0; i < list->length; i++)
1692 if (m_strncmp(list->data[i], "never", 5) != 0 &&
1693 m_strncmp(list->data[i], "failure", 7) != 0 &&
1694 m_strncmp(list->data[i], "delay", 5) != 0 &&
1695 m_strncmp(list->data[i], "success", 7) != 0) {
1697 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1698 (char*) list->data[i], "dsn_notify");
1702 list_del (&list, (list_del_t*)xmemfree);
1706 static int check_num (const char* option, unsigned long p,
1707 char* errbuf, ssize_t errlen) {
1710 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1716 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1717 char* errbuf, ssize_t errlen) {
1718 if (!check_num ("history", p, errbuf, errlen))
1720 mutt_init_history ();
1724 static int check_special (const char* name, unsigned long val,
1725 char* errbuf, ssize_t errlen) {
1728 for (i = 0; SpecialVars[i].name; i++) {
1729 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1730 return (SpecialVars[i].check (SpecialVars[i].name,
1731 val, errbuf, errlen));
1737 static const struct mapping_t* get_sortmap (struct option_t* option) {
1738 const struct mapping_t* map = NULL;
1740 switch (option->type & DT_SUBTYPE_MASK) {
1742 map = SortAliasMethods;
1744 case DT_SORT_BROWSER:
1745 map = SortBrowserMethods;
1748 map = SortKeyMethods;
1751 map = SortAuxMethods;
1760 #define CHECK_PAGER \
1761 if ((CurrentMenu == MENU_PAGER) && \
1762 (!option || (option->flags & R_RESORT))) \
1764 snprintf (err->data, err->dsize, \
1765 _("Not available in this menu.")); \
1769 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1772 int query, unset, inv, reset, r = 0;
1773 struct option_t* option = NULL;
1775 while (MoreArgs (s)) {
1776 /* reset state variables */
1778 unset = data & M_SET_UNSET;
1779 inv = data & M_SET_INV;
1780 reset = data & M_SET_RESET;
1782 if (*s->dptr == '?') {
1786 else if (m_strncmp("no", s->dptr, 2) == 0) {
1790 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1794 else if (*s->dptr == '&') {
1799 /* get the variable name */
1800 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1802 /* resolve synonyms */
1803 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1804 DTYPE (option->type == DT_SYN)) {
1805 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1806 syn_add (newopt, option);
1810 /* see if we need to add $user_ var */
1811 if (!option && m_strncmp("user_", tmp->data, 5) == 0) {
1812 /* there's no option named like this yet so only add one
1813 * if the action isn't any of: reset, unset, query */
1814 if (!(reset || unset || query || *s->dptr != '=')) {
1815 option = add_user_option (tmp->data);
1816 hash_insert (ConfigOptions, option->option, option, 0);
1820 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1821 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1824 s->dptr = vskipspaces(s->dptr);
1827 if (query || unset || inv) {
1828 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1832 if (s && *s->dptr == '=') {
1833 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1837 if (!m_strcmp("all", tmp->data)) {
1838 if (CurrentMenu == MENU_PAGER) {
1839 snprintf (err->data, err->dsize, _("Not available in this menu."));
1842 hash_map (ConfigOptions, mutt_restore_default, 1);
1843 set_option (OPTFORCEREDRAWINDEX);
1844 set_option (OPTFORCEREDRAWPAGER);
1845 set_option (OPTSORTSUBTHREADS);
1846 set_option (OPTNEEDRESORT);
1847 set_option (OPTRESORTINIT);
1848 set_option (OPTREDRAWTREE);
1851 else if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1852 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1857 mutt_restore_default (NULL, option, 1);
1860 else if (DTYPE (option->type) == DT_BOOL) {
1861 /* XXX this currently ignores the function table
1862 * as we don't get invert and stuff into it */
1863 if (s && *s->dptr == '=') {
1864 if (unset || inv || query) {
1865 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1870 mutt_extract_token (tmp, s, 0);
1871 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1873 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1876 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1882 bool_to_string (err->data, err->dsize, option);
1888 unset_option (option->data);
1890 toggle_option (option->data);
1892 set_option (option->data);
1894 else if (DTYPE (option->type) == DT_STR ||
1895 DTYPE (option->type) == DT_PATH ||
1896 DTYPE (option->type) == DT_ADDR ||
1897 DTYPE (option->type) == DT_MAGIC ||
1898 DTYPE (option->type) == DT_NUM ||
1899 DTYPE (option->type) == DT_SORT ||
1900 DTYPE (option->type) == DT_RX ||
1901 DTYPE (option->type) == DT_USER ||
1902 DTYPE (option->type) == DT_SYS) {
1904 /* XXX maybe we need to get unset into handlers? */
1905 if (DTYPE (option->type) == DT_STR ||
1906 DTYPE (option->type) == DT_PATH ||
1907 DTYPE (option->type) == DT_ADDR ||
1908 DTYPE (option->type) == DT_USER ||
1909 DTYPE (option->type) == DT_SYS) {
1912 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1913 snprintf (err->data, err->dsize, _("$%s is read-only"),
1917 } else if (DTYPE (option->type) == DT_ADDR)
1918 address_list_wipe((address_t **) option->data);
1919 else if (DTYPE (option->type) == DT_USER)
1920 /* to unset $user_ means remove */
1921 hash_delete (ConfigOptions, option->option,
1922 option, del_option);
1924 p_delete((void **)(void *)&option->data);
1929 if (query || *s->dptr != '=') {
1930 FuncTable[DTYPE (option->type)].opt_to_string
1931 (err->data, err->dsize, option);
1935 /* the $madmutt_ variables are read-only */
1936 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1937 snprintf (err->data, err->dsize, _("$%s is read-only"),
1944 mutt_extract_token (tmp, s, 0);
1945 if (!FuncTable[DTYPE (option->type)].opt_from_string
1946 (option, tmp->data, err->data, err->dsize))
1950 else if (DTYPE (option->type) == DT_QUAD) {
1953 quad_to_string (err->data, err->dsize, option);
1957 if (*s->dptr == '=') {
1960 mutt_extract_token (tmp, s, 0);
1961 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1962 set_quadoption (option->data, M_YES);
1963 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1964 set_quadoption (option->data, M_NO);
1965 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1966 set_quadoption (option->data, M_ASKYES);
1967 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1968 set_quadoption (option->data, M_ASKNO);
1970 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1971 tmp->data, option->option);
1978 toggle_quadoption (option->data);
1980 set_quadoption (option->data, M_NO);
1982 set_quadoption (option->data, M_YES);
1986 snprintf (err->data, err->dsize, _("%s: unknown type"),
1992 if (option->flags & R_INDEX)
1993 set_option (OPTFORCEREDRAWINDEX);
1994 if (option->flags & R_PAGER)
1995 set_option (OPTFORCEREDRAWPAGER);
1996 if (option->flags & R_RESORT_SUB)
1997 set_option (OPTSORTSUBTHREADS);
1998 if (option->flags & R_RESORT)
1999 set_option (OPTNEEDRESORT);
2000 if (option->flags & R_RESORT_INIT)
2001 set_option (OPTRESORTINIT);
2002 if (option->flags & R_TREE)
2003 set_option (OPTREDRAWTREE);
2010 /* reads the specified initialization file. returns -1 if errors were found
2011 so that we can pause to let the user know... */
2012 static int source_rc (const char *rcfile, BUFFER * err)
2015 int line = 0, rc = 0, conv = 0;
2017 char *linebuf = NULL;
2018 char *currentline = NULL;
2022 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
2023 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
2028 while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
2029 conv = ConfigCharset && (*ConfigCharset) && Charset;
2031 currentline = m_strdup(linebuf);
2034 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
2037 currentline = linebuf;
2042 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
2043 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
2044 if (--rc < -MAXERRS) {
2046 p_delete(¤tline);
2055 p_delete(¤tline);
2057 p_delete(&token.data);
2061 mutt_wait_filter (pid);
2063 /* the muttrc source keyword */
2064 snprintf (err->data, err->dsize,
2065 rc >= -MAXERRS ? _("source: errors in %s")
2066 : _("source: reading aborted due too many errors in %s"),
2075 static int parse_source (BUFFER * tmp, BUFFER * s,
2076 unsigned long data __attribute__ ((unused)),
2079 char path[_POSIX_PATH_MAX];
2083 if (mutt_extract_token (tmp, s, 0) != 0) {
2084 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
2088 m_strcpy(path, sizeof(path), tmp->data);
2089 mutt_expand_path (path, sizeof(path));
2091 rc += source_rc (path, err);
2093 while (MoreArgs (s));
2095 return ((rc < 0) ? -1 : 0);
2098 /* line command to execute
2100 token scratch buffer to be used by parser. caller should free
2101 token->data when finished. the reason for this variable is
2102 to avoid having to allocate and deallocate a lot of memory
2103 if we are parsing many lines. the caller can pass in the
2104 memory to use, which avoids having to create new space for
2105 every call to this function.
2107 err where to write error messages */
2108 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
2114 expn.data = expn.dptr = line;
2115 expn.dsize = m_strlen(line);
2119 expn.dptr = vskipspaces(expn.dptr);
2120 while (*expn.dptr) {
2121 if (*expn.dptr == '#')
2122 break; /* rest of line is a comment */
2123 if (*expn.dptr == ';') {
2127 mutt_extract_token (token, &expn, 0);
2128 for (i = 0; Commands[i].name; i++) {
2129 if (!m_strcmp(token->data, Commands[i].name)) {
2130 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
2135 if (!Commands[i].name) {
2136 snprintf (err->data, err->dsize, _("%s: unknown command"),
2137 NONULL (token->data));
2144 p_delete(&expn.data);
2149 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
2150 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
2151 /* initial string that starts completion. No telling how much crap
2152 * the user has typed so far. Allocate LONG_STRING just to be sure! */
2153 char User_typed[LONG_STRING] = { 0 };
2155 int Num_matched = 0; /* Number of matches for completion */
2156 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
2157 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
2159 /* helper function for completion. Changes the dest buffer if
2160 necessary/possible to aid completion.
2161 dest == completion result gets here.
2162 src == candidate for completion.
2163 try == user entered data for completion.
2164 len == length of dest buffer.
2166 static void candidate (char *dest, char *try, const char *src, int len)
2170 if (strstr (src, try) == src) {
2171 Matches[Num_matched++] = src;
2173 m_strcpy(dest, len, src);
2175 for (l = 0; src[l] && src[l] == dest[l]; l++);
2181 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
2185 int spaces; /* keep track of the number of leading spaces on the line */
2187 buffer = vskipspaces(buffer);
2188 spaces = buffer - pt;
2190 pt = buffer + pos - spaces;
2191 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2194 if (pt == buffer) { /* complete cmd */
2195 /* first TAB. Collect all the matches */
2198 m_strcpy(User_typed, sizeof(User_typed), pt);
2199 p_clear(Matches, countof(Matches));
2200 p_clear(Completed, countof(Completed));
2201 for (num = 0; Commands[num].name; num++)
2202 candidate (Completed, User_typed, Commands[num].name,
2204 Matches[Num_matched++] = User_typed;
2206 /* All matches are stored. Longest non-ambiguous string is ""
2207 * i.e. dont change 'buffer'. Fake successful return this time */
2208 if (User_typed[0] == 0)
2212 if (Completed[0] == 0 && User_typed[0])
2215 /* Num_matched will _always_ be atleast 1 since the initial
2216 * user-typed string is always stored */
2217 if (numtabs == 1 && Num_matched == 2)
2218 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2219 else if (numtabs > 1 && Num_matched > 2)
2220 /* cycle thru all the matches */
2221 snprintf (Completed, sizeof(Completed), "%s",
2222 Matches[(numtabs - 2) % Num_matched]);
2224 /* return the completed command */
2225 m_strcpy(buffer, len - spaces, Completed);
2227 else if (!m_strncmp(buffer, "set", 3)
2228 || !m_strncmp(buffer, "unset", 5)
2229 || !m_strncmp(buffer, "reset", 5)
2230 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
2231 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
2234 /* loop through all the possible prefixes (no, inv, ...) */
2235 if (!m_strncmp(buffer, "set", 3)) {
2236 for (num = 0; prefixes[num]; num++) {
2237 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
2238 pt += m_strlen(prefixes[num]);
2244 /* first TAB. Collect all the matches */
2247 m_strcpy(User_typed, sizeof(User_typed), pt);
2248 p_clear(Matches, countof(Matches));
2249 p_clear(Completed, countof(Completed));
2250 for (num = 0; MuttVars[num].option; num++)
2251 candidate(Completed, User_typed, MuttVars[num].option,
2253 Matches[Num_matched++] = User_typed;
2255 /* All matches are stored. Longest non-ambiguous string is ""
2256 * i.e. dont change 'buffer'. Fake successful return this time */
2257 if (User_typed[0] == 0)
2261 if (Completed[0] == 0 && User_typed[0])
2264 /* Num_matched will _always_ be atleast 1 since the initial
2265 * user-typed string is always stored */
2266 if (numtabs == 1 && Num_matched == 2)
2267 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2268 else if (numtabs > 1 && Num_matched > 2)
2269 /* cycle thru all the matches */
2270 snprintf (Completed, sizeof(Completed), "%s",
2271 Matches[(numtabs - 2) % Num_matched]);
2273 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2275 else if (!m_strncmp(buffer, "exec", 4)) {
2276 struct binding_t *menu = km_get_table (CurrentMenu);
2278 if (!menu && CurrentMenu != MENU_PAGER)
2282 /* first TAB. Collect all the matches */
2285 m_strcpy(User_typed, sizeof(User_typed), pt);
2286 p_clear(Matches, countof(Matches));
2287 p_clear(Completed, countof(Completed));
2288 for (num = 0; menu[num].name; num++)
2289 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
2290 /* try the generic menu */
2291 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2293 for (num = 0; menu[num].name; num++)
2294 candidate (Completed, User_typed, menu[num].name,
2297 Matches[Num_matched++] = User_typed;
2299 /* All matches are stored. Longest non-ambiguous string is ""
2300 * i.e. dont change 'buffer'. Fake successful return this time */
2301 if (User_typed[0] == 0)
2305 if (Completed[0] == 0 && User_typed[0])
2308 /* Num_matched will _always_ be atleast 1 since the initial
2309 * user-typed string is always stored */
2310 if (numtabs == 1 && Num_matched == 2)
2311 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2312 else if (numtabs > 1 && Num_matched > 2)
2313 /* cycle thru all the matches */
2314 snprintf (Completed, sizeof(Completed), "%s",
2315 Matches[(numtabs - 2) % Num_matched]);
2317 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2325 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
2327 char var[STRING], *pt = buffer;
2329 struct option_t* option = NULL;
2334 buffer = vskipspaces(buffer);
2335 spaces = buffer - pt;
2337 pt = buffer + pos - spaces;
2338 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2340 pt++; /* move past the space */
2341 if (*pt == '=') /* abort if no var before the '=' */
2344 if (m_strncmp(buffer, "set", 3) == 0) {
2345 m_strcpy(var, sizeof(var), pt);
2346 /* ignore the trailing '=' when comparing */
2347 var[m_strlen(var) - 1] = 0;
2348 if (!(option = hash_find (ConfigOptions, var)))
2349 return 0; /* no such variable. */
2351 char tmp[LONG_STRING], tmp2[LONG_STRING];
2353 ssize_t dlen = buffer + len - pt - spaces;
2354 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2358 if ((DTYPE (option->type) == DT_STR) ||
2359 (DTYPE (option->type) == DT_PATH) ||
2360 (DTYPE (option->type) == DT_RX)) {
2361 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
2362 if (DTYPE (option->type) == DT_PATH)
2363 mutt_pretty_mailbox (tmp);
2365 else if (DTYPE (option->type) == DT_ADDR) {
2366 rfc822_write_address (tmp, sizeof(tmp),
2367 *((address_t **) option->data), 0);
2369 else if (DTYPE (option->type) == DT_QUAD)
2370 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
2371 else if (DTYPE (option->type) == DT_NUM)
2372 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
2373 else if (DTYPE (option->type) == DT_SORT) {
2374 const struct mapping_t *map;
2377 switch (option->type & DT_SUBTYPE_MASK) {
2379 map = SortAliasMethods;
2381 case DT_SORT_BROWSER:
2382 map = SortBrowserMethods;
2385 map = SortKeyMethods;
2391 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2392 snprintf(tmp, sizeof(tmp), "%s%s%s",
2393 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2394 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2396 else if (DTYPE (option->type) == DT_MAGIC) {
2398 switch (DefaultMagic) {
2414 m_strcpy(tmp, sizeof(tmp), p);
2416 else if (DTYPE (option->type) == DT_BOOL)
2417 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
2421 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
2422 if (*s == '\\' || *s == '"')
2428 m_strcpy(tmp, sizeof(tmp), pt);
2429 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2437 /* Implement the -Q command line flag */
2438 int mutt_query_variables (string_list_t * queries)
2442 char errbuff[STRING];
2443 char command[STRING];
2451 err.dsize = sizeof(errbuff);
2453 for (p = queries; p; p = p->next) {
2454 snprintf (command, sizeof(command), "set ?%s\n", p->data);
2455 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2456 fprintf (stderr, "%s\n", err.data);
2457 p_delete(&token.data);
2460 printf ("%s\n", err.data);
2463 p_delete(&token.data);
2467 static int mutt_execute_commands (string_list_t * p)
2470 char errstr[SHORT_STRING];
2474 err.dsize = sizeof(errstr);
2476 for (; p; p = p->next) {
2477 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2478 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2479 p_delete(&token.data);
2483 p_delete(&token.data);
2487 void mutt_init (int skip_sys_rc, string_list_t * commands)
2490 struct utsname utsname;
2492 char buffer[STRING], error[STRING];
2493 int default_rc = 0, need_pause = 0;
2499 err.dsize = sizeof(error);
2501 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2502 * to have some room for $user_ vars */
2503 ConfigOptions = hash_create (sizeof(MuttVars) * 3);
2504 for (i = 0; MuttVars[i].option; i++) {
2505 if (DTYPE (MuttVars[i].type) != DT_SYS)
2506 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2508 hash_insert (ConfigOptions, MuttVars[i].option,
2509 add_option (MuttVars[i].option, MuttVars[i].init,
2514 * XXX - use something even more difficult to predict?
2516 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2517 "\033]9;%ld\a", (long) time (NULL));
2519 /* on one of the systems I use, getcwd() does not return the same prefix
2520 as is listed in the passwd file */
2521 if ((p = getenv ("HOME")))
2522 Homedir = m_strdup(p);
2524 /* Get some information about the user */
2525 if ((pw = getpwuid (getuid ()))) {
2528 Username = m_strdup(pw->pw_name);
2530 Homedir = m_strdup(pw->pw_dir);
2532 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, GecosMask.rx);
2533 Realname = m_strdup(rnbuf);
2534 Shell = m_strdup(pw->pw_shell);
2540 fputs (_("unable to determine home directory"), stderr);
2543 if ((p = getenv ("USER")))
2544 Username = m_strdup(p);
2547 fputs (_("unable to determine username"), stderr);
2550 Shell = m_strdup((p = getenv ("SHELL")) ? p : "/bin/sh");
2553 /* And about the host... */
2555 /* some systems report the FQDN instead of just the hostname */
2556 if ((p = strchr (utsname.nodename, '.'))) {
2557 Hostname = p_dupstr(utsname.nodename, p - utsname.nodename);
2559 m_strcpy(buffer, sizeof(buffer), p); /* save the domain for below */
2562 Hostname = m_strdup(utsname.nodename);
2564 if (!p && getdnsdomainname(buffer, sizeof(buffer)) == -1)
2565 Fqdn = m_strdup("@");
2567 if (*buffer != '@') {
2568 Fqdn = p_new(char, m_strlen(buffer) + m_strlen(Hostname) + 2);
2569 sprintf (Fqdn, "%s.%s", NONULL(Hostname), buffer); /* __SPRINTF_CHECKED__ */
2572 Fqdn = m_strdup(NONULL (Hostname));
2579 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2581 fgets (buffer, sizeof(buffer), f);
2582 p = vskipspaces(buffer);
2584 while (*q && !isspace(*q))
2587 NewsServer = m_strdup(p);
2591 if ((p = getenv ("NNTPSERVER")))
2592 NewsServer = m_strdup(p);
2595 if ((p = getenv ("MAIL")))
2596 Spoolfile = m_strdup(p);
2597 else if ((p = getenv ("MAILDIR")))
2598 Spoolfile = m_strdup(p);
2601 mutt_concat_path(buffer, sizeof(buffer), NONULL(Homedir), MAILPATH);
2603 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(Username));
2605 Spoolfile = m_strdup(buffer);
2608 if ((p = getenv ("MAILCAPS")))
2609 MailcapPath = m_strdup(p);
2611 /* Default search path from RFC1524 */
2613 m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2614 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2617 Tempdir = m_strdup((p = getenv ("TMPDIR")) ? p : "/tmp");
2619 p = getenv ("VISUAL");
2621 p = getenv ("EDITOR");
2625 Editor = m_strdup(p);
2627 if ((p = getenv ("REPLYTO")) != NULL) {
2630 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2633 buf.data = buf.dptr = buffer;
2634 buf.dsize = m_strlen(buffer);
2637 parse_my_hdr (&token, &buf, 0, &err);
2638 p_delete(&token.data);
2641 if ((p = getenv ("EMAIL")) != NULL)
2642 From = rfc822_parse_adrlist (NULL, p);
2644 charset_initialize();
2646 /* Set standard defaults */
2647 hash_map (ConfigOptions, mutt_set_default, 0);
2648 hash_map (ConfigOptions, mutt_restore_default, 0);
2650 CurrentMenu = MENU_MAIN;
2653 /* Unset suspend by default if we're the session leader */
2654 if (getsid (0) == getpid ())
2655 unset_option (OPTSUSPEND);
2658 mutt_init_history ();
2662 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc-%s", NONULL (Homedir),
2664 if (access (buffer, F_OK) == -1)
2666 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL (Homedir));
2667 if (access (buffer, F_OK) == -1)
2669 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc-%s",
2670 NONULL (Homedir), MUTT_VERSION);
2671 if (access (buffer, F_OK) == -1)
2673 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
2677 Muttrc = m_strdup(buffer);
2680 m_strcpy(buffer, sizeof(buffer), Muttrc);
2682 mutt_expand_path (buffer, sizeof(buffer));
2683 Muttrc = m_strdup(buffer);
2685 p_delete(&AliasFile);
2686 AliasFile = m_strdup(NONULL (Muttrc));
2688 /* Process the global rc file if it exists and the user hasn't explicity
2689 requested not to via "-n". */
2691 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
2693 if (access (buffer, F_OK) == -1)
2694 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
2695 if (access (buffer, F_OK) == -1)
2696 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
2698 if (access (buffer, F_OK) == -1)
2699 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
2700 if (access (buffer, F_OK) != -1) {
2701 if (source_rc (buffer, &err) != 0) {
2702 fputs (err.data, stderr);
2703 fputc ('\n', stderr);
2709 /* Read the user's initialization file. */
2710 if (access (Muttrc, F_OK) != -1) {
2711 if (!option (OPTNOCURSES))
2713 if (source_rc (Muttrc, &err) != 0) {
2714 fputs (err.data, stderr);
2715 fputc ('\n', stderr);
2719 else if (!default_rc) {
2720 /* file specified by -F does not exist */
2721 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2722 mutt_endwin (buffer);
2726 if (mutt_execute_commands (commands) != 0)
2729 /* warn about synonym variables */
2730 if (!list_empty(Synonyms)) {
2732 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2733 for (i = 0; i < Synonyms->length; i++) {
2734 struct option_t* newopt = NULL, *oldopt = NULL;
2735 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2736 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2737 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2738 oldopt ? NONULL (oldopt->option) : "",
2739 newopt ? NONULL (newopt->option) : "",
2740 NONULL(((syn_t*) Synonyms->data[i])->f),
2741 ((syn_t*) Synonyms->data[i])->l);
2743 fprintf (stderr, _("Warning: synonym variables are scheduled"
2744 " for removal.\n"));
2745 list_del (&Synonyms, syn_del);
2749 if (need_pause && !option (OPTNOCURSES)) {
2750 if (mutt_any_key_to_continue (NULL) == -1)
2755 set_option (OPTWEED); /* turn weeding on by default */
2759 int mutt_get_hook_type (const char *name)
2761 struct command_t *c;
2763 for (c = Commands; c->name; c++)
2764 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2769 /* compare two option_t*'s for sorting -t/-T output */
2770 static int opt_cmp (const void* a, const void* b) {
2771 return (m_strcmp((*(struct option_t**) a)->option,
2772 (*(struct option_t**) b)->option));
2775 /* callback for hash_map() to put all non-synonym vars into list */
2776 static void opt_sel_full (const char* key __attribute__ ((unused)),
2778 unsigned long more) {
2779 list2_t** l = (list2_t**) more;
2780 struct option_t* option = (struct option_t*) data;
2782 if (DTYPE (option->type) == DT_SYN)
2784 list_push_back (l, option);
2787 /* callback for hash_map() to put all changed non-synonym vars into list */
2788 static void opt_sel_diff (const char* key __attribute__ ((unused)),
2790 unsigned long more) {
2791 list2_t** l = (list2_t**) more;
2792 struct option_t* option = (struct option_t*) data;
2793 char buf[LONG_STRING];
2795 if (DTYPE (option->type) == DT_SYN)
2798 mutt_option_value (option->option, buf, sizeof(buf));
2799 if (m_strcmp(buf, option->init) != 0)
2800 list_push_back (l, option);
2803 /* dump out the value of all the variables we have */
2804 int mutt_dump_variables (int full) {
2806 char outbuf[STRING];
2807 list2_t* tmp = NULL;
2808 struct option_t* option = NULL;
2810 /* get all non-synonyms into list... */
2811 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2812 (unsigned long) &tmp);
2814 if (!list_empty(tmp)) {
2815 /* ...and dump list sorted */
2816 qsort (tmp->data, tmp->length, sizeof(void*), opt_cmp);
2817 for (i = 0; i < tmp->length; i++) {
2818 option = (struct option_t*) tmp->data[i];
2819 FuncTable[DTYPE (option->type)].opt_to_string
2820 (outbuf, sizeof(outbuf), option);
2821 printf ("%s\n", outbuf);
2824 list_del (&tmp, NULL);