2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
5 * Parts were written/modified by:
6 * Rocco Rutte <pdmef@cs.tu-berlin.de>
8 * This file is part of mutt-ng, see http://www.muttng.org/.
9 * It's licensed under the GNU General Public License,
10 * please see the file GPL in the top level source directory.
21 #include <sys/utsname.h>
25 #include <lib-lib/lib-lib.h>
27 #include <lib-sys/unix.h>
28 #include <lib-sys/mutt_ssl.h>
30 #include <lib-ui/curses.h>
31 #include <lib-ui/history.h>
37 #include <lib-crypt/crypt.h>
38 #include "mutt_idna.h"
40 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
41 #include "mutt_libesmtp.h"
53 static const struct mapping_t* get_sortmap (struct option_t* option);
54 static int parse_sort (struct option_t* dst, const char *s,
55 const struct mapping_t *map,
56 char* errbuf, ssize_t errlen);
58 static HASH *ConfigOptions = NULL;
60 /* for synonym warning reports: synonym found during parsing */
64 struct option_t* n; /* new */
65 struct option_t* o; /* old */
68 /* for synonym warning reports: list of synonyms found */
69 static list2_t* Synonyms;
70 /* for synonym warning reports: current rc file */
71 static const char* CurRCFile = NULL;
72 /* for synonym warning reports: current rc line */
73 static int CurRCLine = 0;
75 /* prototypes for checking for special vars */
76 static int check_dsn_return (const char* option, unsigned long val,
77 char* errbuf, ssize_t errlen);
78 static int check_dsn_notify (const char* option, unsigned long val,
79 char* errbuf, ssize_t errlen);
80 static int check_history (const char* option, unsigned long val,
81 char* errbuf, ssize_t errlen);
82 /* this checks that numbers are >= 0 */
83 static int check_num (const char* option, unsigned long val,
84 char* errbuf, ssize_t errlen);
86 /* use this to check only */
87 static int check_special (const char* option, unsigned long val,
88 char* errbuf, ssize_t errlen);
90 /* variable <-> sanity check function mappings
91 * when changing these, make sure the proper _from_string handler
96 int (*check) (const char* option, unsigned long val,
97 char* errbuf, ssize_t errlen);
99 { "dsn_notify", check_dsn_notify },
100 { "dsn_return", check_dsn_return },
101 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
102 { "smtp_use_tls", mutt_libesmtp_check_usetls },
104 { "history", check_history },
105 { "pager_index_lines", check_num },
110 /* protos for config type handles: convert value to string */
111 static void bool_to_string (char* dst, ssize_t dstlen, struct option_t* option);
112 static void num_to_string (char* dst, ssize_t dstlen, struct option_t* option);
113 static void str_to_string (char* dst, ssize_t dstlen, struct option_t* option);
114 static void quad_to_string (char* dst, ssize_t dstlen, struct option_t* option);
115 static void sort_to_string (char* dst, ssize_t dstlen, struct option_t* option);
116 static void rx_to_string (char* dst, ssize_t dstlen, struct option_t* option);
117 static void magic_to_string (char* dst, ssize_t dstlen, struct option_t* option);
118 static void addr_to_string (char* dst, ssize_t dstlen, struct option_t* option);
119 static void user_to_string (char* dst, ssize_t dstlen, struct option_t* option);
120 static void sys_to_string (char* dst, ssize_t dstlen, struct option_t* option);
122 /* protos for config type handles: convert to value from string */
123 static int bool_from_string (struct option_t* dst, const char* val,
124 char* errbuf, ssize_t errlen);
125 static int num_from_string (struct option_t* dst, const char* val,
126 char* errbuf, ssize_t errlen);
127 static int str_from_string (struct option_t* dst, const char* val,
128 char* errbuf, ssize_t errlen);
129 static int path_from_string (struct option_t* dst, const char* val,
130 char* errbuf, ssize_t errlen);
131 static int quad_from_string (struct option_t* dst, const char* val,
132 char* errbuf, ssize_t errlen);
133 static int sort_from_string (struct option_t* dst, const char* val,
134 char* errbuf, ssize_t errlen);
135 static int rx_from_string (struct option_t* dst, const char* val,
136 char* errbuf, ssize_t errlen);
137 static int magic_from_string (struct option_t* dst, const char* val,
138 char* errbuf, ssize_t errlen);
139 static int addr_from_string (struct option_t* dst, const char* val,
140 char* errbuf, ssize_t errlen);
141 static int user_from_string (struct option_t* dst, const char* val,
142 char* errbuf, ssize_t errlen);
146 void (*opt_to_string) (char* dst, ssize_t dstlen, struct option_t* option);
147 int (*opt_from_string) (struct option_t* dst, const char* val,
148 char* errbuf, ssize_t errlen);
150 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
151 { DT_BOOL, bool_to_string, bool_from_string },
152 { DT_NUM, num_to_string, num_from_string },
153 { DT_STR, str_to_string, str_from_string },
154 { DT_PATH, str_to_string, path_from_string },
155 { DT_QUAD, quad_to_string, quad_from_string },
156 { DT_SORT, sort_to_string, sort_from_string },
157 { DT_RX, rx_to_string, rx_from_string },
158 { DT_MAGIC, magic_to_string, magic_from_string },
159 /* synonyms should be resolved already so we don't need this
160 * but must define it as DT_ is used for indexing */
161 { DT_SYN, NULL, NULL },
162 { DT_ADDR, addr_to_string, addr_from_string },
163 { DT_USER, user_to_string, user_from_string },
164 { DT_SYS, sys_to_string, NULL },
167 static void bool_to_string (char* dst, ssize_t dstlen,
168 struct option_t* option) {
169 snprintf (dst, dstlen, "%s=%s", option->option,
170 option (option->data) ? "yes" : "no");
173 static int bool_from_string (struct option_t* dst, const char* val,
174 char* errbuf __attribute__ ((unused)),
175 ssize_t errlen __attribute__ ((unused))) {
180 if (ascii_strncasecmp (val, "yes", 3) == 0)
182 else if (ascii_strncasecmp (val, "no", 2) == 0)
188 set_option (dst->data);
190 unset_option (dst->data);
194 static void num_to_string (char* dst, ssize_t dstlen,
195 struct option_t* option) {
197 const char* fmt = (m_strcmp(option->option, "umask") == 0) ?
199 snprintf (dst, dstlen, fmt, option->option,
200 *((short*) option->data));
203 static int num_from_string (struct option_t* dst, const char* val,
204 char* errbuf, ssize_t errlen) {
205 int num = 0, old = 0;
211 num = strtol (val, &t, 0);
213 if (!*val || *t || (short) num != num) {
215 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
221 /* just temporarily accept new val so that check_special for
222 * $history already has it when doing history's init() */
223 old = *((short*) dst->data);
224 *((short*) dst->data) = (short) num;
226 if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
227 *((short*) dst->data) = old;
234 static void str_to_string (char* dst, ssize_t dstlen,
235 struct option_t* option) {
236 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
237 NONULL (*((char**) option->data)));
240 static void user_to_string (char* dst, ssize_t dstlen,
241 struct option_t* option) {
242 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
243 NONULL (((char*) option->data)));
246 static void sys_to_string (char* dst, ssize_t dstlen,
247 struct option_t* option) {
248 char *val = NULL, *t = NULL;
251 /* get some $madmutt_ values dynamically */
252 if (ascii_strcmp ("madmutt_pwd", option->option) == 0) {
253 val = p_new(char, _POSIX_PATH_MAX);
254 val = getcwd (val, _POSIX_PATH_MAX-1);
256 } else if (ascii_strcmp ("madmutt_folder_path", option->option) == 0 &&
257 CurrentFolder && *CurrentFolder) {
259 } else if (ascii_strcmp ("madmutt_folder_name", option->option) == 0 &&
260 CurrentFolder && *CurrentFolder) {
262 ssize_t Maildirlength = m_strlen(Maildir);
265 * if name starts with $folder, just strip it to keep hierarchy
266 * $folder=imap://host, path=imap://host/inbox/b -> inbox/b
268 if (Maildirlength > 0 && m_strncmp(CurrentFolder, Maildir,
269 Maildirlength) == 0 &&
270 m_strlen(CurrentFolder) > Maildirlength) {
271 val = CurrentFolder + Maildirlength;
272 if (Maildir[strlen(Maildir)-1]!='/')
274 /* if not $folder, just use everything after last / */
275 } else if ((t = strrchr (CurrentFolder, '/')) != NULL)
277 /* default: use as-is */
279 val = (char *) CurrentFolder;
282 val = (char *) option->init;
284 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (val));
289 static int path_from_string (struct option_t* dst, const char* val,
290 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
291 char path[_POSIX_PATH_MAX];
297 p_delete((char**) dst->data);
302 m_strcpy(path, sizeof(path), val);
303 mutt_expand_path (path, sizeof(path));
304 m_strreplace((char **) dst->data, path);
308 static int str_from_string (struct option_t* dst, const char* val,
309 char* errbuf, ssize_t errlen) {
313 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
316 m_strreplace((char**) dst->data, val);
320 static int user_from_string (struct option_t* dst, const char* val,
321 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
322 /* if dst == NULL, we may get here in case the user did unset it,
323 * see parse_set() where item is free()'d before coming here; so
324 * just silently ignore it */
327 if (m_strlen((char*) dst->data) == 0)
328 dst->data = (unsigned long) m_strdup(val);
330 char* s = (char*) dst->data;
331 m_strreplace(&s, val);
333 if (m_strlen(dst->init) == 0)
334 dst->init = m_strdup((char*) dst->data);
338 static void quad_to_string (char* dst, ssize_t dstlen,
339 struct option_t* option) {
340 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
341 snprintf (dst, dstlen, "%s=%s", option->option,
342 vals[quadoption (option->data)]);
345 static int quad_from_string (struct option_t* dst, const char* val,
346 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
351 if (ascii_strncasecmp (val, "yes", 3) == 0)
353 else if (ascii_strncasecmp (val, "no", 2) == 0)
355 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
357 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
363 set_quadoption (dst->data, flag);
367 static void sort_to_string (char* dst, ssize_t dstlen,
368 struct option_t* option) {
369 const struct mapping_t *map = get_sortmap (option);
370 const char *p = NULL;
373 snprintf (dst, sizeof(dst), "%s=unknown", option->option);
377 p = mutt_getnamebyvalue(*((short *)option->data) & SORT_MASK, map);
379 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
380 (*((short *) option->data) & SORT_REVERSE) ?
382 (*((short *) option->data) & SORT_LAST) ? "last-" :
386 static int sort_from_string (struct option_t* dst, const char* val,
387 char* errbuf, ssize_t errlen) {
388 const struct mapping_t *map = NULL;
389 if (!(map = get_sortmap (dst))) {
391 snprintf (errbuf, errlen, _("%s: Unknown type."),
395 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
400 static void rx_to_string (char* dst, ssize_t dstlen,
401 struct option_t* option) {
402 rx_t* p = (rx_t*) option->data;
403 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
404 NONULL (p->pattern));
407 static int rx_from_string (struct option_t* dst, const char* val,
408 char* errbuf, ssize_t errlen) {
411 int flags = 0, e = 0, not = 0;
417 if (option (OPTATTACHMSG) && !m_strcmp(dst->option, "reply_regexp")) {
419 snprintf (errbuf, errlen,
420 "Operation not permitted when in attach-message mode.");
424 if (!((rx_t*) dst->data))
425 *((rx_t**) dst->data) = p_new(rx_t, 1);
427 p = (rx_t*) dst->data;
429 /* something to do? */
430 if (!val || !*val || (p->pattern && m_strcmp(p->pattern, val) == 0))
433 if (m_strcmp(dst->option, "mask") != 0)
434 flags |= mutt_which_case (val);
437 if (m_strcmp(dst->option, "mask") == 0 && *s == '!') {
442 rx = p_new(regex_t, 1);
444 if ((e = REGCOMP (rx, s, flags)) != 0) {
445 regerror (e, rx, errbuf, errlen);
456 m_strreplace(&p->pattern, val);
460 if (m_strcmp(dst->option, "reply_regexp") == 0)
461 mutt_adjust_all_subjects ();
466 static void magic_to_string (char* dst, ssize_t dstlen,
467 struct option_t* option) {
468 const char* s = NULL;
469 switch (option->data) {
470 case M_MBOX: s = "mbox"; break;
471 case M_MMDF: s = "MMDF"; break;
472 case M_MH: s = "MH"; break;
473 case M_MAILDIR: s = "Maildir"; break;
474 default: s = "unknown"; break;
476 snprintf (dst, dstlen, "%s=%s", option->option, s);
479 static int magic_from_string (struct option_t* dst, const char* val,
480 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
483 if (!dst || !val || !*val)
485 if (ascii_strncasecmp (val, "mbox", 4) == 0)
487 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
489 else if (ascii_strncasecmp (val, "mh", 2) == 0)
491 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
497 *((short*) dst->data) = flag;
502 static void addr_to_string (char* dst, ssize_t dstlen,
503 struct option_t* option) {
506 rfc822_write_address (s, sizeof(s), *((address_t**) option->data), 0);
507 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
510 static int addr_from_string (struct option_t* dst, const char* val,
511 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
514 address_list_wipe((address_t**) dst->data);
516 *((address_t**) dst->data) = rfc822_parse_adrlist (NULL, val);
520 int mutt_option_value (const char* val, char* dst, ssize_t dstlen) {
521 struct option_t* option = NULL;
522 char* tmp = NULL, *t = NULL;
525 if (!(option = hash_find (ConfigOptions, val))) {
529 tmp = p_new(char, dstlen+1);
530 FuncTable[DTYPE (option->type)].opt_to_string (tmp, dstlen, option);
532 /* as we get things of type $var=value and don't want to bloat the
533 * above "just" for expansion, we do the stripping here */
534 t = strchr (tmp, '=');
538 if (t[l-1] == '"' && *t == '"') {
543 memcpy (dst, t, l+1);
549 /* for synonym warning reports: adds synonym to end of list */
550 static void syn_add (struct option_t* n, struct option_t* o) {
551 syn_t* tmp = p_new(syn_t, 1);
552 tmp->f = m_strdup(CurRCFile);
556 list_push_back (&Synonyms, tmp);
559 /* for synonym warning reports: free single item (for list_del()) */
560 static void syn_del (void** p) {
561 p_delete(&(*(syn_t**) p)->f);
565 void toggle_quadoption (int opt)
568 int b = (opt % 4) * 2;
570 QuadOptions[n] ^= (1 << b);
573 void set_quadoption (int opt, int flag)
576 int b = (opt % 4) * 2;
578 QuadOptions[n] &= ~(0x3 << b);
579 QuadOptions[n] |= (flag & 0x3) << b;
582 int quadoption (int opt)
585 int b = (opt % 4) * 2;
587 return (QuadOptions[n] >> b) & 0x3;
590 int query_quadoption (int opt, const char *prompt)
592 int v = quadoption (opt);
600 v = mutt_yesorno (prompt, (v == M_ASKYES));
601 CLEARLINE (LINES - 1);
608 static void add_to_list (string_list_t ** list, const char *str)
610 string_list_t *t, *last = NULL;
612 /* don't add a NULL or empty string to the list */
613 if (!str || *str == '\0')
616 /* check to make sure the item is not already on this list */
617 for (last = *list; last; last = last->next) {
618 if (ascii_strcasecmp (str, last->data) == 0) {
619 /* already on the list, so just ignore it */
627 if (!*list || last) {
628 t = p_new(string_list_t, 1);
629 t->data = m_strdup(str);
639 static int add_to_rx_list (list2_t** list, const char *s, int flags,
648 if (!(rx = rx_compile (s, flags))) {
649 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
653 i = rx_lookup ((*list), rx->pattern);
657 list_push_back (list, rx);
661 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
662 const char *templ, BUFFER * err)
664 SPAM_LIST *t = NULL, *last = NULL;
669 if (!pat || !*pat || !templ)
672 if (!(rx = rx_compile (pat, REG_ICASE))) {
673 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
677 /* check to make sure the item is not already on this list */
678 for (last = *list; last; last = last->next) {
679 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
680 /* Already on the list. Formerly we just skipped this case, but
681 * now we're supporting removals, which means we're supporting
682 * re-adds conceptually. So we probably want this to imply a
683 * removal, then do an add. We can achieve the removal by freeing
684 * the template, and leaving t pointed at the current item.
687 p_delete(&t->template);
694 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
695 * update. Otherwise we want to make a new one to link at the list's end.
698 t = mutt_new_spam_list ();
706 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
707 t->template = m_strdup(templ);
709 /* Find highest match number in template string */
711 for (p = templ; *p;) {
716 while (*p && isdigit ((int) *p))
722 t->nmatch++; /* match 0 is always the whole expr */
727 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
729 SPAM_LIST *spam, *prev;
732 /* Being first is a special case. */
736 if (spam->rx && !m_strcmp(spam->rx->pattern, pat)) {
738 rx_delete(&spam->rx);
739 p_delete(&spam->template);
745 for (spam = prev->next; spam;) {
746 if (!m_strcmp(spam->rx->pattern, pat)) {
747 prev->next = spam->next;
748 rx_delete(&spam->rx);
749 p_delete(&spam->template);
762 static void remove_from_list (string_list_t ** l, const char *str)
764 string_list_t *p, *last = NULL;
766 if (m_strcmp("*", str) == 0)
767 string_list_wipe(l); /* ``unCMD *'' means delete all current entries */
772 if (ascii_strcasecmp (str, p->data) == 0) {
775 last->next = p->next;
788 static int remove_from_rx_list (list2_t** l, const char *str)
792 if (m_strcmp("*", str) == 0) {
793 list_del (l, (list_del_t*) rx_delete);
797 i = rx_lookup ((*l), str);
799 rx_t* r = list_pop_idx ((*l), i);
807 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
811 unsigned long res = 0;
813 struct option_t* option = NULL;
816 mutt_extract_token (tmp, s, 0);
818 /* is the item defined as a variable or a function? */
819 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL)
822 for (i = 0; !res && i < MENU_MAX; i++) {
823 struct binding_t *b = km_get_table (Menus[i].value);
828 for (j = 0; b[j].name; j++)
829 if (!ascii_strncasecmp (tmp->data, b[j].name, m_strlen(tmp->data))
830 && (m_strlen(b[j].name) == m_strlen(tmp->data))) {
836 /* check for feature_* */
837 if (!res && ascii_strncasecmp (tmp->data, "feature_", 8) == 0 &&
838 (j = m_strlen(tmp->data)) > 8) {
840 while (Features[i]) {
841 if (m_strlen(Features[i]) == j-8 &&
842 ascii_strncasecmp (Features[i], tmp->data+8, j-8) == 0) {
852 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
854 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
858 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
861 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
862 mutt_error ("Error: %s", err->data);
863 p_delete(&token.data);
866 p_delete(&token.data);
871 static int parse_unignore (BUFFER * buf, BUFFER * s,
872 unsigned long data __attribute__ ((unused)),
873 BUFFER * err __attribute__ ((unused)))
876 mutt_extract_token (buf, s, 0);
878 /* don't add "*" to the unignore list */
879 if (strcmp (buf->data, "*"))
880 add_to_list (&UnIgnore, buf->data);
882 remove_from_list (&Ignore, buf->data);
884 while (MoreArgs (s));
889 static int parse_ignore (BUFFER * buf, BUFFER * s,
890 unsigned long data __attribute__ ((unused)),
891 BUFFER * err __attribute__ ((unused)))
894 mutt_extract_token (buf, s, 0);
895 remove_from_list (&UnIgnore, buf->data);
896 add_to_list (&Ignore, buf->data);
898 while (MoreArgs (s));
903 static int parse_list (BUFFER * buf, BUFFER * s,
904 unsigned long data __attribute__ ((unused)),
905 BUFFER * err __attribute__ ((unused)))
908 mutt_extract_token (buf, s, 0);
909 add_to_list ((string_list_t **) data, buf->data);
911 while (MoreArgs (s));
916 static void _alternates_clean (void)
920 if (Context && Context->msgcount) {
921 for (i = 0; i < Context->msgcount; i++)
922 Context->hdrs[i]->recip_valid = 0;
926 static int parse_alternates (BUFFER * buf, BUFFER * s,
927 unsigned long data __attribute__ ((unused)),
928 BUFFER * err __attribute__ ((unused)))
930 _alternates_clean ();
932 mutt_extract_token (buf, s, 0);
933 remove_from_rx_list (&UnAlternates, buf->data);
935 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
938 while (MoreArgs (s));
943 static int parse_unalternates (BUFFER * buf, BUFFER * s,
944 unsigned long data __attribute__ ((unused)),
945 BUFFER * err __attribute__ ((unused)))
947 _alternates_clean ();
949 mutt_extract_token (buf, s, 0);
950 remove_from_rx_list (&Alternates, buf->data);
952 if (m_strcmp(buf->data, "*") &&
953 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
957 while (MoreArgs (s));
962 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
969 /* Insist on at least one parameter */
972 m_strcpy(err->data, err->dsize, _("spam: no matching pattern"));
974 m_strcpy(err->data, err->dsize, _("nospam: no matching pattern"));
978 /* Extract the first token, a regexp */
979 mutt_extract_token (buf, s, 0);
981 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
982 if (data == M_SPAM) {
983 /* If there's a second parameter, it's a template for the spam tag. */
985 mutt_extract_token (&templ, s, 0);
987 /* Add to the spam list. */
988 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
989 p_delete(&templ.data);
992 p_delete(&templ.data);
995 /* If not, try to remove from the nospam list. */
997 remove_from_rx_list (&NoSpamList, buf->data);
1003 /* M_NOSPAM is for nospam commands. */
1004 else if (data == M_NOSPAM) {
1005 /* nospam only ever has one parameter. */
1007 /* "*" is a special case. */
1008 if (!m_strcmp(buf->data, "*")) {
1009 mutt_free_spam_list (&SpamList);
1010 list_del (&NoSpamList, (list_del_t*) rx_delete);
1014 /* If it's on the spam list, just remove it. */
1015 if (remove_from_spam_list (&SpamList, buf->data) != 0)
1018 /* Otherwise, add it to the nospam list. */
1019 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
1025 /* This should not happen. */
1026 m_strcpy(err->data, err->dsize, "This is no good at all.");
1030 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
1031 BUFFER * err __attribute__ ((unused)))
1034 mutt_extract_token (buf, s, 0);
1036 * Check for deletion of entire list
1038 if (m_strcmp(buf->data, "*") == 0) {
1039 string_list_wipe((string_list_t **) data);
1042 remove_from_list ((string_list_t **) data, buf->data);
1044 while (MoreArgs (s));
1049 static int parse_lists (BUFFER * buf, BUFFER * s,
1050 unsigned long data __attribute__ ((unused)),
1054 mutt_extract_token (buf, s, 0);
1055 remove_from_rx_list (&UnMailLists, buf->data);
1057 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1060 while (MoreArgs (s));
1065 /* always wise to do what someone else did before */
1066 static void _attachments_clean (void) {
1068 if (Context && Context->msgcount) {
1069 for (i = 0; i < Context->msgcount; i++)
1070 Context->hdrs[i]->attach_valid = 0;
1074 static int parse_attach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
1075 BUFFER *err __attribute__ ((unused))) {
1077 string_list_t *listp, *lastp;
1082 /* Find the last item in the list that data points to. */
1084 for (listp = *ldata; listp; listp = listp->next) {
1085 a = (ATTACH_MATCH *)listp->data;
1090 mutt_extract_token (buf, s, 0);
1092 if (!buf->data || *buf->data == '\0')
1095 a = p_new(ATTACH_MATCH, 1);
1097 /* some cheap hacks that I expect to remove */
1098 if (!m_strcasecmp(buf->data, "any"))
1099 a->major = m_strdup("*/.*");
1100 else if (!m_strcasecmp(buf->data, "none"))
1101 a->major = m_strdup("cheap_hack/this_should_never_match");
1103 a->major = m_strdup(buf->data);
1105 if ((p = strchr(a->major, '/'))) {
1110 a->minor = "unknown";
1113 len = m_strlen(a->minor);
1114 tmpminor = p_new(char, len + 3);
1115 strcpy(&tmpminor[1], a->minor); /* __STRCPY_CHECKED__ */
1117 tmpminor[len+1] = '$';
1118 tmpminor[len+2] = '\0';
1120 a->major_int = mutt_check_mime_type(a->major);
1121 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
1123 p_delete(&tmpminor);
1125 listp = p_new(string_list_t, 1);
1126 listp->data = (char *)a;
1129 lastp->next = listp;
1135 while (MoreArgs (s));
1137 _attachments_clean();
1141 static int parse_unattach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
1142 BUFFER *err __attribute__ ((unused))) {
1144 string_list_t *lp, *lastp, *newlp;
1150 mutt_extract_token (buf, s, 0);
1152 if (!m_strcasecmp(buf->data, "any"))
1153 tmp = m_strdup("*/.*");
1154 else if (!m_strcasecmp(buf->data, "none"))
1155 tmp = m_strdup("cheap_hack/this_should_never_match");
1157 tmp = m_strdup(buf->data);
1159 if ((minor = strchr(tmp, '/'))) {
1163 minor = m_strdup("unknown");
1165 major = mutt_check_mime_type(tmp);
1167 /* We must do our own walk here because remove_from_list() will only
1168 * remove the string_list_t->data, not anything pointed to by the string_list_t->data. */
1170 for(lp = *ldata; lp; ) {
1171 a = (ATTACH_MATCH *)lp->data;
1172 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
1173 regfree(&a->minor_rx);
1174 p_delete(&a->major);
1176 /* Relink backward */
1178 lastp->next = lp->next;
1183 p_delete(&lp->data); /* same as a */
1193 while (MoreArgs (s));
1196 _attachments_clean();
1200 static int print_attach_list (string_list_t *lp, char op, const char *name) {
1202 printf("attachments %c%s %s/%s\n", op, name,
1203 ((ATTACH_MATCH *)lp->data)->major,
1204 ((ATTACH_MATCH *)lp->data)->minor);
1211 static int parse_attachments (BUFFER *buf, BUFFER *s,
1212 unsigned long data __attribute__ ((unused)),
1215 string_list_t **listp;
1217 mutt_extract_token(buf, s, 0);
1218 if (!buf->data || *buf->data == '\0') {
1219 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
1223 category = buf->data;
1229 printf("\nCurrent attachments settings:\n\n");
1230 print_attach_list(AttachAllow, '+', "A");
1231 print_attach_list(AttachExclude, '-', "A");
1232 print_attach_list(InlineAllow, '+', "I");
1233 print_attach_list(InlineExclude, '-', "I");
1234 set_option (OPTFORCEREDRAWINDEX);
1235 set_option (OPTFORCEREDRAWPAGER);
1236 mutt_any_key_to_continue (NULL);
1240 if (op != '+' && op != '-') {
1244 if (!m_strncasecmp(category, "attachment", strlen(category))) {
1246 listp = &AttachAllow;
1248 listp = &AttachExclude;
1250 else if (!m_strncasecmp(category, "inline", strlen(category))) {
1252 listp = &InlineAllow;
1254 listp = &InlineExclude;
1256 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
1260 return parse_attach_list(buf, s, listp, err);
1263 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
1265 string_list_t **listp;
1267 mutt_extract_token(buf, s, 0);
1268 if (!buf->data || *buf->data == '\0') {
1269 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
1275 if (op != '+' && op != '-') {
1279 if (!m_strncasecmp(p, "attachment", strlen(p))) {
1281 listp = &AttachAllow;
1283 listp = &AttachExclude;
1285 else if (!m_strncasecmp(p, "inline", strlen(p))) {
1287 listp = &InlineAllow;
1289 listp = &InlineExclude;
1292 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
1296 return parse_unattach_list(buf, s, listp, err);
1299 static int parse_unlists (BUFFER * buf, BUFFER * s,
1300 unsigned long data __attribute__ ((unused)),
1301 BUFFER * err __attribute__ ((unused)))
1304 mutt_extract_token (buf, s, 0);
1305 remove_from_rx_list (&SubscribedLists, buf->data);
1306 remove_from_rx_list (&MailLists, buf->data);
1308 if (m_strcmp(buf->data, "*") &&
1309 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1312 while (MoreArgs (s));
1317 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1321 mutt_extract_token (buf, s, 0);
1322 remove_from_rx_list (&UnMailLists, buf->data);
1323 remove_from_rx_list (&UnSubscribedLists, buf->data);
1325 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1327 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1330 while (MoreArgs (s));
1335 static int parse_unsubscribe (BUFFER * buf, BUFFER * s,
1336 unsigned long data __attribute__ ((unused)),
1337 BUFFER * err __attribute__ ((unused)))
1340 mutt_extract_token (buf, s, 0);
1341 remove_from_rx_list (&SubscribedLists, buf->data);
1343 if (m_strcmp(buf->data, "*") &&
1344 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1347 while (MoreArgs (s));
1352 static int parse_unalias (BUFFER * buf, BUFFER * s,
1353 unsigned long data __attribute__ ((unused)),
1354 BUFFER * err __attribute__ ((unused)))
1356 alias_t *tmp, *last = NULL;
1359 mutt_extract_token (buf, s, 0);
1361 if (m_strcmp("*", buf->data) == 0) {
1362 if (CurrentMenu == MENU_ALIAS) {
1363 for (tmp = Aliases; tmp; tmp = tmp->next)
1365 set_option (OPTFORCEREDRAWINDEX);
1368 alias_list_wipe(&Aliases);
1372 for (tmp = Aliases; tmp; tmp = tmp->next) {
1373 if (m_strcasecmp(buf->data, tmp->name) == 0) {
1374 if (CurrentMenu == MENU_ALIAS) {
1376 set_option (OPTFORCEREDRAWINDEX);
1381 last->next = tmp->next;
1383 Aliases = tmp->next;
1385 alias_list_wipe(&tmp);
1391 while (MoreArgs (s));
1395 static int parse_alias (BUFFER * buf, BUFFER * s,
1396 unsigned long data __attribute__ ((unused)),
1399 alias_t *tmp = Aliases;
1400 alias_t *last = NULL;
1403 if (!MoreArgs (s)) {
1404 m_strcpy(err->data, err->dsize, _("alias: no address"));
1408 mutt_extract_token (buf, s, 0);
1410 /* check to see if an alias with this name already exists */
1411 for (; tmp; tmp = tmp->next) {
1412 if (!m_strcasecmp(tmp->name, buf->data))
1418 /* create a new alias */
1420 tmp->name = m_strdup(buf->data);
1421 /* give the main addressbook code a chance */
1422 if (CurrentMenu == MENU_ALIAS)
1423 set_option (OPTMENUCALLER);
1426 /* override the previous value */
1427 address_list_wipe(&tmp->addr);
1428 if (CurrentMenu == MENU_ALIAS)
1429 set_option (OPTFORCEREDRAWINDEX);
1432 mutt_extract_token (buf, s,
1433 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1434 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1439 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1440 snprintf (err->data, err->dsize,
1441 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1450 parse_unmy_hdr (BUFFER * buf, BUFFER * s,
1451 unsigned long data __attribute__ ((unused)),
1452 BUFFER * err __attribute__ ((unused)))
1454 string_list_t *last = NULL;
1455 string_list_t *tmp = UserHeader;
1460 mutt_extract_token (buf, s, 0);
1461 if (m_strcmp("*", buf->data) == 0)
1462 string_list_wipe(&UserHeader);
1467 l = m_strlen(buf->data);
1468 if (buf->data[l - 1] == ':')
1472 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1473 && tmp->data[l] == ':') {
1476 last->next = tmp->next;
1478 UserHeader = tmp->next;
1481 string_list_wipe(&ptr);
1490 while (MoreArgs (s));
1494 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1501 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1502 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1503 m_strcpy(err->data, err->dsize, _("invalid header field"));
1506 keylen = p - buf->data + 1;
1509 for (tmp = UserHeader;; tmp = tmp->next) {
1510 /* see if there is already a field by this name */
1511 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1512 /* replace the old value */
1513 p_delete(&tmp->data);
1514 tmp->data = buf->data;
1521 tmp->next = string_item_new();
1525 tmp = string_item_new();
1528 tmp->data = buf->data;
1534 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1535 char* errbuf, ssize_t errlen) {
1538 if (m_strncmp("reverse-", s, 8) == 0) {
1540 flags = SORT_REVERSE;
1543 if (m_strncmp("last-", s, 5) == 0) {
1548 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1550 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1554 *((short*) dst->data) = i | flags;
1558 /* if additional data more == 1, we want to resolve synonyms */
1559 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
1561 char buf[LONG_STRING];
1562 struct option_t *ptr = p;
1564 if (DTYPE(ptr->type) == DT_SYN) {
1567 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1569 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_from_string)
1572 mutt_option_value(ptr->option, buf, sizeof(buf));
1573 if (m_strlen(ptr->init) == 0 && buf && *buf)
1574 ptr->init = m_strdup(buf);
1577 static struct option_t* add_option (const char* name, const char* init,
1578 short type, short dodup) {
1579 struct option_t* option = p_new(struct option_t, 1);
1581 option->option = m_strdup(name);
1582 option->type = type;
1584 option->init = dodup ? m_strdup(init) : (char*) init;
1588 /* creates new option_t* of type DT_USER for $user_ var */
1589 static struct option_t* add_user_option (const char* name) {
1590 return (add_option (name, NULL, DT_USER, 1));
1593 /* free()'s option_t* */
1594 static void del_option (void* p) {
1595 struct option_t *ptr = (struct option_t*) p;
1596 char* s = (char*) ptr->data;
1597 p_delete(&ptr->option);
1599 p_delete(&ptr->init);
1603 static int init_expand (char** dst, struct option_t* src) {
1609 if (DTYPE(src->type) == DT_STR ||
1610 DTYPE(src->type) == DT_PATH) {
1611 /* only expand for string as it's the only place where
1612 * we want to expand vars right now */
1613 if (src->init && *src->init) {
1616 len = m_strlen(src->init) + 2;
1617 in.data = p_new(char, len + 1);
1618 snprintf (in.data, len, "\"%s\"", src->init);
1621 mutt_extract_token (&token, &in, 0);
1622 if (token.data && *token.data)
1623 *dst = m_strdup(token.data);
1625 *dst = m_strdup("");
1627 p_delete(&token.data);
1629 *dst = m_strdup("");
1631 /* for non-string: take value as is */
1632 *dst = m_strdup(src->init);
1636 /* if additional data more == 1, we want to resolve synonyms */
1637 static void mutt_restore_default (const char* name __attribute__ ((unused)),
1638 void* p, unsigned long more) {
1639 char errbuf[STRING];
1640 struct option_t* ptr = (struct option_t*) p;
1643 if (DTYPE (ptr->type) == DT_SYN) {
1646 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1650 if (FuncTable[DTYPE (ptr->type)].opt_from_string) {
1651 init_expand (&init, ptr);
1652 if (!FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, init, errbuf,
1654 if (!option (OPTNOCURSES))
1656 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1657 "Please report this error: \"%s\"\n"),
1658 ptr->option, NONULL (init), errbuf);
1664 if (ptr->flags & R_INDEX)
1665 set_option (OPTFORCEREDRAWINDEX);
1666 if (ptr->flags & R_PAGER)
1667 set_option (OPTFORCEREDRAWPAGER);
1668 if (ptr->flags & R_RESORT_SUB)
1669 set_option (OPTSORTSUBTHREADS);
1670 if (ptr->flags & R_RESORT)
1671 set_option (OPTNEEDRESORT);
1672 if (ptr->flags & R_RESORT_INIT)
1673 set_option (OPTRESORTINIT);
1674 if (ptr->flags & R_TREE)
1675 set_option (OPTREDRAWTREE);
1678 /* check whether value for $dsn_return would be valid */
1679 static int check_dsn_return (const char* option __attribute__ ((unused)), unsigned long p,
1680 char* errbuf, ssize_t errlen) {
1681 char* val = (char*) p;
1682 if (val && *val && m_strncmp(val, "hdrs", 4) != 0 &&
1683 m_strncmp(val, "full", 4) != 0) {
1685 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1691 /* check whether value for $dsn_notify would be valid */
1692 static int check_dsn_notify (const char* option __attribute__ ((unused)), unsigned long p,
1693 char* errbuf, ssize_t errlen) {
1694 list2_t* list = NULL;
1697 char* val = (char*) p;
1701 list = list_from_str (val, ",");
1702 if (list_empty (list))
1705 for (i = 0; i < list->length; i++)
1706 if (m_strncmp(list->data[i], "never", 5) != 0 &&
1707 m_strncmp(list->data[i], "failure", 7) != 0 &&
1708 m_strncmp(list->data[i], "delay", 5) != 0 &&
1709 m_strncmp(list->data[i], "success", 7) != 0) {
1711 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1712 (char*) list->data[i], "dsn_notify");
1716 list_del (&list, (list_del_t*)xmemfree);
1720 static int check_num (const char* option, unsigned long p,
1721 char* errbuf, ssize_t errlen) {
1724 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1730 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1731 char* errbuf, ssize_t errlen) {
1732 if (!check_num ("history", p, errbuf, errlen))
1734 mutt_init_history ();
1738 static int check_special (const char* name, unsigned long val,
1739 char* errbuf, ssize_t errlen) {
1742 for (i = 0; SpecialVars[i].name; i++) {
1743 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1744 return (SpecialVars[i].check (SpecialVars[i].name,
1745 val, errbuf, errlen));
1751 static const struct mapping_t* get_sortmap (struct option_t* option) {
1752 const struct mapping_t* map = NULL;
1754 switch (option->type & DT_SUBTYPE_MASK) {
1756 map = SortAliasMethods;
1758 case DT_SORT_BROWSER:
1759 map = SortBrowserMethods;
1762 map = SortKeyMethods;
1765 map = SortAuxMethods;
1774 #define CHECK_PAGER \
1775 if ((CurrentMenu == MENU_PAGER) && \
1776 (!option || (option->flags & R_RESORT))) \
1778 snprintf (err->data, err->dsize, \
1779 _("Not available in this menu.")); \
1783 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1786 int query, unset, inv, reset, r = 0;
1787 struct option_t* option = NULL;
1789 while (MoreArgs (s)) {
1790 /* reset state variables */
1792 unset = data & M_SET_UNSET;
1793 inv = data & M_SET_INV;
1794 reset = data & M_SET_RESET;
1796 if (*s->dptr == '?') {
1800 else if (m_strncmp("no", s->dptr, 2) == 0) {
1804 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1808 else if (*s->dptr == '&') {
1813 /* get the variable name */
1814 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1816 /* resolve synonyms */
1817 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1818 DTYPE (option->type == DT_SYN)) {
1819 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1820 syn_add (newopt, option);
1824 /* see if we need to add $user_ var */
1825 if (!option && ascii_strncmp ("user_", tmp->data, 5) == 0) {
1826 /* there's no option named like this yet so only add one
1827 * if the action isn't any of: reset, unset, query */
1828 if (!(reset || unset || query || *s->dptr != '=')) {
1829 option = add_user_option (tmp->data);
1830 hash_insert (ConfigOptions, option->option, option, 0);
1834 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1835 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1838 s->dptr = vskipspaces(s->dptr);
1841 if (query || unset || inv) {
1842 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1846 if (s && *s->dptr == '=') {
1847 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1851 if (!m_strcmp("all", tmp->data)) {
1852 if (CurrentMenu == MENU_PAGER) {
1853 snprintf (err->data, err->dsize, _("Not available in this menu."));
1856 hash_map (ConfigOptions, mutt_restore_default, 1);
1857 set_option (OPTFORCEREDRAWINDEX);
1858 set_option (OPTFORCEREDRAWPAGER);
1859 set_option (OPTSORTSUBTHREADS);
1860 set_option (OPTNEEDRESORT);
1861 set_option (OPTRESORTINIT);
1862 set_option (OPTREDRAWTREE);
1865 else if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1866 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1871 mutt_restore_default (NULL, option, 1);
1874 else if (DTYPE (option->type) == DT_BOOL) {
1875 /* XXX this currently ignores the function table
1876 * as we don't get invert and stuff into it */
1877 if (s && *s->dptr == '=') {
1878 if (unset || inv || query) {
1879 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1884 mutt_extract_token (tmp, s, 0);
1885 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1887 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1890 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1896 bool_to_string (err->data, err->dsize, option);
1902 unset_option (option->data);
1904 toggle_option (option->data);
1906 set_option (option->data);
1908 else if (DTYPE (option->type) == DT_STR ||
1909 DTYPE (option->type) == DT_PATH ||
1910 DTYPE (option->type) == DT_ADDR ||
1911 DTYPE (option->type) == DT_MAGIC ||
1912 DTYPE (option->type) == DT_NUM ||
1913 DTYPE (option->type) == DT_SORT ||
1914 DTYPE (option->type) == DT_RX ||
1915 DTYPE (option->type) == DT_USER ||
1916 DTYPE (option->type) == DT_SYS) {
1918 /* XXX maybe we need to get unset into handlers? */
1919 if (DTYPE (option->type) == DT_STR ||
1920 DTYPE (option->type) == DT_PATH ||
1921 DTYPE (option->type) == DT_ADDR ||
1922 DTYPE (option->type) == DT_USER ||
1923 DTYPE (option->type) == DT_SYS) {
1926 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1927 snprintf (err->data, err->dsize, _("$%s is read-only"),
1931 } else if (DTYPE (option->type) == DT_ADDR)
1932 address_list_wipe((address_t **) option->data);
1933 else if (DTYPE (option->type) == DT_USER)
1934 /* to unset $user_ means remove */
1935 hash_delete (ConfigOptions, option->option,
1936 option, del_option);
1938 p_delete((void **)(void *)&option->data);
1943 if (query || *s->dptr != '=') {
1944 FuncTable[DTYPE (option->type)].opt_to_string
1945 (err->data, err->dsize, option);
1949 /* the $madmutt_ variables are read-only */
1950 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1951 snprintf (err->data, err->dsize, _("$%s is read-only"),
1958 mutt_extract_token (tmp, s, 0);
1959 if (!FuncTable[DTYPE (option->type)].opt_from_string
1960 (option, tmp->data, err->data, err->dsize))
1964 else if (DTYPE (option->type) == DT_QUAD) {
1967 quad_to_string (err->data, err->dsize, option);
1971 if (*s->dptr == '=') {
1974 mutt_extract_token (tmp, s, 0);
1975 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1976 set_quadoption (option->data, M_YES);
1977 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1978 set_quadoption (option->data, M_NO);
1979 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1980 set_quadoption (option->data, M_ASKYES);
1981 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1982 set_quadoption (option->data, M_ASKNO);
1984 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1985 tmp->data, option->option);
1992 toggle_quadoption (option->data);
1994 set_quadoption (option->data, M_NO);
1996 set_quadoption (option->data, M_YES);
2000 snprintf (err->data, err->dsize, _("%s: unknown type"),
2006 if (option->flags & R_INDEX)
2007 set_option (OPTFORCEREDRAWINDEX);
2008 if (option->flags & R_PAGER)
2009 set_option (OPTFORCEREDRAWPAGER);
2010 if (option->flags & R_RESORT_SUB)
2011 set_option (OPTSORTSUBTHREADS);
2012 if (option->flags & R_RESORT)
2013 set_option (OPTNEEDRESORT);
2014 if (option->flags & R_RESORT_INIT)
2015 set_option (OPTRESORTINIT);
2016 if (option->flags & R_TREE)
2017 set_option (OPTREDRAWTREE);
2024 /* reads the specified initialization file. returns -1 if errors were found
2025 so that we can pause to let the user know... */
2026 static int source_rc (const char *rcfile, BUFFER * err)
2029 int line = 0, rc = 0, conv = 0;
2031 char *linebuf = NULL;
2032 char *currentline = NULL;
2036 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
2037 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
2042 while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
2043 conv = ConfigCharset && (*ConfigCharset) && Charset;
2045 currentline = m_strdup(linebuf);
2048 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
2051 currentline = linebuf;
2056 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
2057 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
2058 if (--rc < -MAXERRS) {
2060 p_delete(¤tline);
2069 p_delete(¤tline);
2071 p_delete(&token.data);
2075 mutt_wait_filter (pid);
2077 /* the muttrc source keyword */
2078 snprintf (err->data, err->dsize,
2079 rc >= -MAXERRS ? _("source: errors in %s")
2080 : _("source: reading aborted due too many errors in %s"),
2089 static int parse_source (BUFFER * tmp, BUFFER * s,
2090 unsigned long data __attribute__ ((unused)),
2093 char path[_POSIX_PATH_MAX];
2097 if (mutt_extract_token (tmp, s, 0) != 0) {
2098 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
2102 m_strcpy(path, sizeof(path), tmp->data);
2103 mutt_expand_path (path, sizeof(path));
2105 rc += source_rc (path, err);
2107 while (MoreArgs (s));
2109 return ((rc < 0) ? -1 : 0);
2112 /* line command to execute
2114 token scratch buffer to be used by parser. caller should free
2115 token->data when finished. the reason for this variable is
2116 to avoid having to allocate and deallocate a lot of memory
2117 if we are parsing many lines. the caller can pass in the
2118 memory to use, which avoids having to create new space for
2119 every call to this function.
2121 err where to write error messages */
2122 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
2128 expn.data = expn.dptr = line;
2129 expn.dsize = m_strlen(line);
2133 expn.dptr = vskipspaces(expn.dptr);
2134 while (*expn.dptr) {
2135 if (*expn.dptr == '#')
2136 break; /* rest of line is a comment */
2137 if (*expn.dptr == ';') {
2141 mutt_extract_token (token, &expn, 0);
2142 for (i = 0; Commands[i].name; i++) {
2143 if (!m_strcmp(token->data, Commands[i].name)) {
2144 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
2149 if (!Commands[i].name) {
2150 snprintf (err->data, err->dsize, _("%s: unknown command"),
2151 NONULL (token->data));
2158 p_delete(&expn.data);
2163 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
2164 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
2165 /* initial string that starts completion. No telling how much crap
2166 * the user has typed so far. Allocate LONG_STRING just to be sure! */
2167 char User_typed[LONG_STRING] = { 0 };
2169 int Num_matched = 0; /* Number of matches for completion */
2170 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
2171 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
2173 /* helper function for completion. Changes the dest buffer if
2174 necessary/possible to aid completion.
2175 dest == completion result gets here.
2176 src == candidate for completion.
2177 try == user entered data for completion.
2178 len == length of dest buffer.
2180 static void candidate (char *dest, char *try, const char *src, int len)
2184 if (strstr (src, try) == src) {
2185 Matches[Num_matched++] = src;
2187 m_strcpy(dest, len, src);
2189 for (l = 0; src[l] && src[l] == dest[l]; l++);
2195 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
2199 int spaces; /* keep track of the number of leading spaces on the line */
2201 buffer = vskipspaces(buffer);
2202 spaces = buffer - pt;
2204 pt = buffer + pos - spaces;
2205 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2208 if (pt == buffer) { /* complete cmd */
2209 /* first TAB. Collect all the matches */
2212 m_strcpy(User_typed, sizeof(User_typed), pt);
2213 p_clear(Matches, countof(Matches));
2214 p_clear(Completed, countof(Completed));
2215 for (num = 0; Commands[num].name; num++)
2216 candidate (Completed, User_typed, Commands[num].name,
2218 Matches[Num_matched++] = User_typed;
2220 /* All matches are stored. Longest non-ambiguous string is ""
2221 * i.e. dont change 'buffer'. Fake successful return this time */
2222 if (User_typed[0] == 0)
2226 if (Completed[0] == 0 && User_typed[0])
2229 /* Num_matched will _always_ be atleast 1 since the initial
2230 * user-typed string is always stored */
2231 if (numtabs == 1 && Num_matched == 2)
2232 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2233 else if (numtabs > 1 && Num_matched > 2)
2234 /* cycle thru all the matches */
2235 snprintf (Completed, sizeof(Completed), "%s",
2236 Matches[(numtabs - 2) % Num_matched]);
2238 /* return the completed command */
2239 m_strcpy(buffer, len - spaces, Completed);
2241 else if (!m_strncmp(buffer, "set", 3)
2242 || !m_strncmp(buffer, "unset", 5)
2243 || !m_strncmp(buffer, "reset", 5)
2244 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
2245 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
2248 /* loop through all the possible prefixes (no, inv, ...) */
2249 if (!m_strncmp(buffer, "set", 3)) {
2250 for (num = 0; prefixes[num]; num++) {
2251 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
2252 pt += m_strlen(prefixes[num]);
2258 /* first TAB. Collect all the matches */
2261 m_strcpy(User_typed, sizeof(User_typed), pt);
2262 p_clear(Matches, countof(Matches));
2263 p_clear(Completed, countof(Completed));
2264 for (num = 0; MuttVars[num].option; num++)
2265 candidate(Completed, User_typed, MuttVars[num].option,
2267 Matches[Num_matched++] = User_typed;
2269 /* All matches are stored. Longest non-ambiguous string is ""
2270 * i.e. dont change 'buffer'. Fake successful return this time */
2271 if (User_typed[0] == 0)
2275 if (Completed[0] == 0 && User_typed[0])
2278 /* Num_matched will _always_ be atleast 1 since the initial
2279 * user-typed string is always stored */
2280 if (numtabs == 1 && Num_matched == 2)
2281 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2282 else if (numtabs > 1 && Num_matched > 2)
2283 /* cycle thru all the matches */
2284 snprintf (Completed, sizeof(Completed), "%s",
2285 Matches[(numtabs - 2) % Num_matched]);
2287 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2289 else if (!m_strncmp(buffer, "exec", 4)) {
2290 struct binding_t *menu = km_get_table (CurrentMenu);
2292 if (!menu && CurrentMenu != MENU_PAGER)
2296 /* first TAB. Collect all the matches */
2299 m_strcpy(User_typed, sizeof(User_typed), pt);
2300 p_clear(Matches, countof(Matches));
2301 p_clear(Completed, countof(Completed));
2302 for (num = 0; menu[num].name; num++)
2303 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
2304 /* try the generic menu */
2305 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2307 for (num = 0; menu[num].name; num++)
2308 candidate (Completed, User_typed, menu[num].name,
2311 Matches[Num_matched++] = User_typed;
2313 /* All matches are stored. Longest non-ambiguous string is ""
2314 * i.e. dont change 'buffer'. Fake successful return this time */
2315 if (User_typed[0] == 0)
2319 if (Completed[0] == 0 && User_typed[0])
2322 /* Num_matched will _always_ be atleast 1 since the initial
2323 * user-typed string is always stored */
2324 if (numtabs == 1 && Num_matched == 2)
2325 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2326 else if (numtabs > 1 && Num_matched > 2)
2327 /* cycle thru all the matches */
2328 snprintf (Completed, sizeof(Completed), "%s",
2329 Matches[(numtabs - 2) % Num_matched]);
2331 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2339 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
2341 char var[STRING], *pt = buffer;
2343 struct option_t* option = NULL;
2348 buffer = vskipspaces(buffer);
2349 spaces = buffer - pt;
2351 pt = buffer + pos - spaces;
2352 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2354 pt++; /* move past the space */
2355 if (*pt == '=') /* abort if no var before the '=' */
2358 if (m_strncmp(buffer, "set", 3) == 0) {
2359 m_strcpy(var, sizeof(var), pt);
2360 /* ignore the trailing '=' when comparing */
2361 var[m_strlen(var) - 1] = 0;
2362 if (!(option = hash_find (ConfigOptions, var)))
2363 return 0; /* no such variable. */
2365 char tmp[LONG_STRING], tmp2[LONG_STRING];
2367 ssize_t dlen = buffer + len - pt - spaces;
2368 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2372 if ((DTYPE (option->type) == DT_STR) ||
2373 (DTYPE (option->type) == DT_PATH) ||
2374 (DTYPE (option->type) == DT_RX)) {
2375 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
2376 if (DTYPE (option->type) == DT_PATH)
2377 mutt_pretty_mailbox (tmp);
2379 else if (DTYPE (option->type) == DT_ADDR) {
2380 rfc822_write_address (tmp, sizeof(tmp),
2381 *((address_t **) option->data), 0);
2383 else if (DTYPE (option->type) == DT_QUAD)
2384 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
2385 else if (DTYPE (option->type) == DT_NUM)
2386 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
2387 else if (DTYPE (option->type) == DT_SORT) {
2388 const struct mapping_t *map;
2391 switch (option->type & DT_SUBTYPE_MASK) {
2393 map = SortAliasMethods;
2395 case DT_SORT_BROWSER:
2396 map = SortBrowserMethods;
2399 map = SortKeyMethods;
2405 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2406 snprintf(tmp, sizeof(tmp), "%s%s%s",
2407 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2408 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2410 else if (DTYPE (option->type) == DT_MAGIC) {
2412 switch (DefaultMagic) {
2428 m_strcpy(tmp, sizeof(tmp), p);
2430 else if (DTYPE (option->type) == DT_BOOL)
2431 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
2435 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
2436 if (*s == '\\' || *s == '"')
2442 m_strcpy(tmp, sizeof(tmp), pt);
2443 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2451 /* Implement the -Q command line flag */
2452 int mutt_query_variables (string_list_t * queries)
2456 char errbuff[STRING];
2457 char command[STRING];
2465 err.dsize = sizeof(errbuff);
2467 for (p = queries; p; p = p->next) {
2468 snprintf (command, sizeof(command), "set ?%s\n", p->data);
2469 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2470 fprintf (stderr, "%s\n", err.data);
2471 p_delete(&token.data);
2474 printf ("%s\n", err.data);
2477 p_delete(&token.data);
2481 static int mutt_execute_commands (string_list_t * p)
2484 char errstr[SHORT_STRING];
2488 err.dsize = sizeof(errstr);
2490 for (; p; p = p->next) {
2491 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2492 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2493 p_delete(&token.data);
2497 p_delete(&token.data);
2501 void mutt_init (int skip_sys_rc, string_list_t * commands)
2504 struct utsname utsname;
2506 char buffer[STRING], error[STRING];
2507 int default_rc = 0, need_pause = 0;
2513 err.dsize = sizeof(error);
2515 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2516 * to have some room for $user_ vars */
2517 ConfigOptions = hash_create (sizeof(MuttVars) * 3);
2518 for (i = 0; MuttVars[i].option; i++) {
2519 if (DTYPE (MuttVars[i].type) != DT_SYS)
2520 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2522 hash_insert (ConfigOptions, MuttVars[i].option,
2523 add_option (MuttVars[i].option, MuttVars[i].init,
2528 * XXX - use something even more difficult to predict?
2530 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2531 "\033]9;%ld\a", (long) time (NULL));
2533 /* on one of the systems I use, getcwd() does not return the same prefix
2534 as is listed in the passwd file */
2535 if ((p = getenv ("HOME")))
2536 Homedir = m_strdup(p);
2538 /* Get some information about the user */
2539 if ((pw = getpwuid (getuid ()))) {
2542 Username = m_strdup(pw->pw_name);
2544 Homedir = m_strdup(pw->pw_dir);
2546 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, GecosMask.rx);
2547 Realname = m_strdup(rnbuf);
2548 Shell = m_strdup(pw->pw_shell);
2554 fputs (_("unable to determine home directory"), stderr);
2557 if ((p = getenv ("USER")))
2558 Username = m_strdup(p);
2561 fputs (_("unable to determine username"), stderr);
2564 Shell = m_strdup((p = getenv ("SHELL")) ? p : "/bin/sh");
2567 /* And about the host... */
2569 /* some systems report the FQDN instead of just the hostname */
2570 if ((p = strchr (utsname.nodename, '.'))) {
2571 Hostname = p_dupstr(utsname.nodename, p - utsname.nodename);
2573 m_strcpy(buffer, sizeof(buffer), p); /* save the domain for below */
2576 Hostname = m_strdup(utsname.nodename);
2578 if (!p && getdnsdomainname(buffer, sizeof(buffer)) == -1)
2579 Fqdn = m_strdup("@");
2581 if (*buffer != '@') {
2582 Fqdn = p_new(char, m_strlen(buffer) + m_strlen(Hostname) + 2);
2583 sprintf (Fqdn, "%s.%s", NONULL(Hostname), buffer); /* __SPRINTF_CHECKED__ */
2586 Fqdn = m_strdup(NONULL (Hostname));
2593 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2595 fgets (buffer, sizeof(buffer), f);
2596 p = vskipspaces(buffer);
2598 while (*q && !isspace(*q))
2601 NewsServer = m_strdup(p);
2605 if ((p = getenv ("NNTPSERVER")))
2606 NewsServer = m_strdup(p);
2609 if ((p = getenv ("MAIL")))
2610 Spoolfile = m_strdup(p);
2611 else if ((p = getenv ("MAILDIR")))
2612 Spoolfile = m_strdup(p);
2615 mutt_concat_path(buffer, sizeof(buffer), NONULL(Homedir), MAILPATH);
2617 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(Username));
2619 Spoolfile = m_strdup(buffer);
2622 if ((p = getenv ("MAILCAPS")))
2623 MailcapPath = m_strdup(p);
2625 /* Default search path from RFC1524 */
2627 m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2628 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2631 Tempdir = m_strdup((p = getenv ("TMPDIR")) ? p : "/tmp");
2633 p = getenv ("VISUAL");
2635 p = getenv ("EDITOR");
2639 Editor = m_strdup(p);
2641 if ((p = getenv ("REPLYTO")) != NULL) {
2644 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2647 buf.data = buf.dptr = buffer;
2648 buf.dsize = m_strlen(buffer);
2651 parse_my_hdr (&token, &buf, 0, &err);
2652 p_delete(&token.data);
2655 if ((p = getenv ("EMAIL")) != NULL)
2656 From = rfc822_parse_adrlist (NULL, p);
2658 charset_initialize();
2660 /* Set standard defaults */
2661 hash_map (ConfigOptions, mutt_set_default, 0);
2662 hash_map (ConfigOptions, mutt_restore_default, 0);
2664 CurrentMenu = MENU_MAIN;
2667 /* Unset suspend by default if we're the session leader */
2668 if (getsid (0) == getpid ())
2669 unset_option (OPTSUSPEND);
2672 mutt_init_history ();
2676 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc-%s", NONULL (Homedir),
2678 if (access (buffer, F_OK) == -1)
2680 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL (Homedir));
2681 if (access (buffer, F_OK) == -1)
2683 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc-%s",
2684 NONULL (Homedir), MUTT_VERSION);
2685 if (access (buffer, F_OK) == -1)
2687 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
2691 Muttrc = m_strdup(buffer);
2694 m_strcpy(buffer, sizeof(buffer), Muttrc);
2696 mutt_expand_path (buffer, sizeof(buffer));
2697 Muttrc = m_strdup(buffer);
2699 p_delete(&AliasFile);
2700 AliasFile = m_strdup(NONULL (Muttrc));
2702 /* Process the global rc file if it exists and the user hasn't explicity
2703 requested not to via "-n". */
2705 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
2707 if (access (buffer, F_OK) == -1)
2708 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
2709 if (access (buffer, F_OK) == -1)
2710 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
2712 if (access (buffer, F_OK) == -1)
2713 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
2714 if (access (buffer, F_OK) != -1) {
2715 if (source_rc (buffer, &err) != 0) {
2716 fputs (err.data, stderr);
2717 fputc ('\n', stderr);
2723 /* Read the user's initialization file. */
2724 if (access (Muttrc, F_OK) != -1) {
2725 if (!option (OPTNOCURSES))
2727 if (source_rc (Muttrc, &err) != 0) {
2728 fputs (err.data, stderr);
2729 fputc ('\n', stderr);
2733 else if (!default_rc) {
2734 /* file specified by -F does not exist */
2735 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2736 mutt_endwin (buffer);
2740 if (mutt_execute_commands (commands) != 0)
2743 /* warn about synonym variables */
2744 if (!list_empty(Synonyms)) {
2746 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2747 for (i = 0; i < Synonyms->length; i++) {
2748 struct option_t* newopt = NULL, *oldopt = NULL;
2749 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2750 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2751 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2752 oldopt ? NONULL (oldopt->option) : "",
2753 newopt ? NONULL (newopt->option) : "",
2754 NONULL(((syn_t*) Synonyms->data[i])->f),
2755 ((syn_t*) Synonyms->data[i])->l);
2757 fprintf (stderr, _("Warning: synonym variables are scheduled"
2758 " for removal.\n"));
2759 list_del (&Synonyms, syn_del);
2763 if (need_pause && !option (OPTNOCURSES)) {
2764 if (mutt_any_key_to_continue (NULL) == -1)
2769 set_option (OPTWEED); /* turn weeding on by default */
2773 int mutt_get_hook_type (const char *name)
2775 struct command_t *c;
2777 for (c = Commands; c->name; c++)
2778 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2783 /* compare two option_t*'s for sorting -t/-T output */
2784 static int opt_cmp (const void* a, const void* b) {
2785 return (m_strcmp((*(struct option_t**) a)->option,
2786 (*(struct option_t**) b)->option));
2789 /* callback for hash_map() to put all non-synonym vars into list */
2790 static void opt_sel_full (const char* key __attribute__ ((unused)),
2792 unsigned long more) {
2793 list2_t** l = (list2_t**) more;
2794 struct option_t* option = (struct option_t*) data;
2796 if (DTYPE (option->type) == DT_SYN)
2798 list_push_back (l, option);
2801 /* callback for hash_map() to put all changed non-synonym vars into list */
2802 static void opt_sel_diff (const char* key __attribute__ ((unused)),
2804 unsigned long more) {
2805 list2_t** l = (list2_t**) more;
2806 struct option_t* option = (struct option_t*) data;
2807 char buf[LONG_STRING];
2809 if (DTYPE (option->type) == DT_SYN)
2812 mutt_option_value (option->option, buf, sizeof(buf));
2813 if (m_strcmp(buf, option->init) != 0)
2814 list_push_back (l, option);
2817 /* dump out the value of all the variables we have */
2818 int mutt_dump_variables (int full) {
2820 char outbuf[STRING];
2821 list2_t* tmp = NULL;
2822 struct option_t* option = NULL;
2824 /* get all non-synonyms into list... */
2825 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2826 (unsigned long) &tmp);
2828 if (!list_empty(tmp)) {
2829 /* ...and dump list sorted */
2830 qsort (tmp->data, tmp->length, sizeof(void*), opt_cmp);
2831 for (i = 0; i < tmp->length; i++) {
2832 option = (struct option_t*) tmp->data[i];
2833 FuncTable[DTYPE (option->type)].opt_to_string
2834 (outbuf, sizeof(outbuf), option);
2835 printf ("%s\n", outbuf);
2838 list_del (&tmp, NULL);