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 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);
628 static int add_to_rx_list (list2_t** list, const char *s, int flags,
637 if (!(rx = rx_compile (s, flags))) {
638 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
642 i = rx_lookup ((*list), rx->pattern);
646 list_push_back (list, rx);
650 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
651 const char *templ, BUFFER * err)
653 SPAM_LIST *t = NULL, *last = NULL;
658 if (!pat || !*pat || !templ)
661 if (!(rx = rx_compile (pat, REG_ICASE))) {
662 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
666 /* check to make sure the item is not already on this list */
667 for (last = *list; last; last = last->next) {
668 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
669 /* Already on the list. Formerly we just skipped this case, but
670 * now we're supporting removals, which means we're supporting
671 * re-adds conceptually. So we probably want this to imply a
672 * removal, then do an add. We can achieve the removal by freeing
673 * the template, and leaving t pointed at the current item.
676 p_delete(&t->template);
683 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
684 * update. Otherwise we want to make a new one to link at the list's end.
687 t = mutt_new_spam_list ();
695 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
696 t->template = m_strdup(templ);
698 /* Find highest match number in template string */
700 for (p = templ; *p;) {
705 while (*p && isdigit ((int) *p))
711 t->nmatch++; /* match 0 is always the whole expr */
716 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
718 SPAM_LIST *spam, *prev;
721 /* Being first is a special case. */
725 if (spam->rx && !m_strcmp(spam->rx->pattern, pat)) {
727 rx_delete(&spam->rx);
728 p_delete(&spam->template);
734 for (spam = prev->next; spam;) {
735 if (!m_strcmp(spam->rx->pattern, pat)) {
736 prev->next = spam->next;
737 rx_delete(&spam->rx);
738 p_delete(&spam->template);
751 static void remove_from_list (string_list_t ** l, const char *str)
753 string_list_t *p, *last = NULL;
755 if (m_strcmp("*", str) == 0)
756 string_list_wipe(l); /* ``unCMD *'' means delete all current entries */
761 if (ascii_strcasecmp (str, p->data) == 0) {
764 last->next = p->next;
777 static int remove_from_rx_list (list2_t** l, const char *str)
781 if (m_strcmp("*", str) == 0) {
782 list_del (l, (list_del_t*) rx_delete);
786 i = rx_lookup ((*l), str);
788 rx_t* r = list_pop_idx ((*l), i);
796 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
800 unsigned long res = 0;
802 struct option_t* option = NULL;
805 mutt_extract_token (tmp, s, 0);
807 /* is the item defined as a variable or a function? */
808 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL)
811 for (i = 0; !res && i < MENU_MAX; i++) {
812 struct binding_t *b = km_get_table (Menus[i].value);
817 for (j = 0; b[j].name; j++)
818 if (!ascii_strncasecmp (tmp->data, b[j].name, m_strlen(tmp->data))
819 && (m_strlen(b[j].name) == m_strlen(tmp->data))) {
825 /* check for feature_* */
826 if (!res && ascii_strncasecmp (tmp->data, "feature_", 8) == 0 &&
827 (j = m_strlen(tmp->data)) > 8) {
829 while (Features[i]) {
830 if (m_strlen(Features[i]) == j-8 &&
831 ascii_strncasecmp (Features[i], tmp->data+8, j-8) == 0) {
841 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
843 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
847 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
850 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
851 mutt_error ("Error: %s", err->data);
852 p_delete(&token.data);
855 p_delete(&token.data);
860 static int parse_unignore (BUFFER * buf, BUFFER * s,
861 unsigned long data __attribute__ ((unused)),
862 BUFFER * err __attribute__ ((unused)))
865 mutt_extract_token (buf, s, 0);
867 /* don't add "*" to the unignore list */
868 if (strcmp (buf->data, "*"))
869 add_to_list (&UnIgnore, buf->data);
871 remove_from_list (&Ignore, buf->data);
873 while (MoreArgs (s));
878 static int parse_ignore (BUFFER * buf, BUFFER * s,
879 unsigned long data __attribute__ ((unused)),
880 BUFFER * err __attribute__ ((unused)))
883 mutt_extract_token (buf, s, 0);
884 remove_from_list (&UnIgnore, buf->data);
885 add_to_list (&Ignore, buf->data);
887 while (MoreArgs (s));
892 static int parse_list (BUFFER * buf, BUFFER * s,
893 unsigned long data __attribute__ ((unused)),
894 BUFFER * err __attribute__ ((unused)))
897 mutt_extract_token (buf, s, 0);
898 add_to_list ((string_list_t **) data, buf->data);
900 while (MoreArgs (s));
905 static void _alternates_clean (void)
909 if (Context && Context->msgcount) {
910 for (i = 0; i < Context->msgcount; i++)
911 Context->hdrs[i]->recip_valid = 0;
915 static int parse_alternates (BUFFER * buf, BUFFER * s,
916 unsigned long data __attribute__ ((unused)),
917 BUFFER * err __attribute__ ((unused)))
919 _alternates_clean ();
921 mutt_extract_token (buf, s, 0);
922 remove_from_rx_list (&UnAlternates, buf->data);
924 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
927 while (MoreArgs (s));
932 static int parse_unalternates (BUFFER * buf, BUFFER * s,
933 unsigned long data __attribute__ ((unused)),
934 BUFFER * err __attribute__ ((unused)))
936 _alternates_clean ();
938 mutt_extract_token (buf, s, 0);
939 remove_from_rx_list (&Alternates, buf->data);
941 if (m_strcmp(buf->data, "*") &&
942 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
946 while (MoreArgs (s));
951 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
958 /* Insist on at least one parameter */
961 m_strcpy(err->data, err->dsize, _("spam: no matching pattern"));
963 m_strcpy(err->data, err->dsize, _("nospam: no matching pattern"));
967 /* Extract the first token, a regexp */
968 mutt_extract_token (buf, s, 0);
970 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
971 if (data == M_SPAM) {
972 /* If there's a second parameter, it's a template for the spam tag. */
974 mutt_extract_token (&templ, s, 0);
976 /* Add to the spam list. */
977 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
978 p_delete(&templ.data);
981 p_delete(&templ.data);
984 /* If not, try to remove from the nospam list. */
986 remove_from_rx_list (&NoSpamList, buf->data);
992 /* M_NOSPAM is for nospam commands. */
993 else if (data == M_NOSPAM) {
994 /* nospam only ever has one parameter. */
996 /* "*" is a special case. */
997 if (!m_strcmp(buf->data, "*")) {
998 mutt_free_spam_list (&SpamList);
999 list_del (&NoSpamList, (list_del_t*) rx_delete);
1003 /* If it's on the spam list, just remove it. */
1004 if (remove_from_spam_list (&SpamList, buf->data) != 0)
1007 /* Otherwise, add it to the nospam list. */
1008 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
1014 /* This should not happen. */
1015 m_strcpy(err->data, err->dsize, "This is no good at all.");
1019 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
1020 BUFFER * err __attribute__ ((unused)))
1023 mutt_extract_token (buf, s, 0);
1025 * Check for deletion of entire list
1027 if (m_strcmp(buf->data, "*") == 0) {
1028 string_list_wipe((string_list_t **) data);
1031 remove_from_list ((string_list_t **) data, buf->data);
1033 while (MoreArgs (s));
1038 static int parse_lists (BUFFER * buf, BUFFER * s,
1039 unsigned long data __attribute__ ((unused)),
1043 mutt_extract_token (buf, s, 0);
1044 remove_from_rx_list (&UnMailLists, buf->data);
1046 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1049 while (MoreArgs (s));
1054 /* always wise to do what someone else did before */
1055 static void _attachments_clean (void) {
1057 if (Context && Context->msgcount) {
1058 for (i = 0; i < Context->msgcount; i++)
1059 Context->hdrs[i]->attach_valid = 0;
1063 static int parse_attach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
1064 BUFFER *err __attribute__ ((unused))) {
1066 string_list_t *listp, *lastp;
1071 /* Find the last item in the list that data points to. */
1073 for (listp = *ldata; listp; listp = listp->next) {
1074 a = (ATTACH_MATCH *)listp->data;
1079 mutt_extract_token (buf, s, 0);
1081 if (!buf->data || *buf->data == '\0')
1084 a = p_new(ATTACH_MATCH, 1);
1086 /* some cheap hacks that I expect to remove */
1087 if (!m_strcasecmp(buf->data, "any"))
1088 a->major = m_strdup("*/.*");
1089 else if (!m_strcasecmp(buf->data, "none"))
1090 a->major = m_strdup("cheap_hack/this_should_never_match");
1092 a->major = m_strdup(buf->data);
1094 if ((p = strchr(a->major, '/'))) {
1099 a->minor = "unknown";
1102 len = m_strlen(a->minor);
1103 tmpminor = p_new(char, len + 3);
1104 strcpy(&tmpminor[1], a->minor); /* __STRCPY_CHECKED__ */
1106 tmpminor[len+1] = '$';
1107 tmpminor[len+2] = '\0';
1109 a->major_int = mutt_check_mime_type(a->major);
1110 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
1112 p_delete(&tmpminor);
1114 listp = p_new(string_list_t, 1);
1115 listp->data = (char *)a;
1118 lastp->next = listp;
1124 while (MoreArgs (s));
1126 _attachments_clean();
1130 static int parse_unattach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
1131 BUFFER *err __attribute__ ((unused))) {
1133 string_list_t *lp, *lastp, *newlp;
1139 mutt_extract_token (buf, s, 0);
1141 if (!m_strcasecmp(buf->data, "any"))
1142 tmp = m_strdup("*/.*");
1143 else if (!m_strcasecmp(buf->data, "none"))
1144 tmp = m_strdup("cheap_hack/this_should_never_match");
1146 tmp = m_strdup(buf->data);
1148 if ((minor = strchr(tmp, '/'))) {
1152 minor = m_strdup("unknown");
1154 major = mutt_check_mime_type(tmp);
1156 /* We must do our own walk here because remove_from_list() will only
1157 * remove the string_list_t->data, not anything pointed to by the string_list_t->data. */
1159 for(lp = *ldata; lp; ) {
1160 a = (ATTACH_MATCH *)lp->data;
1161 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
1162 regfree(&a->minor_rx);
1163 p_delete(&a->major);
1165 /* Relink backward */
1167 lastp->next = lp->next;
1172 p_delete(&lp->data); /* same as a */
1182 while (MoreArgs (s));
1185 _attachments_clean();
1189 static int print_attach_list (string_list_t *lp, char op, const char *name) {
1191 printf("attachments %c%s %s/%s\n", op, name,
1192 ((ATTACH_MATCH *)lp->data)->major,
1193 ((ATTACH_MATCH *)lp->data)->minor);
1200 static int parse_attachments (BUFFER *buf, BUFFER *s,
1201 unsigned long data __attribute__ ((unused)),
1204 string_list_t **listp;
1206 mutt_extract_token(buf, s, 0);
1207 if (!buf->data || *buf->data == '\0') {
1208 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
1212 category = buf->data;
1218 printf("\nCurrent attachments settings:\n\n");
1219 print_attach_list(AttachAllow, '+', "A");
1220 print_attach_list(AttachExclude, '-', "A");
1221 print_attach_list(InlineAllow, '+', "I");
1222 print_attach_list(InlineExclude, '-', "I");
1223 set_option (OPTFORCEREDRAWINDEX);
1224 set_option (OPTFORCEREDRAWPAGER);
1225 mutt_any_key_to_continue (NULL);
1229 if (op != '+' && op != '-') {
1233 if (!m_strncasecmp(category, "attachment", strlen(category))) {
1235 listp = &AttachAllow;
1237 listp = &AttachExclude;
1239 else if (!m_strncasecmp(category, "inline", strlen(category))) {
1241 listp = &InlineAllow;
1243 listp = &InlineExclude;
1245 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
1249 return parse_attach_list(buf, s, listp, err);
1252 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
1254 string_list_t **listp;
1256 mutt_extract_token(buf, s, 0);
1257 if (!buf->data || *buf->data == '\0') {
1258 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
1264 if (op != '+' && op != '-') {
1268 if (!m_strncasecmp(p, "attachment", strlen(p))) {
1270 listp = &AttachAllow;
1272 listp = &AttachExclude;
1274 else if (!m_strncasecmp(p, "inline", strlen(p))) {
1276 listp = &InlineAllow;
1278 listp = &InlineExclude;
1281 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
1285 return parse_unattach_list(buf, s, listp, err);
1288 static int parse_unlists (BUFFER * buf, BUFFER * s,
1289 unsigned long data __attribute__ ((unused)),
1290 BUFFER * err __attribute__ ((unused)))
1293 mutt_extract_token (buf, s, 0);
1294 remove_from_rx_list (&SubscribedLists, buf->data);
1295 remove_from_rx_list (&MailLists, buf->data);
1297 if (m_strcmp(buf->data, "*") &&
1298 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1301 while (MoreArgs (s));
1306 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1310 mutt_extract_token (buf, s, 0);
1311 remove_from_rx_list (&UnMailLists, buf->data);
1312 remove_from_rx_list (&UnSubscribedLists, buf->data);
1314 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1316 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1319 while (MoreArgs (s));
1324 static int parse_unsubscribe (BUFFER * buf, BUFFER * s,
1325 unsigned long data __attribute__ ((unused)),
1326 BUFFER * err __attribute__ ((unused)))
1329 mutt_extract_token (buf, s, 0);
1330 remove_from_rx_list (&SubscribedLists, buf->data);
1332 if (m_strcmp(buf->data, "*") &&
1333 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1336 while (MoreArgs (s));
1341 static int parse_unalias (BUFFER * buf, BUFFER * s,
1342 unsigned long data __attribute__ ((unused)),
1343 BUFFER * err __attribute__ ((unused)))
1345 alias_t *tmp, *last = NULL;
1348 mutt_extract_token (buf, s, 0);
1350 if (m_strcmp("*", buf->data) == 0) {
1351 if (CurrentMenu == MENU_ALIAS) {
1352 for (tmp = Aliases; tmp; tmp = tmp->next)
1354 set_option (OPTFORCEREDRAWINDEX);
1357 alias_list_wipe(&Aliases);
1361 for (tmp = Aliases; tmp; tmp = tmp->next) {
1362 if (m_strcasecmp(buf->data, tmp->name) == 0) {
1363 if (CurrentMenu == MENU_ALIAS) {
1365 set_option (OPTFORCEREDRAWINDEX);
1370 last->next = tmp->next;
1372 Aliases = tmp->next;
1374 alias_list_wipe(&tmp);
1380 while (MoreArgs (s));
1384 static int parse_alias (BUFFER * buf, BUFFER * s,
1385 unsigned long data __attribute__ ((unused)),
1388 alias_t *tmp = Aliases;
1389 alias_t *last = NULL;
1392 if (!MoreArgs (s)) {
1393 m_strcpy(err->data, err->dsize, _("alias: no address"));
1397 mutt_extract_token (buf, s, 0);
1399 /* check to see if an alias with this name already exists */
1400 for (; tmp; tmp = tmp->next) {
1401 if (!m_strcasecmp(tmp->name, buf->data))
1407 /* create a new alias */
1409 tmp->name = m_strdup(buf->data);
1410 /* give the main addressbook code a chance */
1411 if (CurrentMenu == MENU_ALIAS)
1412 set_option (OPTMENUCALLER);
1415 /* override the previous value */
1416 address_list_wipe(&tmp->addr);
1417 if (CurrentMenu == MENU_ALIAS)
1418 set_option (OPTFORCEREDRAWINDEX);
1421 mutt_extract_token (buf, s,
1422 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1423 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1428 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1429 snprintf (err->data, err->dsize,
1430 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1439 parse_unmy_hdr (BUFFER * buf, BUFFER * s,
1440 unsigned long data __attribute__ ((unused)),
1441 BUFFER * err __attribute__ ((unused)))
1443 string_list_t *last = NULL;
1444 string_list_t *tmp = UserHeader;
1449 mutt_extract_token (buf, s, 0);
1450 if (m_strcmp("*", buf->data) == 0)
1451 string_list_wipe(&UserHeader);
1456 l = m_strlen(buf->data);
1457 if (buf->data[l - 1] == ':')
1461 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1462 && tmp->data[l] == ':') {
1465 last->next = tmp->next;
1467 UserHeader = tmp->next;
1470 string_list_wipe(&ptr);
1479 while (MoreArgs (s));
1483 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1490 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1491 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1492 m_strcpy(err->data, err->dsize, _("invalid header field"));
1495 keylen = p - buf->data + 1;
1498 for (tmp = UserHeader;; tmp = tmp->next) {
1499 /* see if there is already a field by this name */
1500 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1501 /* replace the old value */
1502 p_delete(&tmp->data);
1503 tmp->data = buf->data;
1510 tmp->next = string_item_new();
1514 tmp = string_item_new();
1517 tmp->data = buf->data;
1523 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1524 char* errbuf, ssize_t errlen) {
1527 if (m_strncmp("reverse-", s, 8) == 0) {
1529 flags = SORT_REVERSE;
1532 if (m_strncmp("last-", s, 5) == 0) {
1537 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1539 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1543 *((short*) dst->data) = i | flags;
1547 /* if additional data more == 1, we want to resolve synonyms */
1548 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
1550 char buf[LONG_STRING];
1551 struct option_t *ptr = p;
1553 if (DTYPE(ptr->type) == DT_SYN) {
1556 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1558 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_from_string)
1561 mutt_option_value(ptr->option, buf, sizeof(buf));
1562 if (m_strlen(ptr->init) == 0 && buf && *buf)
1563 ptr->init = m_strdup(buf);
1566 static struct option_t* add_option (const char* name, const char* init,
1567 short type, short dodup) {
1568 struct option_t* option = p_new(struct option_t, 1);
1570 option->option = m_strdup(name);
1571 option->type = type;
1573 option->init = dodup ? m_strdup(init) : (char*) init;
1577 /* creates new option_t* of type DT_USER for $user_ var */
1578 static struct option_t* add_user_option (const char* name) {
1579 return (add_option (name, NULL, DT_USER, 1));
1582 /* free()'s option_t* */
1583 static void del_option (void* p) {
1584 struct option_t *ptr = (struct option_t*) p;
1585 char* s = (char*) ptr->data;
1586 p_delete(&ptr->option);
1588 p_delete(&ptr->init);
1592 static int init_expand (char** dst, struct option_t* src) {
1598 if (DTYPE(src->type) == DT_STR ||
1599 DTYPE(src->type) == DT_PATH) {
1600 /* only expand for string as it's the only place where
1601 * we want to expand vars right now */
1602 if (src->init && *src->init) {
1605 len = m_strlen(src->init) + 2;
1606 in.data = p_new(char, len + 1);
1607 snprintf (in.data, len, "\"%s\"", src->init);
1610 mutt_extract_token (&token, &in, 0);
1611 if (token.data && *token.data)
1612 *dst = m_strdup(token.data);
1614 *dst = m_strdup("");
1616 p_delete(&token.data);
1618 *dst = m_strdup("");
1620 /* for non-string: take value as is */
1621 *dst = m_strdup(src->init);
1625 /* if additional data more == 1, we want to resolve synonyms */
1626 static void mutt_restore_default (const char* name __attribute__ ((unused)),
1627 void* p, unsigned long more) {
1628 char errbuf[STRING];
1629 struct option_t* ptr = (struct option_t*) p;
1632 if (DTYPE (ptr->type) == DT_SYN) {
1635 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1639 if (FuncTable[DTYPE (ptr->type)].opt_from_string) {
1640 init_expand (&init, ptr);
1641 if (!FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, init, errbuf,
1643 if (!option (OPTNOCURSES))
1645 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1646 "Please report this error: \"%s\"\n"),
1647 ptr->option, NONULL (init), errbuf);
1653 if (ptr->flags & R_INDEX)
1654 set_option (OPTFORCEREDRAWINDEX);
1655 if (ptr->flags & R_PAGER)
1656 set_option (OPTFORCEREDRAWPAGER);
1657 if (ptr->flags & R_RESORT_SUB)
1658 set_option (OPTSORTSUBTHREADS);
1659 if (ptr->flags & R_RESORT)
1660 set_option (OPTNEEDRESORT);
1661 if (ptr->flags & R_RESORT_INIT)
1662 set_option (OPTRESORTINIT);
1663 if (ptr->flags & R_TREE)
1664 set_option (OPTREDRAWTREE);
1667 /* check whether value for $dsn_return would be valid */
1668 static int check_dsn_return (const char* option __attribute__ ((unused)), unsigned long p,
1669 char* errbuf, ssize_t errlen) {
1670 char* val = (char*) p;
1671 if (val && *val && m_strncmp(val, "hdrs", 4) != 0 &&
1672 m_strncmp(val, "full", 4) != 0) {
1674 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1680 /* check whether value for $dsn_notify would be valid */
1681 static int check_dsn_notify (const char* option __attribute__ ((unused)), unsigned long p,
1682 char* errbuf, ssize_t errlen) {
1683 list2_t* list = NULL;
1686 char* val = (char*) p;
1690 list = list_from_str (val, ",");
1691 if (list_empty (list))
1694 for (i = 0; i < list->length; i++)
1695 if (m_strncmp(list->data[i], "never", 5) != 0 &&
1696 m_strncmp(list->data[i], "failure", 7) != 0 &&
1697 m_strncmp(list->data[i], "delay", 5) != 0 &&
1698 m_strncmp(list->data[i], "success", 7) != 0) {
1700 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1701 (char*) list->data[i], "dsn_notify");
1705 list_del (&list, (list_del_t*)xmemfree);
1709 static int check_num (const char* option, unsigned long p,
1710 char* errbuf, ssize_t errlen) {
1713 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1719 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1720 char* errbuf, ssize_t errlen) {
1721 if (!check_num ("history", p, errbuf, errlen))
1723 mutt_init_history ();
1727 static int check_special (const char* name, unsigned long val,
1728 char* errbuf, ssize_t errlen) {
1731 for (i = 0; SpecialVars[i].name; i++) {
1732 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1733 return (SpecialVars[i].check (SpecialVars[i].name,
1734 val, errbuf, errlen));
1740 static const struct mapping_t* get_sortmap (struct option_t* option) {
1741 const struct mapping_t* map = NULL;
1743 switch (option->type & DT_SUBTYPE_MASK) {
1745 map = SortAliasMethods;
1747 case DT_SORT_BROWSER:
1748 map = SortBrowserMethods;
1751 map = SortKeyMethods;
1754 map = SortAuxMethods;
1763 #define CHECK_PAGER \
1764 if ((CurrentMenu == MENU_PAGER) && \
1765 (!option || (option->flags & R_RESORT))) \
1767 snprintf (err->data, err->dsize, \
1768 _("Not available in this menu.")); \
1772 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1775 int query, unset, inv, reset, r = 0;
1776 struct option_t* option = NULL;
1778 while (MoreArgs (s)) {
1779 /* reset state variables */
1781 unset = data & M_SET_UNSET;
1782 inv = data & M_SET_INV;
1783 reset = data & M_SET_RESET;
1785 if (*s->dptr == '?') {
1789 else if (m_strncmp("no", s->dptr, 2) == 0) {
1793 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1797 else if (*s->dptr == '&') {
1802 /* get the variable name */
1803 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1805 /* resolve synonyms */
1806 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1807 DTYPE (option->type == DT_SYN)) {
1808 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1809 syn_add (newopt, option);
1813 /* see if we need to add $user_ var */
1814 if (!option && m_strncmp("user_", tmp->data, 5) == 0) {
1815 /* there's no option named like this yet so only add one
1816 * if the action isn't any of: reset, unset, query */
1817 if (!(reset || unset || query || *s->dptr != '=')) {
1818 option = add_user_option (tmp->data);
1819 hash_insert (ConfigOptions, option->option, option, 0);
1823 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1824 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1827 s->dptr = vskipspaces(s->dptr);
1830 if (query || unset || inv) {
1831 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1835 if (s && *s->dptr == '=') {
1836 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1840 if (!m_strcmp("all", tmp->data)) {
1841 if (CurrentMenu == MENU_PAGER) {
1842 snprintf (err->data, err->dsize, _("Not available in this menu."));
1845 hash_map (ConfigOptions, mutt_restore_default, 1);
1846 set_option (OPTFORCEREDRAWINDEX);
1847 set_option (OPTFORCEREDRAWPAGER);
1848 set_option (OPTSORTSUBTHREADS);
1849 set_option (OPTNEEDRESORT);
1850 set_option (OPTRESORTINIT);
1851 set_option (OPTREDRAWTREE);
1854 else if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1855 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1860 mutt_restore_default (NULL, option, 1);
1863 else if (DTYPE (option->type) == DT_BOOL) {
1864 /* XXX this currently ignores the function table
1865 * as we don't get invert and stuff into it */
1866 if (s && *s->dptr == '=') {
1867 if (unset || inv || query) {
1868 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1873 mutt_extract_token (tmp, s, 0);
1874 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1876 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1879 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1885 bool_to_string (err->data, err->dsize, option);
1891 unset_option (option->data);
1893 toggle_option (option->data);
1895 set_option (option->data);
1897 else if (DTYPE (option->type) == DT_STR ||
1898 DTYPE (option->type) == DT_PATH ||
1899 DTYPE (option->type) == DT_ADDR ||
1900 DTYPE (option->type) == DT_MAGIC ||
1901 DTYPE (option->type) == DT_NUM ||
1902 DTYPE (option->type) == DT_SORT ||
1903 DTYPE (option->type) == DT_RX ||
1904 DTYPE (option->type) == DT_USER ||
1905 DTYPE (option->type) == DT_SYS) {
1907 /* XXX maybe we need to get unset into handlers? */
1908 if (DTYPE (option->type) == DT_STR ||
1909 DTYPE (option->type) == DT_PATH ||
1910 DTYPE (option->type) == DT_ADDR ||
1911 DTYPE (option->type) == DT_USER ||
1912 DTYPE (option->type) == DT_SYS) {
1915 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1916 snprintf (err->data, err->dsize, _("$%s is read-only"),
1920 } else if (DTYPE (option->type) == DT_ADDR)
1921 address_list_wipe((address_t **) option->data);
1922 else if (DTYPE (option->type) == DT_USER)
1923 /* to unset $user_ means remove */
1924 hash_delete (ConfigOptions, option->option,
1925 option, del_option);
1927 p_delete((void **)(void *)&option->data);
1932 if (query || *s->dptr != '=') {
1933 FuncTable[DTYPE (option->type)].opt_to_string
1934 (err->data, err->dsize, option);
1938 /* the $madmutt_ variables are read-only */
1939 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1940 snprintf (err->data, err->dsize, _("$%s is read-only"),
1947 mutt_extract_token (tmp, s, 0);
1948 if (!FuncTable[DTYPE (option->type)].opt_from_string
1949 (option, tmp->data, err->data, err->dsize))
1953 else if (DTYPE (option->type) == DT_QUAD) {
1956 quad_to_string (err->data, err->dsize, option);
1960 if (*s->dptr == '=') {
1963 mutt_extract_token (tmp, s, 0);
1964 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1965 set_quadoption (option->data, M_YES);
1966 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1967 set_quadoption (option->data, M_NO);
1968 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1969 set_quadoption (option->data, M_ASKYES);
1970 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1971 set_quadoption (option->data, M_ASKNO);
1973 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1974 tmp->data, option->option);
1981 toggle_quadoption (option->data);
1983 set_quadoption (option->data, M_NO);
1985 set_quadoption (option->data, M_YES);
1989 snprintf (err->data, err->dsize, _("%s: unknown type"),
1995 if (option->flags & R_INDEX)
1996 set_option (OPTFORCEREDRAWINDEX);
1997 if (option->flags & R_PAGER)
1998 set_option (OPTFORCEREDRAWPAGER);
1999 if (option->flags & R_RESORT_SUB)
2000 set_option (OPTSORTSUBTHREADS);
2001 if (option->flags & R_RESORT)
2002 set_option (OPTNEEDRESORT);
2003 if (option->flags & R_RESORT_INIT)
2004 set_option (OPTRESORTINIT);
2005 if (option->flags & R_TREE)
2006 set_option (OPTREDRAWTREE);
2013 /* reads the specified initialization file. returns -1 if errors were found
2014 so that we can pause to let the user know... */
2015 static int source_rc (const char *rcfile, BUFFER * err)
2018 int line = 0, rc = 0, conv = 0;
2020 char *linebuf = NULL;
2021 char *currentline = NULL;
2025 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
2026 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
2031 while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
2032 conv = ConfigCharset && (*ConfigCharset) && Charset;
2034 currentline = m_strdup(linebuf);
2037 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
2040 currentline = linebuf;
2045 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
2046 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
2047 if (--rc < -MAXERRS) {
2049 p_delete(¤tline);
2058 p_delete(¤tline);
2060 p_delete(&token.data);
2064 mutt_wait_filter (pid);
2066 /* the muttrc source keyword */
2067 snprintf (err->data, err->dsize,
2068 rc >= -MAXERRS ? _("source: errors in %s")
2069 : _("source: reading aborted due too many errors in %s"),
2078 static int parse_source (BUFFER * tmp, BUFFER * s,
2079 unsigned long data __attribute__ ((unused)),
2082 char path[_POSIX_PATH_MAX];
2086 if (mutt_extract_token (tmp, s, 0) != 0) {
2087 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
2091 m_strcpy(path, sizeof(path), tmp->data);
2092 mutt_expand_path (path, sizeof(path));
2094 rc += source_rc (path, err);
2096 while (MoreArgs (s));
2098 return ((rc < 0) ? -1 : 0);
2101 /* line command to execute
2103 token scratch buffer to be used by parser. caller should free
2104 token->data when finished. the reason for this variable is
2105 to avoid having to allocate and deallocate a lot of memory
2106 if we are parsing many lines. the caller can pass in the
2107 memory to use, which avoids having to create new space for
2108 every call to this function.
2110 err where to write error messages */
2111 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
2117 expn.data = expn.dptr = line;
2118 expn.dsize = m_strlen(line);
2122 expn.dptr = vskipspaces(expn.dptr);
2123 while (*expn.dptr) {
2124 if (*expn.dptr == '#')
2125 break; /* rest of line is a comment */
2126 if (*expn.dptr == ';') {
2130 mutt_extract_token (token, &expn, 0);
2131 for (i = 0; Commands[i].name; i++) {
2132 if (!m_strcmp(token->data, Commands[i].name)) {
2133 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
2138 if (!Commands[i].name) {
2139 snprintf (err->data, err->dsize, _("%s: unknown command"),
2140 NONULL (token->data));
2147 p_delete(&expn.data);
2152 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
2153 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
2154 /* initial string that starts completion. No telling how much crap
2155 * the user has typed so far. Allocate LONG_STRING just to be sure! */
2156 char User_typed[LONG_STRING] = { 0 };
2158 int Num_matched = 0; /* Number of matches for completion */
2159 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
2160 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
2162 /* helper function for completion. Changes the dest buffer if
2163 necessary/possible to aid completion.
2164 dest == completion result gets here.
2165 src == candidate for completion.
2166 try == user entered data for completion.
2167 len == length of dest buffer.
2169 static void candidate (char *dest, char *try, const char *src, int len)
2173 if (strstr (src, try) == src) {
2174 Matches[Num_matched++] = src;
2176 m_strcpy(dest, len, src);
2178 for (l = 0; src[l] && src[l] == dest[l]; l++);
2184 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
2188 int spaces; /* keep track of the number of leading spaces on the line */
2190 buffer = vskipspaces(buffer);
2191 spaces = buffer - pt;
2193 pt = buffer + pos - spaces;
2194 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2197 if (pt == buffer) { /* complete cmd */
2198 /* first TAB. Collect all the matches */
2201 m_strcpy(User_typed, sizeof(User_typed), pt);
2202 p_clear(Matches, countof(Matches));
2203 p_clear(Completed, countof(Completed));
2204 for (num = 0; Commands[num].name; num++)
2205 candidate (Completed, User_typed, Commands[num].name,
2207 Matches[Num_matched++] = User_typed;
2209 /* All matches are stored. Longest non-ambiguous string is ""
2210 * i.e. dont change 'buffer'. Fake successful return this time */
2211 if (User_typed[0] == 0)
2215 if (Completed[0] == 0 && User_typed[0])
2218 /* Num_matched will _always_ be atleast 1 since the initial
2219 * user-typed string is always stored */
2220 if (numtabs == 1 && Num_matched == 2)
2221 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2222 else if (numtabs > 1 && Num_matched > 2)
2223 /* cycle thru all the matches */
2224 snprintf (Completed, sizeof(Completed), "%s",
2225 Matches[(numtabs - 2) % Num_matched]);
2227 /* return the completed command */
2228 m_strcpy(buffer, len - spaces, Completed);
2230 else if (!m_strncmp(buffer, "set", 3)
2231 || !m_strncmp(buffer, "unset", 5)
2232 || !m_strncmp(buffer, "reset", 5)
2233 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
2234 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
2237 /* loop through all the possible prefixes (no, inv, ...) */
2238 if (!m_strncmp(buffer, "set", 3)) {
2239 for (num = 0; prefixes[num]; num++) {
2240 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
2241 pt += m_strlen(prefixes[num]);
2247 /* first TAB. Collect all the matches */
2250 m_strcpy(User_typed, sizeof(User_typed), pt);
2251 p_clear(Matches, countof(Matches));
2252 p_clear(Completed, countof(Completed));
2253 for (num = 0; MuttVars[num].option; num++)
2254 candidate(Completed, User_typed, MuttVars[num].option,
2256 Matches[Num_matched++] = User_typed;
2258 /* All matches are stored. Longest non-ambiguous string is ""
2259 * i.e. dont change 'buffer'. Fake successful return this time */
2260 if (User_typed[0] == 0)
2264 if (Completed[0] == 0 && User_typed[0])
2267 /* Num_matched will _always_ be atleast 1 since the initial
2268 * user-typed string is always stored */
2269 if (numtabs == 1 && Num_matched == 2)
2270 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2271 else if (numtabs > 1 && Num_matched > 2)
2272 /* cycle thru all the matches */
2273 snprintf (Completed, sizeof(Completed), "%s",
2274 Matches[(numtabs - 2) % Num_matched]);
2276 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2278 else if (!m_strncmp(buffer, "exec", 4)) {
2279 struct binding_t *menu = km_get_table (CurrentMenu);
2281 if (!menu && CurrentMenu != MENU_PAGER)
2285 /* first TAB. Collect all the matches */
2288 m_strcpy(User_typed, sizeof(User_typed), pt);
2289 p_clear(Matches, countof(Matches));
2290 p_clear(Completed, countof(Completed));
2291 for (num = 0; menu[num].name; num++)
2292 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
2293 /* try the generic menu */
2294 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2296 for (num = 0; menu[num].name; num++)
2297 candidate (Completed, User_typed, menu[num].name,
2300 Matches[Num_matched++] = User_typed;
2302 /* All matches are stored. Longest non-ambiguous string is ""
2303 * i.e. dont change 'buffer'. Fake successful return this time */
2304 if (User_typed[0] == 0)
2308 if (Completed[0] == 0 && User_typed[0])
2311 /* Num_matched will _always_ be atleast 1 since the initial
2312 * user-typed string is always stored */
2313 if (numtabs == 1 && Num_matched == 2)
2314 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2315 else if (numtabs > 1 && Num_matched > 2)
2316 /* cycle thru all the matches */
2317 snprintf (Completed, sizeof(Completed), "%s",
2318 Matches[(numtabs - 2) % Num_matched]);
2320 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2328 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
2330 char var[STRING], *pt = buffer;
2332 struct option_t* option = NULL;
2337 buffer = vskipspaces(buffer);
2338 spaces = buffer - pt;
2340 pt = buffer + pos - spaces;
2341 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2343 pt++; /* move past the space */
2344 if (*pt == '=') /* abort if no var before the '=' */
2347 if (m_strncmp(buffer, "set", 3) == 0) {
2348 m_strcpy(var, sizeof(var), pt);
2349 /* ignore the trailing '=' when comparing */
2350 var[m_strlen(var) - 1] = 0;
2351 if (!(option = hash_find (ConfigOptions, var)))
2352 return 0; /* no such variable. */
2354 char tmp[LONG_STRING], tmp2[LONG_STRING];
2356 ssize_t dlen = buffer + len - pt - spaces;
2357 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2361 if ((DTYPE (option->type) == DT_STR) ||
2362 (DTYPE (option->type) == DT_PATH) ||
2363 (DTYPE (option->type) == DT_RX)) {
2364 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
2365 if (DTYPE (option->type) == DT_PATH)
2366 mutt_pretty_mailbox (tmp);
2368 else if (DTYPE (option->type) == DT_ADDR) {
2369 rfc822_write_address (tmp, sizeof(tmp),
2370 *((address_t **) option->data), 0);
2372 else if (DTYPE (option->type) == DT_QUAD)
2373 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
2374 else if (DTYPE (option->type) == DT_NUM)
2375 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
2376 else if (DTYPE (option->type) == DT_SORT) {
2377 const struct mapping_t *map;
2380 switch (option->type & DT_SUBTYPE_MASK) {
2382 map = SortAliasMethods;
2384 case DT_SORT_BROWSER:
2385 map = SortBrowserMethods;
2388 map = SortKeyMethods;
2394 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2395 snprintf(tmp, sizeof(tmp), "%s%s%s",
2396 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2397 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2399 else if (DTYPE (option->type) == DT_MAGIC) {
2401 switch (DefaultMagic) {
2417 m_strcpy(tmp, sizeof(tmp), p);
2419 else if (DTYPE (option->type) == DT_BOOL)
2420 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
2424 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
2425 if (*s == '\\' || *s == '"')
2431 m_strcpy(tmp, sizeof(tmp), pt);
2432 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2440 /* Implement the -Q command line flag */
2441 int mutt_query_variables (string_list_t * queries)
2445 char errbuff[STRING];
2446 char command[STRING];
2454 err.dsize = sizeof(errbuff);
2456 for (p = queries; p; p = p->next) {
2457 snprintf (command, sizeof(command), "set ?%s\n", p->data);
2458 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2459 fprintf (stderr, "%s\n", err.data);
2460 p_delete(&token.data);
2463 printf ("%s\n", err.data);
2466 p_delete(&token.data);
2470 static int mutt_execute_commands (string_list_t * p)
2473 char errstr[SHORT_STRING];
2477 err.dsize = sizeof(errstr);
2479 for (; p; p = p->next) {
2480 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2481 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2482 p_delete(&token.data);
2486 p_delete(&token.data);
2490 void mutt_init (int skip_sys_rc, string_list_t * commands)
2493 struct utsname utsname;
2495 char buffer[STRING], error[STRING];
2496 int default_rc = 0, need_pause = 0;
2502 err.dsize = sizeof(error);
2504 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2505 * to have some room for $user_ vars */
2506 ConfigOptions = hash_create (sizeof(MuttVars) * 3);
2507 for (i = 0; MuttVars[i].option; i++) {
2508 if (DTYPE (MuttVars[i].type) != DT_SYS)
2509 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2511 hash_insert (ConfigOptions, MuttVars[i].option,
2512 add_option (MuttVars[i].option, MuttVars[i].init,
2517 * XXX - use something even more difficult to predict?
2519 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2520 "\033]9;%ld\a", (long) time (NULL));
2522 /* on one of the systems I use, getcwd() does not return the same prefix
2523 as is listed in the passwd file */
2524 if ((p = getenv ("HOME")))
2525 Homedir = m_strdup(p);
2527 /* Get some information about the user */
2528 if ((pw = getpwuid (getuid ()))) {
2531 Username = m_strdup(pw->pw_name);
2533 Homedir = m_strdup(pw->pw_dir);
2535 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, GecosMask.rx);
2536 Realname = m_strdup(rnbuf);
2537 Shell = m_strdup(pw->pw_shell);
2543 fputs (_("unable to determine home directory"), stderr);
2546 if ((p = getenv ("USER")))
2547 Username = m_strdup(p);
2550 fputs (_("unable to determine username"), stderr);
2553 Shell = m_strdup((p = getenv ("SHELL")) ? p : "/bin/sh");
2556 /* And about the host... */
2558 /* some systems report the FQDN instead of just the hostname */
2559 if ((p = strchr (utsname.nodename, '.'))) {
2560 Hostname = p_dupstr(utsname.nodename, p - utsname.nodename);
2562 m_strcpy(buffer, sizeof(buffer), p); /* save the domain for below */
2565 Hostname = m_strdup(utsname.nodename);
2567 if (!p && getdnsdomainname(buffer, sizeof(buffer)) == -1)
2568 Fqdn = m_strdup("@");
2570 if (*buffer != '@') {
2571 Fqdn = p_new(char, m_strlen(buffer) + m_strlen(Hostname) + 2);
2572 sprintf (Fqdn, "%s.%s", NONULL(Hostname), buffer); /* __SPRINTF_CHECKED__ */
2575 Fqdn = m_strdup(NONULL (Hostname));
2582 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2584 fgets (buffer, sizeof(buffer), f);
2585 p = vskipspaces(buffer);
2587 while (*q && !isspace(*q))
2590 NewsServer = m_strdup(p);
2594 if ((p = getenv ("NNTPSERVER")))
2595 NewsServer = m_strdup(p);
2598 if ((p = getenv ("MAIL")))
2599 Spoolfile = m_strdup(p);
2600 else if ((p = getenv ("MAILDIR")))
2601 Spoolfile = m_strdup(p);
2604 mutt_concat_path(buffer, sizeof(buffer), NONULL(Homedir), MAILPATH);
2606 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(Username));
2608 Spoolfile = m_strdup(buffer);
2611 if ((p = getenv ("MAILCAPS")))
2612 MailcapPath = m_strdup(p);
2614 /* Default search path from RFC1524 */
2616 m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2617 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2620 Tempdir = m_strdup((p = getenv ("TMPDIR")) ? p : "/tmp");
2622 p = getenv ("VISUAL");
2624 p = getenv ("EDITOR");
2628 Editor = m_strdup(p);
2630 if ((p = getenv ("REPLYTO")) != NULL) {
2633 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2636 buf.data = buf.dptr = buffer;
2637 buf.dsize = m_strlen(buffer);
2640 parse_my_hdr (&token, &buf, 0, &err);
2641 p_delete(&token.data);
2644 if ((p = getenv ("EMAIL")) != NULL)
2645 From = rfc822_parse_adrlist (NULL, p);
2647 charset_initialize();
2649 /* Set standard defaults */
2650 hash_map (ConfigOptions, mutt_set_default, 0);
2651 hash_map (ConfigOptions, mutt_restore_default, 0);
2653 CurrentMenu = MENU_MAIN;
2656 /* Unset suspend by default if we're the session leader */
2657 if (getsid (0) == getpid ())
2658 unset_option (OPTSUSPEND);
2661 mutt_init_history ();
2665 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc-%s", NONULL (Homedir),
2667 if (access (buffer, F_OK) == -1)
2669 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL (Homedir));
2670 if (access (buffer, F_OK) == -1)
2672 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc-%s",
2673 NONULL (Homedir), MUTT_VERSION);
2674 if (access (buffer, F_OK) == -1)
2676 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
2680 Muttrc = m_strdup(buffer);
2683 m_strcpy(buffer, sizeof(buffer), Muttrc);
2685 mutt_expand_path (buffer, sizeof(buffer));
2686 Muttrc = m_strdup(buffer);
2688 p_delete(&AliasFile);
2689 AliasFile = m_strdup(NONULL (Muttrc));
2691 /* Process the global rc file if it exists and the user hasn't explicity
2692 requested not to via "-n". */
2694 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
2696 if (access (buffer, F_OK) == -1)
2697 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
2698 if (access (buffer, F_OK) == -1)
2699 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
2701 if (access (buffer, F_OK) == -1)
2702 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
2703 if (access (buffer, F_OK) != -1) {
2704 if (source_rc (buffer, &err) != 0) {
2705 fputs (err.data, stderr);
2706 fputc ('\n', stderr);
2712 /* Read the user's initialization file. */
2713 if (access (Muttrc, F_OK) != -1) {
2714 if (!option (OPTNOCURSES))
2716 if (source_rc (Muttrc, &err) != 0) {
2717 fputs (err.data, stderr);
2718 fputc ('\n', stderr);
2722 else if (!default_rc) {
2723 /* file specified by -F does not exist */
2724 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2725 mutt_endwin (buffer);
2729 if (mutt_execute_commands (commands) != 0)
2732 /* warn about synonym variables */
2733 if (!list_empty(Synonyms)) {
2735 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2736 for (i = 0; i < Synonyms->length; i++) {
2737 struct option_t* newopt = NULL, *oldopt = NULL;
2738 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2739 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2740 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2741 oldopt ? NONULL (oldopt->option) : "",
2742 newopt ? NONULL (newopt->option) : "",
2743 NONULL(((syn_t*) Synonyms->data[i])->f),
2744 ((syn_t*) Synonyms->data[i])->l);
2746 fprintf (stderr, _("Warning: synonym variables are scheduled"
2747 " for removal.\n"));
2748 list_del (&Synonyms, syn_del);
2752 if (need_pause && !option (OPTNOCURSES)) {
2753 if (mutt_any_key_to_continue (NULL) == -1)
2758 set_option (OPTWEED); /* turn weeding on by default */
2762 int mutt_get_hook_type (const char *name)
2764 struct command_t *c;
2766 for (c = Commands; c->name; c++)
2767 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2772 /* compare two option_t*'s for sorting -t/-T output */
2773 static int opt_cmp (const void* a, const void* b) {
2774 return (m_strcmp((*(struct option_t**) a)->option,
2775 (*(struct option_t**) b)->option));
2778 /* callback for hash_map() to put all non-synonym vars into list */
2779 static void opt_sel_full (const char* key __attribute__ ((unused)),
2781 unsigned long more) {
2782 list2_t** l = (list2_t**) more;
2783 struct option_t* option = (struct option_t*) data;
2785 if (DTYPE (option->type) == DT_SYN)
2787 list_push_back (l, option);
2790 /* callback for hash_map() to put all changed non-synonym vars into list */
2791 static void opt_sel_diff (const char* key __attribute__ ((unused)),
2793 unsigned long more) {
2794 list2_t** l = (list2_t**) more;
2795 struct option_t* option = (struct option_t*) data;
2796 char buf[LONG_STRING];
2798 if (DTYPE (option->type) == DT_SYN)
2801 mutt_option_value (option->option, buf, sizeof(buf));
2802 if (m_strcmp(buf, option->init) != 0)
2803 list_push_back (l, option);
2806 /* dump out the value of all the variables we have */
2807 int mutt_dump_variables (int full) {
2809 char outbuf[STRING];
2810 list2_t* tmp = NULL;
2811 struct option_t* option = NULL;
2813 /* get all non-synonyms into list... */
2814 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2815 (unsigned long) &tmp);
2817 if (!list_empty(tmp)) {
2818 /* ...and dump list sorted */
2819 qsort (tmp->data, tmp->length, sizeof(void*), opt_cmp);
2820 for (i = 0; i < tmp->length; i++) {
2821 option = (struct option_t*) tmp->data[i];
2822 FuncTable[DTYPE (option->type)].opt_to_string
2823 (outbuf, sizeof(outbuf), option);
2824 printf ("%s\n", outbuf);
2827 list_del (&tmp, NULL);