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/mem.h>
26 #include <lib-lib/str.h>
27 #include <lib-lib/file.h>
28 #include <lib-lib/ascii.h>
29 #include <lib-lib/macros.h>
30 #include <lib-lib/buffer.h>
31 #include <lib-lib/mapping.h>
32 #include <lib-lib/rx.h>
34 #include <lib-sys/unix.h>
35 #include <lib-sys/mutt_ssl.h>
37 #include <lib-ui/curses.h>
38 #include <lib-ui/history.h>
44 #include <lib-crypt/crypt.h>
45 #include "mutt_idna.h"
47 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
48 #include "mutt_libesmtp.h"
60 static const struct mapping_t* get_sortmap (struct option_t* option);
61 static int parse_sort (struct option_t* dst, const char *s,
62 const struct mapping_t *map,
63 char* errbuf, ssize_t errlen);
65 static HASH *ConfigOptions = NULL;
67 /* for synonym warning reports: synonym found during parsing */
71 struct option_t* n; /* new */
72 struct option_t* o; /* old */
75 /* for synonym warning reports: list of synonyms found */
76 static list2_t* Synonyms;
77 /* for synonym warning reports: current rc file */
78 static const char* CurRCFile = NULL;
79 /* for synonym warning reports: current rc line */
80 static int CurRCLine = 0;
82 /* prototypes for checking for special vars */
83 static int check_dsn_return (const char* option, unsigned long val,
84 char* errbuf, ssize_t errlen);
85 static int check_dsn_notify (const char* option, unsigned long val,
86 char* errbuf, ssize_t errlen);
87 static int check_history (const char* option, unsigned long val,
88 char* errbuf, ssize_t errlen);
89 /* this checks that numbers are >= 0 */
90 static int check_num (const char* option, unsigned long val,
91 char* errbuf, ssize_t errlen);
93 /* use this to check only */
94 static int check_special (const char* option, unsigned long val,
95 char* errbuf, ssize_t errlen);
97 /* variable <-> sanity check function mappings
98 * when changing these, make sure the proper _from_string handler
103 int (*check) (const char* option, unsigned long val,
104 char* errbuf, ssize_t errlen);
106 { "dsn_notify", check_dsn_notify },
107 { "dsn_return", check_dsn_return },
108 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
109 { "smtp_use_tls", mutt_libesmtp_check_usetls },
111 { "history", check_history },
112 { "pager_index_lines", check_num },
117 /* protos for config type handles: convert value to string */
118 static void bool_to_string (char* dst, ssize_t dstlen, struct option_t* option);
119 static void num_to_string (char* dst, ssize_t dstlen, struct option_t* option);
120 static void str_to_string (char* dst, ssize_t dstlen, struct option_t* option);
121 static void quad_to_string (char* dst, ssize_t dstlen, struct option_t* option);
122 static void sort_to_string (char* dst, ssize_t dstlen, struct option_t* option);
123 static void rx_to_string (char* dst, ssize_t dstlen, struct option_t* option);
124 static void magic_to_string (char* dst, ssize_t dstlen, struct option_t* option);
125 static void addr_to_string (char* dst, ssize_t dstlen, struct option_t* option);
126 static void user_to_string (char* dst, ssize_t dstlen, struct option_t* option);
127 static void sys_to_string (char* dst, ssize_t dstlen, struct option_t* option);
129 /* protos for config type handles: convert to value from string */
130 static int bool_from_string (struct option_t* dst, const char* val,
131 char* errbuf, ssize_t errlen);
132 static int num_from_string (struct option_t* dst, const char* val,
133 char* errbuf, ssize_t errlen);
134 static int str_from_string (struct option_t* dst, const char* val,
135 char* errbuf, ssize_t errlen);
136 static int path_from_string (struct option_t* dst, const char* val,
137 char* errbuf, ssize_t errlen);
138 static int quad_from_string (struct option_t* dst, const char* val,
139 char* errbuf, ssize_t errlen);
140 static int sort_from_string (struct option_t* dst, const char* val,
141 char* errbuf, ssize_t errlen);
142 static int rx_from_string (struct option_t* dst, const char* val,
143 char* errbuf, ssize_t errlen);
144 static int magic_from_string (struct option_t* dst, const char* val,
145 char* errbuf, ssize_t errlen);
146 static int addr_from_string (struct option_t* dst, const char* val,
147 char* errbuf, ssize_t errlen);
148 static int user_from_string (struct option_t* dst, const char* val,
149 char* errbuf, ssize_t errlen);
153 void (*opt_to_string) (char* dst, ssize_t dstlen, struct option_t* option);
154 int (*opt_from_string) (struct option_t* dst, const char* val,
155 char* errbuf, ssize_t errlen);
157 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
158 { DT_BOOL, bool_to_string, bool_from_string },
159 { DT_NUM, num_to_string, num_from_string },
160 { DT_STR, str_to_string, str_from_string },
161 { DT_PATH, str_to_string, path_from_string },
162 { DT_QUAD, quad_to_string, quad_from_string },
163 { DT_SORT, sort_to_string, sort_from_string },
164 { DT_RX, rx_to_string, rx_from_string },
165 { DT_MAGIC, magic_to_string, magic_from_string },
166 /* synonyms should be resolved already so we don't need this
167 * but must define it as DT_ is used for indexing */
168 { DT_SYN, NULL, NULL },
169 { DT_ADDR, addr_to_string, addr_from_string },
170 { DT_USER, user_to_string, user_from_string },
171 { DT_SYS, sys_to_string, NULL },
174 static void bool_to_string (char* dst, ssize_t dstlen,
175 struct option_t* option) {
176 snprintf (dst, dstlen, "%s=%s", option->option,
177 option (option->data) ? "yes" : "no");
180 static int bool_from_string (struct option_t* dst, const char* val,
181 char* errbuf __attribute__ ((unused)),
182 ssize_t errlen __attribute__ ((unused))) {
187 if (ascii_strncasecmp (val, "yes", 3) == 0)
189 else if (ascii_strncasecmp (val, "no", 2) == 0)
195 set_option (dst->data);
197 unset_option (dst->data);
201 static void num_to_string (char* dst, ssize_t dstlen,
202 struct option_t* option) {
204 const char* fmt = (m_strcmp(option->option, "umask") == 0) ?
206 snprintf (dst, dstlen, fmt, option->option,
207 *((short*) option->data));
210 static int num_from_string (struct option_t* dst, const char* val,
211 char* errbuf, ssize_t errlen) {
212 int num = 0, old = 0;
218 num = strtol (val, &t, 0);
220 if (!*val || *t || (short) num != num) {
222 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
228 /* just temporarily accept new val so that check_special for
229 * $history already has it when doing history's init() */
230 old = *((short*) dst->data);
231 *((short*) dst->data) = (short) num;
233 if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
234 *((short*) dst->data) = old;
241 static void str_to_string (char* dst, ssize_t dstlen,
242 struct option_t* option) {
243 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
244 NONULL (*((char**) option->data)));
247 static void user_to_string (char* dst, ssize_t dstlen,
248 struct option_t* option) {
249 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
250 NONULL (((char*) option->data)));
253 static void sys_to_string (char* dst, ssize_t dstlen,
254 struct option_t* option) {
255 char *val = NULL, *t = NULL;
258 /* get some $madmutt_ values dynamically */
259 if (ascii_strcmp ("madmutt_pwd", option->option) == 0) {
260 val = p_new(char, _POSIX_PATH_MAX);
261 val = getcwd (val, _POSIX_PATH_MAX-1);
263 } else if (ascii_strcmp ("madmutt_folder_path", option->option) == 0 &&
264 CurrentFolder && *CurrentFolder) {
266 } else if (ascii_strcmp ("madmutt_folder_name", option->option) == 0 &&
267 CurrentFolder && *CurrentFolder) {
269 ssize_t Maildirlength = m_strlen(Maildir);
272 * if name starts with $folder, just strip it to keep hierarchy
273 * $folder=imap://host, path=imap://host/inbox/b -> inbox/b
275 if (Maildirlength > 0 && m_strncmp(CurrentFolder, Maildir,
276 Maildirlength) == 0 &&
277 m_strlen(CurrentFolder) > Maildirlength) {
278 val = CurrentFolder + Maildirlength;
279 if (Maildir[strlen(Maildir)-1]!='/')
281 /* if not $folder, just use everything after last / */
282 } else if ((t = strrchr (CurrentFolder, '/')) != NULL)
284 /* default: use as-is */
286 val = (char *) CurrentFolder;
289 val = (char *) option->init;
291 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (val));
296 static int path_from_string (struct option_t* dst, const char* val,
297 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
298 char path[_POSIX_PATH_MAX];
304 p_delete((char**) dst->data);
309 m_strcpy(path, sizeof(path), val);
310 mutt_expand_path (path, sizeof(path));
311 m_strreplace((char **) dst->data, path);
315 static int str_from_string (struct option_t* dst, const char* val,
316 char* errbuf, ssize_t errlen) {
320 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
323 m_strreplace((char**) dst->data, val);
327 static int user_from_string (struct option_t* dst, const char* val,
328 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
329 /* if dst == NULL, we may get here in case the user did unset it,
330 * see parse_set() where item is free()'d before coming here; so
331 * just silently ignore it */
334 if (m_strlen((char*) dst->data) == 0)
335 dst->data = (unsigned long) m_strdup(val);
337 char* s = (char*) dst->data;
338 m_strreplace(&s, val);
340 if (m_strlen(dst->init) == 0)
341 dst->init = m_strdup((char*) dst->data);
345 static void quad_to_string (char* dst, ssize_t dstlen,
346 struct option_t* option) {
347 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
348 snprintf (dst, dstlen, "%s=%s", option->option,
349 vals[quadoption (option->data)]);
352 static int quad_from_string (struct option_t* dst, const char* val,
353 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
358 if (ascii_strncasecmp (val, "yes", 3) == 0)
360 else if (ascii_strncasecmp (val, "no", 2) == 0)
362 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
364 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
370 set_quadoption (dst->data, flag);
374 static void sort_to_string (char* dst, ssize_t dstlen,
375 struct option_t* option) {
376 const struct mapping_t *map = get_sortmap (option);
377 const char *p = NULL;
380 snprintf (dst, sizeof(dst), "%s=unknown", option->option);
384 p = mutt_getnamebyvalue(*((short *)option->data) & SORT_MASK, map);
386 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
387 (*((short *) option->data) & SORT_REVERSE) ?
389 (*((short *) option->data) & SORT_LAST) ? "last-" :
393 static int sort_from_string (struct option_t* dst, const char* val,
394 char* errbuf, ssize_t errlen) {
395 const struct mapping_t *map = NULL;
396 if (!(map = get_sortmap (dst))) {
398 snprintf (errbuf, errlen, _("%s: Unknown type."),
402 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
407 static void rx_to_string (char* dst, ssize_t dstlen,
408 struct option_t* option) {
409 rx_t* p = (rx_t*) option->data;
410 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
411 NONULL (p->pattern));
414 static int rx_from_string (struct option_t* dst, const char* val,
415 char* errbuf, ssize_t errlen) {
418 int flags = 0, e = 0, not = 0;
424 if (option (OPTATTACHMSG) && !m_strcmp(dst->option, "reply_regexp")) {
426 snprintf (errbuf, errlen,
427 "Operation not permitted when in attach-message mode.");
431 if (!((rx_t*) dst->data))
432 *((rx_t**) dst->data) = p_new(rx_t, 1);
434 p = (rx_t*) dst->data;
436 /* something to do? */
437 if (!val || !*val || (p->pattern && m_strcmp(p->pattern, val) == 0))
440 if (m_strcmp(dst->option, "mask") != 0)
441 flags |= mutt_which_case (val);
444 if (m_strcmp(dst->option, "mask") == 0 && *s == '!') {
449 rx = p_new(regex_t, 1);
451 if ((e = REGCOMP (rx, s, flags)) != 0) {
452 regerror (e, rx, errbuf, errlen);
463 m_strreplace(&p->pattern, val);
467 if (m_strcmp(dst->option, "reply_regexp") == 0)
468 mutt_adjust_all_subjects ();
473 static void magic_to_string (char* dst, ssize_t dstlen,
474 struct option_t* option) {
475 const char* s = NULL;
476 switch (option->data) {
477 case M_MBOX: s = "mbox"; break;
478 case M_MMDF: s = "MMDF"; break;
479 case M_MH: s = "MH"; break;
480 case M_MAILDIR: s = "Maildir"; break;
481 default: s = "unknown"; break;
483 snprintf (dst, dstlen, "%s=%s", option->option, s);
486 static int magic_from_string (struct option_t* dst, const char* val,
487 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
490 if (!dst || !val || !*val)
492 if (ascii_strncasecmp (val, "mbox", 4) == 0)
494 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
496 else if (ascii_strncasecmp (val, "mh", 2) == 0)
498 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
504 *((short*) dst->data) = flag;
509 static void addr_to_string (char* dst, ssize_t dstlen,
510 struct option_t* option) {
513 rfc822_write_address (s, sizeof(s), *((address_t**) option->data), 0);
514 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
517 static int addr_from_string (struct option_t* dst, const char* val,
518 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
521 address_list_wipe((address_t**) dst->data);
523 *((address_t**) dst->data) = rfc822_parse_adrlist (NULL, val);
527 int mutt_option_value (const char* val, char* dst, ssize_t dstlen) {
528 struct option_t* option = NULL;
529 char* tmp = NULL, *t = NULL;
532 if (!(option = hash_find (ConfigOptions, val))) {
536 tmp = p_new(char, dstlen+1);
537 FuncTable[DTYPE (option->type)].opt_to_string (tmp, dstlen, option);
539 /* as we get things of type $var=value and don't want to bloat the
540 * above "just" for expansion, we do the stripping here */
541 t = strchr (tmp, '=');
545 if (t[l-1] == '"' && *t == '"') {
550 memcpy (dst, t, l+1);
556 /* for synonym warning reports: adds synonym to end of list */
557 static void syn_add (struct option_t* n, struct option_t* o) {
558 syn_t* tmp = p_new(syn_t, 1);
559 tmp->f = m_strdup(CurRCFile);
563 list_push_back (&Synonyms, tmp);
566 /* for synonym warning reports: free single item (for list_del()) */
567 static void syn_del (void** p) {
568 p_delete(&(*(syn_t**) p)->f);
572 void toggle_quadoption (int opt)
575 int b = (opt % 4) * 2;
577 QuadOptions[n] ^= (1 << b);
580 void set_quadoption (int opt, int flag)
583 int b = (opt % 4) * 2;
585 QuadOptions[n] &= ~(0x3 << b);
586 QuadOptions[n] |= (flag & 0x3) << b;
589 int quadoption (int opt)
592 int b = (opt % 4) * 2;
594 return (QuadOptions[n] >> b) & 0x3;
597 int query_quadoption (int opt, const char *prompt)
599 int v = quadoption (opt);
607 v = mutt_yesorno (prompt, (v == M_ASKYES));
608 CLEARLINE (LINES - 1);
615 static void add_to_list (string_list_t ** list, const char *str)
617 string_list_t *t, *last = NULL;
619 /* don't add a NULL or empty string to the list */
620 if (!str || *str == '\0')
623 /* check to make sure the item is not already on this list */
624 for (last = *list; last; last = last->next) {
625 if (ascii_strcasecmp (str, last->data) == 0) {
626 /* already on the list, so just ignore it */
634 if (!*list || last) {
635 t = p_new(string_list_t, 1);
636 t->data = m_strdup(str);
646 static int add_to_rx_list (list2_t** list, const char *s, int flags,
655 if (!(rx = rx_compile (s, flags))) {
656 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
660 i = rx_lookup ((*list), rx->pattern);
664 list_push_back (list, rx);
668 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
669 const char *templ, BUFFER * err)
671 SPAM_LIST *t = NULL, *last = NULL;
676 if (!pat || !*pat || !templ)
679 if (!(rx = rx_compile (pat, REG_ICASE))) {
680 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
684 /* check to make sure the item is not already on this list */
685 for (last = *list; last; last = last->next) {
686 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
687 /* Already on the list. Formerly we just skipped this case, but
688 * now we're supporting removals, which means we're supporting
689 * re-adds conceptually. So we probably want this to imply a
690 * removal, then do an add. We can achieve the removal by freeing
691 * the template, and leaving t pointed at the current item.
694 p_delete(&t->template);
701 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
702 * update. Otherwise we want to make a new one to link at the list's end.
705 t = mutt_new_spam_list ();
713 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
714 t->template = m_strdup(templ);
716 /* Find highest match number in template string */
718 for (p = templ; *p;) {
723 while (*p && isdigit ((int) *p))
729 t->nmatch++; /* match 0 is always the whole expr */
734 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
736 SPAM_LIST *spam, *prev;
739 /* Being first is a special case. */
743 if (spam->rx && !m_strcmp(spam->rx->pattern, pat)) {
745 rx_delete(&spam->rx);
746 p_delete(&spam->template);
752 for (spam = prev->next; spam;) {
753 if (!m_strcmp(spam->rx->pattern, pat)) {
754 prev->next = spam->next;
755 rx_delete(&spam->rx);
756 p_delete(&spam->template);
769 static void remove_from_list (string_list_t ** l, const char *str)
771 string_list_t *p, *last = NULL;
773 if (m_strcmp("*", str) == 0)
774 string_list_wipe(l); /* ``unCMD *'' means delete all current entries */
779 if (ascii_strcasecmp (str, p->data) == 0) {
782 last->next = p->next;
795 static int remove_from_rx_list (list2_t** l, const char *str)
799 if (m_strcmp("*", str) == 0) {
800 list_del (l, (list_del_t*) rx_delete);
804 i = rx_lookup ((*l), str);
806 rx_t* r = list_pop_idx ((*l), i);
814 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
818 unsigned long res = 0;
820 struct option_t* option = NULL;
823 mutt_extract_token (tmp, s, 0);
825 /* is the item defined as a variable or a function? */
826 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL)
829 for (i = 0; !res && i < MENU_MAX; i++) {
830 struct binding_t *b = km_get_table (Menus[i].value);
835 for (j = 0; b[j].name; j++)
836 if (!ascii_strncasecmp (tmp->data, b[j].name, m_strlen(tmp->data))
837 && (m_strlen(b[j].name) == m_strlen(tmp->data))) {
843 /* check for feature_* */
844 if (!res && ascii_strncasecmp (tmp->data, "feature_", 8) == 0 &&
845 (j = m_strlen(tmp->data)) > 8) {
847 while (Features[i]) {
848 if (m_strlen(Features[i]) == j-8 &&
849 ascii_strncasecmp (Features[i], tmp->data+8, j-8) == 0) {
859 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
861 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
865 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
868 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
869 mutt_error ("Error: %s", err->data);
870 p_delete(&token.data);
873 p_delete(&token.data);
878 static int parse_unignore (BUFFER * buf, BUFFER * s,
879 unsigned long data __attribute__ ((unused)),
880 BUFFER * err __attribute__ ((unused)))
883 mutt_extract_token (buf, s, 0);
885 /* don't add "*" to the unignore list */
886 if (strcmp (buf->data, "*"))
887 add_to_list (&UnIgnore, buf->data);
889 remove_from_list (&Ignore, buf->data);
891 while (MoreArgs (s));
896 static int parse_ignore (BUFFER * buf, BUFFER * s,
897 unsigned long data __attribute__ ((unused)),
898 BUFFER * err __attribute__ ((unused)))
901 mutt_extract_token (buf, s, 0);
902 remove_from_list (&UnIgnore, buf->data);
903 add_to_list (&Ignore, buf->data);
905 while (MoreArgs (s));
910 static int parse_list (BUFFER * buf, BUFFER * s,
911 unsigned long data __attribute__ ((unused)),
912 BUFFER * err __attribute__ ((unused)))
915 mutt_extract_token (buf, s, 0);
916 add_to_list ((string_list_t **) data, buf->data);
918 while (MoreArgs (s));
923 static void _alternates_clean (void)
927 if (Context && Context->msgcount) {
928 for (i = 0; i < Context->msgcount; i++)
929 Context->hdrs[i]->recip_valid = 0;
933 static int parse_alternates (BUFFER * buf, BUFFER * s,
934 unsigned long data __attribute__ ((unused)),
935 BUFFER * err __attribute__ ((unused)))
937 _alternates_clean ();
939 mutt_extract_token (buf, s, 0);
940 remove_from_rx_list (&UnAlternates, buf->data);
942 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
945 while (MoreArgs (s));
950 static int parse_unalternates (BUFFER * buf, BUFFER * s,
951 unsigned long data __attribute__ ((unused)),
952 BUFFER * err __attribute__ ((unused)))
954 _alternates_clean ();
956 mutt_extract_token (buf, s, 0);
957 remove_from_rx_list (&Alternates, buf->data);
959 if (m_strcmp(buf->data, "*") &&
960 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
964 while (MoreArgs (s));
969 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
976 /* Insist on at least one parameter */
979 m_strcpy(err->data, err->dsize, _("spam: no matching pattern"));
981 m_strcpy(err->data, err->dsize, _("nospam: no matching pattern"));
985 /* Extract the first token, a regexp */
986 mutt_extract_token (buf, s, 0);
988 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
989 if (data == M_SPAM) {
990 /* If there's a second parameter, it's a template for the spam tag. */
992 mutt_extract_token (&templ, s, 0);
994 /* Add to the spam list. */
995 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
996 p_delete(&templ.data);
999 p_delete(&templ.data);
1002 /* If not, try to remove from the nospam list. */
1004 remove_from_rx_list (&NoSpamList, buf->data);
1010 /* M_NOSPAM is for nospam commands. */
1011 else if (data == M_NOSPAM) {
1012 /* nospam only ever has one parameter. */
1014 /* "*" is a special case. */
1015 if (!m_strcmp(buf->data, "*")) {
1016 mutt_free_spam_list (&SpamList);
1017 list_del (&NoSpamList, (list_del_t*) rx_delete);
1021 /* If it's on the spam list, just remove it. */
1022 if (remove_from_spam_list (&SpamList, buf->data) != 0)
1025 /* Otherwise, add it to the nospam list. */
1026 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
1032 /* This should not happen. */
1033 m_strcpy(err->data, err->dsize, "This is no good at all.");
1037 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
1038 BUFFER * err __attribute__ ((unused)))
1041 mutt_extract_token (buf, s, 0);
1043 * Check for deletion of entire list
1045 if (m_strcmp(buf->data, "*") == 0) {
1046 string_list_wipe((string_list_t **) data);
1049 remove_from_list ((string_list_t **) data, buf->data);
1051 while (MoreArgs (s));
1056 static int parse_lists (BUFFER * buf, BUFFER * s,
1057 unsigned long data __attribute__ ((unused)),
1061 mutt_extract_token (buf, s, 0);
1062 remove_from_rx_list (&UnMailLists, buf->data);
1064 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1067 while (MoreArgs (s));
1072 /* always wise to do what someone else did before */
1073 static void _attachments_clean (void) {
1075 if (Context && Context->msgcount) {
1076 for (i = 0; i < Context->msgcount; i++)
1077 Context->hdrs[i]->attach_valid = 0;
1081 static int parse_attach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
1082 BUFFER *err __attribute__ ((unused))) {
1084 string_list_t *listp, *lastp;
1089 /* Find the last item in the list that data points to. */
1091 for (listp = *ldata; listp; listp = listp->next) {
1092 a = (ATTACH_MATCH *)listp->data;
1097 mutt_extract_token (buf, s, 0);
1099 if (!buf->data || *buf->data == '\0')
1102 a = p_new(ATTACH_MATCH, 1);
1104 /* some cheap hacks that I expect to remove */
1105 if (!m_strcasecmp(buf->data, "any"))
1106 a->major = m_strdup("*/.*");
1107 else if (!m_strcasecmp(buf->data, "none"))
1108 a->major = m_strdup("cheap_hack/this_should_never_match");
1110 a->major = m_strdup(buf->data);
1112 if ((p = strchr(a->major, '/'))) {
1117 a->minor = "unknown";
1120 len = m_strlen(a->minor);
1121 tmpminor = p_new(char, len + 3);
1122 strcpy(&tmpminor[1], a->minor); /* __STRCPY_CHECKED__ */
1124 tmpminor[len+1] = '$';
1125 tmpminor[len+2] = '\0';
1127 a->major_int = mutt_check_mime_type(a->major);
1128 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
1130 p_delete(&tmpminor);
1132 listp = p_new(string_list_t, 1);
1133 listp->data = (char *)a;
1136 lastp->next = listp;
1142 while (MoreArgs (s));
1144 _attachments_clean();
1148 static int parse_unattach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
1149 BUFFER *err __attribute__ ((unused))) {
1151 string_list_t *lp, *lastp, *newlp;
1157 mutt_extract_token (buf, s, 0);
1159 if (!m_strcasecmp(buf->data, "any"))
1160 tmp = m_strdup("*/.*");
1161 else if (!m_strcasecmp(buf->data, "none"))
1162 tmp = m_strdup("cheap_hack/this_should_never_match");
1164 tmp = m_strdup(buf->data);
1166 if ((minor = strchr(tmp, '/'))) {
1170 minor = m_strdup("unknown");
1172 major = mutt_check_mime_type(tmp);
1174 /* We must do our own walk here because remove_from_list() will only
1175 * remove the string_list_t->data, not anything pointed to by the string_list_t->data. */
1177 for(lp = *ldata; lp; ) {
1178 a = (ATTACH_MATCH *)lp->data;
1179 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
1180 regfree(&a->minor_rx);
1181 p_delete(&a->major);
1183 /* Relink backward */
1185 lastp->next = lp->next;
1190 p_delete(&lp->data); /* same as a */
1200 while (MoreArgs (s));
1203 _attachments_clean();
1207 static int print_attach_list (string_list_t *lp, char op, const char *name) {
1209 printf("attachments %c%s %s/%s\n", op, name,
1210 ((ATTACH_MATCH *)lp->data)->major,
1211 ((ATTACH_MATCH *)lp->data)->minor);
1218 static int parse_attachments (BUFFER *buf, BUFFER *s,
1219 unsigned long data __attribute__ ((unused)),
1222 string_list_t **listp;
1224 mutt_extract_token(buf, s, 0);
1225 if (!buf->data || *buf->data == '\0') {
1226 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
1230 category = buf->data;
1236 printf("\nCurrent attachments settings:\n\n");
1237 print_attach_list(AttachAllow, '+', "A");
1238 print_attach_list(AttachExclude, '-', "A");
1239 print_attach_list(InlineAllow, '+', "I");
1240 print_attach_list(InlineExclude, '-', "I");
1241 set_option (OPTFORCEREDRAWINDEX);
1242 set_option (OPTFORCEREDRAWPAGER);
1243 mutt_any_key_to_continue (NULL);
1247 if (op != '+' && op != '-') {
1251 if (!m_strncasecmp(category, "attachment", strlen(category))) {
1253 listp = &AttachAllow;
1255 listp = &AttachExclude;
1257 else if (!m_strncasecmp(category, "inline", strlen(category))) {
1259 listp = &InlineAllow;
1261 listp = &InlineExclude;
1263 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
1267 return parse_attach_list(buf, s, listp, err);
1270 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
1272 string_list_t **listp;
1274 mutt_extract_token(buf, s, 0);
1275 if (!buf->data || *buf->data == '\0') {
1276 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
1282 if (op != '+' && op != '-') {
1286 if (!m_strncasecmp(p, "attachment", strlen(p))) {
1288 listp = &AttachAllow;
1290 listp = &AttachExclude;
1292 else if (!m_strncasecmp(p, "inline", strlen(p))) {
1294 listp = &InlineAllow;
1296 listp = &InlineExclude;
1299 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
1303 return parse_unattach_list(buf, s, listp, err);
1306 static int parse_unlists (BUFFER * buf, BUFFER * s,
1307 unsigned long data __attribute__ ((unused)),
1308 BUFFER * err __attribute__ ((unused)))
1311 mutt_extract_token (buf, s, 0);
1312 remove_from_rx_list (&SubscribedLists, buf->data);
1313 remove_from_rx_list (&MailLists, buf->data);
1315 if (m_strcmp(buf->data, "*") &&
1316 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1319 while (MoreArgs (s));
1324 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1328 mutt_extract_token (buf, s, 0);
1329 remove_from_rx_list (&UnMailLists, buf->data);
1330 remove_from_rx_list (&UnSubscribedLists, buf->data);
1332 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1334 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1337 while (MoreArgs (s));
1342 static int parse_unsubscribe (BUFFER * buf, BUFFER * s,
1343 unsigned long data __attribute__ ((unused)),
1344 BUFFER * err __attribute__ ((unused)))
1347 mutt_extract_token (buf, s, 0);
1348 remove_from_rx_list (&SubscribedLists, buf->data);
1350 if (m_strcmp(buf->data, "*") &&
1351 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1354 while (MoreArgs (s));
1359 static int parse_unalias (BUFFER * buf, BUFFER * s,
1360 unsigned long data __attribute__ ((unused)),
1361 BUFFER * err __attribute__ ((unused)))
1363 alias_t *tmp, *last = NULL;
1366 mutt_extract_token (buf, s, 0);
1368 if (m_strcmp("*", buf->data) == 0) {
1369 if (CurrentMenu == MENU_ALIAS) {
1370 for (tmp = Aliases; tmp; tmp = tmp->next)
1372 set_option (OPTFORCEREDRAWINDEX);
1375 alias_list_wipe(&Aliases);
1379 for (tmp = Aliases; tmp; tmp = tmp->next) {
1380 if (m_strcasecmp(buf->data, tmp->name) == 0) {
1381 if (CurrentMenu == MENU_ALIAS) {
1383 set_option (OPTFORCEREDRAWINDEX);
1388 last->next = tmp->next;
1390 Aliases = tmp->next;
1392 alias_list_wipe(&tmp);
1398 while (MoreArgs (s));
1402 static int parse_alias (BUFFER * buf, BUFFER * s,
1403 unsigned long data __attribute__ ((unused)),
1406 alias_t *tmp = Aliases;
1407 alias_t *last = NULL;
1408 const char *estr = NULL;
1410 if (!MoreArgs (s)) {
1411 m_strcpy(err->data, err->dsize, _("alias: no address"));
1415 mutt_extract_token (buf, s, 0);
1417 /* check to see if an alias with this name already exists */
1418 for (; tmp; tmp = tmp->next) {
1419 if (!m_strcasecmp(tmp->name, buf->data))
1425 /* create a new alias */
1427 tmp->name = m_strdup(buf->data);
1428 /* give the main addressbook code a chance */
1429 if (CurrentMenu == MENU_ALIAS)
1430 set_option (OPTMENUCALLER);
1433 /* override the previous value */
1434 address_list_wipe(&tmp->addr);
1435 if (CurrentMenu == MENU_ALIAS)
1436 set_option (OPTFORCEREDRAWINDEX);
1439 mutt_extract_token (buf, s,
1440 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1441 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1446 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1447 snprintf (err->data, err->dsize,
1448 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1456 parse_unmy_hdr (BUFFER * buf, BUFFER * s,
1457 unsigned long data __attribute__ ((unused)),
1458 BUFFER * err __attribute__ ((unused)))
1460 string_list_t *last = NULL;
1461 string_list_t *tmp = UserHeader;
1466 mutt_extract_token (buf, s, 0);
1467 if (m_strcmp("*", buf->data) == 0)
1468 string_list_wipe(&UserHeader);
1473 l = m_strlen(buf->data);
1474 if (buf->data[l - 1] == ':')
1478 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1479 && tmp->data[l] == ':') {
1482 last->next = tmp->next;
1484 UserHeader = tmp->next;
1487 string_list_wipe(&ptr);
1496 while (MoreArgs (s));
1500 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1507 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1508 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1509 m_strcpy(err->data, err->dsize, _("invalid header field"));
1512 keylen = p - buf->data + 1;
1515 for (tmp = UserHeader;; tmp = tmp->next) {
1516 /* see if there is already a field by this name */
1517 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1518 /* replace the old value */
1519 p_delete(&tmp->data);
1520 tmp->data = buf->data;
1527 tmp->next = string_item_new();
1531 tmp = string_item_new();
1534 tmp->data = buf->data;
1540 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1541 char* errbuf, ssize_t errlen) {
1544 if (m_strncmp("reverse-", s, 8) == 0) {
1546 flags = SORT_REVERSE;
1549 if (m_strncmp("last-", s, 5) == 0) {
1554 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1556 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1560 *((short*) dst->data) = i | flags;
1564 /* if additional data more == 1, we want to resolve synonyms */
1565 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
1567 char buf[LONG_STRING];
1568 struct option_t *ptr = p;
1570 if (DTYPE(ptr->type) == DT_SYN) {
1573 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1575 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_from_string)
1578 mutt_option_value(ptr->option, buf, sizeof(buf));
1579 if (m_strlen(ptr->init) == 0 && buf && *buf)
1580 ptr->init = m_strdup(buf);
1583 static struct option_t* add_option (const char* name, const char* init,
1584 short type, short dodup) {
1585 struct option_t* option = p_new(struct option_t, 1);
1587 option->option = m_strdup(name);
1588 option->type = type;
1590 option->init = dodup ? m_strdup(init) : (char*) init;
1594 /* creates new option_t* of type DT_USER for $user_ var */
1595 static struct option_t* add_user_option (const char* name) {
1596 return (add_option (name, NULL, DT_USER, 1));
1599 /* free()'s option_t* */
1600 static void del_option (void* p) {
1601 struct option_t *ptr = (struct option_t*) p;
1602 char* s = (char*) ptr->data;
1603 p_delete(&ptr->option);
1605 p_delete(&ptr->init);
1609 static int init_expand (char** dst, struct option_t* src) {
1615 if (DTYPE(src->type) == DT_STR ||
1616 DTYPE(src->type) == DT_PATH) {
1617 /* only expand for string as it's the only place where
1618 * we want to expand vars right now */
1619 if (src->init && *src->init) {
1622 len = m_strlen(src->init) + 2;
1623 in.data = p_new(char, len + 1);
1624 snprintf (in.data, len, "\"%s\"", src->init);
1627 mutt_extract_token (&token, &in, 0);
1628 if (token.data && *token.data)
1629 *dst = m_strdup(token.data);
1631 *dst = m_strdup("");
1633 p_delete(&token.data);
1635 *dst = m_strdup("");
1637 /* for non-string: take value as is */
1638 *dst = m_strdup(src->init);
1642 /* if additional data more == 1, we want to resolve synonyms */
1643 static void mutt_restore_default (const char* name __attribute__ ((unused)),
1644 void* p, unsigned long more) {
1645 char errbuf[STRING];
1646 struct option_t* ptr = (struct option_t*) p;
1649 if (DTYPE (ptr->type) == DT_SYN) {
1652 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1656 if (FuncTable[DTYPE (ptr->type)].opt_from_string) {
1657 init_expand (&init, ptr);
1658 if (!FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, init, errbuf,
1660 if (!option (OPTNOCURSES))
1662 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1663 "Please report this error: \"%s\"\n"),
1664 ptr->option, NONULL (init), errbuf);
1670 if (ptr->flags & R_INDEX)
1671 set_option (OPTFORCEREDRAWINDEX);
1672 if (ptr->flags & R_PAGER)
1673 set_option (OPTFORCEREDRAWPAGER);
1674 if (ptr->flags & R_RESORT_SUB)
1675 set_option (OPTSORTSUBTHREADS);
1676 if (ptr->flags & R_RESORT)
1677 set_option (OPTNEEDRESORT);
1678 if (ptr->flags & R_RESORT_INIT)
1679 set_option (OPTRESORTINIT);
1680 if (ptr->flags & R_TREE)
1681 set_option (OPTREDRAWTREE);
1684 /* check whether value for $dsn_return would be valid */
1685 static int check_dsn_return (const char* option __attribute__ ((unused)), unsigned long p,
1686 char* errbuf, ssize_t errlen) {
1687 char* val = (char*) p;
1688 if (val && *val && m_strncmp(val, "hdrs", 4) != 0 &&
1689 m_strncmp(val, "full", 4) != 0) {
1691 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1697 /* check whether value for $dsn_notify would be valid */
1698 static int check_dsn_notify (const char* option, unsigned long p,
1699 char* errbuf, ssize_t errlen) {
1700 list2_t* list = NULL;
1703 char* val = (char*) p;
1707 list = list_from_str (val, ",");
1708 if (list_empty (list))
1711 for (i = 0; i < list->length; i++)
1712 if (m_strncmp(list->data[i], "never", 5) != 0 &&
1713 m_strncmp(list->data[i], "failure", 7) != 0 &&
1714 m_strncmp(list->data[i], "delay", 5) != 0 &&
1715 m_strncmp(list->data[i], "success", 7) != 0) {
1717 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1718 (char*) list->data[i], "dsn_notify");
1722 list_del (&list, (list_del_t*)xmemfree);
1726 static int check_num (const char* option, unsigned long p,
1727 char* errbuf, ssize_t errlen) {
1730 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1736 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1737 char* errbuf, ssize_t errlen) {
1738 if (!check_num ("history", p, errbuf, errlen))
1740 mutt_init_history ();
1744 static int check_special (const char* name, unsigned long val,
1745 char* errbuf, ssize_t errlen) {
1748 for (i = 0; SpecialVars[i].name; i++) {
1749 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1750 return (SpecialVars[i].check (SpecialVars[i].name,
1751 val, errbuf, errlen));
1757 static const struct mapping_t* get_sortmap (struct option_t* option) {
1758 const struct mapping_t* map = NULL;
1760 switch (option->type & DT_SUBTYPE_MASK) {
1762 map = SortAliasMethods;
1764 case DT_SORT_BROWSER:
1765 map = SortBrowserMethods;
1768 map = SortKeyMethods;
1771 map = SortAuxMethods;
1780 #define CHECK_PAGER \
1781 if ((CurrentMenu == MENU_PAGER) && \
1782 (!option || (option->flags & R_RESORT))) \
1784 snprintf (err->data, err->dsize, \
1785 _("Not available in this menu.")); \
1789 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1792 int query, unset, inv, reset, r = 0;
1793 struct option_t* option = NULL;
1795 while (MoreArgs (s)) {
1796 /* reset state variables */
1798 unset = data & M_SET_UNSET;
1799 inv = data & M_SET_INV;
1800 reset = data & M_SET_RESET;
1802 if (*s->dptr == '?') {
1806 else if (m_strncmp("no", s->dptr, 2) == 0) {
1810 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1814 else if (*s->dptr == '&') {
1819 /* get the variable name */
1820 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1822 /* resolve synonyms */
1823 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1824 DTYPE (option->type == DT_SYN)) {
1825 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1826 syn_add (newopt, option);
1830 /* see if we need to add $user_ var */
1831 if (!option && ascii_strncmp ("user_", tmp->data, 5) == 0) {
1832 /* there's no option named like this yet so only add one
1833 * if the action isn't any of: reset, unset, query */
1834 if (!(reset || unset || query || *s->dptr != '=')) {
1835 option = add_user_option (tmp->data);
1836 hash_insert (ConfigOptions, option->option, option, 0);
1840 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1841 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1844 s->dptr = vskipspaces(s->dptr);
1847 if (query || unset || inv) {
1848 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1852 if (s && *s->dptr == '=') {
1853 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1857 if (!m_strcmp("all", tmp->data)) {
1858 if (CurrentMenu == MENU_PAGER) {
1859 snprintf (err->data, err->dsize, _("Not available in this menu."));
1862 hash_map (ConfigOptions, mutt_restore_default, 1);
1863 set_option (OPTFORCEREDRAWINDEX);
1864 set_option (OPTFORCEREDRAWPAGER);
1865 set_option (OPTSORTSUBTHREADS);
1866 set_option (OPTNEEDRESORT);
1867 set_option (OPTRESORTINIT);
1868 set_option (OPTREDRAWTREE);
1871 else if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1872 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1877 mutt_restore_default (NULL, option, 1);
1880 else if (DTYPE (option->type) == DT_BOOL) {
1881 /* XXX this currently ignores the function table
1882 * as we don't get invert and stuff into it */
1883 if (s && *s->dptr == '=') {
1884 if (unset || inv || query) {
1885 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1890 mutt_extract_token (tmp, s, 0);
1891 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1893 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1896 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1902 bool_to_string (err->data, err->dsize, option);
1908 unset_option (option->data);
1910 toggle_option (option->data);
1912 set_option (option->data);
1914 else if (DTYPE (option->type) == DT_STR ||
1915 DTYPE (option->type) == DT_PATH ||
1916 DTYPE (option->type) == DT_ADDR ||
1917 DTYPE (option->type) == DT_MAGIC ||
1918 DTYPE (option->type) == DT_NUM ||
1919 DTYPE (option->type) == DT_SORT ||
1920 DTYPE (option->type) == DT_RX ||
1921 DTYPE (option->type) == DT_USER ||
1922 DTYPE (option->type) == DT_SYS) {
1924 /* XXX maybe we need to get unset into handlers? */
1925 if (DTYPE (option->type) == DT_STR ||
1926 DTYPE (option->type) == DT_PATH ||
1927 DTYPE (option->type) == DT_ADDR ||
1928 DTYPE (option->type) == DT_USER ||
1929 DTYPE (option->type) == DT_SYS) {
1932 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1933 snprintf (err->data, err->dsize, _("$%s is read-only"),
1937 } else if (DTYPE (option->type) == DT_ADDR)
1938 address_list_wipe((address_t **) option->data);
1939 else if (DTYPE (option->type) == DT_USER)
1940 /* to unset $user_ means remove */
1941 hash_delete (ConfigOptions, option->option,
1942 option, del_option);
1944 p_delete((void **)(void *)&option->data);
1949 if (query || *s->dptr != '=') {
1950 FuncTable[DTYPE (option->type)].opt_to_string
1951 (err->data, err->dsize, option);
1955 /* the $madmutt_ variables are read-only */
1956 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1957 snprintf (err->data, err->dsize, _("$%s is read-only"),
1964 mutt_extract_token (tmp, s, 0);
1965 if (!FuncTable[DTYPE (option->type)].opt_from_string
1966 (option, tmp->data, err->data, err->dsize))
1970 else if (DTYPE (option->type) == DT_QUAD) {
1973 quad_to_string (err->data, err->dsize, option);
1977 if (*s->dptr == '=') {
1980 mutt_extract_token (tmp, s, 0);
1981 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1982 set_quadoption (option->data, M_YES);
1983 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1984 set_quadoption (option->data, M_NO);
1985 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1986 set_quadoption (option->data, M_ASKYES);
1987 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1988 set_quadoption (option->data, M_ASKNO);
1990 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1991 tmp->data, option->option);
1998 toggle_quadoption (option->data);
2000 set_quadoption (option->data, M_NO);
2002 set_quadoption (option->data, M_YES);
2006 snprintf (err->data, err->dsize, _("%s: unknown type"),
2012 if (option->flags & R_INDEX)
2013 set_option (OPTFORCEREDRAWINDEX);
2014 if (option->flags & R_PAGER)
2015 set_option (OPTFORCEREDRAWPAGER);
2016 if (option->flags & R_RESORT_SUB)
2017 set_option (OPTSORTSUBTHREADS);
2018 if (option->flags & R_RESORT)
2019 set_option (OPTNEEDRESORT);
2020 if (option->flags & R_RESORT_INIT)
2021 set_option (OPTRESORTINIT);
2022 if (option->flags & R_TREE)
2023 set_option (OPTREDRAWTREE);
2030 /* reads the specified initialization file. returns -1 if errors were found
2031 so that we can pause to let the user know... */
2032 static int source_rc (const char *rcfile, BUFFER * err)
2035 int line = 0, rc = 0, conv = 0;
2037 char *linebuf = NULL;
2038 char *currentline = NULL;
2042 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
2043 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
2048 while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
2049 conv = ConfigCharset && (*ConfigCharset) && Charset;
2051 currentline = m_strdup(linebuf);
2054 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
2057 currentline = linebuf;
2062 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
2063 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
2064 if (--rc < -MAXERRS) {
2066 p_delete(¤tline);
2075 p_delete(¤tline);
2077 p_delete(&token.data);
2081 mutt_wait_filter (pid);
2083 /* the muttrc source keyword */
2084 snprintf (err->data, err->dsize,
2085 rc >= -MAXERRS ? _("source: errors in %s")
2086 : _("source: reading aborted due too many errors in %s"),
2095 static int parse_source (BUFFER * tmp, BUFFER * s,
2096 unsigned long data __attribute__ ((unused)),
2099 char path[_POSIX_PATH_MAX];
2103 if (mutt_extract_token (tmp, s, 0) != 0) {
2104 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
2108 m_strcpy(path, sizeof(path), tmp->data);
2109 mutt_expand_path (path, sizeof(path));
2111 rc += source_rc (path, err);
2113 while (MoreArgs (s));
2115 return ((rc < 0) ? -1 : 0);
2118 /* line command to execute
2120 token scratch buffer to be used by parser. caller should free
2121 token->data when finished. the reason for this variable is
2122 to avoid having to allocate and deallocate a lot of memory
2123 if we are parsing many lines. the caller can pass in the
2124 memory to use, which avoids having to create new space for
2125 every call to this function.
2127 err where to write error messages */
2128 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
2134 expn.data = expn.dptr = line;
2135 expn.dsize = m_strlen(line);
2139 expn.dptr = vskipspaces(expn.dptr);
2140 while (*expn.dptr) {
2141 if (*expn.dptr == '#')
2142 break; /* rest of line is a comment */
2143 if (*expn.dptr == ';') {
2147 mutt_extract_token (token, &expn, 0);
2148 for (i = 0; Commands[i].name; i++) {
2149 if (!m_strcmp(token->data, Commands[i].name)) {
2150 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
2155 if (!Commands[i].name) {
2156 snprintf (err->data, err->dsize, _("%s: unknown command"),
2157 NONULL (token->data));
2164 p_delete(&expn.data);
2169 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
2170 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
2171 /* initial string that starts completion. No telling how much crap
2172 * the user has typed so far. Allocate LONG_STRING just to be sure! */
2173 char User_typed[LONG_STRING] = { 0 };
2175 int Num_matched = 0; /* Number of matches for completion */
2176 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
2177 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
2179 /* helper function for completion. Changes the dest buffer if
2180 necessary/possible to aid completion.
2181 dest == completion result gets here.
2182 src == candidate for completion.
2183 try == user entered data for completion.
2184 len == length of dest buffer.
2186 static void candidate (char *dest, char *try, const char *src, int len)
2190 if (strstr (src, try) == src) {
2191 Matches[Num_matched++] = src;
2193 m_strcpy(dest, len, src);
2195 for (l = 0; src[l] && src[l] == dest[l]; l++);
2201 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
2205 int spaces; /* keep track of the number of leading spaces on the line */
2207 buffer = vskipspaces(buffer);
2208 spaces = buffer - pt;
2210 pt = buffer + pos - spaces;
2211 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2214 if (pt == buffer) { /* complete cmd */
2215 /* first TAB. Collect all the matches */
2218 m_strcpy(User_typed, sizeof(User_typed), pt);
2219 p_clear(Matches, countof(Matches));
2220 p_clear(Completed, countof(Completed));
2221 for (num = 0; Commands[num].name; num++)
2222 candidate (Completed, User_typed, Commands[num].name,
2224 Matches[Num_matched++] = User_typed;
2226 /* All matches are stored. Longest non-ambiguous string is ""
2227 * i.e. dont change 'buffer'. Fake successful return this time */
2228 if (User_typed[0] == 0)
2232 if (Completed[0] == 0 && User_typed[0])
2235 /* Num_matched will _always_ be atleast 1 since the initial
2236 * user-typed string is always stored */
2237 if (numtabs == 1 && Num_matched == 2)
2238 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2239 else if (numtabs > 1 && Num_matched > 2)
2240 /* cycle thru all the matches */
2241 snprintf (Completed, sizeof(Completed), "%s",
2242 Matches[(numtabs - 2) % Num_matched]);
2244 /* return the completed command */
2245 m_strcpy(buffer, len - spaces, Completed);
2247 else if (!m_strncmp(buffer, "set", 3)
2248 || !m_strncmp(buffer, "unset", 5)
2249 || !m_strncmp(buffer, "reset", 5)
2250 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
2251 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
2254 /* loop through all the possible prefixes (no, inv, ...) */
2255 if (!m_strncmp(buffer, "set", 3)) {
2256 for (num = 0; prefixes[num]; num++) {
2257 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
2258 pt += m_strlen(prefixes[num]);
2264 /* first TAB. Collect all the matches */
2267 m_strcpy(User_typed, sizeof(User_typed), pt);
2268 p_clear(Matches, countof(Matches));
2269 p_clear(Completed, countof(Completed));
2270 for (num = 0; MuttVars[num].option; num++)
2271 candidate(Completed, User_typed, MuttVars[num].option,
2273 Matches[Num_matched++] = User_typed;
2275 /* All matches are stored. Longest non-ambiguous string is ""
2276 * i.e. dont change 'buffer'. Fake successful return this time */
2277 if (User_typed[0] == 0)
2281 if (Completed[0] == 0 && User_typed[0])
2284 /* Num_matched will _always_ be atleast 1 since the initial
2285 * user-typed string is always stored */
2286 if (numtabs == 1 && Num_matched == 2)
2287 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2288 else if (numtabs > 1 && Num_matched > 2)
2289 /* cycle thru all the matches */
2290 snprintf (Completed, sizeof(Completed), "%s",
2291 Matches[(numtabs - 2) % Num_matched]);
2293 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2295 else if (!m_strncmp(buffer, "exec", 4)) {
2296 struct binding_t *menu = km_get_table (CurrentMenu);
2298 if (!menu && CurrentMenu != MENU_PAGER)
2302 /* first TAB. Collect all the matches */
2305 m_strcpy(User_typed, sizeof(User_typed), pt);
2306 p_clear(Matches, countof(Matches));
2307 p_clear(Completed, countof(Completed));
2308 for (num = 0; menu[num].name; num++)
2309 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
2310 /* try the generic menu */
2311 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2313 for (num = 0; menu[num].name; num++)
2314 candidate (Completed, User_typed, menu[num].name,
2317 Matches[Num_matched++] = User_typed;
2319 /* All matches are stored. Longest non-ambiguous string is ""
2320 * i.e. dont change 'buffer'. Fake successful return this time */
2321 if (User_typed[0] == 0)
2325 if (Completed[0] == 0 && User_typed[0])
2328 /* Num_matched will _always_ be atleast 1 since the initial
2329 * user-typed string is always stored */
2330 if (numtabs == 1 && Num_matched == 2)
2331 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2332 else if (numtabs > 1 && Num_matched > 2)
2333 /* cycle thru all the matches */
2334 snprintf (Completed, sizeof(Completed), "%s",
2335 Matches[(numtabs - 2) % Num_matched]);
2337 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2345 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
2347 char var[STRING], *pt = buffer;
2349 struct option_t* option = NULL;
2354 buffer = vskipspaces(buffer);
2355 spaces = buffer - pt;
2357 pt = buffer + pos - spaces;
2358 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2360 pt++; /* move past the space */
2361 if (*pt == '=') /* abort if no var before the '=' */
2364 if (m_strncmp(buffer, "set", 3) == 0) {
2365 m_strcpy(var, sizeof(var), pt);
2366 /* ignore the trailing '=' when comparing */
2367 var[m_strlen(var) - 1] = 0;
2368 if (!(option = hash_find (ConfigOptions, var)))
2369 return 0; /* no such variable. */
2371 char tmp[LONG_STRING], tmp2[LONG_STRING];
2373 ssize_t dlen = buffer + len - pt - spaces;
2374 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2378 if ((DTYPE (option->type) == DT_STR) ||
2379 (DTYPE (option->type) == DT_PATH) ||
2380 (DTYPE (option->type) == DT_RX)) {
2381 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
2382 if (DTYPE (option->type) == DT_PATH)
2383 mutt_pretty_mailbox (tmp);
2385 else if (DTYPE (option->type) == DT_ADDR) {
2386 rfc822_write_address (tmp, sizeof(tmp),
2387 *((address_t **) option->data), 0);
2389 else if (DTYPE (option->type) == DT_QUAD)
2390 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
2391 else if (DTYPE (option->type) == DT_NUM)
2392 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
2393 else if (DTYPE (option->type) == DT_SORT) {
2394 const struct mapping_t *map;
2397 switch (option->type & DT_SUBTYPE_MASK) {
2399 map = SortAliasMethods;
2401 case DT_SORT_BROWSER:
2402 map = SortBrowserMethods;
2405 map = SortKeyMethods;
2411 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2412 snprintf(tmp, sizeof(tmp), "%s%s%s",
2413 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2414 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2416 else if (DTYPE (option->type) == DT_MAGIC) {
2418 switch (DefaultMagic) {
2434 m_strcpy(tmp, sizeof(tmp), p);
2436 else if (DTYPE (option->type) == DT_BOOL)
2437 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
2441 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
2442 if (*s == '\\' || *s == '"')
2448 m_strcpy(tmp, sizeof(tmp), pt);
2449 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2457 /* Implement the -Q command line flag */
2458 int mutt_query_variables (string_list_t * queries)
2462 char errbuff[STRING];
2463 char command[STRING];
2471 err.dsize = sizeof(errbuff);
2473 for (p = queries; p; p = p->next) {
2474 snprintf (command, sizeof(command), "set ?%s\n", p->data);
2475 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2476 fprintf (stderr, "%s\n", err.data);
2477 p_delete(&token.data);
2480 printf ("%s\n", err.data);
2483 p_delete(&token.data);
2487 static int mutt_execute_commands (string_list_t * p)
2490 char errstr[SHORT_STRING];
2494 err.dsize = sizeof(errstr);
2496 for (; p; p = p->next) {
2497 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2498 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2499 p_delete(&token.data);
2503 p_delete(&token.data);
2507 void mutt_init (int skip_sys_rc, string_list_t * commands)
2510 struct utsname utsname;
2512 char buffer[STRING], error[STRING];
2513 int default_rc = 0, need_pause = 0;
2519 err.dsize = sizeof(error);
2521 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2522 * to have some room for $user_ vars */
2523 ConfigOptions = hash_create (sizeof(MuttVars) * 3);
2524 for (i = 0; MuttVars[i].option; i++) {
2525 if (DTYPE (MuttVars[i].type) != DT_SYS)
2526 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2528 hash_insert (ConfigOptions, MuttVars[i].option,
2529 add_option (MuttVars[i].option, MuttVars[i].init,
2534 * XXX - use something even more difficult to predict?
2536 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2537 "\033]9;%ld\a", (long) time (NULL));
2539 /* on one of the systems I use, getcwd() does not return the same prefix
2540 as is listed in the passwd file */
2541 if ((p = getenv ("HOME")))
2542 Homedir = m_strdup(p);
2544 /* Get some information about the user */
2545 if ((pw = getpwuid (getuid ()))) {
2548 Username = m_strdup(pw->pw_name);
2550 Homedir = m_strdup(pw->pw_dir);
2552 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, GecosMask.rx);
2553 Realname = m_strdup(rnbuf);
2554 Shell = m_strdup(pw->pw_shell);
2560 fputs (_("unable to determine home directory"), stderr);
2563 if ((p = getenv ("USER")))
2564 Username = m_strdup(p);
2567 fputs (_("unable to determine username"), stderr);
2570 Shell = m_strdup((p = getenv ("SHELL")) ? p : "/bin/sh");
2573 /* And about the host... */
2575 /* some systems report the FQDN instead of just the hostname */
2576 if ((p = strchr (utsname.nodename, '.'))) {
2577 Hostname = p_dupstr(utsname.nodename, p - utsname.nodename);
2579 m_strcpy(buffer, sizeof(buffer), p); /* save the domain for below */
2582 Hostname = m_strdup(utsname.nodename);
2584 if (!p && getdnsdomainname(buffer, sizeof(buffer)) == -1)
2585 Fqdn = m_strdup("@");
2587 if (*buffer != '@') {
2588 Fqdn = p_new(char, m_strlen(buffer) + m_strlen(Hostname) + 2);
2589 sprintf (Fqdn, "%s.%s", NONULL(Hostname), buffer); /* __SPRINTF_CHECKED__ */
2592 Fqdn = m_strdup(NONULL (Hostname));
2599 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2601 fgets (buffer, sizeof(buffer), f);
2602 p = vskipspaces(buffer);
2604 while (*q && !isspace(*q))
2607 NewsServer = m_strdup(p);
2611 if ((p = getenv ("NNTPSERVER")))
2612 NewsServer = m_strdup(p);
2615 if ((p = getenv ("MAIL")))
2616 Spoolfile = m_strdup(p);
2617 else if ((p = getenv ("MAILDIR")))
2618 Spoolfile = m_strdup(p);
2621 mutt_concat_path(buffer, sizeof(buffer), NONULL(Homedir), MAILPATH);
2623 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(Username));
2625 Spoolfile = m_strdup(buffer);
2628 if ((p = getenv ("MAILCAPS")))
2629 MailcapPath = m_strdup(p);
2631 /* Default search path from RFC1524 */
2633 m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2634 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2637 Tempdir = m_strdup((p = getenv ("TMPDIR")) ? p : "/tmp");
2639 p = getenv ("VISUAL");
2641 p = getenv ("EDITOR");
2645 Editor = m_strdup(p);
2647 if ((p = getenv ("REPLYTO")) != NULL) {
2650 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2653 buf.data = buf.dptr = buffer;
2654 buf.dsize = m_strlen(buffer);
2657 parse_my_hdr (&token, &buf, 0, &err);
2658 p_delete(&token.data);
2661 if ((p = getenv ("EMAIL")) != NULL)
2662 From = rfc822_parse_adrlist (NULL, p);
2664 charset_initialize();
2666 /* Set standard defaults */
2667 hash_map (ConfigOptions, mutt_set_default, 0);
2668 hash_map (ConfigOptions, mutt_restore_default, 0);
2670 CurrentMenu = MENU_MAIN;
2672 /* Do we have a locale definition? */
2673 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2674 ((p = getenv ("LANG")) != NULL && p[0]) ||
2675 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2676 set_option (OPTLOCALES);
2679 /* Unset suspend by default if we're the session leader */
2680 if (getsid (0) == getpid ())
2681 unset_option (OPTSUSPEND);
2684 mutt_init_history ();
2693 * When changing the code which looks for a configuration file,
2694 * please also change the corresponding code in muttbug.sh.in.
2704 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc-%s", NONULL (Homedir),
2706 if (access (buffer, F_OK) == -1)
2708 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL (Homedir));
2709 if (access (buffer, F_OK) == -1)
2711 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc-%s",
2712 NONULL (Homedir), MUTT_VERSION);
2713 if (access (buffer, F_OK) == -1)
2715 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
2719 Muttrc = m_strdup(buffer);
2722 m_strcpy(buffer, sizeof(buffer), Muttrc);
2724 mutt_expand_path (buffer, sizeof(buffer));
2725 Muttrc = m_strdup(buffer);
2727 p_delete(&AliasFile);
2728 AliasFile = m_strdup(NONULL (Muttrc));
2730 /* Process the global rc file if it exists and the user hasn't explicity
2731 requested not to via "-n". */
2733 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
2735 if (access (buffer, F_OK) == -1)
2736 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
2737 if (access (buffer, F_OK) == -1)
2738 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
2740 if (access (buffer, F_OK) == -1)
2741 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
2742 if (access (buffer, F_OK) != -1) {
2743 if (source_rc (buffer, &err) != 0) {
2744 fputs (err.data, stderr);
2745 fputc ('\n', stderr);
2751 /* Read the user's initialization file. */
2752 if (access (Muttrc, F_OK) != -1) {
2753 if (!option (OPTNOCURSES))
2755 if (source_rc (Muttrc, &err) != 0) {
2756 fputs (err.data, stderr);
2757 fputc ('\n', stderr);
2761 else if (!default_rc) {
2762 /* file specified by -F does not exist */
2763 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2764 mutt_endwin (buffer);
2768 if (mutt_execute_commands (commands) != 0)
2771 /* warn about synonym variables */
2772 if (!list_empty(Synonyms)) {
2774 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2775 for (i = 0; i < Synonyms->length; i++) {
2776 struct option_t* newopt = NULL, *oldopt = NULL;
2777 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2778 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2779 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2780 oldopt ? NONULL (oldopt->option) : "",
2781 newopt ? NONULL (newopt->option) : "",
2782 NONULL(((syn_t*) Synonyms->data[i])->f),
2783 ((syn_t*) Synonyms->data[i])->l);
2785 fprintf (stderr, _("Warning: synonym variables are scheduled"
2786 " for removal.\n"));
2787 list_del (&Synonyms, syn_del);
2791 if (need_pause && !option (OPTNOCURSES)) {
2792 if (mutt_any_key_to_continue (NULL) == -1)
2797 set_option (OPTWEED); /* turn weeding on by default */
2801 int mutt_get_hook_type (const char *name)
2803 struct command_t *c;
2805 for (c = Commands; c->name; c++)
2806 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2811 /* compare two option_t*'s for sorting -t/-T output */
2812 static int opt_cmp (const void* a, const void* b) {
2813 return (m_strcmp((*(struct option_t**) a)->option,
2814 (*(struct option_t**) b)->option));
2817 /* callback for hash_map() to put all non-synonym vars into list */
2818 static void opt_sel_full (const char* key __attribute__ ((unused)),
2820 unsigned long more) {
2821 list2_t** l = (list2_t**) more;
2822 struct option_t* option = (struct option_t*) data;
2824 if (DTYPE (option->type) == DT_SYN)
2826 list_push_back (l, option);
2829 /* callback for hash_map() to put all changed non-synonym vars into list */
2830 static void opt_sel_diff (const char* key __attribute__ ((unused)),
2832 unsigned long more) {
2833 list2_t** l = (list2_t**) more;
2834 struct option_t* option = (struct option_t*) data;
2835 char buf[LONG_STRING];
2837 if (DTYPE (option->type) == DT_SYN)
2840 mutt_option_value (option->option, buf, sizeof(buf));
2841 if (m_strcmp(buf, option->init) != 0)
2842 list_push_back (l, option);
2845 /* dump out the value of all the variables we have */
2846 int mutt_dump_variables (int full) {
2848 char outbuf[STRING];
2849 list2_t* tmp = NULL;
2850 struct option_t* option = NULL;
2852 /* get all non-synonyms into list... */
2853 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2854 (unsigned long) &tmp);
2856 if (!list_empty(tmp)) {
2857 /* ...and dump list sorted */
2858 qsort (tmp->data, tmp->length, sizeof(void*), opt_cmp);
2859 for (i = 0; i < tmp->length; i++) {
2860 option = (struct option_t*) tmp->data[i];
2861 FuncTable[DTYPE (option->type)].opt_to_string
2862 (outbuf, sizeof(outbuf), option);
2863 printf ("%s\n", outbuf);
2866 list_del (&tmp, NULL);