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;
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);
1457 parse_unmy_hdr (BUFFER * buf, BUFFER * s,
1458 unsigned long data __attribute__ ((unused)),
1459 BUFFER * err __attribute__ ((unused)))
1461 string_list_t *last = NULL;
1462 string_list_t *tmp = UserHeader;
1467 mutt_extract_token (buf, s, 0);
1468 if (m_strcmp("*", buf->data) == 0)
1469 string_list_wipe(&UserHeader);
1474 l = m_strlen(buf->data);
1475 if (buf->data[l - 1] == ':')
1479 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1480 && tmp->data[l] == ':') {
1483 last->next = tmp->next;
1485 UserHeader = tmp->next;
1488 string_list_wipe(&ptr);
1497 while (MoreArgs (s));
1501 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1508 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1509 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1510 m_strcpy(err->data, err->dsize, _("invalid header field"));
1513 keylen = p - buf->data + 1;
1516 for (tmp = UserHeader;; tmp = tmp->next) {
1517 /* see if there is already a field by this name */
1518 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1519 /* replace the old value */
1520 p_delete(&tmp->data);
1521 tmp->data = buf->data;
1528 tmp->next = string_item_new();
1532 tmp = string_item_new();
1535 tmp->data = buf->data;
1541 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1542 char* errbuf, ssize_t errlen) {
1545 if (m_strncmp("reverse-", s, 8) == 0) {
1547 flags = SORT_REVERSE;
1550 if (m_strncmp("last-", s, 5) == 0) {
1555 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1557 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1561 *((short*) dst->data) = i | flags;
1565 /* if additional data more == 1, we want to resolve synonyms */
1566 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
1568 char buf[LONG_STRING];
1569 struct option_t *ptr = p;
1571 if (DTYPE(ptr->type) == DT_SYN) {
1574 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1576 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_from_string)
1579 mutt_option_value(ptr->option, buf, sizeof(buf));
1580 if (m_strlen(ptr->init) == 0 && buf && *buf)
1581 ptr->init = m_strdup(buf);
1584 static struct option_t* add_option (const char* name, const char* init,
1585 short type, short dodup) {
1586 struct option_t* option = p_new(struct option_t, 1);
1588 option->option = m_strdup(name);
1589 option->type = type;
1591 option->init = dodup ? m_strdup(init) : (char*) init;
1595 /* creates new option_t* of type DT_USER for $user_ var */
1596 static struct option_t* add_user_option (const char* name) {
1597 return (add_option (name, NULL, DT_USER, 1));
1600 /* free()'s option_t* */
1601 static void del_option (void* p) {
1602 struct option_t *ptr = (struct option_t*) p;
1603 char* s = (char*) ptr->data;
1604 p_delete(&ptr->option);
1606 p_delete(&ptr->init);
1610 static int init_expand (char** dst, struct option_t* src) {
1616 if (DTYPE(src->type) == DT_STR ||
1617 DTYPE(src->type) == DT_PATH) {
1618 /* only expand for string as it's the only place where
1619 * we want to expand vars right now */
1620 if (src->init && *src->init) {
1623 len = m_strlen(src->init) + 2;
1624 in.data = p_new(char, len + 1);
1625 snprintf (in.data, len, "\"%s\"", src->init);
1628 mutt_extract_token (&token, &in, 0);
1629 if (token.data && *token.data)
1630 *dst = m_strdup(token.data);
1632 *dst = m_strdup("");
1634 p_delete(&token.data);
1636 *dst = m_strdup("");
1638 /* for non-string: take value as is */
1639 *dst = m_strdup(src->init);
1643 /* if additional data more == 1, we want to resolve synonyms */
1644 static void mutt_restore_default (const char* name __attribute__ ((unused)),
1645 void* p, unsigned long more) {
1646 char errbuf[STRING];
1647 struct option_t* ptr = (struct option_t*) p;
1650 if (DTYPE (ptr->type) == DT_SYN) {
1653 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1657 if (FuncTable[DTYPE (ptr->type)].opt_from_string) {
1658 init_expand (&init, ptr);
1659 if (!FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, init, errbuf,
1661 if (!option (OPTNOCURSES))
1663 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1664 "Please report this error: \"%s\"\n"),
1665 ptr->option, NONULL (init), errbuf);
1671 if (ptr->flags & R_INDEX)
1672 set_option (OPTFORCEREDRAWINDEX);
1673 if (ptr->flags & R_PAGER)
1674 set_option (OPTFORCEREDRAWPAGER);
1675 if (ptr->flags & R_RESORT_SUB)
1676 set_option (OPTSORTSUBTHREADS);
1677 if (ptr->flags & R_RESORT)
1678 set_option (OPTNEEDRESORT);
1679 if (ptr->flags & R_RESORT_INIT)
1680 set_option (OPTRESORTINIT);
1681 if (ptr->flags & R_TREE)
1682 set_option (OPTREDRAWTREE);
1685 /* check whether value for $dsn_return would be valid */
1686 static int check_dsn_return (const char* option __attribute__ ((unused)), unsigned long p,
1687 char* errbuf, ssize_t errlen) {
1688 char* val = (char*) p;
1689 if (val && *val && m_strncmp(val, "hdrs", 4) != 0 &&
1690 m_strncmp(val, "full", 4) != 0) {
1692 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1698 /* check whether value for $dsn_notify would be valid */
1699 static int check_dsn_notify (const char* option __attribute__ ((unused)), unsigned long p,
1700 char* errbuf, ssize_t errlen) {
1701 list2_t* list = NULL;
1704 char* val = (char*) p;
1708 list = list_from_str (val, ",");
1709 if (list_empty (list))
1712 for (i = 0; i < list->length; i++)
1713 if (m_strncmp(list->data[i], "never", 5) != 0 &&
1714 m_strncmp(list->data[i], "failure", 7) != 0 &&
1715 m_strncmp(list->data[i], "delay", 5) != 0 &&
1716 m_strncmp(list->data[i], "success", 7) != 0) {
1718 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1719 (char*) list->data[i], "dsn_notify");
1723 list_del (&list, (list_del_t*)xmemfree);
1727 static int check_num (const char* option, unsigned long p,
1728 char* errbuf, ssize_t errlen) {
1731 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1737 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1738 char* errbuf, ssize_t errlen) {
1739 if (!check_num ("history", p, errbuf, errlen))
1741 mutt_init_history ();
1745 static int check_special (const char* name, unsigned long val,
1746 char* errbuf, ssize_t errlen) {
1749 for (i = 0; SpecialVars[i].name; i++) {
1750 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1751 return (SpecialVars[i].check (SpecialVars[i].name,
1752 val, errbuf, errlen));
1758 static const struct mapping_t* get_sortmap (struct option_t* option) {
1759 const struct mapping_t* map = NULL;
1761 switch (option->type & DT_SUBTYPE_MASK) {
1763 map = SortAliasMethods;
1765 case DT_SORT_BROWSER:
1766 map = SortBrowserMethods;
1769 map = SortKeyMethods;
1772 map = SortAuxMethods;
1781 #define CHECK_PAGER \
1782 if ((CurrentMenu == MENU_PAGER) && \
1783 (!option || (option->flags & R_RESORT))) \
1785 snprintf (err->data, err->dsize, \
1786 _("Not available in this menu.")); \
1790 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1793 int query, unset, inv, reset, r = 0;
1794 struct option_t* option = NULL;
1796 while (MoreArgs (s)) {
1797 /* reset state variables */
1799 unset = data & M_SET_UNSET;
1800 inv = data & M_SET_INV;
1801 reset = data & M_SET_RESET;
1803 if (*s->dptr == '?') {
1807 else if (m_strncmp("no", s->dptr, 2) == 0) {
1811 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1815 else if (*s->dptr == '&') {
1820 /* get the variable name */
1821 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1823 /* resolve synonyms */
1824 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1825 DTYPE (option->type == DT_SYN)) {
1826 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1827 syn_add (newopt, option);
1831 /* see if we need to add $user_ var */
1832 if (!option && ascii_strncmp ("user_", tmp->data, 5) == 0) {
1833 /* there's no option named like this yet so only add one
1834 * if the action isn't any of: reset, unset, query */
1835 if (!(reset || unset || query || *s->dptr != '=')) {
1836 option = add_user_option (tmp->data);
1837 hash_insert (ConfigOptions, option->option, option, 0);
1841 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1842 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1845 s->dptr = vskipspaces(s->dptr);
1848 if (query || unset || inv) {
1849 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1853 if (s && *s->dptr == '=') {
1854 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1858 if (!m_strcmp("all", tmp->data)) {
1859 if (CurrentMenu == MENU_PAGER) {
1860 snprintf (err->data, err->dsize, _("Not available in this menu."));
1863 hash_map (ConfigOptions, mutt_restore_default, 1);
1864 set_option (OPTFORCEREDRAWINDEX);
1865 set_option (OPTFORCEREDRAWPAGER);
1866 set_option (OPTSORTSUBTHREADS);
1867 set_option (OPTNEEDRESORT);
1868 set_option (OPTRESORTINIT);
1869 set_option (OPTREDRAWTREE);
1872 else if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1873 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1878 mutt_restore_default (NULL, option, 1);
1881 else if (DTYPE (option->type) == DT_BOOL) {
1882 /* XXX this currently ignores the function table
1883 * as we don't get invert and stuff into it */
1884 if (s && *s->dptr == '=') {
1885 if (unset || inv || query) {
1886 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1891 mutt_extract_token (tmp, s, 0);
1892 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1894 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1897 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1903 bool_to_string (err->data, err->dsize, option);
1909 unset_option (option->data);
1911 toggle_option (option->data);
1913 set_option (option->data);
1915 else if (DTYPE (option->type) == DT_STR ||
1916 DTYPE (option->type) == DT_PATH ||
1917 DTYPE (option->type) == DT_ADDR ||
1918 DTYPE (option->type) == DT_MAGIC ||
1919 DTYPE (option->type) == DT_NUM ||
1920 DTYPE (option->type) == DT_SORT ||
1921 DTYPE (option->type) == DT_RX ||
1922 DTYPE (option->type) == DT_USER ||
1923 DTYPE (option->type) == DT_SYS) {
1925 /* XXX maybe we need to get unset into handlers? */
1926 if (DTYPE (option->type) == DT_STR ||
1927 DTYPE (option->type) == DT_PATH ||
1928 DTYPE (option->type) == DT_ADDR ||
1929 DTYPE (option->type) == DT_USER ||
1930 DTYPE (option->type) == DT_SYS) {
1933 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1934 snprintf (err->data, err->dsize, _("$%s is read-only"),
1938 } else if (DTYPE (option->type) == DT_ADDR)
1939 address_list_wipe((address_t **) option->data);
1940 else if (DTYPE (option->type) == DT_USER)
1941 /* to unset $user_ means remove */
1942 hash_delete (ConfigOptions, option->option,
1943 option, del_option);
1945 p_delete((void **)(void *)&option->data);
1950 if (query || *s->dptr != '=') {
1951 FuncTable[DTYPE (option->type)].opt_to_string
1952 (err->data, err->dsize, option);
1956 /* the $madmutt_ variables are read-only */
1957 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1958 snprintf (err->data, err->dsize, _("$%s is read-only"),
1965 mutt_extract_token (tmp, s, 0);
1966 if (!FuncTable[DTYPE (option->type)].opt_from_string
1967 (option, tmp->data, err->data, err->dsize))
1971 else if (DTYPE (option->type) == DT_QUAD) {
1974 quad_to_string (err->data, err->dsize, option);
1978 if (*s->dptr == '=') {
1981 mutt_extract_token (tmp, s, 0);
1982 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1983 set_quadoption (option->data, M_YES);
1984 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1985 set_quadoption (option->data, M_NO);
1986 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1987 set_quadoption (option->data, M_ASKYES);
1988 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1989 set_quadoption (option->data, M_ASKNO);
1991 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1992 tmp->data, option->option);
1999 toggle_quadoption (option->data);
2001 set_quadoption (option->data, M_NO);
2003 set_quadoption (option->data, M_YES);
2007 snprintf (err->data, err->dsize, _("%s: unknown type"),
2013 if (option->flags & R_INDEX)
2014 set_option (OPTFORCEREDRAWINDEX);
2015 if (option->flags & R_PAGER)
2016 set_option (OPTFORCEREDRAWPAGER);
2017 if (option->flags & R_RESORT_SUB)
2018 set_option (OPTSORTSUBTHREADS);
2019 if (option->flags & R_RESORT)
2020 set_option (OPTNEEDRESORT);
2021 if (option->flags & R_RESORT_INIT)
2022 set_option (OPTRESORTINIT);
2023 if (option->flags & R_TREE)
2024 set_option (OPTREDRAWTREE);
2031 /* reads the specified initialization file. returns -1 if errors were found
2032 so that we can pause to let the user know... */
2033 static int source_rc (const char *rcfile, BUFFER * err)
2036 int line = 0, rc = 0, conv = 0;
2038 char *linebuf = NULL;
2039 char *currentline = NULL;
2043 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
2044 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
2049 while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
2050 conv = ConfigCharset && (*ConfigCharset) && Charset;
2052 currentline = m_strdup(linebuf);
2055 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
2058 currentline = linebuf;
2063 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
2064 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
2065 if (--rc < -MAXERRS) {
2067 p_delete(¤tline);
2076 p_delete(¤tline);
2078 p_delete(&token.data);
2082 mutt_wait_filter (pid);
2084 /* the muttrc source keyword */
2085 snprintf (err->data, err->dsize,
2086 rc >= -MAXERRS ? _("source: errors in %s")
2087 : _("source: reading aborted due too many errors in %s"),
2096 static int parse_source (BUFFER * tmp, BUFFER * s,
2097 unsigned long data __attribute__ ((unused)),
2100 char path[_POSIX_PATH_MAX];
2104 if (mutt_extract_token (tmp, s, 0) != 0) {
2105 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
2109 m_strcpy(path, sizeof(path), tmp->data);
2110 mutt_expand_path (path, sizeof(path));
2112 rc += source_rc (path, err);
2114 while (MoreArgs (s));
2116 return ((rc < 0) ? -1 : 0);
2119 /* line command to execute
2121 token scratch buffer to be used by parser. caller should free
2122 token->data when finished. the reason for this variable is
2123 to avoid having to allocate and deallocate a lot of memory
2124 if we are parsing many lines. the caller can pass in the
2125 memory to use, which avoids having to create new space for
2126 every call to this function.
2128 err where to write error messages */
2129 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
2135 expn.data = expn.dptr = line;
2136 expn.dsize = m_strlen(line);
2140 expn.dptr = vskipspaces(expn.dptr);
2141 while (*expn.dptr) {
2142 if (*expn.dptr == '#')
2143 break; /* rest of line is a comment */
2144 if (*expn.dptr == ';') {
2148 mutt_extract_token (token, &expn, 0);
2149 for (i = 0; Commands[i].name; i++) {
2150 if (!m_strcmp(token->data, Commands[i].name)) {
2151 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
2156 if (!Commands[i].name) {
2157 snprintf (err->data, err->dsize, _("%s: unknown command"),
2158 NONULL (token->data));
2165 p_delete(&expn.data);
2170 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
2171 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
2172 /* initial string that starts completion. No telling how much crap
2173 * the user has typed so far. Allocate LONG_STRING just to be sure! */
2174 char User_typed[LONG_STRING] = { 0 };
2176 int Num_matched = 0; /* Number of matches for completion */
2177 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
2178 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
2180 /* helper function for completion. Changes the dest buffer if
2181 necessary/possible to aid completion.
2182 dest == completion result gets here.
2183 src == candidate for completion.
2184 try == user entered data for completion.
2185 len == length of dest buffer.
2187 static void candidate (char *dest, char *try, const char *src, int len)
2191 if (strstr (src, try) == src) {
2192 Matches[Num_matched++] = src;
2194 m_strcpy(dest, len, src);
2196 for (l = 0; src[l] && src[l] == dest[l]; l++);
2202 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
2206 int spaces; /* keep track of the number of leading spaces on the line */
2208 buffer = vskipspaces(buffer);
2209 spaces = buffer - pt;
2211 pt = buffer + pos - spaces;
2212 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2215 if (pt == buffer) { /* complete cmd */
2216 /* first TAB. Collect all the matches */
2219 m_strcpy(User_typed, sizeof(User_typed), pt);
2220 p_clear(Matches, countof(Matches));
2221 p_clear(Completed, countof(Completed));
2222 for (num = 0; Commands[num].name; num++)
2223 candidate (Completed, User_typed, Commands[num].name,
2225 Matches[Num_matched++] = User_typed;
2227 /* All matches are stored. Longest non-ambiguous string is ""
2228 * i.e. dont change 'buffer'. Fake successful return this time */
2229 if (User_typed[0] == 0)
2233 if (Completed[0] == 0 && User_typed[0])
2236 /* Num_matched will _always_ be atleast 1 since the initial
2237 * user-typed string is always stored */
2238 if (numtabs == 1 && Num_matched == 2)
2239 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2240 else if (numtabs > 1 && Num_matched > 2)
2241 /* cycle thru all the matches */
2242 snprintf (Completed, sizeof(Completed), "%s",
2243 Matches[(numtabs - 2) % Num_matched]);
2245 /* return the completed command */
2246 m_strcpy(buffer, len - spaces, Completed);
2248 else if (!m_strncmp(buffer, "set", 3)
2249 || !m_strncmp(buffer, "unset", 5)
2250 || !m_strncmp(buffer, "reset", 5)
2251 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
2252 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
2255 /* loop through all the possible prefixes (no, inv, ...) */
2256 if (!m_strncmp(buffer, "set", 3)) {
2257 for (num = 0; prefixes[num]; num++) {
2258 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
2259 pt += m_strlen(prefixes[num]);
2265 /* first TAB. Collect all the matches */
2268 m_strcpy(User_typed, sizeof(User_typed), pt);
2269 p_clear(Matches, countof(Matches));
2270 p_clear(Completed, countof(Completed));
2271 for (num = 0; MuttVars[num].option; num++)
2272 candidate(Completed, User_typed, MuttVars[num].option,
2274 Matches[Num_matched++] = User_typed;
2276 /* All matches are stored. Longest non-ambiguous string is ""
2277 * i.e. dont change 'buffer'. Fake successful return this time */
2278 if (User_typed[0] == 0)
2282 if (Completed[0] == 0 && User_typed[0])
2285 /* Num_matched will _always_ be atleast 1 since the initial
2286 * user-typed string is always stored */
2287 if (numtabs == 1 && Num_matched == 2)
2288 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2289 else if (numtabs > 1 && Num_matched > 2)
2290 /* cycle thru all the matches */
2291 snprintf (Completed, sizeof(Completed), "%s",
2292 Matches[(numtabs - 2) % Num_matched]);
2294 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2296 else if (!m_strncmp(buffer, "exec", 4)) {
2297 struct binding_t *menu = km_get_table (CurrentMenu);
2299 if (!menu && CurrentMenu != MENU_PAGER)
2303 /* first TAB. Collect all the matches */
2306 m_strcpy(User_typed, sizeof(User_typed), pt);
2307 p_clear(Matches, countof(Matches));
2308 p_clear(Completed, countof(Completed));
2309 for (num = 0; menu[num].name; num++)
2310 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
2311 /* try the generic menu */
2312 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2314 for (num = 0; menu[num].name; num++)
2315 candidate (Completed, User_typed, menu[num].name,
2318 Matches[Num_matched++] = User_typed;
2320 /* All matches are stored. Longest non-ambiguous string is ""
2321 * i.e. dont change 'buffer'. Fake successful return this time */
2322 if (User_typed[0] == 0)
2326 if (Completed[0] == 0 && User_typed[0])
2329 /* Num_matched will _always_ be atleast 1 since the initial
2330 * user-typed string is always stored */
2331 if (numtabs == 1 && Num_matched == 2)
2332 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2333 else if (numtabs > 1 && Num_matched > 2)
2334 /* cycle thru all the matches */
2335 snprintf (Completed, sizeof(Completed), "%s",
2336 Matches[(numtabs - 2) % Num_matched]);
2338 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2346 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
2348 char var[STRING], *pt = buffer;
2350 struct option_t* option = NULL;
2355 buffer = vskipspaces(buffer);
2356 spaces = buffer - pt;
2358 pt = buffer + pos - spaces;
2359 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2361 pt++; /* move past the space */
2362 if (*pt == '=') /* abort if no var before the '=' */
2365 if (m_strncmp(buffer, "set", 3) == 0) {
2366 m_strcpy(var, sizeof(var), pt);
2367 /* ignore the trailing '=' when comparing */
2368 var[m_strlen(var) - 1] = 0;
2369 if (!(option = hash_find (ConfigOptions, var)))
2370 return 0; /* no such variable. */
2372 char tmp[LONG_STRING], tmp2[LONG_STRING];
2374 ssize_t dlen = buffer + len - pt - spaces;
2375 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2379 if ((DTYPE (option->type) == DT_STR) ||
2380 (DTYPE (option->type) == DT_PATH) ||
2381 (DTYPE (option->type) == DT_RX)) {
2382 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
2383 if (DTYPE (option->type) == DT_PATH)
2384 mutt_pretty_mailbox (tmp);
2386 else if (DTYPE (option->type) == DT_ADDR) {
2387 rfc822_write_address (tmp, sizeof(tmp),
2388 *((address_t **) option->data), 0);
2390 else if (DTYPE (option->type) == DT_QUAD)
2391 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
2392 else if (DTYPE (option->type) == DT_NUM)
2393 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
2394 else if (DTYPE (option->type) == DT_SORT) {
2395 const struct mapping_t *map;
2398 switch (option->type & DT_SUBTYPE_MASK) {
2400 map = SortAliasMethods;
2402 case DT_SORT_BROWSER:
2403 map = SortBrowserMethods;
2406 map = SortKeyMethods;
2412 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2413 snprintf(tmp, sizeof(tmp), "%s%s%s",
2414 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2415 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2417 else if (DTYPE (option->type) == DT_MAGIC) {
2419 switch (DefaultMagic) {
2435 m_strcpy(tmp, sizeof(tmp), p);
2437 else if (DTYPE (option->type) == DT_BOOL)
2438 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
2442 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
2443 if (*s == '\\' || *s == '"')
2449 m_strcpy(tmp, sizeof(tmp), pt);
2450 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2458 /* Implement the -Q command line flag */
2459 int mutt_query_variables (string_list_t * queries)
2463 char errbuff[STRING];
2464 char command[STRING];
2472 err.dsize = sizeof(errbuff);
2474 for (p = queries; p; p = p->next) {
2475 snprintf (command, sizeof(command), "set ?%s\n", p->data);
2476 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2477 fprintf (stderr, "%s\n", err.data);
2478 p_delete(&token.data);
2481 printf ("%s\n", err.data);
2484 p_delete(&token.data);
2488 static int mutt_execute_commands (string_list_t * p)
2491 char errstr[SHORT_STRING];
2495 err.dsize = sizeof(errstr);
2497 for (; p; p = p->next) {
2498 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2499 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2500 p_delete(&token.data);
2504 p_delete(&token.data);
2508 void mutt_init (int skip_sys_rc, string_list_t * commands)
2511 struct utsname utsname;
2513 char buffer[STRING], error[STRING];
2514 int default_rc = 0, need_pause = 0;
2520 err.dsize = sizeof(error);
2522 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2523 * to have some room for $user_ vars */
2524 ConfigOptions = hash_create (sizeof(MuttVars) * 3);
2525 for (i = 0; MuttVars[i].option; i++) {
2526 if (DTYPE (MuttVars[i].type) != DT_SYS)
2527 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2529 hash_insert (ConfigOptions, MuttVars[i].option,
2530 add_option (MuttVars[i].option, MuttVars[i].init,
2535 * XXX - use something even more difficult to predict?
2537 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2538 "\033]9;%ld\a", (long) time (NULL));
2540 /* on one of the systems I use, getcwd() does not return the same prefix
2541 as is listed in the passwd file */
2542 if ((p = getenv ("HOME")))
2543 Homedir = m_strdup(p);
2545 /* Get some information about the user */
2546 if ((pw = getpwuid (getuid ()))) {
2549 Username = m_strdup(pw->pw_name);
2551 Homedir = m_strdup(pw->pw_dir);
2553 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, GecosMask.rx);
2554 Realname = m_strdup(rnbuf);
2555 Shell = m_strdup(pw->pw_shell);
2561 fputs (_("unable to determine home directory"), stderr);
2564 if ((p = getenv ("USER")))
2565 Username = m_strdup(p);
2568 fputs (_("unable to determine username"), stderr);
2571 Shell = m_strdup((p = getenv ("SHELL")) ? p : "/bin/sh");
2574 /* And about the host... */
2576 /* some systems report the FQDN instead of just the hostname */
2577 if ((p = strchr (utsname.nodename, '.'))) {
2578 Hostname = p_dupstr(utsname.nodename, p - utsname.nodename);
2580 m_strcpy(buffer, sizeof(buffer), p); /* save the domain for below */
2583 Hostname = m_strdup(utsname.nodename);
2585 if (!p && getdnsdomainname(buffer, sizeof(buffer)) == -1)
2586 Fqdn = m_strdup("@");
2588 if (*buffer != '@') {
2589 Fqdn = p_new(char, m_strlen(buffer) + m_strlen(Hostname) + 2);
2590 sprintf (Fqdn, "%s.%s", NONULL(Hostname), buffer); /* __SPRINTF_CHECKED__ */
2593 Fqdn = m_strdup(NONULL (Hostname));
2600 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2602 fgets (buffer, sizeof(buffer), f);
2603 p = vskipspaces(buffer);
2605 while (*q && !isspace(*q))
2608 NewsServer = m_strdup(p);
2612 if ((p = getenv ("NNTPSERVER")))
2613 NewsServer = m_strdup(p);
2616 if ((p = getenv ("MAIL")))
2617 Spoolfile = m_strdup(p);
2618 else if ((p = getenv ("MAILDIR")))
2619 Spoolfile = m_strdup(p);
2622 mutt_concat_path(buffer, sizeof(buffer), NONULL(Homedir), MAILPATH);
2624 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(Username));
2626 Spoolfile = m_strdup(buffer);
2629 if ((p = getenv ("MAILCAPS")))
2630 MailcapPath = m_strdup(p);
2632 /* Default search path from RFC1524 */
2634 m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2635 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2638 Tempdir = m_strdup((p = getenv ("TMPDIR")) ? p : "/tmp");
2640 p = getenv ("VISUAL");
2642 p = getenv ("EDITOR");
2646 Editor = m_strdup(p);
2648 if ((p = getenv ("REPLYTO")) != NULL) {
2651 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2654 buf.data = buf.dptr = buffer;
2655 buf.dsize = m_strlen(buffer);
2658 parse_my_hdr (&token, &buf, 0, &err);
2659 p_delete(&token.data);
2662 if ((p = getenv ("EMAIL")) != NULL)
2663 From = rfc822_parse_adrlist (NULL, p);
2665 charset_initialize();
2667 /* Set standard defaults */
2668 hash_map (ConfigOptions, mutt_set_default, 0);
2669 hash_map (ConfigOptions, mutt_restore_default, 0);
2671 CurrentMenu = MENU_MAIN;
2673 /* Do we have a locale definition? */
2674 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2675 ((p = getenv ("LANG")) != NULL && p[0]) ||
2676 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2677 set_option (OPTLOCALES);
2680 /* Unset suspend by default if we're the session leader */
2681 if (getsid (0) == getpid ())
2682 unset_option (OPTSUSPEND);
2685 mutt_init_history ();
2694 * When changing the code which looks for a configuration file,
2695 * please also change the corresponding code in muttbug.sh.in.
2705 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc-%s", NONULL (Homedir),
2707 if (access (buffer, F_OK) == -1)
2709 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL (Homedir));
2710 if (access (buffer, F_OK) == -1)
2712 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc-%s",
2713 NONULL (Homedir), MUTT_VERSION);
2714 if (access (buffer, F_OK) == -1)
2716 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
2720 Muttrc = m_strdup(buffer);
2723 m_strcpy(buffer, sizeof(buffer), Muttrc);
2725 mutt_expand_path (buffer, sizeof(buffer));
2726 Muttrc = m_strdup(buffer);
2728 p_delete(&AliasFile);
2729 AliasFile = m_strdup(NONULL (Muttrc));
2731 /* Process the global rc file if it exists and the user hasn't explicity
2732 requested not to via "-n". */
2734 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
2736 if (access (buffer, F_OK) == -1)
2737 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
2738 if (access (buffer, F_OK) == -1)
2739 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
2741 if (access (buffer, F_OK) == -1)
2742 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
2743 if (access (buffer, F_OK) != -1) {
2744 if (source_rc (buffer, &err) != 0) {
2745 fputs (err.data, stderr);
2746 fputc ('\n', stderr);
2752 /* Read the user's initialization file. */
2753 if (access (Muttrc, F_OK) != -1) {
2754 if (!option (OPTNOCURSES))
2756 if (source_rc (Muttrc, &err) != 0) {
2757 fputs (err.data, stderr);
2758 fputc ('\n', stderr);
2762 else if (!default_rc) {
2763 /* file specified by -F does not exist */
2764 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2765 mutt_endwin (buffer);
2769 if (mutt_execute_commands (commands) != 0)
2772 /* warn about synonym variables */
2773 if (!list_empty(Synonyms)) {
2775 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2776 for (i = 0; i < Synonyms->length; i++) {
2777 struct option_t* newopt = NULL, *oldopt = NULL;
2778 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2779 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2780 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2781 oldopt ? NONULL (oldopt->option) : "",
2782 newopt ? NONULL (newopt->option) : "",
2783 NONULL(((syn_t*) Synonyms->data[i])->f),
2784 ((syn_t*) Synonyms->data[i])->l);
2786 fprintf (stderr, _("Warning: synonym variables are scheduled"
2787 " for removal.\n"));
2788 list_del (&Synonyms, syn_del);
2792 if (need_pause && !option (OPTNOCURSES)) {
2793 if (mutt_any_key_to_continue (NULL) == -1)
2798 set_option (OPTWEED); /* turn weeding on by default */
2802 int mutt_get_hook_type (const char *name)
2804 struct command_t *c;
2806 for (c = Commands; c->name; c++)
2807 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2812 /* compare two option_t*'s for sorting -t/-T output */
2813 static int opt_cmp (const void* a, const void* b) {
2814 return (m_strcmp((*(struct option_t**) a)->option,
2815 (*(struct option_t**) b)->option));
2818 /* callback for hash_map() to put all non-synonym vars into list */
2819 static void opt_sel_full (const char* key __attribute__ ((unused)),
2821 unsigned long more) {
2822 list2_t** l = (list2_t**) more;
2823 struct option_t* option = (struct option_t*) data;
2825 if (DTYPE (option->type) == DT_SYN)
2827 list_push_back (l, option);
2830 /* callback for hash_map() to put all changed non-synonym vars into list */
2831 static void opt_sel_diff (const char* key __attribute__ ((unused)),
2833 unsigned long more) {
2834 list2_t** l = (list2_t**) more;
2835 struct option_t* option = (struct option_t*) data;
2836 char buf[LONG_STRING];
2838 if (DTYPE (option->type) == DT_SYN)
2841 mutt_option_value (option->option, buf, sizeof(buf));
2842 if (m_strcmp(buf, option->init) != 0)
2843 list_push_back (l, option);
2846 /* dump out the value of all the variables we have */
2847 int mutt_dump_variables (int full) {
2849 char outbuf[STRING];
2850 list2_t* tmp = NULL;
2851 struct option_t* option = NULL;
2853 /* get all non-synonyms into list... */
2854 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2855 (unsigned long) &tmp);
2857 if (!list_empty(tmp)) {
2858 /* ...and dump list sorted */
2859 qsort (tmp->data, tmp->length, sizeof(void*), opt_cmp);
2860 for (i = 0; i < tmp->length; i++) {
2861 option = (struct option_t*) tmp->data[i];
2862 FuncTable[DTYPE (option->type)].opt_to_string
2863 (outbuf, sizeof(outbuf), option);
2864 printf ("%s\n", outbuf);
2867 list_del (&tmp, NULL);