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_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
644 BUFFER * err __attribute__ ((unused)))
647 mutt_extract_token (buf, s, 0);
649 * Check for deletion of entire list
651 if (m_strcmp(buf->data, "*") == 0) {
652 string_list_wipe((string_list_t **) data);
655 remove_from_list ((string_list_t **) data, buf->data);
657 while (MoreArgs (s));
662 static int parse_lists (BUFFER * buf, BUFFER * s,
663 unsigned long data __attribute__ ((unused)),
667 mutt_extract_token (buf, s, 0);
668 remove_from_rx_list (&UnMailLists, buf->data);
670 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
673 while (MoreArgs (s));
678 /* always wise to do what someone else did before */
679 static void _attachments_clean (void) {
681 if (Context && Context->msgcount) {
682 for (i = 0; i < Context->msgcount; i++)
683 Context->hdrs[i]->attach_valid = 0;
687 static int parse_attach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
688 BUFFER *err __attribute__ ((unused))) {
690 string_list_t *listp, *lastp;
695 /* Find the last item in the list that data points to. */
697 for (listp = *ldata; listp; listp = listp->next) {
698 a = (ATTACH_MATCH *)listp->data;
703 mutt_extract_token (buf, s, 0);
705 if (!buf->data || *buf->data == '\0')
708 a = p_new(ATTACH_MATCH, 1);
710 /* some cheap hacks that I expect to remove */
711 if (!m_strcasecmp(buf->data, "any"))
712 a->major = m_strdup("*/.*");
713 else if (!m_strcasecmp(buf->data, "none"))
714 a->major = m_strdup("cheap_hack/this_should_never_match");
716 a->major = m_strdup(buf->data);
718 if ((p = strchr(a->major, '/'))) {
723 a->minor = "unknown";
726 len = m_strlen(a->minor);
727 tmpminor = p_new(char, len + 3);
728 m_strcpy(&tmpminor[1], len + 3, a->minor);
730 tmpminor[len+1] = '$';
731 tmpminor[len+2] = '\0';
733 a->major_int = mutt_check_mime_type(a->major);
734 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
738 listp = p_new(string_list_t, 1);
739 listp->data = (char *)a;
748 while (MoreArgs (s));
750 _attachments_clean();
754 static int parse_unattach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
755 BUFFER *err __attribute__ ((unused))) {
757 string_list_t *lp, *lastp, *newlp;
763 mutt_extract_token (buf, s, 0);
765 if (!m_strcasecmp(buf->data, "any"))
766 tmp = m_strdup("*/.*");
767 else if (!m_strcasecmp(buf->data, "none"))
768 tmp = m_strdup("cheap_hack/this_should_never_match");
770 tmp = m_strdup(buf->data);
772 if ((minor = strchr(tmp, '/'))) {
776 minor = m_strdup("unknown");
778 major = mutt_check_mime_type(tmp);
780 /* We must do our own walk here because remove_from_list() will only
781 * remove the string_list_t->data, not anything pointed to by the string_list_t->data. */
783 for(lp = *ldata; lp; ) {
784 a = (ATTACH_MATCH *)lp->data;
785 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
786 regfree(&a->minor_rx);
789 /* Relink backward */
791 lastp->next = lp->next;
796 p_delete(&lp->data); /* same as a */
806 while (MoreArgs (s));
809 _attachments_clean();
813 static int print_attach_list (string_list_t *lp, char op, const char *name) {
815 printf("attachments %c%s %s/%s\n", op, name,
816 ((ATTACH_MATCH *)lp->data)->major,
817 ((ATTACH_MATCH *)lp->data)->minor);
824 static int parse_attachments (BUFFER *buf, BUFFER *s,
825 unsigned long data __attribute__ ((unused)),
828 string_list_t **listp;
830 mutt_extract_token(buf, s, 0);
831 if (!buf->data || *buf->data == '\0') {
832 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
836 category = buf->data;
842 printf("\nCurrent attachments settings:\n\n");
843 print_attach_list(AttachAllow, '+', "A");
844 print_attach_list(AttachExclude, '-', "A");
845 print_attach_list(InlineAllow, '+', "I");
846 print_attach_list(InlineExclude, '-', "I");
847 set_option (OPTFORCEREDRAWINDEX);
848 set_option (OPTFORCEREDRAWPAGER);
849 mutt_any_key_to_continue (NULL);
853 if (op != '+' && op != '-') {
857 if (!m_strncasecmp(category, "attachment", strlen(category))) {
859 listp = &AttachAllow;
861 listp = &AttachExclude;
863 else if (!m_strncasecmp(category, "inline", strlen(category))) {
865 listp = &InlineAllow;
867 listp = &InlineExclude;
869 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
873 return parse_attach_list(buf, s, listp, err);
876 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
878 string_list_t **listp;
880 mutt_extract_token(buf, s, 0);
881 if (!buf->data || *buf->data == '\0') {
882 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
888 if (op != '+' && op != '-') {
892 if (!m_strncasecmp(p, "attachment", strlen(p))) {
894 listp = &AttachAllow;
896 listp = &AttachExclude;
898 else if (!m_strncasecmp(p, "inline", strlen(p))) {
900 listp = &InlineAllow;
902 listp = &InlineExclude;
905 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
909 return parse_unattach_list(buf, s, listp, err);
912 static int parse_unlists (BUFFER * buf, BUFFER * s,
913 unsigned long data __attribute__ ((unused)),
914 BUFFER * err __attribute__ ((unused)))
917 mutt_extract_token (buf, s, 0);
918 remove_from_rx_list (&SubscribedLists, buf->data);
919 remove_from_rx_list (&MailLists, buf->data);
921 if (m_strcmp(buf->data, "*") &&
922 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
925 while (MoreArgs (s));
930 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
934 mutt_extract_token (buf, s, 0);
935 remove_from_rx_list (&UnMailLists, buf->data);
936 remove_from_rx_list (&UnSubscribedLists, buf->data);
938 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
940 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
943 while (MoreArgs (s));
948 static int parse_unsubscribe (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);
956 if (m_strcmp(buf->data, "*") &&
957 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
960 while (MoreArgs (s));
965 static int parse_unalias (BUFFER * buf, BUFFER * s,
966 unsigned long data __attribute__ ((unused)),
967 BUFFER * err __attribute__ ((unused)))
969 alias_t *tmp, **last;
972 mutt_extract_token (buf, s, 0);
974 if (!m_strcmp("*", buf->data) == 0) {
975 if (CurrentMenu == MENU_ALIAS) {
976 for (tmp = Aliases; tmp; tmp = tmp->next)
978 set_option(OPTFORCEREDRAWINDEX);
980 alias_list_wipe(&Aliases);
986 for (last = &Aliases; *last; last = &(*last)->next) {
987 if (!m_strcasecmp(buf->data, (*last)->name)) {
988 if (CurrentMenu == MENU_ALIAS) {
990 set_option (OPTFORCEREDRAWINDEX);
992 tmp = alias_list_pop(last);
998 } while (MoreArgs(s));
1003 static int parse_alias (BUFFER * buf, BUFFER * s,
1004 unsigned long data __attribute__ ((unused)),
1010 if (!MoreArgs (s)) {
1011 m_strcpy(err->data, err->dsize, _("alias: no address"));
1015 mutt_extract_token (buf, s, 0);
1017 /* check to see if an alias with this name already exists */
1018 for (last = &Aliases; *last; last = &(*last)->next) {
1019 if (!m_strcasecmp((*last)->name, buf->data))
1024 /* create a new alias */
1025 *last = alias_new();
1026 (*last)->name = m_strdup(buf->data);
1027 /* give the main addressbook code a chance */
1028 if (CurrentMenu == MENU_ALIAS)
1029 set_option (OPTMENUCALLER);
1031 /* override the previous value */
1032 address_list_wipe(&(*last)->addr);
1033 if (CurrentMenu == MENU_ALIAS)
1034 set_option (OPTFORCEREDRAWINDEX);
1037 mutt_extract_token(buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE);
1038 (*last)->addr = mutt_parse_adrlist((*last)->addr, buf->data);
1039 if (mutt_addrlist_to_idna((*last)->addr, &estr)) {
1040 snprintf (err->data, err->dsize,
1041 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, (*last)->name);
1050 parse_unmy_hdr(BUFFER * buf, BUFFER * s,
1051 unsigned long data __attribute__ ((unused)),
1052 BUFFER * err __attribute__ ((unused)))
1055 mutt_extract_token (buf, s, 0);
1057 if (!m_strcmp("*", buf->data)) {
1058 string_list_wipe(&UserHeader);
1060 string_list_t **last = &UserHeader;
1061 ssize_t l = m_strlen(buf->data);
1063 if (buf->data[l - 1] == ':')
1067 if (!ascii_strncasecmp(buf->data, (*last)->data, l)
1068 && (*last)->data[l] == ':')
1070 string_list_t *tmp = string_list_pop(last);
1071 string_item_delete(&tmp);
1073 last = &(*last)->next;
1077 } while (MoreArgs(s));
1082 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1089 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1090 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1091 m_strcpy(err->data, err->dsize, _("invalid header field"));
1094 keylen = p - buf->data + 1;
1097 for (tmp = UserHeader;; tmp = tmp->next) {
1098 /* see if there is already a field by this name */
1099 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1100 /* replace the old value */
1101 p_delete(&tmp->data);
1102 tmp->data = buf->data;
1109 tmp->next = string_item_new();
1113 tmp = string_item_new();
1116 tmp->data = buf->data;
1122 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1123 char* errbuf, ssize_t errlen) {
1126 if (m_strncmp("reverse-", s, 8) == 0) {
1128 flags = SORT_REVERSE;
1131 if (m_strncmp("last-", s, 5) == 0) {
1136 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1138 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1142 *((short*) dst->data) = i | flags;
1146 /* if additional data more == 1, we want to resolve synonyms */
1147 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
1149 char buf[LONG_STRING];
1150 struct option_t *ptr = p;
1152 if (DTYPE(ptr->type) == DT_SYN) {
1155 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1157 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_fromstr)
1160 mutt_option_value(ptr->option, buf, sizeof(buf));
1161 if (m_strlen(ptr->init) == 0 && buf && *buf)
1162 ptr->init = m_strdup(buf);
1165 static int init_expand (char** dst, struct option_t* src) {
1171 if (DTYPE(src->type) == DT_STR ||
1172 DTYPE(src->type) == DT_PATH) {
1173 /* only expand for string as it's the only place where
1174 * we want to expand vars right now */
1175 if (src->init && *src->init) {
1178 len = m_strlen(src->init) + 2;
1179 in.data = p_new(char, len + 1);
1180 snprintf (in.data, len, "\"%s\"", src->init);
1183 mutt_extract_token (&token, &in, 0);
1184 if (token.data && *token.data)
1185 *dst = m_strdup(token.data);
1187 *dst = m_strdup("");
1189 p_delete(&token.data);
1191 *dst = m_strdup("");
1193 /* for non-string: take value as is */
1194 *dst = m_strdup(src->init);
1198 /* if additional data more == 1, we want to resolve synonyms */
1199 static void mutt_restore_default (const char* name __attribute__ ((unused)),
1200 void* p, unsigned long more) {
1201 char errbuf[STRING];
1202 struct option_t* ptr = (struct option_t*) p;
1205 if (DTYPE (ptr->type) == DT_SYN) {
1208 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1212 if (FuncTable[DTYPE (ptr->type)].opt_fromstr) {
1213 init_expand (&init, ptr);
1214 if (!FuncTable[DTYPE (ptr->type)].opt_fromstr (ptr, init, errbuf,
1216 if (!option (OPTNOCURSES))
1218 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1219 "Please report this error: \"%s\"\n"),
1220 ptr->option, NONULL (init), errbuf);
1226 if (ptr->flags & R_INDEX)
1227 set_option (OPTFORCEREDRAWINDEX);
1228 if (ptr->flags & R_PAGER)
1229 set_option (OPTFORCEREDRAWPAGER);
1230 if (ptr->flags & R_RESORT_SUB)
1231 set_option (OPTSORTSUBTHREADS);
1232 if (ptr->flags & R_RESORT)
1233 set_option (OPTNEEDRESORT);
1234 if (ptr->flags & R_RESORT_INIT)
1235 set_option (OPTRESORTINIT);
1236 if (ptr->flags & R_TREE)
1237 set_option (OPTREDRAWTREE);
1240 static int check_num (const char* option, unsigned long p,
1241 char* errbuf, ssize_t errlen) {
1244 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1250 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1251 char* errbuf, ssize_t errlen) {
1252 if (!check_num ("history", p, errbuf, errlen))
1254 mutt_init_history ();
1258 static int check_special (const char* name, unsigned long val,
1259 char* errbuf, ssize_t errlen) {
1262 for (i = 0; SpecialVars[i].name; i++) {
1263 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1264 return (SpecialVars[i].check (SpecialVars[i].name,
1265 val, errbuf, errlen));
1271 static const struct mapping_t* get_sortmap (struct option_t* option) {
1272 const struct mapping_t* map = NULL;
1274 switch (option->type & DT_SUBTYPE_MASK) {
1276 map = SortAliasMethods;
1278 case DT_SORT_BROWSER:
1279 map = SortBrowserMethods;
1282 map = SortKeyMethods;
1285 map = SortAuxMethods;
1294 #define CHECK_PAGER \
1295 if ((CurrentMenu == MENU_PAGER) && \
1296 (!option || (option->flags & R_RESORT))) \
1298 snprintf (err->data, err->dsize, \
1299 _("Not available in this menu.")); \
1303 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1306 int query, unset, inv, reset, r = 0;
1307 struct option_t* option = NULL;
1309 while (MoreArgs (s)) {
1310 /* reset state variables */
1312 unset = data & M_SET_UNSET;
1313 inv = data & M_SET_INV;
1314 reset = data & M_SET_RESET;
1316 if (*s->dptr == '?') {
1320 else if (m_strncmp("no", s->dptr, 2) == 0) {
1324 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1328 else if (*s->dptr == '&') {
1333 /* get the variable name */
1334 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1336 /* resolve synonyms */
1337 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1338 DTYPE (option->type == DT_SYN))
1340 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1341 syn_t* syn = syn_new();
1342 syn->f = m_strdup(CurRCFile);
1346 syn_list_push(&Synonyms, syn);
1350 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1351 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1354 s->dptr = vskipspaces(s->dptr);
1357 if (query || unset || inv) {
1358 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1362 if (s && *s->dptr == '=') {
1363 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1367 if (!m_strcmp("all", tmp->data)) {
1368 if (CurrentMenu == MENU_PAGER) {
1369 snprintf (err->data, err->dsize, _("Not available in this menu."));
1372 hash_map (ConfigOptions, mutt_restore_default, 1);
1373 set_option (OPTFORCEREDRAWINDEX);
1374 set_option (OPTFORCEREDRAWPAGER);
1375 set_option (OPTSORTSUBTHREADS);
1376 set_option (OPTNEEDRESORT);
1377 set_option (OPTRESORTINIT);
1378 set_option (OPTREDRAWTREE);
1382 mutt_restore_default (NULL, option, 1);
1385 else if (DTYPE (option->type) == DT_BOOL) {
1386 /* XXX this currently ignores the function table
1387 * as we don't get invert and stuff into it */
1388 if (s && *s->dptr == '=') {
1389 if (unset || inv || query) {
1390 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1395 mutt_extract_token (tmp, s, 0);
1396 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1398 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1401 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1407 bool_to_string (err->data, err->dsize, option);
1413 unset_option (option->data);
1415 toggle_option (option->data);
1417 set_option (option->data);
1419 else if (DTYPE (option->type) == DT_STR ||
1420 DTYPE (option->type) == DT_PATH ||
1421 DTYPE (option->type) == DT_ADDR ||
1422 DTYPE (option->type) == DT_MAGIC ||
1423 DTYPE (option->type) == DT_NUM ||
1424 DTYPE (option->type) == DT_SORT ||
1425 DTYPE (option->type) == DT_RX)
1427 /* XXX maybe we need to get unset into handlers? */
1428 if (DTYPE (option->type) == DT_STR ||
1429 DTYPE (option->type) == DT_PATH ||
1430 DTYPE (option->type) == DT_ADDR)
1434 if (DTYPE (option->type) == DT_ADDR)
1435 address_list_wipe((address_t **) option->data);
1437 p_delete((void **)(void *)&option->data);
1442 if (query || *s->dptr != '=') {
1443 FuncTable[DTYPE (option->type)].opt_tostr
1444 (err->data, err->dsize, option);
1450 mutt_extract_token (tmp, s, 0);
1451 if (!FuncTable[DTYPE (option->type)].opt_fromstr
1452 (option, tmp->data, err->data, err->dsize))
1455 else if (DTYPE (option->type) == DT_QUAD) {
1458 quad_to_string (err->data, err->dsize, option);
1462 if (*s->dptr == '=') {
1465 mutt_extract_token (tmp, s, 0);
1466 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1467 set_quadoption (option->data, M_YES);
1468 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1469 set_quadoption (option->data, M_NO);
1470 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1471 set_quadoption (option->data, M_ASKYES);
1472 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1473 set_quadoption (option->data, M_ASKNO);
1475 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1476 tmp->data, option->option);
1483 toggle_quadoption (option->data);
1485 set_quadoption (option->data, M_NO);
1487 set_quadoption (option->data, M_YES);
1491 snprintf (err->data, err->dsize, _("%s: unknown type"),
1497 if (option->flags & R_INDEX)
1498 set_option (OPTFORCEREDRAWINDEX);
1499 if (option->flags & R_PAGER)
1500 set_option (OPTFORCEREDRAWPAGER);
1501 if (option->flags & R_RESORT_SUB)
1502 set_option (OPTSORTSUBTHREADS);
1503 if (option->flags & R_RESORT)
1504 set_option (OPTNEEDRESORT);
1505 if (option->flags & R_RESORT_INIT)
1506 set_option (OPTRESORTINIT);
1507 if (option->flags & R_TREE)
1508 set_option (OPTREDRAWTREE);
1515 /* reads the specified initialization file. returns -1 if errors were found
1516 so that we can pause to let the user know... */
1517 static int source_rc (const char *rcfile, BUFFER * err)
1520 int line = 0, rc = 0, conv = 0;
1522 char *linebuf = NULL;
1523 char *currentline = NULL;
1527 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1528 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1533 while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
1534 conv = ConfigCharset && (*ConfigCharset) && MCharset.charset;
1536 currentline = m_strdup(linebuf);
1539 mutt_convert_string (¤tline, ConfigCharset, MCharset.charset, 0);
1542 currentline = linebuf;
1547 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1548 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1549 if (--rc < -MAXERRS) {
1551 p_delete(¤tline);
1560 p_delete(¤tline);
1562 p_delete(&token.data);
1566 mutt_wait_filter (pid);
1568 /* the muttrc source keyword */
1569 snprintf (err->data, err->dsize,
1570 rc >= -MAXERRS ? _("source: errors in %s")
1571 : _("source: reading aborted due too many errors in %s"),
1580 static int parse_source (BUFFER * tmp, BUFFER * s,
1581 unsigned long data __attribute__ ((unused)),
1584 char path[_POSIX_PATH_MAX];
1588 if (mutt_extract_token (tmp, s, 0) != 0) {
1589 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1593 m_strcpy(path, sizeof(path), tmp->data);
1594 mutt_expand_path (path, sizeof(path));
1596 rc += source_rc (path, err);
1598 while (MoreArgs (s));
1600 return ((rc < 0) ? -1 : 0);
1603 /* line command to execute
1605 token scratch buffer to be used by parser. caller should free
1606 token->data when finished. the reason for this variable is
1607 to avoid having to allocate and deallocate a lot of memory
1608 if we are parsing many lines. the caller can pass in the
1609 memory to use, which avoids having to create new space for
1610 every call to this function.
1612 err where to write error messages */
1613 int mutt_parse_rc_line (const char *line, BUFFER * token, BUFFER * err)
1619 expn.data = expn.dptr = line;
1620 expn.dsize = m_strlen(line);
1624 expn.dptr = vskipspaces(expn.dptr);
1625 while (*expn.dptr) {
1626 if (*expn.dptr == '#')
1627 break; /* rest of line is a comment */
1628 if (*expn.dptr == ';') {
1632 mutt_extract_token (token, &expn, 0);
1633 for (i = 0; Commands[i].name; i++) {
1634 if (!m_strcmp(token->data, Commands[i].name)) {
1635 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1640 if (!Commands[i].name) {
1641 snprintf (err->data, err->dsize, _("%s: unknown command"),
1642 NONULL (token->data));
1649 p_delete(&expn.data);
1654 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
1655 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
1656 /* initial string that starts completion. No telling how much crap
1657 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1658 char User_typed[LONG_STRING] = { 0 };
1660 int Num_matched = 0; /* Number of matches for completion */
1661 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1662 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1664 /* helper function for completion. Changes the dest buffer if
1665 necessary/possible to aid completion.
1666 dest == completion result gets here.
1667 src == candidate for completion.
1668 try == user entered data for completion.
1669 len == length of dest buffer.
1671 static void candidate (char *dest, char *try, const char *src, int len)
1675 if (strstr (src, try) == src) {
1676 Matches[Num_matched++] = src;
1678 m_strcpy(dest, len, src);
1680 for (l = 0; src[l] && src[l] == dest[l]; l++);
1686 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
1690 int spaces; /* keep track of the number of leading spaces on the line */
1692 buffer = vskipspaces(buffer);
1693 spaces = buffer - pt;
1695 pt = buffer + pos - spaces;
1696 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1699 if (pt == buffer) { /* complete cmd */
1700 /* first TAB. Collect all the matches */
1703 m_strcpy(User_typed, sizeof(User_typed), pt);
1704 p_clear(Matches, countof(Matches));
1705 p_clear(Completed, countof(Completed));
1706 for (num = 0; Commands[num].name; num++)
1707 candidate (Completed, User_typed, Commands[num].name,
1709 Matches[Num_matched++] = User_typed;
1711 /* All matches are stored. Longest non-ambiguous string is ""
1712 * i.e. dont change 'buffer'. Fake successful return this time */
1713 if (User_typed[0] == 0)
1717 if (Completed[0] == 0 && User_typed[0])
1720 /* Num_matched will _always_ be atleast 1 since the initial
1721 * user-typed string is always stored */
1722 if (numtabs == 1 && Num_matched == 2)
1723 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1724 else if (numtabs > 1 && Num_matched > 2)
1725 /* cycle thru all the matches */
1726 snprintf (Completed, sizeof(Completed), "%s",
1727 Matches[(numtabs - 2) % Num_matched]);
1729 /* return the completed command */
1730 m_strcpy(buffer, len - spaces, Completed);
1732 else if (!m_strncmp(buffer, "set", 3)
1733 || !m_strncmp(buffer, "unset", 5)
1734 || !m_strncmp(buffer, "reset", 5)
1735 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
1736 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
1739 /* loop through all the possible prefixes (no, inv, ...) */
1740 if (!m_strncmp(buffer, "set", 3)) {
1741 for (num = 0; prefixes[num]; num++) {
1742 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
1743 pt += m_strlen(prefixes[num]);
1749 /* first TAB. Collect all the matches */
1752 m_strcpy(User_typed, sizeof(User_typed), pt);
1753 p_clear(Matches, countof(Matches));
1754 p_clear(Completed, countof(Completed));
1755 for (num = 0; MuttVars[num].option; num++)
1756 candidate(Completed, User_typed, MuttVars[num].option,
1758 Matches[Num_matched++] = User_typed;
1760 /* All matches are stored. Longest non-ambiguous string is ""
1761 * i.e. dont change 'buffer'. Fake successful return this time */
1762 if (User_typed[0] == 0)
1766 if (Completed[0] == 0 && User_typed[0])
1769 /* Num_matched will _always_ be atleast 1 since the initial
1770 * user-typed string is always stored */
1771 if (numtabs == 1 && Num_matched == 2)
1772 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1773 else if (numtabs > 1 && Num_matched > 2)
1774 /* cycle thru all the matches */
1775 snprintf (Completed, sizeof(Completed), "%s",
1776 Matches[(numtabs - 2) % Num_matched]);
1778 m_strcpy(pt, buffer + len - pt - spaces, Completed);
1780 else if (!m_strncmp(buffer, "exec", 4)) {
1781 struct binding_t *menu = km_get_table (CurrentMenu);
1783 if (!menu && CurrentMenu != MENU_PAGER)
1787 /* first TAB. Collect all the matches */
1790 m_strcpy(User_typed, sizeof(User_typed), pt);
1791 p_clear(Matches, countof(Matches));
1792 p_clear(Completed, countof(Completed));
1793 for (num = 0; menu[num].name; num++)
1794 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
1795 /* try the generic menu */
1796 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1798 for (num = 0; menu[num].name; num++)
1799 candidate (Completed, User_typed, menu[num].name,
1802 Matches[Num_matched++] = User_typed;
1804 /* All matches are stored. Longest non-ambiguous string is ""
1805 * i.e. dont change 'buffer'. Fake successful return this time */
1806 if (User_typed[0] == 0)
1810 if (Completed[0] == 0 && User_typed[0])
1813 /* Num_matched will _always_ be atleast 1 since the initial
1814 * user-typed string is always stored */
1815 if (numtabs == 1 && Num_matched == 2)
1816 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1817 else if (numtabs > 1 && Num_matched > 2)
1818 /* cycle thru all the matches */
1819 snprintf (Completed, sizeof(Completed), "%s",
1820 Matches[(numtabs - 2) % Num_matched]);
1822 m_strcpy(pt, buffer + len - pt - spaces, Completed);
1830 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
1832 char var[STRING], *pt = buffer;
1834 struct option_t* option = NULL;
1839 buffer = vskipspaces(buffer);
1840 spaces = buffer - pt;
1842 pt = buffer + pos - spaces;
1843 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1845 pt++; /* move past the space */
1846 if (*pt == '=') /* abort if no var before the '=' */
1849 if (m_strncmp(buffer, "set", 3) == 0) {
1850 m_strcpy(var, sizeof(var), pt);
1851 /* ignore the trailing '=' when comparing */
1852 var[m_strlen(var) - 1] = 0;
1853 if (!(option = hash_find (ConfigOptions, var)))
1854 return 0; /* no such variable. */
1856 char tmp[LONG_STRING], tmp2[LONG_STRING];
1858 ssize_t dlen = buffer + len - pt - spaces;
1859 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1863 if ((DTYPE (option->type) == DT_STR) ||
1864 (DTYPE (option->type) == DT_PATH) ||
1865 (DTYPE (option->type) == DT_RX)) {
1866 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
1867 if (DTYPE (option->type) == DT_PATH)
1868 mutt_pretty_mailbox (tmp);
1870 else if (DTYPE (option->type) == DT_ADDR) {
1871 rfc822_addrcat(tmp, sizeof(tmp), *((address_t **) option->data), 0);
1873 else if (DTYPE (option->type) == DT_QUAD)
1874 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
1875 else if (DTYPE (option->type) == DT_NUM)
1876 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
1877 else if (DTYPE (option->type) == DT_SORT) {
1878 const struct mapping_t *map;
1881 switch (option->type & DT_SUBTYPE_MASK) {
1883 map = SortAliasMethods;
1885 case DT_SORT_BROWSER:
1886 map = SortBrowserMethods;
1889 map = SortKeyMethods;
1895 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
1896 snprintf(tmp, sizeof(tmp), "%s%s%s",
1897 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
1898 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
1900 else if (DTYPE (option->type) == DT_MAGIC) {
1902 switch (DefaultMagic) {
1918 m_strcpy(tmp, sizeof(tmp), p);
1920 else if (DTYPE (option->type) == DT_BOOL)
1921 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
1925 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
1926 if (*s == '\\' || *s == '"')
1932 m_strcpy(tmp, sizeof(tmp), pt);
1933 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
1941 /* Implement the -Q command line flag */
1942 int mutt_query_variables (string_list_t * queries)
1946 char errbuff[STRING];
1947 char command[STRING];
1955 err.dsize = sizeof(errbuff);
1957 for (p = queries; p; p = p->next) {
1958 snprintf (command, sizeof(command), "set ?%s\n", p->data);
1959 if (mutt_parse_rc_line (command, &token, &err) == -1) {
1960 fprintf (stderr, "%s\n", err.data);
1961 p_delete(&token.data);
1964 printf ("%s\n", err.data);
1967 p_delete(&token.data);
1971 static int mutt_execute_commands (string_list_t * p)
1974 char errstr[STRING];
1978 err.dsize = sizeof(errstr);
1980 for (; p; p = p->next) {
1981 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
1982 fprintf (stderr, _("Error in command line: %s\n"), err.data);
1983 p_delete(&token.data);
1987 p_delete(&token.data);
1991 void mutt_init (int skip_sys_rc, string_list_t * commands)
1995 char buffer[STRING], error[STRING];
1996 int default_rc = 0, need_pause = 0;
2002 err.dsize = sizeof(error);
2004 ConfigOptions = hash_new (sizeof(MuttVars) * 2, 0);
2005 for (i = 0; MuttVars[i].option; i++) {
2006 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i]);
2010 * XXX - use something even more difficult to predict?
2012 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2013 "\033]9;%ld\a", (long) time (NULL));
2016 /* Get some information about the user */
2017 if ((pw = getpwuid (getuid ()))) {
2019 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, MCore.gecos_mask);
2020 Realname = m_strdup(rnbuf);
2028 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2030 fgets (buffer, sizeof(buffer), f);
2031 p = vskipspaces(buffer);
2033 while (*q && !isspace(*q))
2036 NewsServer = m_strdup(p);
2040 if ((p = getenv ("NNTPSERVER")))
2041 NewsServer = m_strdup(p);
2044 if ((p = getenv("MAIL") ?: getenv("MAILDIR"))) {
2045 Spoolfile = m_strdup(p);
2048 mutt_concat_path(buffer, sizeof(buffer), NONULL(MCore.homedir), MAILPATH);
2050 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(MCore.username));
2052 Spoolfile = m_strdup(buffer);
2055 if ((p = getenv ("MAILCAPS")))
2056 MailcapPath = m_strdup(p);
2058 /* Default search path from RFC1524 */
2060 m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2061 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2064 if ((p = getenv ("REPLYTO")) != NULL) {
2067 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2070 buf.data = buf.dptr = buffer;
2071 buf.dsize = m_strlen(buffer);
2074 parse_my_hdr (&token, &buf, 0, &err);
2075 p_delete(&token.data);
2078 if ((p = getenv ("EMAIL")) != NULL)
2079 From = rfc822_parse_adrlist (NULL, p);
2081 /* Set standard defaults */
2082 hash_map (ConfigOptions, mutt_set_default, 0);
2083 hash_map (ConfigOptions, mutt_restore_default, 0);
2085 CurrentMenu = MENU_MAIN;
2088 /* Unset suspend by default if we're the session leader */
2089 if (getsid (0) == getpid ())
2090 unset_option (OPTSUSPEND);
2093 mutt_init_history ();
2096 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL(MCore.homedir));
2097 if (access (buffer, F_OK) == -1)
2098 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
2099 NONULL(MCore.homedir));
2102 Muttrc = m_strdup(buffer);
2105 m_strcpy(buffer, sizeof(buffer), Muttrc);
2107 mutt_expand_path (buffer, sizeof(buffer));
2108 Muttrc = m_strdup(buffer);
2111 /* Process the global rc file if it exists and the user hasn't explicity
2112 requested not to via "-n". */
2114 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
2116 if (access (buffer, F_OK) == -1)
2117 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
2118 if (access (buffer, F_OK) == -1)
2119 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
2121 if (access (buffer, F_OK) == -1)
2122 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
2123 if (access (buffer, F_OK) != -1) {
2124 if (source_rc (buffer, &err) != 0) {
2125 fputs (err.data, stderr);
2126 fputc ('\n', stderr);
2132 /* Read the user's initialization file. */
2133 if (access (Muttrc, F_OK) != -1) {
2134 if (!option (OPTNOCURSES))
2136 if (source_rc (Muttrc, &err) != 0) {
2137 fputs (err.data, stderr);
2138 fputc ('\n', stderr);
2142 else if (!default_rc) {
2143 /* file specified by -F does not exist */
2144 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2145 mutt_endwin (buffer);
2150 snprintf(buffer, sizeof(buffer), "%s/.madmutt.lua", NONULL(MCore.homedir));
2151 if (access(buffer, F_OK) < 0)
2152 snprintf(buffer, sizeof(buffer), "%s/.madmutt/cfg.lua", NONULL(MCore.homedir));
2153 if (!access(buffer, F_OK)) {
2154 need_pause = luaM_wrap(mutt_error, luaM_dofile(buffer));
2158 if (mutt_execute_commands (commands) != 0)
2161 /* warn about synonym variables */
2165 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2167 for (syn = Synonyms; syn; syn = syn->next) {
2168 fprintf(stderr, "$%s ($%s should be used) (%s:%d)\n",
2169 syn->o ? NONULL(syn->o->option) : "",
2170 syn->n ? NONULL(syn->n->option) : "",
2171 NONULL(syn->f), syn->l);
2173 fprintf (stderr, _("Warning: synonym variables are scheduled"
2174 " for removal.\n"));
2175 syn_list_wipe(&Synonyms);
2179 if (need_pause && !option (OPTNOCURSES)) {
2180 if (mutt_any_key_to_continue (NULL) == -1)
2185 int mutt_get_hook_type (const char *name)
2187 struct command_t *c;
2189 for (c = Commands; c->name; c++)
2190 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2195 /* dump out the value of all the variables we have */
2196 int mutt_dump_variables (int full) {
2199 /* get all non-synonyms into list... */
2200 for (i = 0; MuttVars[i].option; i++) {
2201 struct option_t *option = MuttVars + i;
2202 char buf[LONG_STRING];
2204 if (DTYPE(option->type) == DT_SYN)
2208 mutt_option_value(option->option, buf, sizeof(buf));
2209 if (!m_strcmp(buf, option->init))
2214 FuncTable[DTYPE(option->type)].opt_tostr(buf, sizeof(buf), option);
2215 printf ("%s\n", buf);
2218 printf ("\n# vi""m:set ft=muttrc:\n");