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 /* synonyms should be resolved already so we don't need this
397 * but must define it as DT_ is used for indexing */
398 { DT_SYN, NULL, NULL },
399 { DT_ADDR, addr_to_string, addr_from_string },
403 int mutt_option_value (const char* val, char* dst, ssize_t dstlen) {
404 struct option_t* option = NULL;
405 char* tmp = NULL, *t = NULL;
408 if (!(option = hash_find (ConfigOptions, val))) {
412 tmp = p_new(char, dstlen+1);
413 FuncTable[DTYPE(option->type)].opt_tostr (tmp, dstlen, option);
415 /* as we get things of type $var=value and don't want to bloat the
416 * above "just" for expansion, we do the stripping here */
417 t = strchr (tmp, '=');
421 if (t[l-1] == '"' && *t == '"') {
426 memcpy (dst, t, l+1);
432 static void toggle_quadoption (int opt)
435 int b = (opt % 4) * 2;
437 QuadOptions[n] ^= (1 << b);
440 void set_quadoption (int opt, int flag)
443 int b = (opt % 4) * 2;
445 QuadOptions[n] &= ~(0x3 << b);
446 QuadOptions[n] |= (flag & 0x3) << b;
449 int quadoption (int opt)
452 int b = (opt % 4) * 2;
454 return (QuadOptions[n] >> b) & 0x3;
457 int query_quadoption2(int v, const char *prompt)
465 v = mutt_yesorno(prompt, (v == M_ASKYES));
466 CLEARLINE (LINES - 1);
471 int query_quadoption (int opt, const char *prompt)
473 int v = quadoption (opt);
481 v = mutt_yesorno (prompt, (v == M_ASKYES));
482 CLEARLINE (LINES - 1);
489 /* always wise to do what someone else did before */
490 static void _attachments_clean (void) {
492 if (Context && Context->msgcount) {
493 for (i = 0; i < Context->msgcount; i++)
494 Context->hdrs[i]->attach_valid = 0;
498 static int parse_attach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
499 BUFFER *err __attribute__ ((unused))) {
501 string_list_t *listp, *lastp;
506 /* Find the last item in the list that data points to. */
508 for (listp = *ldata; listp; listp = listp->next) {
509 a = (ATTACH_MATCH *)listp->data;
514 mutt_extract_token (buf, s, 0);
516 if (!buf->data || *buf->data == '\0')
519 a = p_new(ATTACH_MATCH, 1);
521 /* some cheap hacks that I expect to remove */
522 if (!m_strcasecmp(buf->data, "any"))
523 a->major = m_strdup("*/.*");
524 else if (!m_strcasecmp(buf->data, "none"))
525 a->major = m_strdup("cheap_hack/this_should_never_match");
527 a->major = m_strdup(buf->data);
529 if ((p = strchr(a->major, '/'))) {
534 a->minor = "unknown";
537 len = m_strlen(a->minor);
538 tmpminor = p_new(char, len + 3);
539 m_strcpy(&tmpminor[1], len + 3, a->minor);
541 tmpminor[len+1] = '$';
542 tmpminor[len+2] = '\0';
544 a->major_int = mutt_check_mime_type(a->major);
545 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
549 listp = p_new(string_list_t, 1);
550 listp->data = (char *)a;
559 while (MoreArgs (s));
561 _attachments_clean();
565 static int parse_unattach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
566 BUFFER *err __attribute__ ((unused))) {
568 string_list_t *lp, *lastp, *newlp;
574 mutt_extract_token (buf, s, 0);
576 if (!m_strcasecmp(buf->data, "any"))
577 tmp = m_strdup("*/.*");
578 else if (!m_strcasecmp(buf->data, "none"))
579 tmp = m_strdup("cheap_hack/this_should_never_match");
581 tmp = m_strdup(buf->data);
583 if ((minor = strchr(tmp, '/'))) {
587 minor = m_strdup("unknown");
589 major = mutt_check_mime_type(tmp);
591 /* We must do our own walk here because string_list_remove() will only
592 * remove the string_list_t->data, not anything pointed to by the string_list_t->data. */
594 for(lp = *ldata; lp; ) {
595 a = (ATTACH_MATCH *)lp->data;
596 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
597 regfree(&a->minor_rx);
600 /* Relink backward */
602 lastp->next = lp->next;
607 p_delete(&lp->data); /* same as a */
617 while (MoreArgs (s));
620 _attachments_clean();
624 static int print_attach_list (string_list_t *lp, char op, const char *name) {
626 printf("attachments %c%s %s/%s\n", op, name,
627 ((ATTACH_MATCH *)lp->data)->major,
628 ((ATTACH_MATCH *)lp->data)->minor);
635 static int parse_attachments (BUFFER *buf, BUFFER *s,
636 unsigned long data __attribute__ ((unused)),
639 string_list_t **listp;
641 mutt_extract_token(buf, s, 0);
642 if (!buf->data || *buf->data == '\0') {
643 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
647 category = buf->data;
653 printf("\nCurrent attachments settings:\n\n");
654 print_attach_list(AttachAllow, '+', "A");
655 print_attach_list(AttachExclude, '-', "A");
656 print_attach_list(InlineAllow, '+', "I");
657 print_attach_list(InlineExclude, '-', "I");
658 set_option (OPTFORCEREDRAWINDEX);
659 set_option (OPTFORCEREDRAWPAGER);
660 mutt_any_key_to_continue (NULL);
664 if (op != '+' && op != '-') {
668 if (!m_strncasecmp(category, "attachment", strlen(category))) {
670 listp = &AttachAllow;
672 listp = &AttachExclude;
674 else if (!m_strncasecmp(category, "inline", strlen(category))) {
676 listp = &InlineAllow;
678 listp = &InlineExclude;
680 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
684 return parse_attach_list(buf, s, listp, err);
687 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
689 string_list_t **listp;
691 mutt_extract_token(buf, s, 0);
692 if (!buf->data || *buf->data == '\0') {
693 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
699 if (op != '+' && op != '-') {
703 if (!m_strncasecmp(p, "attachment", strlen(p))) {
705 listp = &AttachAllow;
707 listp = &AttachExclude;
709 else if (!m_strncasecmp(p, "inline", strlen(p))) {
711 listp = &InlineAllow;
713 listp = &InlineExclude;
716 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
720 return parse_unattach_list(buf, s, listp, err);
723 static int parse_unalias (BUFFER * buf, BUFFER * s,
724 unsigned long data __attribute__ ((unused)),
725 BUFFER * err __attribute__ ((unused)))
727 alias_t *tmp, **last;
730 mutt_extract_token (buf, s, 0);
732 if (!m_strcmp("*", buf->data) == 0) {
733 if (CurrentMenu == MENU_ALIAS) {
734 for (tmp = Aliases; tmp; tmp = tmp->next)
736 set_option(OPTFORCEREDRAWINDEX);
738 alias_list_wipe(&Aliases);
744 for (last = &Aliases; *last; last = &(*last)->next) {
745 if (!m_strcasecmp(buf->data, (*last)->name)) {
746 if (CurrentMenu == MENU_ALIAS) {
748 set_option (OPTFORCEREDRAWINDEX);
750 tmp = alias_list_pop(last);
756 } while (MoreArgs(s));
761 static int parse_alias (BUFFER * buf, BUFFER * s,
762 unsigned long data __attribute__ ((unused)),
769 m_strcpy(err->data, err->dsize, _("alias: no address"));
773 mutt_extract_token (buf, s, 0);
775 /* check to see if an alias with this name already exists */
776 for (last = &Aliases; *last; last = &(*last)->next) {
777 if (!m_strcasecmp((*last)->name, buf->data))
782 /* create a new alias */
784 (*last)->name = m_strdup(buf->data);
785 /* give the main addressbook code a chance */
786 if (CurrentMenu == MENU_ALIAS)
787 set_option (OPTMENUCALLER);
789 /* override the previous value */
790 address_list_wipe(&(*last)->addr);
791 if (CurrentMenu == MENU_ALIAS)
792 set_option (OPTFORCEREDRAWINDEX);
795 mutt_extract_token(buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE);
796 (*last)->addr = mutt_parse_adrlist((*last)->addr, buf->data);
797 if (mutt_addrlist_to_idna((*last)->addr, &estr)) {
798 snprintf (err->data, err->dsize,
799 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, (*last)->name);
808 parse_unmy_hdr(BUFFER * buf, BUFFER * s,
809 unsigned long data __attribute__ ((unused)),
810 BUFFER * err __attribute__ ((unused)))
813 mutt_extract_token (buf, s, 0);
815 if (!m_strcmp("*", buf->data)) {
816 string_list_wipe(&UserHeader);
818 string_list_t **last = &UserHeader;
819 ssize_t l = m_strlen(buf->data);
821 if (buf->data[l - 1] == ':')
825 if (!ascii_strncasecmp(buf->data, (*last)->data, l)
826 && (*last)->data[l] == ':')
828 string_list_t *tmp = string_list_pop(last);
829 string_item_delete(&tmp);
831 last = &(*last)->next;
835 } while (MoreArgs(s));
840 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
847 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
848 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
849 m_strcpy(err->data, err->dsize, _("invalid header field"));
852 keylen = p - buf->data + 1;
855 for (tmp = UserHeader;; tmp = tmp->next) {
856 /* see if there is already a field by this name */
857 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
858 /* replace the old value */
859 p_delete(&tmp->data);
860 tmp->data = buf->data;
867 tmp->next = string_item_new();
871 tmp = string_item_new();
874 tmp->data = buf->data;
880 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
881 char* errbuf, ssize_t errlen) {
884 if (m_strncmp("reverse-", s, 8) == 0) {
886 flags = SORT_REVERSE;
889 if (m_strncmp("last-", s, 5) == 0) {
894 if ((i = mutt_getvaluebyname (s, map)) == -1) {
896 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
900 *((short*) dst->data) = i | flags;
904 /* if additional data more == 1, we want to resolve synonyms */
905 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
907 char buf[LONG_STRING];
908 struct option_t *ptr = p;
910 if (DTYPE(ptr->type) == DT_SYN) {
913 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
915 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_fromstr)
918 mutt_option_value(ptr->option, buf, sizeof(buf));
919 if (m_strlen(ptr->init) == 0 && buf && *buf)
920 ptr->init = m_strdup(buf);
923 static int init_expand (char** dst, struct option_t* src) {
929 if (DTYPE(src->type) == DT_STR ||
930 DTYPE(src->type) == DT_PATH) {
931 /* only expand for string as it's the only place where
932 * we want to expand vars right now */
933 if (src->init && *src->init) {
936 len = m_strlen(src->init) + 2;
937 in.data = p_new(char, len + 1);
938 snprintf (in.data, len, "\"%s\"", src->init);
941 mutt_extract_token (&token, &in, 0);
942 if (token.data && *token.data)
943 *dst = m_strdup(token.data);
947 p_delete(&token.data);
951 /* for non-string: take value as is */
952 *dst = m_strdup(src->init);
956 /* if additional data more == 1, we want to resolve synonyms */
957 static void mutt_restore_default (const char* name __attribute__ ((unused)),
958 void* p, unsigned long more) {
960 struct option_t* ptr = (struct option_t*) p;
963 if (DTYPE (ptr->type) == DT_SYN) {
966 ptr = hash_find (ConfigOptions, (char*) ptr->data);
970 if (FuncTable[DTYPE (ptr->type)].opt_fromstr) {
971 init_expand (&init, ptr);
972 if (!FuncTable[DTYPE (ptr->type)].opt_fromstr (ptr, init, errbuf,
974 if (!option (OPTNOCURSES))
976 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
977 "Please report this error: \"%s\"\n"),
978 ptr->option, NONULL (init), errbuf);
984 if (ptr->flags & R_INDEX)
985 set_option (OPTFORCEREDRAWINDEX);
986 if (ptr->flags & R_PAGER)
987 set_option (OPTFORCEREDRAWPAGER);
988 if (ptr->flags & R_RESORT_SUB)
989 set_option (OPTSORTSUBTHREADS);
990 if (ptr->flags & R_RESORT)
991 set_option (OPTNEEDRESORT);
992 if (ptr->flags & R_RESORT_INIT)
993 set_option (OPTRESORTINIT);
994 if (ptr->flags & R_TREE)
995 set_option (OPTREDRAWTREE);
998 static int check_num (const char* option, unsigned long p,
999 char* errbuf, ssize_t errlen) {
1002 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1008 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1009 char* errbuf, ssize_t errlen) {
1010 if (!check_num ("history", p, errbuf, errlen))
1012 mutt_init_history ();
1016 static int check_special (const char* name, unsigned long val,
1017 char* errbuf, ssize_t errlen) {
1020 for (i = 0; SpecialVars[i].name; i++) {
1021 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1022 return (SpecialVars[i].check (SpecialVars[i].name,
1023 val, errbuf, errlen));
1029 static const struct mapping_t* get_sortmap (struct option_t* option) {
1030 const struct mapping_t* map = NULL;
1032 switch (option->type & DT_SUBTYPE_MASK) {
1034 map = SortAliasMethods;
1036 case DT_SORT_BROWSER:
1037 map = SortBrowserMethods;
1040 map = SortKeyMethods;
1043 map = SortAuxMethods;
1052 #define CHECK_PAGER \
1053 if ((CurrentMenu == MENU_PAGER) && \
1054 (!option || (option->flags & R_RESORT))) \
1056 snprintf (err->data, err->dsize, \
1057 _("Not available in this menu.")); \
1061 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1064 int query, unset, inv, reset, r = 0;
1065 struct option_t* option = NULL;
1067 while (MoreArgs (s)) {
1068 /* reset state variables */
1070 unset = data & M_SET_UNSET;
1071 inv = data & M_SET_INV;
1072 reset = data & M_SET_RESET;
1074 if (*s->dptr == '?') {
1078 else if (m_strncmp("no", s->dptr, 2) == 0) {
1082 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1086 else if (*s->dptr == '&') {
1091 /* get the variable name */
1092 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1094 /* resolve synonyms */
1095 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1096 DTYPE (option->type == DT_SYN))
1098 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1099 syn_t* syn = syn_new();
1100 syn->f = m_strdup(CurRCFile);
1104 syn_list_push(&Synonyms, syn);
1108 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1109 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1112 s->dptr = vskipspaces(s->dptr);
1115 if (query || unset || inv) {
1116 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1120 if (s && *s->dptr == '=') {
1121 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1125 if (!m_strcmp("all", tmp->data)) {
1126 if (CurrentMenu == MENU_PAGER) {
1127 snprintf (err->data, err->dsize, _("Not available in this menu."));
1130 hash_map (ConfigOptions, mutt_restore_default, 1);
1131 set_option (OPTFORCEREDRAWINDEX);
1132 set_option (OPTFORCEREDRAWPAGER);
1133 set_option (OPTSORTSUBTHREADS);
1134 set_option (OPTNEEDRESORT);
1135 set_option (OPTRESORTINIT);
1136 set_option (OPTREDRAWTREE);
1140 mutt_restore_default (NULL, option, 1);
1143 else if (DTYPE (option->type) == DT_BOOL) {
1144 /* XXX this currently ignores the function table
1145 * as we don't get invert and stuff into it */
1146 if (s && *s->dptr == '=') {
1147 if (unset || inv || query) {
1148 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1153 mutt_extract_token (tmp, s, 0);
1154 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1156 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1159 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1165 bool_to_string (err->data, err->dsize, option);
1171 unset_option (option->data);
1173 toggle_option (option->data);
1175 set_option (option->data);
1177 else if (DTYPE (option->type) == DT_STR ||
1178 DTYPE (option->type) == DT_PATH ||
1179 DTYPE (option->type) == DT_ADDR ||
1180 DTYPE (option->type) == DT_MAGIC ||
1181 DTYPE (option->type) == DT_NUM ||
1182 DTYPE (option->type) == DT_SORT ||
1183 DTYPE (option->type) == DT_RX)
1185 /* XXX maybe we need to get unset into handlers? */
1186 if (DTYPE (option->type) == DT_STR ||
1187 DTYPE (option->type) == DT_PATH ||
1188 DTYPE (option->type) == DT_ADDR)
1192 if (DTYPE (option->type) == DT_ADDR)
1193 address_list_wipe((address_t **) option->data);
1195 p_delete((void **)(void *)&option->data);
1200 if (query || *s->dptr != '=') {
1201 FuncTable[DTYPE (option->type)].opt_tostr
1202 (err->data, err->dsize, option);
1208 mutt_extract_token (tmp, s, 0);
1209 if (!FuncTable[DTYPE (option->type)].opt_fromstr
1210 (option, tmp->data, err->data, err->dsize))
1213 else if (DTYPE (option->type) == DT_QUAD) {
1216 quad_to_string (err->data, err->dsize, option);
1220 if (*s->dptr == '=') {
1223 mutt_extract_token (tmp, s, 0);
1224 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1225 set_quadoption (option->data, M_YES);
1226 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1227 set_quadoption (option->data, M_NO);
1228 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1229 set_quadoption (option->data, M_ASKYES);
1230 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1231 set_quadoption (option->data, M_ASKNO);
1233 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1234 tmp->data, option->option);
1241 toggle_quadoption (option->data);
1243 set_quadoption (option->data, M_NO);
1245 set_quadoption (option->data, M_YES);
1249 snprintf (err->data, err->dsize, _("%s: unknown type"),
1255 if (option->flags & R_INDEX)
1256 set_option (OPTFORCEREDRAWINDEX);
1257 if (option->flags & R_PAGER)
1258 set_option (OPTFORCEREDRAWPAGER);
1259 if (option->flags & R_RESORT_SUB)
1260 set_option (OPTSORTSUBTHREADS);
1261 if (option->flags & R_RESORT)
1262 set_option (OPTNEEDRESORT);
1263 if (option->flags & R_RESORT_INIT)
1264 set_option (OPTRESORTINIT);
1265 if (option->flags & R_TREE)
1266 set_option (OPTREDRAWTREE);
1273 /* reads the specified initialization file. returns -1 if errors were found
1274 so that we can pause to let the user know... */
1275 static int source_rc (const char *rcfile, BUFFER * err)
1278 int line = 0, rc = 0, conv = 0;
1280 char *linebuf = NULL;
1281 char *currentline = NULL;
1285 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1286 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1291 while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
1292 conv = ConfigCharset && (*ConfigCharset) && MCharset.charset;
1294 currentline = m_strdup(linebuf);
1297 mutt_convert_string (¤tline, ConfigCharset, MCharset.charset, 0);
1300 currentline = linebuf;
1305 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1306 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1307 if (--rc < -MAXERRS) {
1309 p_delete(¤tline);
1318 p_delete(¤tline);
1320 p_delete(&token.data);
1324 mutt_wait_filter (pid);
1326 /* the muttrc source keyword */
1327 snprintf (err->data, err->dsize,
1328 rc >= -MAXERRS ? _("source: errors in %s")
1329 : _("source: reading aborted due too many errors in %s"),
1338 static int parse_source (BUFFER * tmp, BUFFER * s,
1339 unsigned long data __attribute__ ((unused)),
1342 char path[_POSIX_PATH_MAX];
1346 if (mutt_extract_token (tmp, s, 0) != 0) {
1347 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1351 m_strcpy(path, sizeof(path), tmp->data);
1352 mutt_expand_path (path, sizeof(path));
1354 rc += source_rc (path, err);
1356 while (MoreArgs (s));
1358 return ((rc < 0) ? -1 : 0);
1361 /* line command to execute
1363 token scratch buffer to be used by parser. caller should free
1364 token->data when finished. the reason for this variable is
1365 to avoid having to allocate and deallocate a lot of memory
1366 if we are parsing many lines. the caller can pass in the
1367 memory to use, which avoids having to create new space for
1368 every call to this function.
1370 err where to write error messages */
1371 int mutt_parse_rc_line (const char *line, BUFFER * token, BUFFER * err)
1377 expn.data = expn.dptr = line;
1378 expn.dsize = m_strlen(line);
1382 expn.dptr = vskipspaces(expn.dptr);
1383 while (*expn.dptr) {
1384 if (*expn.dptr == '#')
1385 break; /* rest of line is a comment */
1386 if (*expn.dptr == ';') {
1390 mutt_extract_token (token, &expn, 0);
1391 for (i = 0; Commands[i].name; i++) {
1392 if (!m_strcmp(token->data, Commands[i].name)) {
1393 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1398 if (!Commands[i].name) {
1399 snprintf (err->data, err->dsize, _("%s: unknown command"),
1400 NONULL (token->data));
1407 p_delete(&expn.data);
1412 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
1413 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
1414 /* initial string that starts completion. No telling how much crap
1415 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1416 char User_typed[LONG_STRING] = { 0 };
1418 int Num_matched = 0; /* Number of matches for completion */
1419 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1420 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1422 /* helper function for completion. Changes the dest buffer if
1423 necessary/possible to aid completion.
1424 dest == completion result gets here.
1425 src == candidate for completion.
1426 try == user entered data for completion.
1427 len == length of dest buffer.
1429 static void candidate (char *dest, char *try, const char *src, int len)
1433 if (strstr (src, try) == src) {
1434 Matches[Num_matched++] = src;
1436 m_strcpy(dest, len, src);
1438 for (l = 0; src[l] && src[l] == dest[l]; l++);
1444 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
1448 int spaces; /* keep track of the number of leading spaces on the line */
1450 buffer = vskipspaces(buffer);
1451 spaces = buffer - pt;
1453 pt = buffer + pos - spaces;
1454 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1457 if (pt == buffer) { /* complete cmd */
1458 /* first TAB. Collect all the matches */
1461 m_strcpy(User_typed, sizeof(User_typed), pt);
1462 p_clear(Matches, countof(Matches));
1463 p_clear(Completed, countof(Completed));
1464 for (num = 0; Commands[num].name; num++)
1465 candidate (Completed, User_typed, Commands[num].name,
1467 Matches[Num_matched++] = User_typed;
1469 /* All matches are stored. Longest non-ambiguous string is ""
1470 * i.e. dont change 'buffer'. Fake successful return this time */
1471 if (User_typed[0] == 0)
1475 if (Completed[0] == 0 && User_typed[0])
1478 /* Num_matched will _always_ be atleast 1 since the initial
1479 * user-typed string is always stored */
1480 if (numtabs == 1 && Num_matched == 2)
1481 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1482 else if (numtabs > 1 && Num_matched > 2)
1483 /* cycle thru all the matches */
1484 snprintf (Completed, sizeof(Completed), "%s",
1485 Matches[(numtabs - 2) % Num_matched]);
1487 /* return the completed command */
1488 m_strcpy(buffer, len - spaces, Completed);
1490 else if (!m_strncmp(buffer, "set", 3)
1491 || !m_strncmp(buffer, "unset", 5)
1492 || !m_strncmp(buffer, "reset", 5)
1493 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
1494 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
1497 /* loop through all the possible prefixes (no, inv, ...) */
1498 if (!m_strncmp(buffer, "set", 3)) {
1499 for (num = 0; prefixes[num]; num++) {
1500 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
1501 pt += m_strlen(prefixes[num]);
1507 /* first TAB. Collect all the matches */
1510 m_strcpy(User_typed, sizeof(User_typed), pt);
1511 p_clear(Matches, countof(Matches));
1512 p_clear(Completed, countof(Completed));
1513 for (num = 0; MuttVars[num].option; num++)
1514 candidate(Completed, User_typed, MuttVars[num].option,
1516 Matches[Num_matched++] = User_typed;
1518 /* All matches are stored. Longest non-ambiguous string is ""
1519 * i.e. dont change 'buffer'. Fake successful return this time */
1520 if (User_typed[0] == 0)
1524 if (Completed[0] == 0 && User_typed[0])
1527 /* Num_matched will _always_ be atleast 1 since the initial
1528 * user-typed string is always stored */
1529 if (numtabs == 1 && Num_matched == 2)
1530 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1531 else if (numtabs > 1 && Num_matched > 2)
1532 /* cycle thru all the matches */
1533 snprintf (Completed, sizeof(Completed), "%s",
1534 Matches[(numtabs - 2) % Num_matched]);
1536 m_strcpy(pt, buffer + len - pt - spaces, Completed);
1538 else if (!m_strncmp(buffer, "exec", 4)) {
1539 struct binding_t *menu = km_get_table (CurrentMenu);
1541 if (!menu && CurrentMenu != MENU_PAGER)
1545 /* first TAB. Collect all the matches */
1548 m_strcpy(User_typed, sizeof(User_typed), pt);
1549 p_clear(Matches, countof(Matches));
1550 p_clear(Completed, countof(Completed));
1551 for (num = 0; menu[num].name; num++)
1552 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
1553 /* try the generic menu */
1554 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1556 for (num = 0; menu[num].name; num++)
1557 candidate (Completed, User_typed, menu[num].name,
1560 Matches[Num_matched++] = User_typed;
1562 /* All matches are stored. Longest non-ambiguous string is ""
1563 * i.e. dont change 'buffer'. Fake successful return this time */
1564 if (User_typed[0] == 0)
1568 if (Completed[0] == 0 && User_typed[0])
1571 /* Num_matched will _always_ be atleast 1 since the initial
1572 * user-typed string is always stored */
1573 if (numtabs == 1 && Num_matched == 2)
1574 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1575 else if (numtabs > 1 && Num_matched > 2)
1576 /* cycle thru all the matches */
1577 snprintf (Completed, sizeof(Completed), "%s",
1578 Matches[(numtabs - 2) % Num_matched]);
1580 m_strcpy(pt, buffer + len - pt - spaces, Completed);
1588 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
1590 char var[STRING], *pt = buffer;
1592 struct option_t* option = NULL;
1597 buffer = vskipspaces(buffer);
1598 spaces = buffer - pt;
1600 pt = buffer + pos - spaces;
1601 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1603 pt++; /* move past the space */
1604 if (*pt == '=') /* abort if no var before the '=' */
1607 if (m_strncmp(buffer, "set", 3) == 0) {
1608 m_strcpy(var, sizeof(var), pt);
1609 /* ignore the trailing '=' when comparing */
1610 var[m_strlen(var) - 1] = 0;
1611 if (!(option = hash_find (ConfigOptions, var)))
1612 return 0; /* no such variable. */
1614 char tmp[LONG_STRING], tmp2[LONG_STRING];
1616 ssize_t dlen = buffer + len - pt - spaces;
1617 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1621 if ((DTYPE (option->type) == DT_STR) ||
1622 (DTYPE (option->type) == DT_PATH) ||
1623 (DTYPE (option->type) == DT_RX)) {
1624 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
1625 if (DTYPE (option->type) == DT_PATH)
1626 mutt_pretty_mailbox (tmp);
1628 else if (DTYPE (option->type) == DT_ADDR) {
1629 rfc822_addrcat(tmp, sizeof(tmp), *((address_t **) option->data), 0);
1631 else if (DTYPE (option->type) == DT_QUAD)
1632 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
1633 else if (DTYPE (option->type) == DT_NUM)
1634 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
1635 else if (DTYPE (option->type) == DT_SORT) {
1636 const struct mapping_t *map;
1639 switch (option->type & DT_SUBTYPE_MASK) {
1641 map = SortAliasMethods;
1643 case DT_SORT_BROWSER:
1644 map = SortBrowserMethods;
1647 map = SortKeyMethods;
1653 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
1654 snprintf(tmp, sizeof(tmp), "%s%s%s",
1655 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
1656 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
1658 else if (DTYPE (option->type) == DT_MAGIC) {
1660 switch (DefaultMagic) {
1676 m_strcpy(tmp, sizeof(tmp), p);
1678 else if (DTYPE (option->type) == DT_BOOL)
1679 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
1683 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
1684 if (*s == '\\' || *s == '"')
1690 m_strcpy(tmp, sizeof(tmp), pt);
1691 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
1699 /* Implement the -Q command line flag */
1700 int mutt_query_variables (string_list_t * queries)
1704 char errbuff[STRING];
1705 char command[STRING];
1713 err.dsize = sizeof(errbuff);
1715 for (p = queries; p; p = p->next) {
1716 snprintf (command, sizeof(command), "set ?%s\n", p->data);
1717 if (mutt_parse_rc_line (command, &token, &err) == -1) {
1718 fprintf (stderr, "%s\n", err.data);
1719 p_delete(&token.data);
1722 printf ("%s\n", err.data);
1725 p_delete(&token.data);
1729 static int mutt_execute_commands (string_list_t * p)
1732 char errstr[STRING];
1736 err.dsize = sizeof(errstr);
1738 for (; p; p = p->next) {
1739 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
1740 fprintf (stderr, _("Error in command line: %s\n"), err.data);
1741 p_delete(&token.data);
1745 p_delete(&token.data);
1749 void mutt_init (int skip_sys_rc, string_list_t * commands)
1753 char buffer[STRING], error[STRING];
1754 int default_rc = 0, need_pause = 0;
1760 err.dsize = sizeof(error);
1762 ConfigOptions = hash_new (sizeof(MuttVars) * 2, 0);
1763 for (i = 0; MuttVars[i].option; i++) {
1764 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i]);
1768 * XXX - use something even more difficult to predict?
1770 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
1771 "\033]9;%ld\a", (long) time (NULL));
1774 /* Get some information about the user */
1775 if ((pw = getpwuid (getuid ()))) {
1777 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, MCore.gecos_mask);
1778 Realname = m_strdup(rnbuf);
1786 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
1788 fgets (buffer, sizeof(buffer), f);
1789 p = vskipspaces(buffer);
1791 while (*q && !isspace(*q))
1794 NewsServer = m_strdup(p);
1798 if ((p = getenv ("NNTPSERVER")))
1799 NewsServer = m_strdup(p);
1802 if ((p = getenv("MAIL") ?: getenv("MAILDIR"))) {
1803 Spoolfile = m_strdup(p);
1806 mutt_concat_path(buffer, sizeof(buffer), NONULL(MCore.homedir), MAILPATH);
1808 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(MCore.username));
1810 Spoolfile = m_strdup(buffer);
1813 if ((p = getenv ("REPLYTO")) != NULL) {
1816 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
1819 buf.data = buf.dptr = buffer;
1820 buf.dsize = m_strlen(buffer);
1823 parse_my_hdr (&token, &buf, 0, &err);
1824 p_delete(&token.data);
1827 if ((p = getenv ("EMAIL")) != NULL)
1828 From = rfc822_parse_adrlist (NULL, p);
1830 /* Set standard defaults */
1831 hash_map (ConfigOptions, mutt_set_default, 0);
1832 hash_map (ConfigOptions, mutt_restore_default, 0);
1834 CurrentMenu = MENU_MAIN;
1837 /* Unset suspend by default if we're the session leader */
1838 if (getsid (0) == getpid ())
1839 unset_option (OPTSUSPEND);
1842 mutt_init_history ();
1845 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL(MCore.homedir));
1846 if (access (buffer, F_OK) == -1)
1847 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
1848 NONULL(MCore.homedir));
1851 Muttrc = m_strdup(buffer);
1854 m_strcpy(buffer, sizeof(buffer), Muttrc);
1856 mutt_expand_path (buffer, sizeof(buffer));
1857 Muttrc = m_strdup(buffer);
1860 /* Process the global rc file if it exists and the user hasn't explicity
1861 requested not to via "-n". */
1863 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
1865 if (access (buffer, F_OK) == -1)
1866 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
1867 if (access (buffer, F_OK) == -1)
1868 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
1870 if (access (buffer, F_OK) == -1)
1871 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
1872 if (access (buffer, F_OK) != -1) {
1873 if (source_rc (buffer, &err) != 0) {
1874 fputs (err.data, stderr);
1875 fputc ('\n', stderr);
1881 /* Read the user's initialization file. */
1882 if (access (Muttrc, F_OK) != -1) {
1883 if (!option (OPTNOCURSES))
1885 if (source_rc (Muttrc, &err) != 0) {
1886 fputs (err.data, stderr);
1887 fputc ('\n', stderr);
1891 else if (!default_rc) {
1892 /* file specified by -F does not exist */
1893 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
1894 mutt_endwin (buffer);
1899 snprintf(buffer, sizeof(buffer), "%s/.madmutt.lua", NONULL(MCore.homedir));
1900 if (access(buffer, F_OK) < 0)
1901 snprintf(buffer, sizeof(buffer), "%s/.madmutt/cfg.lua", NONULL(MCore.homedir));
1902 if (!access(buffer, F_OK)) {
1903 need_pause = luaM_wrap(mutt_error, luaM_dofile(buffer));
1907 if (mutt_execute_commands (commands) != 0)
1910 /* warn about synonym variables */
1914 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
1916 for (syn = Synonyms; syn; syn = syn->next) {
1917 fprintf(stderr, "$%s ($%s should be used) (%s:%d)\n",
1918 syn->o ? NONULL(syn->o->option) : "",
1919 syn->n ? NONULL(syn->n->option) : "",
1920 NONULL(syn->f), syn->l);
1922 fprintf (stderr, _("Warning: synonym variables are scheduled"
1923 " for removal.\n"));
1924 syn_list_wipe(&Synonyms);
1928 if (need_pause && !option (OPTNOCURSES)) {
1929 if (mutt_any_key_to_continue (NULL) == -1)
1934 int mutt_get_hook_type (const char *name)
1936 struct command_t *c;
1938 for (c = Commands; c->name; c++)
1939 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
1944 /* dump out the value of all the variables we have */
1945 int mutt_dump_variables (int full) {
1948 /* get all non-synonyms into list... */
1949 for (i = 0; MuttVars[i].option; i++) {
1950 struct option_t *option = MuttVars + i;
1951 char buf[LONG_STRING];
1953 if (DTYPE(option->type) == DT_SYN)
1957 mutt_option_value(option->option, buf, sizeof(buf));
1958 if (!m_strcmp(buf, option->init))
1963 FuncTable[DTYPE(option->type)].opt_tostr(buf, sizeof(buf), option);
1964 printf ("%s\n", buf);
1967 printf ("\n# vi""m:set ft=muttrc:\n");