2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
5 * Parts were written/modified by:
6 * Rocco Rutte <pdmef@cs.tu-berlin.de>
8 * This file is part of mutt-ng, see http://www.muttng.org/.
9 * It's licensed under the GNU General Public License,
10 * please see the file GPL in the top level source directory.
13 #include <lib-lib/lib-lib.h>
15 #include <lib-lua/lib-lua.h>
16 #include <lib-sys/unix.h>
17 #include <lib-sys/mutt_ssl.h>
18 #include <lib-ui/curses.h>
19 #include <lib-ui/history.h>
20 #include <lib-mx/mx.h>
21 #include <lib-crypt/crypt.h>
27 #include "mutt_idna.h"
28 #include "mutt_libesmtp.h"
35 static const struct mapping_t* get_sortmap (struct option_t* option);
36 static int parse_sort (struct option_t* dst, const char *s,
37 const struct mapping_t *map,
38 char* errbuf, ssize_t errlen);
40 static hash_t *ConfigOptions = NULL;
42 /* for synonym warning reports: synonym found during parsing */
43 typedef struct syn_t {
47 struct option_t* n; /* new */
48 struct option_t* o; /* old */
52 static void syn_wipe(syn_t *syn) {
56 DO_DELETE(syn_t, syn);
57 DO_SLIST(syn_t, syn, syn_delete);
59 /* for synonym warning reports: list of synonyms found */
60 static syn_t *Synonyms = NULL;
61 /* for synonym warning reports: current rc file */
62 static const char* CurRCFile = NULL;
63 /* for synonym warning reports: current rc line */
64 static int CurRCLine = 0;
66 /* prototypes for checking for special vars */
67 static int check_history (const char* option, unsigned long val,
68 char* errbuf, ssize_t errlen);
69 /* this checks that numbers are >= 0 */
70 static int check_num (const char* option, unsigned long val,
71 char* errbuf, ssize_t errlen);
73 /* use this to check only */
74 static int check_special (const char* option, unsigned long val,
75 char* errbuf, ssize_t errlen);
77 /* variable <-> sanity check function mappings
78 * when changing these, make sure the proper _from_string handler
83 int (*check) (const char* option, unsigned long val,
84 char* errbuf, ssize_t errlen);
86 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
87 { "smtp_use_tls", mutt_libesmtp_check_usetls },
89 { "history", check_history },
90 { "pager_index_lines", check_num },
95 static void bool_to_string (char* dst, ssize_t dstlen,
96 struct option_t* option) {
97 snprintf (dst, dstlen, "%s=%s", option->option,
98 option (option->data) ? "yes" : "no");
101 static int bool_from_string (struct option_t* dst, const char* val,
102 char* errbuf __attribute__ ((unused)),
103 ssize_t errlen __attribute__ ((unused))) {
108 if (ascii_strncasecmp (val, "yes", 3) == 0)
110 else if (ascii_strncasecmp (val, "no", 2) == 0)
116 set_option (dst->data);
118 unset_option (dst->data);
122 static void num_to_string (char* dst, ssize_t dstlen,
123 struct option_t* option) {
125 const char* fmt = (m_strcmp(option->option, "umask") == 0) ?
127 snprintf (dst, dstlen, fmt, option->option,
128 *((short*) option->data));
131 static int num_from_string (struct option_t* dst, const char* val,
132 char* errbuf, ssize_t errlen) {
133 int num = 0, old = 0;
139 num = strtol (val, &t, 0);
141 if (m_strisempty(val) || *t || (short) num != num) {
143 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
149 /* just temporarily accept new val so that check_special for
150 * $history already has it when doing history's init() */
151 old = *((short*) dst->data);
152 *((short*) dst->data) = (short) num;
154 if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
155 *((short*) dst->data) = old;
162 static void str_to_string (char* dst, ssize_t dstlen,
163 struct option_t* option) {
164 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
165 NONULL (*((char**) option->data)));
168 static int path_from_string (struct option_t* dst, const char* val,
169 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
170 char path[_POSIX_PATH_MAX];
175 if (m_strisempty(val)) {
176 p_delete((char**) dst->data);
181 m_strcpy(path, sizeof(path), val);
182 mutt_expand_path (path, sizeof(path));
183 m_strreplace((char **) dst->data, path);
187 static int str_from_string (struct option_t* dst, const char* val,
188 char* errbuf, ssize_t errlen) {
192 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
195 m_strreplace((char**) dst->data, val);
199 static void quad_to_string (char* dst, ssize_t dstlen,
200 struct option_t* option) {
201 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
202 snprintf (dst, dstlen, "%s=%s", option->option,
203 vals[quadoption (option->data)]);
206 static int quad_from_string (struct option_t* dst, const char* val,
207 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
212 if (ascii_strncasecmp (val, "yes", 3) == 0)
214 else if (ascii_strncasecmp (val, "no", 2) == 0)
216 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
218 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
224 set_quadoption (dst->data, flag);
228 static void sort_to_string (char* dst, ssize_t dstlen,
229 struct option_t* option) {
230 const struct mapping_t *map = get_sortmap (option);
231 const char *p = NULL;
234 snprintf (dst, sizeof(dst), "%s=unknown", option->option);
238 p = mutt_getnamebyvalue(*((short *)option->data) & SORT_MASK, map);
240 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
241 (*((short *) option->data) & SORT_REVERSE) ?
243 (*((short *) option->data) & SORT_LAST) ? "last-" :
247 static int sort_from_string (struct option_t* dst, const char* val,
248 char* errbuf, ssize_t errlen) {
249 const struct mapping_t *map = NULL;
250 if (!(map = get_sortmap (dst))) {
252 snprintf (errbuf, errlen, _("%s: Unknown type."),
256 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
261 static void rx_to_string (char* dst, ssize_t dstlen,
262 struct option_t* option) {
263 rx_t* p = (rx_t*) option->data;
264 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
265 NONULL (p->pattern));
268 static int rx_from_string (struct option_t* dst, const char* val,
269 char* errbuf, ssize_t errlen) {
272 int flags = 0, e = 0, neg = 0;
278 if (option (OPTATTACHMSG) && !m_strcmp(dst->option, "reply_regexp")) {
280 snprintf (errbuf, errlen,
281 "Operation not permitted when in attach-message mode.");
285 if (!((rx_t*) dst->data))
286 *((rx_t**) dst->data) = p_new(rx_t, 1);
288 p = (rx_t*) dst->data;
290 /* something to do? */
291 if (m_strisempty(val) || (p->pattern && m_strcmp(p->pattern, val) == 0))
294 if (m_strcmp(dst->option, "mask") != 0)
295 flags |= mutt_which_case (val);
298 if (m_strcmp(dst->option, "mask") == 0 && *s == '!') {
303 rx = p_new(regex_t, 1);
305 if ((e = REGCOMP (rx, s, flags)) != 0) {
306 regerror (e, rx, errbuf, errlen);
317 m_strreplace(&p->pattern, val);
321 if (m_strcmp(dst->option, "reply_regexp") == 0)
322 mutt_adjust_all_subjects ();
327 static void magic_to_string (char* dst, ssize_t dstlen,
328 struct option_t* option) {
329 const char* s = NULL;
330 switch (option->data) {
331 case M_MBOX: s = "mbox"; break;
332 case M_MMDF: s = "MMDF"; break;
333 case M_MH: s = "MH"; break;
334 case M_MAILDIR: s = "Maildir"; break;
335 default: s = "unknown"; break;
337 snprintf (dst, dstlen, "%s=%s", option->option, s);
340 static int magic_from_string (struct option_t* dst, const char* val,
341 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
344 if (!dst || m_strisempty(val))
346 if (ascii_strncasecmp (val, "mbox", 4) == 0)
348 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
350 else if (ascii_strncasecmp (val, "mh", 2) == 0)
352 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
358 *((short*) dst->data) = flag;
363 static void addr_to_string (char* dst, ssize_t dstlen,
364 struct option_t* option) {
367 rfc822_addrcat(s, sizeof(s), *((address_t**) option->data), 0);
368 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
371 static int addr_from_string (struct option_t* dst, const char* val,
372 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
375 address_list_wipe((address_t**) dst->data);
377 *((address_t**) dst->data) = rfc822_parse_adrlist (NULL, val);
383 void (*opt_tostr) (char* dst, ssize_t dstlen, struct option_t* option);
384 int (*opt_fromstr) (struct option_t* dst, const char* val,
385 char* errbuf, ssize_t errlen);
387 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
388 { DT_BOOL, bool_to_string, bool_from_string },
389 { DT_NUM, num_to_string, num_from_string },
390 { DT_STR, str_to_string, str_from_string },
391 { DT_PATH, str_to_string, path_from_string },
392 { DT_QUAD, quad_to_string, quad_from_string },
393 { DT_SORT, sort_to_string, sort_from_string },
394 { DT_RX, rx_to_string, rx_from_string },
395 { DT_MAGIC, magic_to_string, magic_from_string },
396 { DT_ADDR, addr_to_string, addr_from_string },
400 int mutt_option_value (const char* val, char* dst, ssize_t dstlen) {
401 struct option_t* option = NULL;
402 char* tmp = NULL, *t = NULL;
405 if (!(option = hash_find (ConfigOptions, val))) {
409 tmp = p_new(char, dstlen+1);
410 FuncTable[DTYPE(option->type)].opt_tostr (tmp, dstlen, option);
412 /* as we get things of type $var=value and don't want to bloat the
413 * above "just" for expansion, we do the stripping here */
414 t = strchr (tmp, '=');
418 if (t[l-1] == '"' && *t == '"') {
423 memcpy (dst, t, l+1);
429 static void toggle_quadoption (int opt)
432 int b = (opt % 4) * 2;
434 QuadOptions[n] ^= (1 << b);
437 void set_quadoption (int opt, int flag)
440 int b = (opt % 4) * 2;
442 QuadOptions[n] &= ~(0x3 << b);
443 QuadOptions[n] |= (flag & 0x3) << b;
446 int quadoption (int opt)
449 int b = (opt % 4) * 2;
451 return (QuadOptions[n] >> b) & 0x3;
454 int query_quadoption2(int v, const char *prompt)
462 v = mutt_yesorno(prompt, (v == M_ASKYES));
463 CLEARLINE (LINES - 1);
468 int query_quadoption (int opt, const char *prompt)
470 int v = quadoption (opt);
478 v = mutt_yesorno (prompt, (v == M_ASKYES));
479 CLEARLINE (LINES - 1);
486 /* always wise to do what someone else did before */
487 static void _attachments_clean (void) {
489 if (Context && Context->msgcount) {
490 for (i = 0; i < Context->msgcount; i++)
491 Context->hdrs[i]->attach_valid = 0;
495 static int parse_attach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
496 BUFFER *err __attribute__ ((unused))) {
498 string_list_t *listp, *lastp;
503 /* Find the last item in the list that data points to. */
505 for (listp = *ldata; listp; listp = listp->next) {
506 a = (ATTACH_MATCH *)listp->data;
511 mutt_extract_token (buf, s, 0);
513 if (!buf->data || *buf->data == '\0')
516 a = p_new(ATTACH_MATCH, 1);
518 /* some cheap hacks that I expect to remove */
519 if (!m_strcasecmp(buf->data, "any"))
520 a->major = m_strdup("*/.*");
521 else if (!m_strcasecmp(buf->data, "none"))
522 a->major = m_strdup("cheap_hack/this_should_never_match");
524 a->major = m_strdup(buf->data);
526 if ((p = strchr(a->major, '/'))) {
531 a->minor = "unknown";
534 len = m_strlen(a->minor);
535 tmpminor = p_new(char, len + 3);
536 m_strcpy(&tmpminor[1], len + 3, a->minor);
538 tmpminor[len+1] = '$';
539 tmpminor[len+2] = '\0';
541 a->major_int = mutt_check_mime_type(a->major);
542 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
546 listp = p_new(string_list_t, 1);
547 listp->data = (char *)a;
556 while (MoreArgs (s));
558 _attachments_clean();
562 static int parse_unattach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
563 BUFFER *err __attribute__ ((unused))) {
565 string_list_t *lp, *lastp, *newlp;
571 mutt_extract_token (buf, s, 0);
573 if (!m_strcasecmp(buf->data, "any"))
574 tmp = m_strdup("*/.*");
575 else if (!m_strcasecmp(buf->data, "none"))
576 tmp = m_strdup("cheap_hack/this_should_never_match");
578 tmp = m_strdup(buf->data);
580 if ((minor = strchr(tmp, '/'))) {
584 minor = m_strdup("unknown");
586 major = mutt_check_mime_type(tmp);
588 /* We must do our own walk here because string_list_remove() will only
589 * remove the string_list_t->data, not anything pointed to by the string_list_t->data. */
591 for(lp = *ldata; lp; ) {
592 a = (ATTACH_MATCH *)lp->data;
593 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
594 regfree(&a->minor_rx);
597 /* Relink backward */
599 lastp->next = lp->next;
604 p_delete(&lp->data); /* same as a */
614 while (MoreArgs (s));
617 _attachments_clean();
621 static int print_attach_list (string_list_t *lp, char op, const char *name) {
623 printf("attachments %c%s %s/%s\n", op, name,
624 ((ATTACH_MATCH *)lp->data)->major,
625 ((ATTACH_MATCH *)lp->data)->minor);
632 static int parse_attachments (BUFFER *buf, BUFFER *s,
633 unsigned long data __attribute__ ((unused)),
636 string_list_t **listp;
638 mutt_extract_token(buf, s, 0);
639 if (!buf->data || *buf->data == '\0') {
640 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
644 category = buf->data;
650 printf("\nCurrent attachments settings:\n\n");
651 print_attach_list(AttachAllow, '+', "A");
652 print_attach_list(AttachExclude, '-', "A");
653 print_attach_list(InlineAllow, '+', "I");
654 print_attach_list(InlineExclude, '-', "I");
655 set_option (OPTFORCEREDRAWINDEX);
656 set_option (OPTFORCEREDRAWPAGER);
657 mutt_any_key_to_continue (NULL);
661 if (op != '+' && op != '-') {
665 if (!m_strncasecmp(category, "attachment", strlen(category))) {
667 listp = &AttachAllow;
669 listp = &AttachExclude;
671 else if (!m_strncasecmp(category, "inline", strlen(category))) {
673 listp = &InlineAllow;
675 listp = &InlineExclude;
677 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
681 return parse_attach_list(buf, s, listp, err);
684 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
686 string_list_t **listp;
688 mutt_extract_token(buf, s, 0);
689 if (!buf->data || *buf->data == '\0') {
690 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
696 if (op != '+' && op != '-') {
700 if (!m_strncasecmp(p, "attachment", strlen(p))) {
702 listp = &AttachAllow;
704 listp = &AttachExclude;
706 else if (!m_strncasecmp(p, "inline", strlen(p))) {
708 listp = &InlineAllow;
710 listp = &InlineExclude;
713 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
717 return parse_unattach_list(buf, s, listp, err);
720 static int parse_unalias (BUFFER * buf, BUFFER * s,
721 unsigned long data __attribute__ ((unused)),
722 BUFFER * err __attribute__ ((unused)))
724 alias_t *tmp, **last;
727 mutt_extract_token (buf, s, 0);
729 if (!m_strcmp("*", buf->data) == 0) {
730 if (CurrentMenu == MENU_ALIAS) {
731 for (tmp = Aliases; tmp; tmp = tmp->next)
733 set_option(OPTFORCEREDRAWINDEX);
735 alias_list_wipe(&Aliases);
741 for (last = &Aliases; *last; last = &(*last)->next) {
742 if (!m_strcasecmp(buf->data, (*last)->name)) {
743 if (CurrentMenu == MENU_ALIAS) {
745 set_option (OPTFORCEREDRAWINDEX);
747 tmp = alias_list_pop(last);
753 } while (MoreArgs(s));
758 static int parse_alias (BUFFER * buf, BUFFER * s,
759 unsigned long data __attribute__ ((unused)),
766 m_strcpy(err->data, err->dsize, _("alias: no address"));
770 mutt_extract_token (buf, s, 0);
772 /* check to see if an alias with this name already exists */
773 for (last = &Aliases; *last; last = &(*last)->next) {
774 if (!m_strcasecmp((*last)->name, buf->data))
779 /* create a new alias */
781 (*last)->name = m_strdup(buf->data);
782 /* give the main addressbook code a chance */
783 if (CurrentMenu == MENU_ALIAS)
784 set_option (OPTMENUCALLER);
786 /* override the previous value */
787 address_list_wipe(&(*last)->addr);
788 if (CurrentMenu == MENU_ALIAS)
789 set_option (OPTFORCEREDRAWINDEX);
792 mutt_extract_token(buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE);
793 (*last)->addr = mutt_parse_adrlist((*last)->addr, buf->data);
794 if (mutt_addrlist_to_idna((*last)->addr, &estr)) {
795 snprintf (err->data, err->dsize,
796 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, (*last)->name);
805 parse_unmy_hdr(BUFFER * buf, BUFFER * s,
806 unsigned long data __attribute__ ((unused)),
807 BUFFER * err __attribute__ ((unused)))
810 mutt_extract_token (buf, s, 0);
812 if (!m_strcmp("*", buf->data)) {
813 string_list_wipe(&UserHeader);
815 string_list_t **last = &UserHeader;
816 ssize_t l = m_strlen(buf->data);
818 if (buf->data[l - 1] == ':')
822 if (!ascii_strncasecmp(buf->data, (*last)->data, l)
823 && (*last)->data[l] == ':')
825 string_list_t *tmp = string_list_pop(last);
826 string_item_delete(&tmp);
828 last = &(*last)->next;
832 } while (MoreArgs(s));
837 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
844 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
845 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
846 m_strcpy(err->data, err->dsize, _("invalid header field"));
849 keylen = p - buf->data + 1;
852 for (tmp = UserHeader;; tmp = tmp->next) {
853 /* see if there is already a field by this name */
854 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
855 /* replace the old value */
856 p_delete(&tmp->data);
857 tmp->data = buf->data;
864 tmp->next = string_item_new();
868 tmp = string_item_new();
871 tmp->data = buf->data;
877 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
878 char* errbuf, ssize_t errlen) {
881 if (m_strncmp("reverse-", s, 8) == 0) {
883 flags = SORT_REVERSE;
886 if (m_strncmp("last-", s, 5) == 0) {
891 if ((i = mutt_getvaluebyname (s, map)) == -1) {
893 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
897 *((short*) dst->data) = i | flags;
901 /* if additional data more == 1, we want to resolve synonyms */
902 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
904 char buf[LONG_STRING];
905 struct option_t *ptr = p;
907 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_fromstr)
910 mutt_option_value(ptr->option, buf, sizeof(buf));
911 if (m_strlen(ptr->init) == 0 && buf && *buf)
912 ptr->init = m_strdup(buf);
915 static int init_expand (char** dst, struct option_t* src) {
921 if (DTYPE(src->type) == DT_STR || DTYPE(src->type) == DT_PATH) {
922 /* only expand for string as it's the only place where
923 * we want to expand vars right now */
924 if (src->init && *src->init) {
927 len = m_strlen(src->init) + 2;
928 in.data = p_new(char, len + 1);
929 snprintf (in.data, len, "\"%s\"", src->init);
932 mutt_extract_token (&token, &in, 0);
933 if (token.data && *token.data)
934 *dst = m_strdup(token.data);
938 p_delete(&token.data);
942 /* for non-string: take value as is */
943 *dst = m_strdup(src->init);
947 /* if additional data more == 1, we want to resolve synonyms */
948 static void mutt_restore_default (const char* name __attribute__ ((unused)),
949 void* p, unsigned long more) {
951 struct option_t* ptr = (struct option_t*) p;
956 if (FuncTable[DTYPE (ptr->type)].opt_fromstr) {
957 init_expand (&init, ptr);
958 if (!FuncTable[DTYPE (ptr->type)].opt_fromstr (ptr, init, errbuf,
960 if (!option (OPTNOCURSES))
962 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
963 "Please report this error: \"%s\"\n"),
964 ptr->option, NONULL (init), errbuf);
970 if (ptr->flags & R_INDEX)
971 set_option (OPTFORCEREDRAWINDEX);
972 if (ptr->flags & R_PAGER)
973 set_option (OPTFORCEREDRAWPAGER);
974 if (ptr->flags & R_RESORT_SUB)
975 set_option (OPTSORTSUBTHREADS);
976 if (ptr->flags & R_RESORT)
977 set_option (OPTNEEDRESORT);
978 if (ptr->flags & R_RESORT_INIT)
979 set_option (OPTRESORTINIT);
980 if (ptr->flags & R_TREE)
981 set_option (OPTREDRAWTREE);
984 static int check_num (const char* option, unsigned long p,
985 char* errbuf, ssize_t errlen) {
988 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
994 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
995 char* errbuf, ssize_t errlen) {
996 if (!check_num ("history", p, errbuf, errlen))
998 mutt_init_history ();
1002 static int check_special (const char* name, unsigned long val,
1003 char* errbuf, ssize_t errlen) {
1006 for (i = 0; SpecialVars[i].name; i++) {
1007 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1008 return (SpecialVars[i].check (SpecialVars[i].name,
1009 val, errbuf, errlen));
1015 static const struct mapping_t* get_sortmap (struct option_t* option) {
1016 const struct mapping_t* map = NULL;
1018 switch (option->type & DT_SUBTYPE_MASK) {
1020 map = SortAliasMethods;
1022 case DT_SORT_BROWSER:
1023 map = SortBrowserMethods;
1026 map = SortKeyMethods;
1029 map = SortAuxMethods;
1038 #define CHECK_PAGER \
1039 if ((CurrentMenu == MENU_PAGER) && \
1040 (!option || (option->flags & R_RESORT))) \
1042 snprintf (err->data, err->dsize, \
1043 _("Not available in this menu.")); \
1047 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1050 int query, unset, inv, reset, r = 0;
1051 struct option_t* option = NULL;
1053 while (MoreArgs (s)) {
1054 /* reset state variables */
1056 unset = data & M_SET_UNSET;
1057 inv = data & M_SET_INV;
1058 reset = data & M_SET_RESET;
1060 if (*s->dptr == '?') {
1064 else if (m_strncmp("no", s->dptr, 2) == 0) {
1068 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1072 else if (*s->dptr == '&') {
1077 /* get the variable name */
1078 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1079 option = hash_find(ConfigOptions, tmp->data);
1080 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1081 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1084 s->dptr = vskipspaces(s->dptr);
1087 if (query || unset || inv) {
1088 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1092 if (s && *s->dptr == '=') {
1093 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1097 if (!m_strcmp("all", tmp->data)) {
1098 if (CurrentMenu == MENU_PAGER) {
1099 snprintf (err->data, err->dsize, _("Not available in this menu."));
1102 hash_map (ConfigOptions, mutt_restore_default, 1);
1103 set_option (OPTFORCEREDRAWINDEX);
1104 set_option (OPTFORCEREDRAWPAGER);
1105 set_option (OPTSORTSUBTHREADS);
1106 set_option (OPTNEEDRESORT);
1107 set_option (OPTRESORTINIT);
1108 set_option (OPTREDRAWTREE);
1112 mutt_restore_default (NULL, option, 1);
1115 else if (DTYPE (option->type) == DT_BOOL) {
1116 /* XXX this currently ignores the function table
1117 * as we don't get invert and stuff into it */
1118 if (s && *s->dptr == '=') {
1119 if (unset || inv || query) {
1120 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1125 mutt_extract_token (tmp, s, 0);
1126 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1128 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1131 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1137 bool_to_string (err->data, err->dsize, option);
1143 unset_option (option->data);
1145 toggle_option (option->data);
1147 set_option (option->data);
1149 else if (DTYPE (option->type) == DT_STR ||
1150 DTYPE (option->type) == DT_PATH ||
1151 DTYPE (option->type) == DT_ADDR ||
1152 DTYPE (option->type) == DT_MAGIC ||
1153 DTYPE (option->type) == DT_NUM ||
1154 DTYPE (option->type) == DT_SORT ||
1155 DTYPE (option->type) == DT_RX)
1157 /* XXX maybe we need to get unset into handlers? */
1158 if (DTYPE (option->type) == DT_STR ||
1159 DTYPE (option->type) == DT_PATH ||
1160 DTYPE (option->type) == DT_ADDR)
1164 if (DTYPE (option->type) == DT_ADDR)
1165 address_list_wipe((address_t **) option->data);
1167 p_delete((void **)(void *)&option->data);
1172 if (query || *s->dptr != '=') {
1173 FuncTable[DTYPE (option->type)].opt_tostr
1174 (err->data, err->dsize, option);
1180 mutt_extract_token (tmp, s, 0);
1181 if (!FuncTable[DTYPE (option->type)].opt_fromstr
1182 (option, tmp->data, err->data, err->dsize))
1185 else if (DTYPE (option->type) == DT_QUAD) {
1188 quad_to_string (err->data, err->dsize, option);
1192 if (*s->dptr == '=') {
1195 mutt_extract_token (tmp, s, 0);
1196 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1197 set_quadoption (option->data, M_YES);
1198 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1199 set_quadoption (option->data, M_NO);
1200 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1201 set_quadoption (option->data, M_ASKYES);
1202 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1203 set_quadoption (option->data, M_ASKNO);
1205 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1206 tmp->data, option->option);
1213 toggle_quadoption (option->data);
1215 set_quadoption (option->data, M_NO);
1217 set_quadoption (option->data, M_YES);
1221 snprintf (err->data, err->dsize, _("%s: unknown type"),
1227 if (option->flags & R_INDEX)
1228 set_option (OPTFORCEREDRAWINDEX);
1229 if (option->flags & R_PAGER)
1230 set_option (OPTFORCEREDRAWPAGER);
1231 if (option->flags & R_RESORT_SUB)
1232 set_option (OPTSORTSUBTHREADS);
1233 if (option->flags & R_RESORT)
1234 set_option (OPTNEEDRESORT);
1235 if (option->flags & R_RESORT_INIT)
1236 set_option (OPTRESORTINIT);
1237 if (option->flags & R_TREE)
1238 set_option (OPTREDRAWTREE);
1245 /* reads the specified initialization file. returns -1 if errors were found
1246 so that we can pause to let the user know... */
1247 static int source_rc (const char *rcfile, BUFFER * err)
1250 int line = 0, rc = 0, conv = 0;
1252 char *linebuf = NULL;
1253 char *currentline = NULL;
1257 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1258 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1263 while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
1264 conv = ConfigCharset && (*ConfigCharset) && MCharset.charset;
1266 currentline = m_strdup(linebuf);
1269 mutt_convert_string (¤tline, ConfigCharset, MCharset.charset, 0);
1272 currentline = linebuf;
1277 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1278 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1279 if (--rc < -MAXERRS) {
1281 p_delete(¤tline);
1290 p_delete(¤tline);
1292 p_delete(&token.data);
1296 mutt_wait_filter (pid);
1298 /* the muttrc source keyword */
1299 snprintf (err->data, err->dsize,
1300 rc >= -MAXERRS ? _("source: errors in %s")
1301 : _("source: reading aborted due too many errors in %s"),
1310 static int parse_source (BUFFER * tmp, BUFFER * s,
1311 unsigned long data __attribute__ ((unused)),
1314 char path[_POSIX_PATH_MAX];
1318 if (mutt_extract_token (tmp, s, 0) != 0) {
1319 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1323 m_strcpy(path, sizeof(path), tmp->data);
1324 mutt_expand_path (path, sizeof(path));
1326 rc += source_rc (path, err);
1328 while (MoreArgs (s));
1330 return ((rc < 0) ? -1 : 0);
1333 /* line command to execute
1335 token scratch buffer to be used by parser. caller should free
1336 token->data when finished. the reason for this variable is
1337 to avoid having to allocate and deallocate a lot of memory
1338 if we are parsing many lines. the caller can pass in the
1339 memory to use, which avoids having to create new space for
1340 every call to this function.
1342 err where to write error messages */
1343 int mutt_parse_rc_line (const char *line, BUFFER * token, BUFFER * err)
1349 expn.data = expn.dptr = line;
1350 expn.dsize = m_strlen(line);
1354 expn.dptr = vskipspaces(expn.dptr);
1355 while (*expn.dptr) {
1356 if (*expn.dptr == '#')
1357 break; /* rest of line is a comment */
1358 if (*expn.dptr == ';') {
1362 mutt_extract_token (token, &expn, 0);
1363 for (i = 0; Commands[i].name; i++) {
1364 if (!m_strcmp(token->data, Commands[i].name)) {
1365 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1370 if (!Commands[i].name) {
1371 snprintf (err->data, err->dsize, _("%s: unknown command"),
1372 NONULL (token->data));
1379 p_delete(&expn.data);
1384 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
1385 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
1386 /* initial string that starts completion. No telling how much crap
1387 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1388 char User_typed[LONG_STRING] = { 0 };
1390 int Num_matched = 0; /* Number of matches for completion */
1391 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1392 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1394 /* helper function for completion. Changes the dest buffer if
1395 necessary/possible to aid completion.
1396 dest == completion result gets here.
1397 src == candidate for completion.
1398 try == user entered data for completion.
1399 len == length of dest buffer.
1401 static void candidate (char *dest, char *try, const char *src, int len)
1405 if (strstr (src, try) == src) {
1406 Matches[Num_matched++] = src;
1408 m_strcpy(dest, len, src);
1410 for (l = 0; src[l] && src[l] == dest[l]; l++);
1416 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
1420 int spaces; /* keep track of the number of leading spaces on the line */
1422 buffer = vskipspaces(buffer);
1423 spaces = buffer - pt;
1425 pt = buffer + pos - spaces;
1426 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1429 if (pt == buffer) { /* complete cmd */
1430 /* first TAB. Collect all the matches */
1433 m_strcpy(User_typed, sizeof(User_typed), pt);
1434 p_clear(Matches, countof(Matches));
1435 p_clear(Completed, countof(Completed));
1436 for (num = 0; Commands[num].name; num++)
1437 candidate (Completed, User_typed, Commands[num].name,
1439 Matches[Num_matched++] = User_typed;
1441 /* All matches are stored. Longest non-ambiguous string is ""
1442 * i.e. dont change 'buffer'. Fake successful return this time */
1443 if (User_typed[0] == 0)
1447 if (Completed[0] == 0 && User_typed[0])
1450 /* Num_matched will _always_ be atleast 1 since the initial
1451 * user-typed string is always stored */
1452 if (numtabs == 1 && Num_matched == 2)
1453 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1454 else if (numtabs > 1 && Num_matched > 2)
1455 /* cycle thru all the matches */
1456 snprintf (Completed, sizeof(Completed), "%s",
1457 Matches[(numtabs - 2) % Num_matched]);
1459 /* return the completed command */
1460 m_strcpy(buffer, len - spaces, Completed);
1462 else if (!m_strncmp(buffer, "set", 3)
1463 || !m_strncmp(buffer, "unset", 5)
1464 || !m_strncmp(buffer, "reset", 5)
1465 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
1466 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
1469 /* loop through all the possible prefixes (no, inv, ...) */
1470 if (!m_strncmp(buffer, "set", 3)) {
1471 for (num = 0; prefixes[num]; num++) {
1472 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
1473 pt += m_strlen(prefixes[num]);
1479 /* first TAB. Collect all the matches */
1482 m_strcpy(User_typed, sizeof(User_typed), pt);
1483 p_clear(Matches, countof(Matches));
1484 p_clear(Completed, countof(Completed));
1485 for (num = 0; MuttVars[num].option; num++)
1486 candidate(Completed, User_typed, MuttVars[num].option,
1488 Matches[Num_matched++] = User_typed;
1490 /* All matches are stored. Longest non-ambiguous string is ""
1491 * i.e. dont change 'buffer'. Fake successful return this time */
1492 if (User_typed[0] == 0)
1496 if (Completed[0] == 0 && User_typed[0])
1499 /* Num_matched will _always_ be atleast 1 since the initial
1500 * user-typed string is always stored */
1501 if (numtabs == 1 && Num_matched == 2)
1502 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1503 else if (numtabs > 1 && Num_matched > 2)
1504 /* cycle thru all the matches */
1505 snprintf (Completed, sizeof(Completed), "%s",
1506 Matches[(numtabs - 2) % Num_matched]);
1508 m_strcpy(pt, buffer + len - pt - spaces, Completed);
1510 else if (!m_strncmp(buffer, "exec", 4)) {
1511 struct binding_t *menu = km_get_table (CurrentMenu);
1513 if (!menu && CurrentMenu != MENU_PAGER)
1517 /* first TAB. Collect all the matches */
1520 m_strcpy(User_typed, sizeof(User_typed), pt);
1521 p_clear(Matches, countof(Matches));
1522 p_clear(Completed, countof(Completed));
1523 for (num = 0; menu[num].name; num++)
1524 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
1525 /* try the generic menu */
1526 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1528 for (num = 0; menu[num].name; num++)
1529 candidate (Completed, User_typed, menu[num].name,
1532 Matches[Num_matched++] = User_typed;
1534 /* All matches are stored. Longest non-ambiguous string is ""
1535 * i.e. dont change 'buffer'. Fake successful return this time */
1536 if (User_typed[0] == 0)
1540 if (Completed[0] == 0 && User_typed[0])
1543 /* Num_matched will _always_ be atleast 1 since the initial
1544 * user-typed string is always stored */
1545 if (numtabs == 1 && Num_matched == 2)
1546 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1547 else if (numtabs > 1 && Num_matched > 2)
1548 /* cycle thru all the matches */
1549 snprintf (Completed, sizeof(Completed), "%s",
1550 Matches[(numtabs - 2) % Num_matched]);
1552 m_strcpy(pt, buffer + len - pt - spaces, Completed);
1560 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
1562 char var[STRING], *pt = buffer;
1564 struct option_t* option = NULL;
1569 buffer = vskipspaces(buffer);
1570 spaces = buffer - pt;
1572 pt = buffer + pos - spaces;
1573 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1575 pt++; /* move past the space */
1576 if (*pt == '=') /* abort if no var before the '=' */
1579 if (m_strncmp(buffer, "set", 3) == 0) {
1580 m_strcpy(var, sizeof(var), pt);
1581 /* ignore the trailing '=' when comparing */
1582 var[m_strlen(var) - 1] = 0;
1583 if (!(option = hash_find (ConfigOptions, var)))
1584 return 0; /* no such variable. */
1586 char tmp[LONG_STRING], tmp2[LONG_STRING];
1588 ssize_t dlen = buffer + len - pt - spaces;
1589 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1593 if ((DTYPE (option->type) == DT_STR) ||
1594 (DTYPE (option->type) == DT_PATH) ||
1595 (DTYPE (option->type) == DT_RX)) {
1596 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
1597 if (DTYPE (option->type) == DT_PATH)
1598 mutt_pretty_mailbox (tmp);
1600 else if (DTYPE (option->type) == DT_ADDR) {
1601 rfc822_addrcat(tmp, sizeof(tmp), *((address_t **) option->data), 0);
1603 else if (DTYPE (option->type) == DT_QUAD)
1604 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
1605 else if (DTYPE (option->type) == DT_NUM)
1606 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
1607 else if (DTYPE (option->type) == DT_SORT) {
1608 const struct mapping_t *map;
1611 switch (option->type & DT_SUBTYPE_MASK) {
1613 map = SortAliasMethods;
1615 case DT_SORT_BROWSER:
1616 map = SortBrowserMethods;
1619 map = SortKeyMethods;
1625 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
1626 snprintf(tmp, sizeof(tmp), "%s%s%s",
1627 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
1628 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
1630 else if (DTYPE (option->type) == DT_MAGIC) {
1632 switch (DefaultMagic) {
1648 m_strcpy(tmp, sizeof(tmp), p);
1650 else if (DTYPE (option->type) == DT_BOOL)
1651 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
1655 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
1656 if (*s == '\\' || *s == '"')
1662 m_strcpy(tmp, sizeof(tmp), pt);
1663 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
1671 /* Implement the -Q command line flag */
1672 int mutt_query_variables (string_list_t * queries)
1676 char errbuff[STRING];
1677 char command[STRING];
1685 err.dsize = sizeof(errbuff);
1687 for (p = queries; p; p = p->next) {
1688 snprintf (command, sizeof(command), "set ?%s\n", p->data);
1689 if (mutt_parse_rc_line (command, &token, &err) == -1) {
1690 fprintf (stderr, "%s\n", err.data);
1691 p_delete(&token.data);
1694 printf ("%s\n", err.data);
1697 p_delete(&token.data);
1701 static int mutt_execute_commands (string_list_t * p)
1704 char errstr[STRING];
1708 err.dsize = sizeof(errstr);
1710 for (; p; p = p->next) {
1711 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
1712 fprintf (stderr, _("Error in command line: %s\n"), err.data);
1713 p_delete(&token.data);
1717 p_delete(&token.data);
1721 void mutt_init (int skip_sys_rc, string_list_t * commands)
1725 char buffer[STRING], error[STRING];
1726 int default_rc = 0, need_pause = 0;
1732 err.dsize = sizeof(error);
1734 ConfigOptions = hash_new (sizeof(MuttVars) * 2, 0);
1735 for (i = 0; MuttVars[i].option; i++) {
1736 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i]);
1740 * XXX - use something even more difficult to predict?
1742 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
1743 "\033]9;%ld\a", (long) time (NULL));
1746 /* Get some information about the user */
1747 if ((pw = getpwuid (getuid ()))) {
1749 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, MCore.gecos_mask);
1750 Realname = m_strdup(rnbuf);
1758 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
1760 fgets (buffer, sizeof(buffer), f);
1761 p = vskipspaces(buffer);
1763 while (*q && !isspace(*q))
1766 NewsServer = m_strdup(p);
1770 if ((p = getenv ("NNTPSERVER")))
1771 NewsServer = m_strdup(p);
1774 if ((p = getenv("MAIL") ?: getenv("MAILDIR"))) {
1775 Spoolfile = m_strdup(p);
1778 mutt_concat_path(buffer, sizeof(buffer), NONULL(MCore.homedir), MAILPATH);
1780 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(MCore.username));
1782 Spoolfile = m_strdup(buffer);
1785 if ((p = getenv ("REPLYTO")) != NULL) {
1788 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
1791 buf.data = buf.dptr = buffer;
1792 buf.dsize = m_strlen(buffer);
1795 parse_my_hdr (&token, &buf, 0, &err);
1796 p_delete(&token.data);
1799 if ((p = getenv ("EMAIL")) != NULL)
1800 From = rfc822_parse_adrlist (NULL, p);
1802 /* Set standard defaults */
1803 hash_map (ConfigOptions, mutt_set_default, 0);
1804 hash_map (ConfigOptions, mutt_restore_default, 0);
1806 CurrentMenu = MENU_MAIN;
1809 /* Unset suspend by default if we're the session leader */
1810 if (getsid (0) == getpid ())
1811 unset_option (OPTSUSPEND);
1814 mutt_init_history ();
1817 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL(MCore.homedir));
1818 if (access (buffer, F_OK) == -1)
1819 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
1820 NONULL(MCore.homedir));
1823 Muttrc = m_strdup(buffer);
1826 m_strcpy(buffer, sizeof(buffer), Muttrc);
1828 mutt_expand_path (buffer, sizeof(buffer));
1829 Muttrc = m_strdup(buffer);
1832 /* Process the global rc file if it exists and the user hasn't explicity
1833 requested not to via "-n". */
1835 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
1837 if (access (buffer, F_OK) == -1)
1838 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
1839 if (access (buffer, F_OK) == -1)
1840 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
1842 if (access (buffer, F_OK) == -1)
1843 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
1844 if (access (buffer, F_OK) != -1) {
1845 if (source_rc (buffer, &err) != 0) {
1846 fputs (err.data, stderr);
1847 fputc ('\n', stderr);
1853 /* Read the user's initialization file. */
1854 if (access (Muttrc, F_OK) != -1) {
1855 if (!option (OPTNOCURSES))
1857 if (source_rc (Muttrc, &err) != 0) {
1858 fputs (err.data, stderr);
1859 fputc ('\n', stderr);
1863 else if (!default_rc) {
1864 /* file specified by -F does not exist */
1865 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
1866 mutt_endwin (buffer);
1871 snprintf(buffer, sizeof(buffer), "%s/.madmutt.lua", NONULL(MCore.homedir));
1872 if (access(buffer, F_OK) < 0)
1873 snprintf(buffer, sizeof(buffer), "%s/.madmutt/cfg.lua", NONULL(MCore.homedir));
1874 if (!access(buffer, F_OK)) {
1875 need_pause = luaM_wrap(mutt_error, luaM_dofile(buffer));
1879 if (mutt_execute_commands (commands) != 0)
1882 /* warn about synonym variables */
1886 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
1888 for (syn = Synonyms; syn; syn = syn->next) {
1889 fprintf(stderr, "$%s ($%s should be used) (%s:%d)\n",
1890 syn->o ? NONULL(syn->o->option) : "",
1891 syn->n ? NONULL(syn->n->option) : "",
1892 NONULL(syn->f), syn->l);
1894 fprintf (stderr, _("Warning: synonym variables are scheduled"
1895 " for removal.\n"));
1896 syn_list_wipe(&Synonyms);
1900 if (need_pause && !option (OPTNOCURSES)) {
1901 if (mutt_any_key_to_continue (NULL) == -1)
1906 int mutt_get_hook_type (const char *name)
1908 struct command_t *c;
1910 for (c = Commands; c->name; c++)
1911 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
1916 /* dump out the value of all the variables we have */
1917 int mutt_dump_variables (int full) {
1920 /* get all non-synonyms into list... */
1921 for (i = 0; MuttVars[i].option; i++) {
1922 struct option_t *option = MuttVars + i;
1923 char buf[LONG_STRING];
1926 mutt_option_value(option->option, buf, sizeof(buf));
1927 if (!m_strcmp(buf, option->init))
1932 FuncTable[DTYPE(option->type)].opt_tostr(buf, sizeof(buf), option);
1933 printf ("%s\n", buf);
1936 printf ("\n# vi""m:set ft=muttrc:\n");