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, not = 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 static void add_to_list(string_list_t **list, const char *str)
524 /* don't add a NULL or empty string to the list */
525 if (m_strisempty(str))
528 /* check to make sure the item is not already on this list */
530 if (!ascii_strcasecmp(str, (*list)->data))
532 list = &(*list)->next;
535 *list = p_new(string_list_t, 1);
536 (*list)->data = m_strdup(str);
540 add_to_rx_list(rx_t **list, const char *s, int flags, BUFFER *err)
547 if (rx_lookup(list, s))
550 rx = rx_compile(s, flags);
552 snprintf(err->data, err->dsize, "Bad regexp: %s\n", s);
556 rx_list_append(list, rx);
560 static void remove_from_list(string_list_t **l, const char *str)
562 if (!m_strcmp("*", str)) {
563 string_list_wipe(l); /* ``unCMD *'' means delete all current entries */
568 if (!ascii_strcasecmp(str, (*l)->data)) {
569 string_list_t *it = string_list_pop(l);
570 string_item_delete(&it);
577 static int remove_from_rx_list(rx_t **l, const char *str)
579 if (m_strcmp("*", str) == 0) {
584 l = rx_lookup(l, str);
586 rx_t *r = rx_list_pop(l);
594 static int parse_unignore (BUFFER * buf, BUFFER * s,
595 unsigned long data __attribute__ ((unused)),
596 BUFFER * err __attribute__ ((unused)))
599 mutt_extract_token (buf, s, 0);
601 /* don't add "*" to the unignore list */
602 if (m_strcmp (buf->data, "*"))
603 add_to_list (&UnIgnore, buf->data);
605 remove_from_list (&Ignore, buf->data);
606 } while (MoreArgs (s));
611 static int parse_ignore (BUFFER * buf, BUFFER * s,
612 unsigned long data __attribute__ ((unused)),
613 BUFFER * err __attribute__ ((unused)))
616 mutt_extract_token (buf, s, 0);
617 remove_from_list (&UnIgnore, buf->data);
618 add_to_list (&Ignore, buf->data);
619 } while (MoreArgs(s));
623 static int parse_list(BUFFER * buf, BUFFER * s, unsigned long data,
624 BUFFER * err __attribute__ ((unused)))
627 mutt_extract_token (buf, s, 0);
628 add_to_list ((string_list_t **) data, buf->data);
629 } while (MoreArgs(s));
633 static void _alternates_clean (void)
637 if (Context && Context->msgcount) {
638 for (i = 0; i < Context->msgcount; i++)
639 Context->hdrs[i]->recip_valid = 0;
643 static int parse_alternates (BUFFER * buf, BUFFER * s,
644 unsigned long data __attribute__ ((unused)),
645 BUFFER * err __attribute__ ((unused)))
647 _alternates_clean ();
649 mutt_extract_token (buf, s, 0);
650 remove_from_rx_list (&UnAlternates, buf->data);
652 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
655 while (MoreArgs (s));
660 static int parse_unalternates (BUFFER * buf, BUFFER * s,
661 unsigned long data __attribute__ ((unused)),
662 BUFFER * err __attribute__ ((unused)))
664 _alternates_clean ();
666 mutt_extract_token (buf, s, 0);
667 remove_from_rx_list (&Alternates, buf->data);
669 if (m_strcmp(buf->data, "*") &&
670 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
674 while (MoreArgs (s));
679 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
680 BUFFER * err __attribute__ ((unused)))
683 mutt_extract_token (buf, s, 0);
685 * Check for deletion of entire list
687 if (m_strcmp(buf->data, "*") == 0) {
688 string_list_wipe((string_list_t **) data);
691 remove_from_list ((string_list_t **) data, buf->data);
693 while (MoreArgs (s));
698 static int parse_lists (BUFFER * buf, BUFFER * s,
699 unsigned long data __attribute__ ((unused)),
703 mutt_extract_token (buf, s, 0);
704 remove_from_rx_list (&UnMailLists, buf->data);
706 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
709 while (MoreArgs (s));
714 /* always wise to do what someone else did before */
715 static void _attachments_clean (void) {
717 if (Context && Context->msgcount) {
718 for (i = 0; i < Context->msgcount; i++)
719 Context->hdrs[i]->attach_valid = 0;
723 static int parse_attach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
724 BUFFER *err __attribute__ ((unused))) {
726 string_list_t *listp, *lastp;
731 /* Find the last item in the list that data points to. */
733 for (listp = *ldata; listp; listp = listp->next) {
734 a = (ATTACH_MATCH *)listp->data;
739 mutt_extract_token (buf, s, 0);
741 if (!buf->data || *buf->data == '\0')
744 a = p_new(ATTACH_MATCH, 1);
746 /* some cheap hacks that I expect to remove */
747 if (!m_strcasecmp(buf->data, "any"))
748 a->major = m_strdup("*/.*");
749 else if (!m_strcasecmp(buf->data, "none"))
750 a->major = m_strdup("cheap_hack/this_should_never_match");
752 a->major = m_strdup(buf->data);
754 if ((p = strchr(a->major, '/'))) {
759 a->minor = "unknown";
762 len = m_strlen(a->minor);
763 tmpminor = p_new(char, len + 3);
764 m_strcpy(&tmpminor[1], len + 3, a->minor);
766 tmpminor[len+1] = '$';
767 tmpminor[len+2] = '\0';
769 a->major_int = mutt_check_mime_type(a->major);
770 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
774 listp = p_new(string_list_t, 1);
775 listp->data = (char *)a;
784 while (MoreArgs (s));
786 _attachments_clean();
790 static int parse_unattach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
791 BUFFER *err __attribute__ ((unused))) {
793 string_list_t *lp, *lastp, *newlp;
799 mutt_extract_token (buf, s, 0);
801 if (!m_strcasecmp(buf->data, "any"))
802 tmp = m_strdup("*/.*");
803 else if (!m_strcasecmp(buf->data, "none"))
804 tmp = m_strdup("cheap_hack/this_should_never_match");
806 tmp = m_strdup(buf->data);
808 if ((minor = strchr(tmp, '/'))) {
812 minor = m_strdup("unknown");
814 major = mutt_check_mime_type(tmp);
816 /* We must do our own walk here because remove_from_list() will only
817 * remove the string_list_t->data, not anything pointed to by the string_list_t->data. */
819 for(lp = *ldata; lp; ) {
820 a = (ATTACH_MATCH *)lp->data;
821 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
822 regfree(&a->minor_rx);
825 /* Relink backward */
827 lastp->next = lp->next;
832 p_delete(&lp->data); /* same as a */
842 while (MoreArgs (s));
845 _attachments_clean();
849 static int print_attach_list (string_list_t *lp, char op, const char *name) {
851 printf("attachments %c%s %s/%s\n", op, name,
852 ((ATTACH_MATCH *)lp->data)->major,
853 ((ATTACH_MATCH *)lp->data)->minor);
860 static int parse_attachments (BUFFER *buf, BUFFER *s,
861 unsigned long data __attribute__ ((unused)),
864 string_list_t **listp;
866 mutt_extract_token(buf, s, 0);
867 if (!buf->data || *buf->data == '\0') {
868 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
872 category = buf->data;
878 printf("\nCurrent attachments settings:\n\n");
879 print_attach_list(AttachAllow, '+', "A");
880 print_attach_list(AttachExclude, '-', "A");
881 print_attach_list(InlineAllow, '+', "I");
882 print_attach_list(InlineExclude, '-', "I");
883 set_option (OPTFORCEREDRAWINDEX);
884 set_option (OPTFORCEREDRAWPAGER);
885 mutt_any_key_to_continue (NULL);
889 if (op != '+' && op != '-') {
893 if (!m_strncasecmp(category, "attachment", strlen(category))) {
895 listp = &AttachAllow;
897 listp = &AttachExclude;
899 else if (!m_strncasecmp(category, "inline", strlen(category))) {
901 listp = &InlineAllow;
903 listp = &InlineExclude;
905 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
909 return parse_attach_list(buf, s, listp, err);
912 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
914 string_list_t **listp;
916 mutt_extract_token(buf, s, 0);
917 if (!buf->data || *buf->data == '\0') {
918 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
924 if (op != '+' && op != '-') {
928 if (!m_strncasecmp(p, "attachment", strlen(p))) {
930 listp = &AttachAllow;
932 listp = &AttachExclude;
934 else if (!m_strncasecmp(p, "inline", strlen(p))) {
936 listp = &InlineAllow;
938 listp = &InlineExclude;
941 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
945 return parse_unattach_list(buf, s, listp, err);
948 static int parse_unlists (BUFFER * buf, BUFFER * s,
949 unsigned long data __attribute__ ((unused)),
950 BUFFER * err __attribute__ ((unused)))
953 mutt_extract_token (buf, s, 0);
954 remove_from_rx_list (&SubscribedLists, buf->data);
955 remove_from_rx_list (&MailLists, buf->data);
957 if (m_strcmp(buf->data, "*") &&
958 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
961 while (MoreArgs (s));
966 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
970 mutt_extract_token (buf, s, 0);
971 remove_from_rx_list (&UnMailLists, buf->data);
972 remove_from_rx_list (&UnSubscribedLists, buf->data);
974 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
976 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
979 while (MoreArgs (s));
984 static int parse_unsubscribe (BUFFER * buf, BUFFER * s,
985 unsigned long data __attribute__ ((unused)),
986 BUFFER * err __attribute__ ((unused)))
989 mutt_extract_token (buf, s, 0);
990 remove_from_rx_list (&SubscribedLists, buf->data);
992 if (m_strcmp(buf->data, "*") &&
993 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
996 while (MoreArgs (s));
1001 static int parse_unalias (BUFFER * buf, BUFFER * s,
1002 unsigned long data __attribute__ ((unused)),
1003 BUFFER * err __attribute__ ((unused)))
1005 alias_t *tmp, **last;
1008 mutt_extract_token (buf, s, 0);
1010 if (!m_strcmp("*", buf->data) == 0) {
1011 if (CurrentMenu == MENU_ALIAS) {
1012 for (tmp = Aliases; tmp; tmp = tmp->next)
1014 set_option(OPTFORCEREDRAWINDEX);
1016 alias_list_wipe(&Aliases);
1022 for (last = &Aliases; *last; last = &(*last)->next) {
1023 if (!m_strcasecmp(buf->data, (*last)->name)) {
1024 if (CurrentMenu == MENU_ALIAS) {
1026 set_option (OPTFORCEREDRAWINDEX);
1028 tmp = alias_list_pop(last);
1034 } while (MoreArgs(s));
1039 static int parse_alias (BUFFER * buf, BUFFER * s,
1040 unsigned long data __attribute__ ((unused)),
1046 if (!MoreArgs (s)) {
1047 m_strcpy(err->data, err->dsize, _("alias: no address"));
1051 mutt_extract_token (buf, s, 0);
1053 /* check to see if an alias with this name already exists */
1054 for (last = &Aliases; *last; last = &(*last)->next) {
1055 if (!m_strcasecmp((*last)->name, buf->data))
1060 /* create a new alias */
1061 *last = alias_new();
1062 (*last)->name = m_strdup(buf->data);
1063 /* give the main addressbook code a chance */
1064 if (CurrentMenu == MENU_ALIAS)
1065 set_option (OPTMENUCALLER);
1067 /* override the previous value */
1068 address_list_wipe(&(*last)->addr);
1069 if (CurrentMenu == MENU_ALIAS)
1070 set_option (OPTFORCEREDRAWINDEX);
1073 mutt_extract_token(buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE);
1074 (*last)->addr = mutt_parse_adrlist((*last)->addr, buf->data);
1075 if (mutt_addrlist_to_idna((*last)->addr, &estr)) {
1076 snprintf (err->data, err->dsize,
1077 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, (*last)->name);
1086 parse_unmy_hdr(BUFFER * buf, BUFFER * s,
1087 unsigned long data __attribute__ ((unused)),
1088 BUFFER * err __attribute__ ((unused)))
1091 mutt_extract_token (buf, s, 0);
1093 if (!m_strcmp("*", buf->data)) {
1094 string_list_wipe(&UserHeader);
1096 string_list_t **last = &UserHeader;
1097 ssize_t l = m_strlen(buf->data);
1099 if (buf->data[l - 1] == ':')
1103 if (!ascii_strncasecmp(buf->data, (*last)->data, l)
1104 && (*last)->data[l] == ':')
1106 string_list_t *tmp = string_list_pop(last);
1107 string_item_delete(&tmp);
1109 last = &(*last)->next;
1113 } while (MoreArgs(s));
1118 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1125 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1126 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1127 m_strcpy(err->data, err->dsize, _("invalid header field"));
1130 keylen = p - buf->data + 1;
1133 for (tmp = UserHeader;; tmp = tmp->next) {
1134 /* see if there is already a field by this name */
1135 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1136 /* replace the old value */
1137 p_delete(&tmp->data);
1138 tmp->data = buf->data;
1145 tmp->next = string_item_new();
1149 tmp = string_item_new();
1152 tmp->data = buf->data;
1158 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1159 char* errbuf, ssize_t errlen) {
1162 if (m_strncmp("reverse-", s, 8) == 0) {
1164 flags = SORT_REVERSE;
1167 if (m_strncmp("last-", s, 5) == 0) {
1172 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1174 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1178 *((short*) dst->data) = i | flags;
1182 /* if additional data more == 1, we want to resolve synonyms */
1183 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
1185 char buf[LONG_STRING];
1186 struct option_t *ptr = p;
1188 if (DTYPE(ptr->type) == DT_SYN) {
1191 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1193 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_fromstr)
1196 mutt_option_value(ptr->option, buf, sizeof(buf));
1197 if (m_strlen(ptr->init) == 0 && buf && *buf)
1198 ptr->init = m_strdup(buf);
1201 static int init_expand (char** dst, struct option_t* src) {
1207 if (DTYPE(src->type) == DT_STR ||
1208 DTYPE(src->type) == DT_PATH) {
1209 /* only expand for string as it's the only place where
1210 * we want to expand vars right now */
1211 if (src->init && *src->init) {
1214 len = m_strlen(src->init) + 2;
1215 in.data = p_new(char, len + 1);
1216 snprintf (in.data, len, "\"%s\"", src->init);
1219 mutt_extract_token (&token, &in, 0);
1220 if (token.data && *token.data)
1221 *dst = m_strdup(token.data);
1223 *dst = m_strdup("");
1225 p_delete(&token.data);
1227 *dst = m_strdup("");
1229 /* for non-string: take value as is */
1230 *dst = m_strdup(src->init);
1234 /* if additional data more == 1, we want to resolve synonyms */
1235 static void mutt_restore_default (const char* name __attribute__ ((unused)),
1236 void* p, unsigned long more) {
1237 char errbuf[STRING];
1238 struct option_t* ptr = (struct option_t*) p;
1241 if (DTYPE (ptr->type) == DT_SYN) {
1244 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1248 if (FuncTable[DTYPE (ptr->type)].opt_fromstr) {
1249 init_expand (&init, ptr);
1250 if (!FuncTable[DTYPE (ptr->type)].opt_fromstr (ptr, init, errbuf,
1252 if (!option (OPTNOCURSES))
1254 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1255 "Please report this error: \"%s\"\n"),
1256 ptr->option, NONULL (init), errbuf);
1262 if (ptr->flags & R_INDEX)
1263 set_option (OPTFORCEREDRAWINDEX);
1264 if (ptr->flags & R_PAGER)
1265 set_option (OPTFORCEREDRAWPAGER);
1266 if (ptr->flags & R_RESORT_SUB)
1267 set_option (OPTSORTSUBTHREADS);
1268 if (ptr->flags & R_RESORT)
1269 set_option (OPTNEEDRESORT);
1270 if (ptr->flags & R_RESORT_INIT)
1271 set_option (OPTRESORTINIT);
1272 if (ptr->flags & R_TREE)
1273 set_option (OPTREDRAWTREE);
1276 static int check_num (const char* option, unsigned long p,
1277 char* errbuf, ssize_t errlen) {
1280 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1286 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1287 char* errbuf, ssize_t errlen) {
1288 if (!check_num ("history", p, errbuf, errlen))
1290 mutt_init_history ();
1294 static int check_special (const char* name, unsigned long val,
1295 char* errbuf, ssize_t errlen) {
1298 for (i = 0; SpecialVars[i].name; i++) {
1299 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1300 return (SpecialVars[i].check (SpecialVars[i].name,
1301 val, errbuf, errlen));
1307 static const struct mapping_t* get_sortmap (struct option_t* option) {
1308 const struct mapping_t* map = NULL;
1310 switch (option->type & DT_SUBTYPE_MASK) {
1312 map = SortAliasMethods;
1314 case DT_SORT_BROWSER:
1315 map = SortBrowserMethods;
1318 map = SortKeyMethods;
1321 map = SortAuxMethods;
1330 #define CHECK_PAGER \
1331 if ((CurrentMenu == MENU_PAGER) && \
1332 (!option || (option->flags & R_RESORT))) \
1334 snprintf (err->data, err->dsize, \
1335 _("Not available in this menu.")); \
1339 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1342 int query, unset, inv, reset, r = 0;
1343 struct option_t* option = NULL;
1345 while (MoreArgs (s)) {
1346 /* reset state variables */
1348 unset = data & M_SET_UNSET;
1349 inv = data & M_SET_INV;
1350 reset = data & M_SET_RESET;
1352 if (*s->dptr == '?') {
1356 else if (m_strncmp("no", s->dptr, 2) == 0) {
1360 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1364 else if (*s->dptr == '&') {
1369 /* get the variable name */
1370 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1372 /* resolve synonyms */
1373 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1374 DTYPE (option->type == DT_SYN))
1376 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1377 syn_t* syn = syn_new();
1378 syn->f = m_strdup(CurRCFile);
1382 syn_list_push(&Synonyms, syn);
1386 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1387 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1390 s->dptr = vskipspaces(s->dptr);
1393 if (query || unset || inv) {
1394 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1398 if (s && *s->dptr == '=') {
1399 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1403 if (!m_strcmp("all", tmp->data)) {
1404 if (CurrentMenu == MENU_PAGER) {
1405 snprintf (err->data, err->dsize, _("Not available in this menu."));
1408 hash_map (ConfigOptions, mutt_restore_default, 1);
1409 set_option (OPTFORCEREDRAWINDEX);
1410 set_option (OPTFORCEREDRAWPAGER);
1411 set_option (OPTSORTSUBTHREADS);
1412 set_option (OPTNEEDRESORT);
1413 set_option (OPTRESORTINIT);
1414 set_option (OPTREDRAWTREE);
1418 mutt_restore_default (NULL, option, 1);
1421 else if (DTYPE (option->type) == DT_BOOL) {
1422 /* XXX this currently ignores the function table
1423 * as we don't get invert and stuff into it */
1424 if (s && *s->dptr == '=') {
1425 if (unset || inv || query) {
1426 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1431 mutt_extract_token (tmp, s, 0);
1432 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1434 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1437 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1443 bool_to_string (err->data, err->dsize, option);
1449 unset_option (option->data);
1451 toggle_option (option->data);
1453 set_option (option->data);
1455 else if (DTYPE (option->type) == DT_STR ||
1456 DTYPE (option->type) == DT_PATH ||
1457 DTYPE (option->type) == DT_ADDR ||
1458 DTYPE (option->type) == DT_MAGIC ||
1459 DTYPE (option->type) == DT_NUM ||
1460 DTYPE (option->type) == DT_SORT ||
1461 DTYPE (option->type) == DT_RX)
1463 /* XXX maybe we need to get unset into handlers? */
1464 if (DTYPE (option->type) == DT_STR ||
1465 DTYPE (option->type) == DT_PATH ||
1466 DTYPE (option->type) == DT_ADDR)
1470 if (DTYPE (option->type) == DT_ADDR)
1471 address_list_wipe((address_t **) option->data);
1473 p_delete((void **)(void *)&option->data);
1478 if (query || *s->dptr != '=') {
1479 FuncTable[DTYPE (option->type)].opt_tostr
1480 (err->data, err->dsize, option);
1486 mutt_extract_token (tmp, s, 0);
1487 if (!FuncTable[DTYPE (option->type)].opt_fromstr
1488 (option, tmp->data, err->data, err->dsize))
1491 else if (DTYPE (option->type) == DT_QUAD) {
1494 quad_to_string (err->data, err->dsize, option);
1498 if (*s->dptr == '=') {
1501 mutt_extract_token (tmp, s, 0);
1502 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1503 set_quadoption (option->data, M_YES);
1504 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1505 set_quadoption (option->data, M_NO);
1506 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1507 set_quadoption (option->data, M_ASKYES);
1508 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1509 set_quadoption (option->data, M_ASKNO);
1511 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1512 tmp->data, option->option);
1519 toggle_quadoption (option->data);
1521 set_quadoption (option->data, M_NO);
1523 set_quadoption (option->data, M_YES);
1527 snprintf (err->data, err->dsize, _("%s: unknown type"),
1533 if (option->flags & R_INDEX)
1534 set_option (OPTFORCEREDRAWINDEX);
1535 if (option->flags & R_PAGER)
1536 set_option (OPTFORCEREDRAWPAGER);
1537 if (option->flags & R_RESORT_SUB)
1538 set_option (OPTSORTSUBTHREADS);
1539 if (option->flags & R_RESORT)
1540 set_option (OPTNEEDRESORT);
1541 if (option->flags & R_RESORT_INIT)
1542 set_option (OPTRESORTINIT);
1543 if (option->flags & R_TREE)
1544 set_option (OPTREDRAWTREE);
1551 /* reads the specified initialization file. returns -1 if errors were found
1552 so that we can pause to let the user know... */
1553 static int source_rc (const char *rcfile, BUFFER * err)
1556 int line = 0, rc = 0, conv = 0;
1558 char *linebuf = NULL;
1559 char *currentline = NULL;
1563 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1564 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1569 while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
1570 conv = ConfigCharset && (*ConfigCharset) && MCharset.charset;
1572 currentline = m_strdup(linebuf);
1575 mutt_convert_string (¤tline, ConfigCharset, MCharset.charset, 0);
1578 currentline = linebuf;
1583 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1584 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1585 if (--rc < -MAXERRS) {
1587 p_delete(¤tline);
1596 p_delete(¤tline);
1598 p_delete(&token.data);
1602 mutt_wait_filter (pid);
1604 /* the muttrc source keyword */
1605 snprintf (err->data, err->dsize,
1606 rc >= -MAXERRS ? _("source: errors in %s")
1607 : _("source: reading aborted due too many errors in %s"),
1616 static int parse_source (BUFFER * tmp, BUFFER * s,
1617 unsigned long data __attribute__ ((unused)),
1620 char path[_POSIX_PATH_MAX];
1624 if (mutt_extract_token (tmp, s, 0) != 0) {
1625 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1629 m_strcpy(path, sizeof(path), tmp->data);
1630 mutt_expand_path (path, sizeof(path));
1632 rc += source_rc (path, err);
1634 while (MoreArgs (s));
1636 return ((rc < 0) ? -1 : 0);
1639 /* line command to execute
1641 token scratch buffer to be used by parser. caller should free
1642 token->data when finished. the reason for this variable is
1643 to avoid having to allocate and deallocate a lot of memory
1644 if we are parsing many lines. the caller can pass in the
1645 memory to use, which avoids having to create new space for
1646 every call to this function.
1648 err where to write error messages */
1649 int mutt_parse_rc_line (const char *line, BUFFER * token, BUFFER * err)
1655 expn.data = expn.dptr = line;
1656 expn.dsize = m_strlen(line);
1660 expn.dptr = vskipspaces(expn.dptr);
1661 while (*expn.dptr) {
1662 if (*expn.dptr == '#')
1663 break; /* rest of line is a comment */
1664 if (*expn.dptr == ';') {
1668 mutt_extract_token (token, &expn, 0);
1669 for (i = 0; Commands[i].name; i++) {
1670 if (!m_strcmp(token->data, Commands[i].name)) {
1671 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1676 if (!Commands[i].name) {
1677 snprintf (err->data, err->dsize, _("%s: unknown command"),
1678 NONULL (token->data));
1685 p_delete(&expn.data);
1690 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
1691 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
1692 /* initial string that starts completion. No telling how much crap
1693 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1694 char User_typed[LONG_STRING] = { 0 };
1696 int Num_matched = 0; /* Number of matches for completion */
1697 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1698 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1700 /* helper function for completion. Changes the dest buffer if
1701 necessary/possible to aid completion.
1702 dest == completion result gets here.
1703 src == candidate for completion.
1704 try == user entered data for completion.
1705 len == length of dest buffer.
1707 static void candidate (char *dest, char *try, const char *src, int len)
1711 if (strstr (src, try) == src) {
1712 Matches[Num_matched++] = src;
1714 m_strcpy(dest, len, src);
1716 for (l = 0; src[l] && src[l] == dest[l]; l++);
1722 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
1726 int spaces; /* keep track of the number of leading spaces on the line */
1728 buffer = vskipspaces(buffer);
1729 spaces = buffer - pt;
1731 pt = buffer + pos - spaces;
1732 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1735 if (pt == buffer) { /* complete cmd */
1736 /* first TAB. Collect all the matches */
1739 m_strcpy(User_typed, sizeof(User_typed), pt);
1740 p_clear(Matches, countof(Matches));
1741 p_clear(Completed, countof(Completed));
1742 for (num = 0; Commands[num].name; num++)
1743 candidate (Completed, User_typed, Commands[num].name,
1745 Matches[Num_matched++] = User_typed;
1747 /* All matches are stored. Longest non-ambiguous string is ""
1748 * i.e. dont change 'buffer'. Fake successful return this time */
1749 if (User_typed[0] == 0)
1753 if (Completed[0] == 0 && User_typed[0])
1756 /* Num_matched will _always_ be atleast 1 since the initial
1757 * user-typed string is always stored */
1758 if (numtabs == 1 && Num_matched == 2)
1759 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1760 else if (numtabs > 1 && Num_matched > 2)
1761 /* cycle thru all the matches */
1762 snprintf (Completed, sizeof(Completed), "%s",
1763 Matches[(numtabs - 2) % Num_matched]);
1765 /* return the completed command */
1766 m_strcpy(buffer, len - spaces, Completed);
1768 else if (!m_strncmp(buffer, "set", 3)
1769 || !m_strncmp(buffer, "unset", 5)
1770 || !m_strncmp(buffer, "reset", 5)
1771 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
1772 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
1775 /* loop through all the possible prefixes (no, inv, ...) */
1776 if (!m_strncmp(buffer, "set", 3)) {
1777 for (num = 0; prefixes[num]; num++) {
1778 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
1779 pt += m_strlen(prefixes[num]);
1785 /* first TAB. Collect all the matches */
1788 m_strcpy(User_typed, sizeof(User_typed), pt);
1789 p_clear(Matches, countof(Matches));
1790 p_clear(Completed, countof(Completed));
1791 for (num = 0; MuttVars[num].option; num++)
1792 candidate(Completed, User_typed, MuttVars[num].option,
1794 Matches[Num_matched++] = User_typed;
1796 /* All matches are stored. Longest non-ambiguous string is ""
1797 * i.e. dont change 'buffer'. Fake successful return this time */
1798 if (User_typed[0] == 0)
1802 if (Completed[0] == 0 && User_typed[0])
1805 /* Num_matched will _always_ be atleast 1 since the initial
1806 * user-typed string is always stored */
1807 if (numtabs == 1 && Num_matched == 2)
1808 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1809 else if (numtabs > 1 && Num_matched > 2)
1810 /* cycle thru all the matches */
1811 snprintf (Completed, sizeof(Completed), "%s",
1812 Matches[(numtabs - 2) % Num_matched]);
1814 m_strcpy(pt, buffer + len - pt - spaces, Completed);
1816 else if (!m_strncmp(buffer, "exec", 4)) {
1817 struct binding_t *menu = km_get_table (CurrentMenu);
1819 if (!menu && CurrentMenu != MENU_PAGER)
1823 /* first TAB. Collect all the matches */
1826 m_strcpy(User_typed, sizeof(User_typed), pt);
1827 p_clear(Matches, countof(Matches));
1828 p_clear(Completed, countof(Completed));
1829 for (num = 0; menu[num].name; num++)
1830 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
1831 /* try the generic menu */
1832 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1834 for (num = 0; menu[num].name; num++)
1835 candidate (Completed, User_typed, menu[num].name,
1838 Matches[Num_matched++] = User_typed;
1840 /* All matches are stored. Longest non-ambiguous string is ""
1841 * i.e. dont change 'buffer'. Fake successful return this time */
1842 if (User_typed[0] == 0)
1846 if (Completed[0] == 0 && User_typed[0])
1849 /* Num_matched will _always_ be atleast 1 since the initial
1850 * user-typed string is always stored */
1851 if (numtabs == 1 && Num_matched == 2)
1852 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1853 else if (numtabs > 1 && Num_matched > 2)
1854 /* cycle thru all the matches */
1855 snprintf (Completed, sizeof(Completed), "%s",
1856 Matches[(numtabs - 2) % Num_matched]);
1858 m_strcpy(pt, buffer + len - pt - spaces, Completed);
1866 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
1868 char var[STRING], *pt = buffer;
1870 struct option_t* option = NULL;
1875 buffer = vskipspaces(buffer);
1876 spaces = buffer - pt;
1878 pt = buffer + pos - spaces;
1879 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1881 pt++; /* move past the space */
1882 if (*pt == '=') /* abort if no var before the '=' */
1885 if (m_strncmp(buffer, "set", 3) == 0) {
1886 m_strcpy(var, sizeof(var), pt);
1887 /* ignore the trailing '=' when comparing */
1888 var[m_strlen(var) - 1] = 0;
1889 if (!(option = hash_find (ConfigOptions, var)))
1890 return 0; /* no such variable. */
1892 char tmp[LONG_STRING], tmp2[LONG_STRING];
1894 ssize_t dlen = buffer + len - pt - spaces;
1895 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1899 if ((DTYPE (option->type) == DT_STR) ||
1900 (DTYPE (option->type) == DT_PATH) ||
1901 (DTYPE (option->type) == DT_RX)) {
1902 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
1903 if (DTYPE (option->type) == DT_PATH)
1904 mutt_pretty_mailbox (tmp);
1906 else if (DTYPE (option->type) == DT_ADDR) {
1907 rfc822_addrcat(tmp, sizeof(tmp), *((address_t **) option->data), 0);
1909 else if (DTYPE (option->type) == DT_QUAD)
1910 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
1911 else if (DTYPE (option->type) == DT_NUM)
1912 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
1913 else if (DTYPE (option->type) == DT_SORT) {
1914 const struct mapping_t *map;
1917 switch (option->type & DT_SUBTYPE_MASK) {
1919 map = SortAliasMethods;
1921 case DT_SORT_BROWSER:
1922 map = SortBrowserMethods;
1925 map = SortKeyMethods;
1931 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
1932 snprintf(tmp, sizeof(tmp), "%s%s%s",
1933 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
1934 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
1936 else if (DTYPE (option->type) == DT_MAGIC) {
1938 switch (DefaultMagic) {
1954 m_strcpy(tmp, sizeof(tmp), p);
1956 else if (DTYPE (option->type) == DT_BOOL)
1957 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
1961 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
1962 if (*s == '\\' || *s == '"')
1968 m_strcpy(tmp, sizeof(tmp), pt);
1969 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
1977 /* Implement the -Q command line flag */
1978 int mutt_query_variables (string_list_t * queries)
1982 char errbuff[STRING];
1983 char command[STRING];
1991 err.dsize = sizeof(errbuff);
1993 for (p = queries; p; p = p->next) {
1994 snprintf (command, sizeof(command), "set ?%s\n", p->data);
1995 if (mutt_parse_rc_line (command, &token, &err) == -1) {
1996 fprintf (stderr, "%s\n", err.data);
1997 p_delete(&token.data);
2000 printf ("%s\n", err.data);
2003 p_delete(&token.data);
2007 static int mutt_execute_commands (string_list_t * p)
2010 char errstr[STRING];
2014 err.dsize = sizeof(errstr);
2016 for (; p; p = p->next) {
2017 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2018 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2019 p_delete(&token.data);
2023 p_delete(&token.data);
2027 void mutt_init (int skip_sys_rc, string_list_t * commands)
2031 char buffer[STRING], error[STRING];
2032 int default_rc = 0, need_pause = 0;
2038 err.dsize = sizeof(error);
2040 ConfigOptions = hash_new (sizeof(MuttVars) * 2, 0);
2041 for (i = 0; MuttVars[i].option; i++) {
2042 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i]);
2046 * XXX - use something even more difficult to predict?
2048 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2049 "\033]9;%ld\a", (long) time (NULL));
2052 /* Get some information about the user */
2053 if ((pw = getpwuid (getuid ()))) {
2055 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, MCore.gecos_mask);
2056 Realname = m_strdup(rnbuf);
2064 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2066 fgets (buffer, sizeof(buffer), f);
2067 p = vskipspaces(buffer);
2069 while (*q && !isspace(*q))
2072 NewsServer = m_strdup(p);
2076 if ((p = getenv ("NNTPSERVER")))
2077 NewsServer = m_strdup(p);
2080 if ((p = getenv("MAIL") ?: getenv("MAILDIR"))) {
2081 Spoolfile = m_strdup(p);
2084 mutt_concat_path(buffer, sizeof(buffer), NONULL(MCore.homedir), MAILPATH);
2086 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(MCore.username));
2088 Spoolfile = m_strdup(buffer);
2091 if ((p = getenv ("MAILCAPS")))
2092 MailcapPath = m_strdup(p);
2094 /* Default search path from RFC1524 */
2096 m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2097 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2100 if ((p = getenv ("REPLYTO")) != NULL) {
2103 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2106 buf.data = buf.dptr = buffer;
2107 buf.dsize = m_strlen(buffer);
2110 parse_my_hdr (&token, &buf, 0, &err);
2111 p_delete(&token.data);
2114 if ((p = getenv ("EMAIL")) != NULL)
2115 From = rfc822_parse_adrlist (NULL, p);
2117 /* Set standard defaults */
2118 hash_map (ConfigOptions, mutt_set_default, 0);
2119 hash_map (ConfigOptions, mutt_restore_default, 0);
2121 CurrentMenu = MENU_MAIN;
2124 /* Unset suspend by default if we're the session leader */
2125 if (getsid (0) == getpid ())
2126 unset_option (OPTSUSPEND);
2129 mutt_init_history ();
2132 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL(MCore.homedir));
2133 if (access (buffer, F_OK) == -1)
2134 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
2135 NONULL(MCore.homedir));
2138 Muttrc = m_strdup(buffer);
2141 m_strcpy(buffer, sizeof(buffer), Muttrc);
2143 mutt_expand_path (buffer, sizeof(buffer));
2144 Muttrc = m_strdup(buffer);
2147 /* Process the global rc file if it exists and the user hasn't explicity
2148 requested not to via "-n". */
2150 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
2152 if (access (buffer, F_OK) == -1)
2153 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
2154 if (access (buffer, F_OK) == -1)
2155 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
2157 if (access (buffer, F_OK) == -1)
2158 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
2159 if (access (buffer, F_OK) != -1) {
2160 if (source_rc (buffer, &err) != 0) {
2161 fputs (err.data, stderr);
2162 fputc ('\n', stderr);
2168 /* Read the user's initialization file. */
2169 if (access (Muttrc, F_OK) != -1) {
2170 if (!option (OPTNOCURSES))
2172 if (source_rc (Muttrc, &err) != 0) {
2173 fputs (err.data, stderr);
2174 fputc ('\n', stderr);
2178 else if (!default_rc) {
2179 /* file specified by -F does not exist */
2180 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2181 mutt_endwin (buffer);
2186 snprintf(buffer, sizeof(buffer), "%s/.madmutt.lua", NONULL(MCore.homedir));
2187 if (access(buffer, F_OK) < 0)
2188 snprintf(buffer, sizeof(buffer), "%s/.madmutt/cfg.lua", NONULL(MCore.homedir));
2189 if (!access(buffer, F_OK)) {
2190 need_pause = luaM_wrap(mutt_error, luaM_dofile(buffer));
2194 if (mutt_execute_commands (commands) != 0)
2197 /* warn about synonym variables */
2201 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2203 for (syn = Synonyms; syn; syn = syn->next) {
2204 fprintf(stderr, "$%s ($%s should be used) (%s:%d)\n",
2205 syn->o ? NONULL(syn->o->option) : "",
2206 syn->n ? NONULL(syn->n->option) : "",
2207 NONULL(syn->f), syn->l);
2209 fprintf (stderr, _("Warning: synonym variables are scheduled"
2210 " for removal.\n"));
2211 syn_list_wipe(&Synonyms);
2215 if (need_pause && !option (OPTNOCURSES)) {
2216 if (mutt_any_key_to_continue (NULL) == -1)
2221 int mutt_get_hook_type (const char *name)
2223 struct command_t *c;
2225 for (c = Commands; c->name; c++)
2226 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2231 /* dump out the value of all the variables we have */
2232 int mutt_dump_variables (int full) {
2235 /* get all non-synonyms into list... */
2236 for (i = 0; MuttVars[i].option; i++) {
2237 struct option_t *option = MuttVars + i;
2238 char buf[LONG_STRING];
2240 if (DTYPE(option->type) == DT_SYN)
2244 mutt_option_value(option->option, buf, sizeof(buf));
2245 if (!m_strcmp(buf, option->init))
2250 FuncTable[DTYPE(option->type)].opt_tostr(buf, sizeof(buf), option);
2251 printf ("%s\n", buf);
2254 printf ("\n# vi""m:set ft=muttrc:\n");