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"
29 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
30 #include "mutt_libesmtp.h"
39 static const struct mapping_t* get_sortmap (struct option_t* option);
40 static int parse_sort (struct option_t* dst, const char *s,
41 const struct mapping_t *map,
42 char* errbuf, ssize_t errlen);
44 static hash_t *ConfigOptions = NULL;
46 /* for synonym warning reports: synonym found during parsing */
47 typedef struct syn_t {
51 struct option_t* n; /* new */
52 struct option_t* o; /* old */
56 static void syn_wipe(syn_t *syn) {
60 DO_DELETE(syn_t, syn);
61 DO_SLIST(syn_t, syn, syn_delete);
63 /* for synonym warning reports: list of synonyms found */
64 static syn_t *Synonyms = NULL;
65 /* for synonym warning reports: current rc file */
66 static const char* CurRCFile = NULL;
67 /* for synonym warning reports: current rc line */
68 static int CurRCLine = 0;
70 /* prototypes for checking for special vars */
71 static int check_history (const char* option, unsigned long val,
72 char* errbuf, ssize_t errlen);
73 /* this checks that numbers are >= 0 */
74 static int check_num (const char* option, unsigned long val,
75 char* errbuf, ssize_t errlen);
77 /* use this to check only */
78 static int check_special (const char* option, unsigned long val,
79 char* errbuf, ssize_t errlen);
81 /* variable <-> sanity check function mappings
82 * when changing these, make sure the proper _from_string handler
87 int (*check) (const char* option, unsigned long val,
88 char* errbuf, ssize_t errlen);
90 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
91 { "smtp_use_tls", mutt_libesmtp_check_usetls },
93 { "history", check_history },
94 { "pager_index_lines", check_num },
99 /* protos for config type handles: convert value to string */
100 static void bool_to_string (char* dst, ssize_t dstlen, struct option_t* option);
101 static void num_to_string (char* dst, ssize_t dstlen, struct option_t* option);
102 static void str_to_string (char* dst, ssize_t dstlen, struct option_t* option);
103 static void quad_to_string (char* dst, ssize_t dstlen, struct option_t* option);
104 static void sort_to_string (char* dst, ssize_t dstlen, struct option_t* option);
105 static void rx_to_string (char* dst, ssize_t dstlen, struct option_t* option);
106 static void magic_to_string (char* dst, ssize_t dstlen, struct option_t* option);
107 static void addr_to_string (char* dst, ssize_t dstlen, struct option_t* option);
109 /* protos for config type handles: convert to value from string */
110 static int bool_from_string (struct option_t* dst, const char* val,
111 char* errbuf, ssize_t errlen);
112 static int num_from_string (struct option_t* dst, const char* val,
113 char* errbuf, ssize_t errlen);
114 static int str_from_string (struct option_t* dst, const char* val,
115 char* errbuf, ssize_t errlen);
116 static int path_from_string (struct option_t* dst, const char* val,
117 char* errbuf, ssize_t errlen);
118 static int quad_from_string (struct option_t* dst, const char* val,
119 char* errbuf, ssize_t errlen);
120 static int sort_from_string (struct option_t* dst, const char* val,
121 char* errbuf, ssize_t errlen);
122 static int rx_from_string (struct option_t* dst, const char* val,
123 char* errbuf, ssize_t errlen);
124 static int magic_from_string (struct option_t* dst, const char* val,
125 char* errbuf, ssize_t errlen);
126 static int addr_from_string (struct option_t* dst, const char* val,
127 char* errbuf, ssize_t errlen);
131 void (*opt_tostr) (char* dst, ssize_t dstlen, struct option_t* option);
132 int (*opt_fromstr) (struct option_t* dst, const char* val,
133 char* errbuf, ssize_t errlen);
135 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
136 { DT_BOOL, bool_to_string, bool_from_string },
137 { DT_NUM, num_to_string, num_from_string },
138 { DT_STR, str_to_string, str_from_string },
139 { DT_PATH, str_to_string, path_from_string },
140 { DT_QUAD, quad_to_string, quad_from_string },
141 { DT_SORT, sort_to_string, sort_from_string },
142 { DT_RX, rx_to_string, rx_from_string },
143 { DT_MAGIC, magic_to_string, magic_from_string },
144 /* synonyms should be resolved already so we don't need this
145 * but must define it as DT_ is used for indexing */
146 { DT_SYN, NULL, NULL },
147 { DT_ADDR, addr_to_string, addr_from_string },
150 static void bool_to_string (char* dst, ssize_t dstlen,
151 struct option_t* option) {
152 snprintf (dst, dstlen, "%s=%s", option->option,
153 option (option->data) ? "yes" : "no");
156 static int bool_from_string (struct option_t* dst, const char* val,
157 char* errbuf __attribute__ ((unused)),
158 ssize_t errlen __attribute__ ((unused))) {
163 if (ascii_strncasecmp (val, "yes", 3) == 0)
165 else if (ascii_strncasecmp (val, "no", 2) == 0)
171 set_option (dst->data);
173 unset_option (dst->data);
177 static void num_to_string (char* dst, ssize_t dstlen,
178 struct option_t* option) {
180 const char* fmt = (m_strcmp(option->option, "umask") == 0) ?
182 snprintf (dst, dstlen, fmt, option->option,
183 *((short*) option->data));
186 static int num_from_string (struct option_t* dst, const char* val,
187 char* errbuf, ssize_t errlen) {
188 int num = 0, old = 0;
194 num = strtol (val, &t, 0);
196 if (m_strisempty(val) || *t || (short) num != num) {
198 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
204 /* just temporarily accept new val so that check_special for
205 * $history already has it when doing history's init() */
206 old = *((short*) dst->data);
207 *((short*) dst->data) = (short) num;
209 if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
210 *((short*) dst->data) = old;
217 static void str_to_string (char* dst, ssize_t dstlen,
218 struct option_t* option) {
219 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
220 NONULL (*((char**) option->data)));
223 static int path_from_string (struct option_t* dst, const char* val,
224 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
225 char path[_POSIX_PATH_MAX];
230 if (m_strisempty(val)) {
231 p_delete((char**) dst->data);
236 m_strcpy(path, sizeof(path), val);
237 mutt_expand_path (path, sizeof(path));
238 m_strreplace((char **) dst->data, path);
242 static int str_from_string (struct option_t* dst, const char* val,
243 char* errbuf, ssize_t errlen) {
247 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
250 m_strreplace((char**) dst->data, val);
254 static void quad_to_string (char* dst, ssize_t dstlen,
255 struct option_t* option) {
256 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
257 snprintf (dst, dstlen, "%s=%s", option->option,
258 vals[quadoption (option->data)]);
261 static int quad_from_string (struct option_t* dst, const char* val,
262 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
267 if (ascii_strncasecmp (val, "yes", 3) == 0)
269 else if (ascii_strncasecmp (val, "no", 2) == 0)
271 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
273 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
279 set_quadoption (dst->data, flag);
283 static void sort_to_string (char* dst, ssize_t dstlen,
284 struct option_t* option) {
285 const struct mapping_t *map = get_sortmap (option);
286 const char *p = NULL;
289 snprintf (dst, sizeof(dst), "%s=unknown", option->option);
293 p = mutt_getnamebyvalue(*((short *)option->data) & SORT_MASK, map);
295 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
296 (*((short *) option->data) & SORT_REVERSE) ?
298 (*((short *) option->data) & SORT_LAST) ? "last-" :
302 static int sort_from_string (struct option_t* dst, const char* val,
303 char* errbuf, ssize_t errlen) {
304 const struct mapping_t *map = NULL;
305 if (!(map = get_sortmap (dst))) {
307 snprintf (errbuf, errlen, _("%s: Unknown type."),
311 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
316 static void rx_to_string (char* dst, ssize_t dstlen,
317 struct option_t* option) {
318 rx_t* p = (rx_t*) option->data;
319 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
320 NONULL (p->pattern));
323 static int rx_from_string (struct option_t* dst, const char* val,
324 char* errbuf, ssize_t errlen) {
327 int flags = 0, e = 0, neg = 0;
333 if (option (OPTATTACHMSG) && !m_strcmp(dst->option, "reply_regexp")) {
335 snprintf (errbuf, errlen,
336 "Operation not permitted when in attach-message mode.");
340 if (!((rx_t*) dst->data))
341 *((rx_t**) dst->data) = p_new(rx_t, 1);
343 p = (rx_t*) dst->data;
345 /* something to do? */
346 if (m_strisempty(val) || (p->pattern && m_strcmp(p->pattern, val) == 0))
349 if (m_strcmp(dst->option, "mask") != 0)
350 flags |= mutt_which_case (val);
353 if (m_strcmp(dst->option, "mask") == 0 && *s == '!') {
358 rx = p_new(regex_t, 1);
360 if ((e = REGCOMP (rx, s, flags)) != 0) {
361 regerror (e, rx, errbuf, errlen);
372 m_strreplace(&p->pattern, val);
376 if (m_strcmp(dst->option, "reply_regexp") == 0)
377 mutt_adjust_all_subjects ();
382 static void magic_to_string (char* dst, ssize_t dstlen,
383 struct option_t* option) {
384 const char* s = NULL;
385 switch (option->data) {
386 case M_MBOX: s = "mbox"; break;
387 case M_MMDF: s = "MMDF"; break;
388 case M_MH: s = "MH"; break;
389 case M_MAILDIR: s = "Maildir"; break;
390 default: s = "unknown"; break;
392 snprintf (dst, dstlen, "%s=%s", option->option, s);
395 static int magic_from_string (struct option_t* dst, const char* val,
396 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
399 if (!dst || m_strisempty(val))
401 if (ascii_strncasecmp (val, "mbox", 4) == 0)
403 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
405 else if (ascii_strncasecmp (val, "mh", 2) == 0)
407 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
413 *((short*) dst->data) = flag;
418 static void addr_to_string (char* dst, ssize_t dstlen,
419 struct option_t* option) {
422 rfc822_addrcat(s, sizeof(s), *((address_t**) option->data), 0);
423 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
426 static int addr_from_string (struct option_t* dst, const char* val,
427 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
430 address_list_wipe((address_t**) dst->data);
432 *((address_t**) dst->data) = rfc822_parse_adrlist (NULL, val);
436 int mutt_option_value (const char* val, char* dst, ssize_t dstlen) {
437 struct option_t* option = NULL;
438 char* tmp = NULL, *t = NULL;
441 if (!(option = hash_find (ConfigOptions, val))) {
445 tmp = p_new(char, dstlen+1);
446 FuncTable[DTYPE(option->type)].opt_tostr (tmp, dstlen, option);
448 /* as we get things of type $var=value and don't want to bloat the
449 * above "just" for expansion, we do the stripping here */
450 t = strchr (tmp, '=');
454 if (t[l-1] == '"' && *t == '"') {
459 memcpy (dst, t, l+1);
465 static void toggle_quadoption (int opt)
468 int b = (opt % 4) * 2;
470 QuadOptions[n] ^= (1 << b);
473 void set_quadoption (int opt, int flag)
476 int b = (opt % 4) * 2;
478 QuadOptions[n] &= ~(0x3 << b);
479 QuadOptions[n] |= (flag & 0x3) << b;
482 int quadoption (int opt)
485 int b = (opt % 4) * 2;
487 return (QuadOptions[n] >> b) & 0x3;
490 int query_quadoption2(int v, const char *prompt)
498 v = mutt_yesorno(prompt, (v == M_ASKYES));
499 CLEARLINE (LINES - 1);
504 int query_quadoption (int opt, const char *prompt)
506 int v = quadoption (opt);
514 v = mutt_yesorno (prompt, (v == M_ASKYES));
515 CLEARLINE (LINES - 1);
522 /* always wise to do what someone else did before */
523 static void _attachments_clean (void) {
525 if (Context && Context->msgcount) {
526 for (i = 0; i < Context->msgcount; i++)
527 Context->hdrs[i]->attach_valid = 0;
531 static int parse_attach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
532 BUFFER *err __attribute__ ((unused))) {
534 string_list_t *listp, *lastp;
539 /* Find the last item in the list that data points to. */
541 for (listp = *ldata; listp; listp = listp->next) {
542 a = (ATTACH_MATCH *)listp->data;
547 mutt_extract_token (buf, s, 0);
549 if (!buf->data || *buf->data == '\0')
552 a = p_new(ATTACH_MATCH, 1);
554 /* some cheap hacks that I expect to remove */
555 if (!m_strcasecmp(buf->data, "any"))
556 a->major = m_strdup("*/.*");
557 else if (!m_strcasecmp(buf->data, "none"))
558 a->major = m_strdup("cheap_hack/this_should_never_match");
560 a->major = m_strdup(buf->data);
562 if ((p = strchr(a->major, '/'))) {
567 a->minor = "unknown";
570 len = m_strlen(a->minor);
571 tmpminor = p_new(char, len + 3);
572 m_strcpy(&tmpminor[1], len + 3, a->minor);
574 tmpminor[len+1] = '$';
575 tmpminor[len+2] = '\0';
577 a->major_int = mutt_check_mime_type(a->major);
578 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
582 listp = p_new(string_list_t, 1);
583 listp->data = (char *)a;
592 while (MoreArgs (s));
594 _attachments_clean();
598 static int parse_unattach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
599 BUFFER *err __attribute__ ((unused))) {
601 string_list_t *lp, *lastp, *newlp;
607 mutt_extract_token (buf, s, 0);
609 if (!m_strcasecmp(buf->data, "any"))
610 tmp = m_strdup("*/.*");
611 else if (!m_strcasecmp(buf->data, "none"))
612 tmp = m_strdup("cheap_hack/this_should_never_match");
614 tmp = m_strdup(buf->data);
616 if ((minor = strchr(tmp, '/'))) {
620 minor = m_strdup("unknown");
622 major = mutt_check_mime_type(tmp);
624 /* We must do our own walk here because string_list_remove() will only
625 * remove the string_list_t->data, not anything pointed to by the string_list_t->data. */
627 for(lp = *ldata; lp; ) {
628 a = (ATTACH_MATCH *)lp->data;
629 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
630 regfree(&a->minor_rx);
633 /* Relink backward */
635 lastp->next = lp->next;
640 p_delete(&lp->data); /* same as a */
650 while (MoreArgs (s));
653 _attachments_clean();
657 static int print_attach_list (string_list_t *lp, char op, const char *name) {
659 printf("attachments %c%s %s/%s\n", op, name,
660 ((ATTACH_MATCH *)lp->data)->major,
661 ((ATTACH_MATCH *)lp->data)->minor);
668 static int parse_attachments (BUFFER *buf, BUFFER *s,
669 unsigned long data __attribute__ ((unused)),
672 string_list_t **listp;
674 mutt_extract_token(buf, s, 0);
675 if (!buf->data || *buf->data == '\0') {
676 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
680 category = buf->data;
686 printf("\nCurrent attachments settings:\n\n");
687 print_attach_list(AttachAllow, '+', "A");
688 print_attach_list(AttachExclude, '-', "A");
689 print_attach_list(InlineAllow, '+', "I");
690 print_attach_list(InlineExclude, '-', "I");
691 set_option (OPTFORCEREDRAWINDEX);
692 set_option (OPTFORCEREDRAWPAGER);
693 mutt_any_key_to_continue (NULL);
697 if (op != '+' && op != '-') {
701 if (!m_strncasecmp(category, "attachment", strlen(category))) {
703 listp = &AttachAllow;
705 listp = &AttachExclude;
707 else if (!m_strncasecmp(category, "inline", strlen(category))) {
709 listp = &InlineAllow;
711 listp = &InlineExclude;
713 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
717 return parse_attach_list(buf, s, listp, err);
720 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
722 string_list_t **listp;
724 mutt_extract_token(buf, s, 0);
725 if (!buf->data || *buf->data == '\0') {
726 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
732 if (op != '+' && op != '-') {
736 if (!m_strncasecmp(p, "attachment", strlen(p))) {
738 listp = &AttachAllow;
740 listp = &AttachExclude;
742 else if (!m_strncasecmp(p, "inline", strlen(p))) {
744 listp = &InlineAllow;
746 listp = &InlineExclude;
749 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
753 return parse_unattach_list(buf, s, listp, err);
756 static int parse_unalias (BUFFER * buf, BUFFER * s,
757 unsigned long data __attribute__ ((unused)),
758 BUFFER * err __attribute__ ((unused)))
760 alias_t *tmp, **last;
763 mutt_extract_token (buf, s, 0);
765 if (!m_strcmp("*", buf->data) == 0) {
766 if (CurrentMenu == MENU_ALIAS) {
767 for (tmp = Aliases; tmp; tmp = tmp->next)
769 set_option(OPTFORCEREDRAWINDEX);
771 alias_list_wipe(&Aliases);
777 for (last = &Aliases; *last; last = &(*last)->next) {
778 if (!m_strcasecmp(buf->data, (*last)->name)) {
779 if (CurrentMenu == MENU_ALIAS) {
781 set_option (OPTFORCEREDRAWINDEX);
783 tmp = alias_list_pop(last);
789 } while (MoreArgs(s));
794 static int parse_alias (BUFFER * buf, BUFFER * s,
795 unsigned long data __attribute__ ((unused)),
802 m_strcpy(err->data, err->dsize, _("alias: no address"));
806 mutt_extract_token (buf, s, 0);
808 /* check to see if an alias with this name already exists */
809 for (last = &Aliases; *last; last = &(*last)->next) {
810 if (!m_strcasecmp((*last)->name, buf->data))
815 /* create a new alias */
817 (*last)->name = m_strdup(buf->data);
818 /* give the main addressbook code a chance */
819 if (CurrentMenu == MENU_ALIAS)
820 set_option (OPTMENUCALLER);
822 /* override the previous value */
823 address_list_wipe(&(*last)->addr);
824 if (CurrentMenu == MENU_ALIAS)
825 set_option (OPTFORCEREDRAWINDEX);
828 mutt_extract_token(buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE);
829 (*last)->addr = mutt_parse_adrlist((*last)->addr, buf->data);
830 if (mutt_addrlist_to_idna((*last)->addr, &estr)) {
831 snprintf (err->data, err->dsize,
832 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, (*last)->name);
841 parse_unmy_hdr(BUFFER * buf, BUFFER * s,
842 unsigned long data __attribute__ ((unused)),
843 BUFFER * err __attribute__ ((unused)))
846 mutt_extract_token (buf, s, 0);
848 if (!m_strcmp("*", buf->data)) {
849 string_list_wipe(&UserHeader);
851 string_list_t **last = &UserHeader;
852 ssize_t l = m_strlen(buf->data);
854 if (buf->data[l - 1] == ':')
858 if (!ascii_strncasecmp(buf->data, (*last)->data, l)
859 && (*last)->data[l] == ':')
861 string_list_t *tmp = string_list_pop(last);
862 string_item_delete(&tmp);
864 last = &(*last)->next;
868 } while (MoreArgs(s));
873 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
880 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
881 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
882 m_strcpy(err->data, err->dsize, _("invalid header field"));
885 keylen = p - buf->data + 1;
888 for (tmp = UserHeader;; tmp = tmp->next) {
889 /* see if there is already a field by this name */
890 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
891 /* replace the old value */
892 p_delete(&tmp->data);
893 tmp->data = buf->data;
900 tmp->next = string_item_new();
904 tmp = string_item_new();
907 tmp->data = buf->data;
913 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
914 char* errbuf, ssize_t errlen) {
917 if (m_strncmp("reverse-", s, 8) == 0) {
919 flags = SORT_REVERSE;
922 if (m_strncmp("last-", s, 5) == 0) {
927 if ((i = mutt_getvaluebyname (s, map)) == -1) {
929 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
933 *((short*) dst->data) = i | flags;
937 /* if additional data more == 1, we want to resolve synonyms */
938 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
940 char buf[LONG_STRING];
941 struct option_t *ptr = p;
943 if (DTYPE(ptr->type) == DT_SYN) {
946 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
948 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_fromstr)
951 mutt_option_value(ptr->option, buf, sizeof(buf));
952 if (m_strlen(ptr->init) == 0 && buf && *buf)
953 ptr->init = m_strdup(buf);
956 static int init_expand (char** dst, struct option_t* src) {
962 if (DTYPE(src->type) == DT_STR ||
963 DTYPE(src->type) == DT_PATH) {
964 /* only expand for string as it's the only place where
965 * we want to expand vars right now */
966 if (src->init && *src->init) {
969 len = m_strlen(src->init) + 2;
970 in.data = p_new(char, len + 1);
971 snprintf (in.data, len, "\"%s\"", src->init);
974 mutt_extract_token (&token, &in, 0);
975 if (token.data && *token.data)
976 *dst = m_strdup(token.data);
980 p_delete(&token.data);
984 /* for non-string: take value as is */
985 *dst = m_strdup(src->init);
989 /* if additional data more == 1, we want to resolve synonyms */
990 static void mutt_restore_default (const char* name __attribute__ ((unused)),
991 void* p, unsigned long more) {
993 struct option_t* ptr = (struct option_t*) p;
996 if (DTYPE (ptr->type) == DT_SYN) {
999 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1003 if (FuncTable[DTYPE (ptr->type)].opt_fromstr) {
1004 init_expand (&init, ptr);
1005 if (!FuncTable[DTYPE (ptr->type)].opt_fromstr (ptr, init, errbuf,
1007 if (!option (OPTNOCURSES))
1009 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1010 "Please report this error: \"%s\"\n"),
1011 ptr->option, NONULL (init), errbuf);
1017 if (ptr->flags & R_INDEX)
1018 set_option (OPTFORCEREDRAWINDEX);
1019 if (ptr->flags & R_PAGER)
1020 set_option (OPTFORCEREDRAWPAGER);
1021 if (ptr->flags & R_RESORT_SUB)
1022 set_option (OPTSORTSUBTHREADS);
1023 if (ptr->flags & R_RESORT)
1024 set_option (OPTNEEDRESORT);
1025 if (ptr->flags & R_RESORT_INIT)
1026 set_option (OPTRESORTINIT);
1027 if (ptr->flags & R_TREE)
1028 set_option (OPTREDRAWTREE);
1031 static int check_num (const char* option, unsigned long p,
1032 char* errbuf, ssize_t errlen) {
1035 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1041 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1042 char* errbuf, ssize_t errlen) {
1043 if (!check_num ("history", p, errbuf, errlen))
1045 mutt_init_history ();
1049 static int check_special (const char* name, unsigned long val,
1050 char* errbuf, ssize_t errlen) {
1053 for (i = 0; SpecialVars[i].name; i++) {
1054 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1055 return (SpecialVars[i].check (SpecialVars[i].name,
1056 val, errbuf, errlen));
1062 static const struct mapping_t* get_sortmap (struct option_t* option) {
1063 const struct mapping_t* map = NULL;
1065 switch (option->type & DT_SUBTYPE_MASK) {
1067 map = SortAliasMethods;
1069 case DT_SORT_BROWSER:
1070 map = SortBrowserMethods;
1073 map = SortKeyMethods;
1076 map = SortAuxMethods;
1085 #define CHECK_PAGER \
1086 if ((CurrentMenu == MENU_PAGER) && \
1087 (!option || (option->flags & R_RESORT))) \
1089 snprintf (err->data, err->dsize, \
1090 _("Not available in this menu.")); \
1094 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1097 int query, unset, inv, reset, r = 0;
1098 struct option_t* option = NULL;
1100 while (MoreArgs (s)) {
1101 /* reset state variables */
1103 unset = data & M_SET_UNSET;
1104 inv = data & M_SET_INV;
1105 reset = data & M_SET_RESET;
1107 if (*s->dptr == '?') {
1111 else if (m_strncmp("no", s->dptr, 2) == 0) {
1115 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1119 else if (*s->dptr == '&') {
1124 /* get the variable name */
1125 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1127 /* resolve synonyms */
1128 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1129 DTYPE (option->type == DT_SYN))
1131 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1132 syn_t* syn = syn_new();
1133 syn->f = m_strdup(CurRCFile);
1137 syn_list_push(&Synonyms, syn);
1141 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1142 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1145 s->dptr = vskipspaces(s->dptr);
1148 if (query || unset || inv) {
1149 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1153 if (s && *s->dptr == '=') {
1154 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1158 if (!m_strcmp("all", tmp->data)) {
1159 if (CurrentMenu == MENU_PAGER) {
1160 snprintf (err->data, err->dsize, _("Not available in this menu."));
1163 hash_map (ConfigOptions, mutt_restore_default, 1);
1164 set_option (OPTFORCEREDRAWINDEX);
1165 set_option (OPTFORCEREDRAWPAGER);
1166 set_option (OPTSORTSUBTHREADS);
1167 set_option (OPTNEEDRESORT);
1168 set_option (OPTRESORTINIT);
1169 set_option (OPTREDRAWTREE);
1173 mutt_restore_default (NULL, option, 1);
1176 else if (DTYPE (option->type) == DT_BOOL) {
1177 /* XXX this currently ignores the function table
1178 * as we don't get invert and stuff into it */
1179 if (s && *s->dptr == '=') {
1180 if (unset || inv || query) {
1181 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1186 mutt_extract_token (tmp, s, 0);
1187 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1189 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1192 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1198 bool_to_string (err->data, err->dsize, option);
1204 unset_option (option->data);
1206 toggle_option (option->data);
1208 set_option (option->data);
1210 else if (DTYPE (option->type) == DT_STR ||
1211 DTYPE (option->type) == DT_PATH ||
1212 DTYPE (option->type) == DT_ADDR ||
1213 DTYPE (option->type) == DT_MAGIC ||
1214 DTYPE (option->type) == DT_NUM ||
1215 DTYPE (option->type) == DT_SORT ||
1216 DTYPE (option->type) == DT_RX)
1218 /* XXX maybe we need to get unset into handlers? */
1219 if (DTYPE (option->type) == DT_STR ||
1220 DTYPE (option->type) == DT_PATH ||
1221 DTYPE (option->type) == DT_ADDR)
1225 if (DTYPE (option->type) == DT_ADDR)
1226 address_list_wipe((address_t **) option->data);
1228 p_delete((void **)(void *)&option->data);
1233 if (query || *s->dptr != '=') {
1234 FuncTable[DTYPE (option->type)].opt_tostr
1235 (err->data, err->dsize, option);
1241 mutt_extract_token (tmp, s, 0);
1242 if (!FuncTable[DTYPE (option->type)].opt_fromstr
1243 (option, tmp->data, err->data, err->dsize))
1246 else if (DTYPE (option->type) == DT_QUAD) {
1249 quad_to_string (err->data, err->dsize, option);
1253 if (*s->dptr == '=') {
1256 mutt_extract_token (tmp, s, 0);
1257 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1258 set_quadoption (option->data, M_YES);
1259 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1260 set_quadoption (option->data, M_NO);
1261 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1262 set_quadoption (option->data, M_ASKYES);
1263 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1264 set_quadoption (option->data, M_ASKNO);
1266 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1267 tmp->data, option->option);
1274 toggle_quadoption (option->data);
1276 set_quadoption (option->data, M_NO);
1278 set_quadoption (option->data, M_YES);
1282 snprintf (err->data, err->dsize, _("%s: unknown type"),
1288 if (option->flags & R_INDEX)
1289 set_option (OPTFORCEREDRAWINDEX);
1290 if (option->flags & R_PAGER)
1291 set_option (OPTFORCEREDRAWPAGER);
1292 if (option->flags & R_RESORT_SUB)
1293 set_option (OPTSORTSUBTHREADS);
1294 if (option->flags & R_RESORT)
1295 set_option (OPTNEEDRESORT);
1296 if (option->flags & R_RESORT_INIT)
1297 set_option (OPTRESORTINIT);
1298 if (option->flags & R_TREE)
1299 set_option (OPTREDRAWTREE);
1306 /* reads the specified initialization file. returns -1 if errors were found
1307 so that we can pause to let the user know... */
1308 static int source_rc (const char *rcfile, BUFFER * err)
1311 int line = 0, rc = 0, conv = 0;
1313 char *linebuf = NULL;
1314 char *currentline = NULL;
1318 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1319 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1324 while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
1325 conv = ConfigCharset && (*ConfigCharset) && MCharset.charset;
1327 currentline = m_strdup(linebuf);
1330 mutt_convert_string (¤tline, ConfigCharset, MCharset.charset, 0);
1333 currentline = linebuf;
1338 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1339 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1340 if (--rc < -MAXERRS) {
1342 p_delete(¤tline);
1351 p_delete(¤tline);
1353 p_delete(&token.data);
1357 mutt_wait_filter (pid);
1359 /* the muttrc source keyword */
1360 snprintf (err->data, err->dsize,
1361 rc >= -MAXERRS ? _("source: errors in %s")
1362 : _("source: reading aborted due too many errors in %s"),
1371 static int parse_source (BUFFER * tmp, BUFFER * s,
1372 unsigned long data __attribute__ ((unused)),
1375 char path[_POSIX_PATH_MAX];
1379 if (mutt_extract_token (tmp, s, 0) != 0) {
1380 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1384 m_strcpy(path, sizeof(path), tmp->data);
1385 mutt_expand_path (path, sizeof(path));
1387 rc += source_rc (path, err);
1389 while (MoreArgs (s));
1391 return ((rc < 0) ? -1 : 0);
1394 /* line command to execute
1396 token scratch buffer to be used by parser. caller should free
1397 token->data when finished. the reason for this variable is
1398 to avoid having to allocate and deallocate a lot of memory
1399 if we are parsing many lines. the caller can pass in the
1400 memory to use, which avoids having to create new space for
1401 every call to this function.
1403 err where to write error messages */
1404 int mutt_parse_rc_line (const char *line, BUFFER * token, BUFFER * err)
1410 expn.data = expn.dptr = line;
1411 expn.dsize = m_strlen(line);
1415 expn.dptr = vskipspaces(expn.dptr);
1416 while (*expn.dptr) {
1417 if (*expn.dptr == '#')
1418 break; /* rest of line is a comment */
1419 if (*expn.dptr == ';') {
1423 mutt_extract_token (token, &expn, 0);
1424 for (i = 0; Commands[i].name; i++) {
1425 if (!m_strcmp(token->data, Commands[i].name)) {
1426 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1431 if (!Commands[i].name) {
1432 snprintf (err->data, err->dsize, _("%s: unknown command"),
1433 NONULL (token->data));
1440 p_delete(&expn.data);
1445 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
1446 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
1447 /* initial string that starts completion. No telling how much crap
1448 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1449 char User_typed[LONG_STRING] = { 0 };
1451 int Num_matched = 0; /* Number of matches for completion */
1452 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1453 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1455 /* helper function for completion. Changes the dest buffer if
1456 necessary/possible to aid completion.
1457 dest == completion result gets here.
1458 src == candidate for completion.
1459 try == user entered data for completion.
1460 len == length of dest buffer.
1462 static void candidate (char *dest, char *try, const char *src, int len)
1466 if (strstr (src, try) == src) {
1467 Matches[Num_matched++] = src;
1469 m_strcpy(dest, len, src);
1471 for (l = 0; src[l] && src[l] == dest[l]; l++);
1477 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
1481 int spaces; /* keep track of the number of leading spaces on the line */
1483 buffer = vskipspaces(buffer);
1484 spaces = buffer - pt;
1486 pt = buffer + pos - spaces;
1487 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1490 if (pt == buffer) { /* complete cmd */
1491 /* first TAB. Collect all the matches */
1494 m_strcpy(User_typed, sizeof(User_typed), pt);
1495 p_clear(Matches, countof(Matches));
1496 p_clear(Completed, countof(Completed));
1497 for (num = 0; Commands[num].name; num++)
1498 candidate (Completed, User_typed, Commands[num].name,
1500 Matches[Num_matched++] = User_typed;
1502 /* All matches are stored. Longest non-ambiguous string is ""
1503 * i.e. dont change 'buffer'. Fake successful return this time */
1504 if (User_typed[0] == 0)
1508 if (Completed[0] == 0 && User_typed[0])
1511 /* Num_matched will _always_ be atleast 1 since the initial
1512 * user-typed string is always stored */
1513 if (numtabs == 1 && Num_matched == 2)
1514 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1515 else if (numtabs > 1 && Num_matched > 2)
1516 /* cycle thru all the matches */
1517 snprintf (Completed, sizeof(Completed), "%s",
1518 Matches[(numtabs - 2) % Num_matched]);
1520 /* return the completed command */
1521 m_strcpy(buffer, len - spaces, Completed);
1523 else if (!m_strncmp(buffer, "set", 3)
1524 || !m_strncmp(buffer, "unset", 5)
1525 || !m_strncmp(buffer, "reset", 5)
1526 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
1527 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
1530 /* loop through all the possible prefixes (no, inv, ...) */
1531 if (!m_strncmp(buffer, "set", 3)) {
1532 for (num = 0; prefixes[num]; num++) {
1533 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
1534 pt += m_strlen(prefixes[num]);
1540 /* first TAB. Collect all the matches */
1543 m_strcpy(User_typed, sizeof(User_typed), pt);
1544 p_clear(Matches, countof(Matches));
1545 p_clear(Completed, countof(Completed));
1546 for (num = 0; MuttVars[num].option; num++)
1547 candidate(Completed, User_typed, MuttVars[num].option,
1549 Matches[Num_matched++] = User_typed;
1551 /* All matches are stored. Longest non-ambiguous string is ""
1552 * i.e. dont change 'buffer'. Fake successful return this time */
1553 if (User_typed[0] == 0)
1557 if (Completed[0] == 0 && User_typed[0])
1560 /* Num_matched will _always_ be atleast 1 since the initial
1561 * user-typed string is always stored */
1562 if (numtabs == 1 && Num_matched == 2)
1563 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1564 else if (numtabs > 1 && Num_matched > 2)
1565 /* cycle thru all the matches */
1566 snprintf (Completed, sizeof(Completed), "%s",
1567 Matches[(numtabs - 2) % Num_matched]);
1569 m_strcpy(pt, buffer + len - pt - spaces, Completed);
1571 else if (!m_strncmp(buffer, "exec", 4)) {
1572 struct binding_t *menu = km_get_table (CurrentMenu);
1574 if (!menu && CurrentMenu != MENU_PAGER)
1578 /* first TAB. Collect all the matches */
1581 m_strcpy(User_typed, sizeof(User_typed), pt);
1582 p_clear(Matches, countof(Matches));
1583 p_clear(Completed, countof(Completed));
1584 for (num = 0; menu[num].name; num++)
1585 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
1586 /* try the generic menu */
1587 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1589 for (num = 0; menu[num].name; num++)
1590 candidate (Completed, User_typed, menu[num].name,
1593 Matches[Num_matched++] = User_typed;
1595 /* All matches are stored. Longest non-ambiguous string is ""
1596 * i.e. dont change 'buffer'. Fake successful return this time */
1597 if (User_typed[0] == 0)
1601 if (Completed[0] == 0 && User_typed[0])
1604 /* Num_matched will _always_ be atleast 1 since the initial
1605 * user-typed string is always stored */
1606 if (numtabs == 1 && Num_matched == 2)
1607 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1608 else if (numtabs > 1 && Num_matched > 2)
1609 /* cycle thru all the matches */
1610 snprintf (Completed, sizeof(Completed), "%s",
1611 Matches[(numtabs - 2) % Num_matched]);
1613 m_strcpy(pt, buffer + len - pt - spaces, Completed);
1621 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
1623 char var[STRING], *pt = buffer;
1625 struct option_t* option = NULL;
1630 buffer = vskipspaces(buffer);
1631 spaces = buffer - pt;
1633 pt = buffer + pos - spaces;
1634 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1636 pt++; /* move past the space */
1637 if (*pt == '=') /* abort if no var before the '=' */
1640 if (m_strncmp(buffer, "set", 3) == 0) {
1641 m_strcpy(var, sizeof(var), pt);
1642 /* ignore the trailing '=' when comparing */
1643 var[m_strlen(var) - 1] = 0;
1644 if (!(option = hash_find (ConfigOptions, var)))
1645 return 0; /* no such variable. */
1647 char tmp[LONG_STRING], tmp2[LONG_STRING];
1649 ssize_t dlen = buffer + len - pt - spaces;
1650 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1654 if ((DTYPE (option->type) == DT_STR) ||
1655 (DTYPE (option->type) == DT_PATH) ||
1656 (DTYPE (option->type) == DT_RX)) {
1657 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
1658 if (DTYPE (option->type) == DT_PATH)
1659 mutt_pretty_mailbox (tmp);
1661 else if (DTYPE (option->type) == DT_ADDR) {
1662 rfc822_addrcat(tmp, sizeof(tmp), *((address_t **) option->data), 0);
1664 else if (DTYPE (option->type) == DT_QUAD)
1665 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
1666 else if (DTYPE (option->type) == DT_NUM)
1667 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
1668 else if (DTYPE (option->type) == DT_SORT) {
1669 const struct mapping_t *map;
1672 switch (option->type & DT_SUBTYPE_MASK) {
1674 map = SortAliasMethods;
1676 case DT_SORT_BROWSER:
1677 map = SortBrowserMethods;
1680 map = SortKeyMethods;
1686 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
1687 snprintf(tmp, sizeof(tmp), "%s%s%s",
1688 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
1689 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
1691 else if (DTYPE (option->type) == DT_MAGIC) {
1693 switch (DefaultMagic) {
1709 m_strcpy(tmp, sizeof(tmp), p);
1711 else if (DTYPE (option->type) == DT_BOOL)
1712 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
1716 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
1717 if (*s == '\\' || *s == '"')
1723 m_strcpy(tmp, sizeof(tmp), pt);
1724 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
1732 /* Implement the -Q command line flag */
1733 int mutt_query_variables (string_list_t * queries)
1737 char errbuff[STRING];
1738 char command[STRING];
1746 err.dsize = sizeof(errbuff);
1748 for (p = queries; p; p = p->next) {
1749 snprintf (command, sizeof(command), "set ?%s\n", p->data);
1750 if (mutt_parse_rc_line (command, &token, &err) == -1) {
1751 fprintf (stderr, "%s\n", err.data);
1752 p_delete(&token.data);
1755 printf ("%s\n", err.data);
1758 p_delete(&token.data);
1762 static int mutt_execute_commands (string_list_t * p)
1765 char errstr[STRING];
1769 err.dsize = sizeof(errstr);
1771 for (; p; p = p->next) {
1772 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
1773 fprintf (stderr, _("Error in command line: %s\n"), err.data);
1774 p_delete(&token.data);
1778 p_delete(&token.data);
1782 void mutt_init (int skip_sys_rc, string_list_t * commands)
1786 char buffer[STRING], error[STRING];
1787 int default_rc = 0, need_pause = 0;
1793 err.dsize = sizeof(error);
1795 ConfigOptions = hash_new (sizeof(MuttVars) * 2, 0);
1796 for (i = 0; MuttVars[i].option; i++) {
1797 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i]);
1801 * XXX - use something even more difficult to predict?
1803 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
1804 "\033]9;%ld\a", (long) time (NULL));
1807 /* Get some information about the user */
1808 if ((pw = getpwuid (getuid ()))) {
1810 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, MCore.gecos_mask);
1811 Realname = m_strdup(rnbuf);
1819 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
1821 fgets (buffer, sizeof(buffer), f);
1822 p = vskipspaces(buffer);
1824 while (*q && !isspace(*q))
1827 NewsServer = m_strdup(p);
1831 if ((p = getenv ("NNTPSERVER")))
1832 NewsServer = m_strdup(p);
1835 if ((p = getenv("MAIL") ?: getenv("MAILDIR"))) {
1836 Spoolfile = m_strdup(p);
1839 mutt_concat_path(buffer, sizeof(buffer), NONULL(MCore.homedir), MAILPATH);
1841 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(MCore.username));
1843 Spoolfile = m_strdup(buffer);
1846 if ((p = getenv ("REPLYTO")) != NULL) {
1849 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
1852 buf.data = buf.dptr = buffer;
1853 buf.dsize = m_strlen(buffer);
1856 parse_my_hdr (&token, &buf, 0, &err);
1857 p_delete(&token.data);
1860 if ((p = getenv ("EMAIL")) != NULL)
1861 From = rfc822_parse_adrlist (NULL, p);
1863 /* Set standard defaults */
1864 hash_map (ConfigOptions, mutt_set_default, 0);
1865 hash_map (ConfigOptions, mutt_restore_default, 0);
1867 CurrentMenu = MENU_MAIN;
1870 /* Unset suspend by default if we're the session leader */
1871 if (getsid (0) == getpid ())
1872 unset_option (OPTSUSPEND);
1875 mutt_init_history ();
1878 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL(MCore.homedir));
1879 if (access (buffer, F_OK) == -1)
1880 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
1881 NONULL(MCore.homedir));
1884 Muttrc = m_strdup(buffer);
1887 m_strcpy(buffer, sizeof(buffer), Muttrc);
1889 mutt_expand_path (buffer, sizeof(buffer));
1890 Muttrc = m_strdup(buffer);
1893 /* Process the global rc file if it exists and the user hasn't explicity
1894 requested not to via "-n". */
1896 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
1898 if (access (buffer, F_OK) == -1)
1899 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
1900 if (access (buffer, F_OK) == -1)
1901 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
1903 if (access (buffer, F_OK) == -1)
1904 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
1905 if (access (buffer, F_OK) != -1) {
1906 if (source_rc (buffer, &err) != 0) {
1907 fputs (err.data, stderr);
1908 fputc ('\n', stderr);
1914 /* Read the user's initialization file. */
1915 if (access (Muttrc, F_OK) != -1) {
1916 if (!option (OPTNOCURSES))
1918 if (source_rc (Muttrc, &err) != 0) {
1919 fputs (err.data, stderr);
1920 fputc ('\n', stderr);
1924 else if (!default_rc) {
1925 /* file specified by -F does not exist */
1926 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
1927 mutt_endwin (buffer);
1932 snprintf(buffer, sizeof(buffer), "%s/.madmutt.lua", NONULL(MCore.homedir));
1933 if (access(buffer, F_OK) < 0)
1934 snprintf(buffer, sizeof(buffer), "%s/.madmutt/cfg.lua", NONULL(MCore.homedir));
1935 if (!access(buffer, F_OK)) {
1936 need_pause = luaM_wrap(mutt_error, luaM_dofile(buffer));
1940 if (mutt_execute_commands (commands) != 0)
1943 /* warn about synonym variables */
1947 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
1949 for (syn = Synonyms; syn; syn = syn->next) {
1950 fprintf(stderr, "$%s ($%s should be used) (%s:%d)\n",
1951 syn->o ? NONULL(syn->o->option) : "",
1952 syn->n ? NONULL(syn->n->option) : "",
1953 NONULL(syn->f), syn->l);
1955 fprintf (stderr, _("Warning: synonym variables are scheduled"
1956 " for removal.\n"));
1957 syn_list_wipe(&Synonyms);
1961 if (need_pause && !option (OPTNOCURSES)) {
1962 if (mutt_any_key_to_continue (NULL) == -1)
1967 int mutt_get_hook_type (const char *name)
1969 struct command_t *c;
1971 for (c = Commands; c->name; c++)
1972 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
1977 /* dump out the value of all the variables we have */
1978 int mutt_dump_variables (int full) {
1981 /* get all non-synonyms into list... */
1982 for (i = 0; MuttVars[i].option; i++) {
1983 struct option_t *option = MuttVars + i;
1984 char buf[LONG_STRING];
1986 if (DTYPE(option->type) == DT_SYN)
1990 mutt_option_value(option->option, buf, sizeof(buf));
1991 if (!m_strcmp(buf, option->init))
1996 FuncTable[DTYPE(option->type)].opt_tostr(buf, sizeof(buf), option);
1997 printf ("%s\n", buf);
2000 printf ("\n# vi""m:set ft=muttrc:\n");