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>
14 #include <sys/utsname.h>
16 #include <lib-sys/unix.h>
17 #include <lib-sys/mutt_ssl.h>
19 #include <lib-ui/curses.h>
20 #include <lib-ui/history.h>
21 #include <lib-mx/mx.h>
27 #include <lib-crypt/crypt.h>
28 #include "mutt_idna.h"
30 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
31 #include "mutt_libesmtp.h"
40 static const struct mapping_t* get_sortmap (struct option_t* option);
41 static int parse_sort (struct option_t* dst, const char *s,
42 const struct mapping_t *map,
43 char* errbuf, ssize_t errlen);
45 static HASH *ConfigOptions = NULL;
47 /* for synonym warning reports: synonym found during parsing */
48 typedef struct syn_t {
52 struct option_t* n; /* new */
53 struct option_t* o; /* old */
57 static void syn_wipe(syn_t *syn) {
61 DO_DELETE(syn_t, syn);
62 DO_SLIST(syn_t, syn, syn_delete);
64 /* for synonym warning reports: list of synonyms found */
65 static syn_t *Synonyms = NULL;
66 /* for synonym warning reports: current rc file */
67 static const char* CurRCFile = NULL;
68 /* for synonym warning reports: current rc line */
69 static int CurRCLine = 0;
71 /* prototypes for checking for special vars */
72 static int check_dsn_return (const char* option, unsigned long val,
73 char* errbuf, ssize_t errlen);
74 static int check_dsn_notify (const char* option, unsigned long val,
75 char* errbuf, ssize_t errlen);
76 static int check_history (const char* option, unsigned long val,
77 char* errbuf, ssize_t errlen);
78 /* this checks that numbers are >= 0 */
79 static int check_num (const char* option, unsigned long val,
80 char* errbuf, ssize_t errlen);
82 /* use this to check only */
83 static int check_special (const char* option, unsigned long val,
84 char* errbuf, ssize_t errlen);
86 /* variable <-> sanity check function mappings
87 * when changing these, make sure the proper _from_string handler
92 int (*check) (const char* option, unsigned long val,
93 char* errbuf, ssize_t errlen);
95 { "dsn_notify", check_dsn_notify },
96 { "dsn_return", check_dsn_return },
97 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
98 { "smtp_use_tls", mutt_libesmtp_check_usetls },
100 { "history", check_history },
101 { "pager_index_lines", check_num },
106 /* protos for config type handles: convert value to string */
107 static void bool_to_string (char* dst, ssize_t dstlen, struct option_t* option);
108 static void num_to_string (char* dst, ssize_t dstlen, struct option_t* option);
109 static void str_to_string (char* dst, ssize_t dstlen, struct option_t* option);
110 static void quad_to_string (char* dst, ssize_t dstlen, struct option_t* option);
111 static void sort_to_string (char* dst, ssize_t dstlen, struct option_t* option);
112 static void rx_to_string (char* dst, ssize_t dstlen, struct option_t* option);
113 static void magic_to_string (char* dst, ssize_t dstlen, struct option_t* option);
114 static void addr_to_string (char* dst, ssize_t dstlen, struct option_t* option);
115 static void user_to_string (char* dst, ssize_t dstlen, struct option_t* option);
116 static void sys_to_string (char* dst, ssize_t dstlen, struct option_t* option);
118 /* protos for config type handles: convert to value from string */
119 static int bool_from_string (struct option_t* dst, const char* val,
120 char* errbuf, ssize_t errlen);
121 static int num_from_string (struct option_t* dst, const char* val,
122 char* errbuf, ssize_t errlen);
123 static int str_from_string (struct option_t* dst, const char* val,
124 char* errbuf, ssize_t errlen);
125 static int path_from_string (struct option_t* dst, const char* val,
126 char* errbuf, ssize_t errlen);
127 static int quad_from_string (struct option_t* dst, const char* val,
128 char* errbuf, ssize_t errlen);
129 static int sort_from_string (struct option_t* dst, const char* val,
130 char* errbuf, ssize_t errlen);
131 static int rx_from_string (struct option_t* dst, const char* val,
132 char* errbuf, ssize_t errlen);
133 static int magic_from_string (struct option_t* dst, const char* val,
134 char* errbuf, ssize_t errlen);
135 static int addr_from_string (struct option_t* dst, const char* val,
136 char* errbuf, ssize_t errlen);
137 static int user_from_string (struct option_t* dst, const char* val,
138 char* errbuf, ssize_t errlen);
142 void (*opt_tostr) (char* dst, ssize_t dstlen, struct option_t* option);
143 int (*opt_fromstr) (struct option_t* dst, const char* val,
144 char* errbuf, ssize_t errlen);
146 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
147 { DT_BOOL, bool_to_string, bool_from_string },
148 { DT_NUM, num_to_string, num_from_string },
149 { DT_STR, str_to_string, str_from_string },
150 { DT_PATH, str_to_string, path_from_string },
151 { DT_QUAD, quad_to_string, quad_from_string },
152 { DT_SORT, sort_to_string, sort_from_string },
153 { DT_RX, rx_to_string, rx_from_string },
154 { DT_MAGIC, magic_to_string, magic_from_string },
155 /* synonyms should be resolved already so we don't need this
156 * but must define it as DT_ is used for indexing */
157 { DT_SYN, NULL, NULL },
158 { DT_ADDR, addr_to_string, addr_from_string },
159 { DT_USER, user_to_string, user_from_string },
160 { DT_SYS, sys_to_string, NULL },
163 static void bool_to_string (char* dst, ssize_t dstlen,
164 struct option_t* option) {
165 snprintf (dst, dstlen, "%s=%s", option->option,
166 option (option->data) ? "yes" : "no");
169 static int bool_from_string (struct option_t* dst, const char* val,
170 char* errbuf __attribute__ ((unused)),
171 ssize_t errlen __attribute__ ((unused))) {
176 if (ascii_strncasecmp (val, "yes", 3) == 0)
178 else if (ascii_strncasecmp (val, "no", 2) == 0)
184 set_option (dst->data);
186 unset_option (dst->data);
190 static void num_to_string (char* dst, ssize_t dstlen,
191 struct option_t* option) {
193 const char* fmt = (m_strcmp(option->option, "umask") == 0) ?
195 snprintf (dst, dstlen, fmt, option->option,
196 *((short*) option->data));
199 static int num_from_string (struct option_t* dst, const char* val,
200 char* errbuf, ssize_t errlen) {
201 int num = 0, old = 0;
207 num = strtol (val, &t, 0);
209 if (m_strisempty(val) || *t || (short) num != num) {
211 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
217 /* just temporarily accept new val so that check_special for
218 * $history already has it when doing history's init() */
219 old = *((short*) dst->data);
220 *((short*) dst->data) = (short) num;
222 if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
223 *((short*) dst->data) = old;
230 static void str_to_string (char* dst, ssize_t dstlen,
231 struct option_t* option) {
232 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
233 NONULL (*((char**) option->data)));
236 static void user_to_string (char* dst, ssize_t dstlen,
237 struct option_t* option) {
238 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
239 NONULL (((char*) option->data)));
242 static void sys_to_string (char* dst, ssize_t dstlen,
243 struct option_t* option) {
244 char *val = NULL, *t = NULL;
247 /* get some $madmutt_ values dynamically */
248 if (m_strcmp("madmutt_pwd", option->option) == 0) {
249 val = p_new(char, _POSIX_PATH_MAX);
250 val = getcwd (val, _POSIX_PATH_MAX-1);
252 } else if (m_strcmp("madmutt_folder_path", option->option) == 0 &&
253 CurrentFolder && *CurrentFolder) {
255 } else if (m_strcmp("madmutt_folder_name", option->option) == 0 &&
256 CurrentFolder && *CurrentFolder) {
258 ssize_t Maildirlength = m_strlen(Maildir);
261 * if name starts with $folder, just strip it to keep hierarchy
262 * $folder=imap://host, path=imap://host/inbox/b -> inbox/b
264 if (Maildirlength > 0 && m_strncmp(CurrentFolder, Maildir,
265 Maildirlength) == 0 &&
266 m_strlen(CurrentFolder) > Maildirlength) {
267 val = CurrentFolder + Maildirlength;
268 if (Maildir[Maildirlength]!='/')
270 /* if not $folder, just use everything after last / */
271 } else if ((t = strrchr (CurrentFolder, '/')) != NULL)
273 /* default: use as-is */
275 val = (char *) CurrentFolder;
278 val = (char *) option->init;
280 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (val));
285 static int path_from_string (struct option_t* dst, const char* val,
286 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
287 char path[_POSIX_PATH_MAX];
292 if (m_strisempty(val)) {
293 p_delete((char**) dst->data);
298 m_strcpy(path, sizeof(path), val);
299 mutt_expand_path (path, sizeof(path));
300 m_strreplace((char **) dst->data, path);
304 static int str_from_string (struct option_t* dst, const char* val,
305 char* errbuf, ssize_t errlen) {
309 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
312 m_strreplace((char**) dst->data, val);
316 static int user_from_string (struct option_t* dst, const char* val,
317 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
318 /* if dst == NULL, we may get here in case the user did unset it,
319 * see parse_set() where item is free()'d before coming here; so
320 * just silently ignore it */
323 if (m_strlen((char*) dst->data) == 0)
324 dst->data = (unsigned long) m_strdup(val);
326 char* s = (char*) dst->data;
327 m_strreplace(&s, val);
329 if (m_strlen(dst->init) == 0)
330 dst->init = m_strdup((char*) dst->data);
334 static void quad_to_string (char* dst, ssize_t dstlen,
335 struct option_t* option) {
336 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
337 snprintf (dst, dstlen, "%s=%s", option->option,
338 vals[quadoption (option->data)]);
341 static int quad_from_string (struct option_t* dst, const char* val,
342 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
347 if (ascii_strncasecmp (val, "yes", 3) == 0)
349 else if (ascii_strncasecmp (val, "no", 2) == 0)
351 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
353 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
359 set_quadoption (dst->data, flag);
363 static void sort_to_string (char* dst, ssize_t dstlen,
364 struct option_t* option) {
365 const struct mapping_t *map = get_sortmap (option);
366 const char *p = NULL;
369 snprintf (dst, sizeof(dst), "%s=unknown", option->option);
373 p = mutt_getnamebyvalue(*((short *)option->data) & SORT_MASK, map);
375 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
376 (*((short *) option->data) & SORT_REVERSE) ?
378 (*((short *) option->data) & SORT_LAST) ? "last-" :
382 static int sort_from_string (struct option_t* dst, const char* val,
383 char* errbuf, ssize_t errlen) {
384 const struct mapping_t *map = NULL;
385 if (!(map = get_sortmap (dst))) {
387 snprintf (errbuf, errlen, _("%s: Unknown type."),
391 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
396 static void rx_to_string (char* dst, ssize_t dstlen,
397 struct option_t* option) {
398 rx_t* p = (rx_t*) option->data;
399 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
400 NONULL (p->pattern));
403 static int rx_from_string (struct option_t* dst, const char* val,
404 char* errbuf, ssize_t errlen) {
407 int flags = 0, e = 0, not = 0;
413 if (option (OPTATTACHMSG) && !m_strcmp(dst->option, "reply_regexp")) {
415 snprintf (errbuf, errlen,
416 "Operation not permitted when in attach-message mode.");
420 if (!((rx_t*) dst->data))
421 *((rx_t**) dst->data) = p_new(rx_t, 1);
423 p = (rx_t*) dst->data;
425 /* something to do? */
426 if (m_strisempty(val) || (p->pattern && m_strcmp(p->pattern, val) == 0))
429 if (m_strcmp(dst->option, "mask") != 0)
430 flags |= mutt_which_case (val);
433 if (m_strcmp(dst->option, "mask") == 0 && *s == '!') {
438 rx = p_new(regex_t, 1);
440 if ((e = REGCOMP (rx, s, flags)) != 0) {
441 regerror (e, rx, errbuf, errlen);
452 m_strreplace(&p->pattern, val);
456 if (m_strcmp(dst->option, "reply_regexp") == 0)
457 mutt_adjust_all_subjects ();
462 static void magic_to_string (char* dst, ssize_t dstlen,
463 struct option_t* option) {
464 const char* s = NULL;
465 switch (option->data) {
466 case M_MBOX: s = "mbox"; break;
467 case M_MMDF: s = "MMDF"; break;
468 case M_MH: s = "MH"; break;
469 case M_MAILDIR: s = "Maildir"; break;
470 default: s = "unknown"; break;
472 snprintf (dst, dstlen, "%s=%s", option->option, s);
475 static int magic_from_string (struct option_t* dst, const char* val,
476 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
479 if (!dst || m_strisempty(val))
481 if (ascii_strncasecmp (val, "mbox", 4) == 0)
483 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
485 else if (ascii_strncasecmp (val, "mh", 2) == 0)
487 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
493 *((short*) dst->data) = flag;
498 static void addr_to_string (char* dst, ssize_t dstlen,
499 struct option_t* option) {
502 rfc822_addrcat(s, sizeof(s), *((address_t**) option->data), 0);
503 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
506 static int addr_from_string (struct option_t* dst, const char* val,
507 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
510 address_list_wipe((address_t**) dst->data);
512 *((address_t**) dst->data) = rfc822_parse_adrlist (NULL, val);
516 int mutt_option_value (const char* val, char* dst, ssize_t dstlen) {
517 struct option_t* option = NULL;
518 char* tmp = NULL, *t = NULL;
521 if (!(option = hash_find (ConfigOptions, val))) {
525 tmp = p_new(char, dstlen+1);
526 FuncTable[DTYPE(option->type)].opt_tostr (tmp, dstlen, option);
528 /* as we get things of type $var=value and don't want to bloat the
529 * above "just" for expansion, we do the stripping here */
530 t = strchr (tmp, '=');
534 if (t[l-1] == '"' && *t == '"') {
539 memcpy (dst, t, l+1);
545 static void toggle_quadoption (int opt)
548 int b = (opt % 4) * 2;
550 QuadOptions[n] ^= (1 << b);
553 void set_quadoption (int opt, int flag)
556 int b = (opt % 4) * 2;
558 QuadOptions[n] &= ~(0x3 << b);
559 QuadOptions[n] |= (flag & 0x3) << b;
562 int quadoption (int opt)
565 int b = (opt % 4) * 2;
567 return (QuadOptions[n] >> b) & 0x3;
570 int query_quadoption (int opt, const char *prompt)
572 int v = quadoption (opt);
580 v = mutt_yesorno (prompt, (v == M_ASKYES));
581 CLEARLINE (LINES - 1);
588 static void add_to_list (string_list_t ** list, const char *str)
590 string_list_t *t, *last = NULL;
592 /* don't add a NULL or empty string to the list */
593 if (!str || *str == '\0')
596 /* check to make sure the item is not already on this list */
597 for (last = *list; last; last = last->next) {
598 if (ascii_strcasecmp (str, last->data) == 0) {
599 /* already on the list, so just ignore it */
607 if (!*list || last) {
608 t = p_new(string_list_t, 1);
609 t->data = m_strdup(str);
620 add_to_rx_list(rx_t **list, const char *s, int flags, BUFFER *err)
627 if (rx_lookup(list, s))
630 rx = rx_compile(s, flags);
632 snprintf(err->data, err->dsize, "Bad regexp: %s\n", s);
636 rx_list_append(list, rx);
640 static int add_to_spam_list(rx_t **list, const char *pat,
641 const char *templ, BUFFER * err)
645 if (m_strisempty(pat) || !templ)
648 if (!(rx = rx_compile (pat, REG_ICASE))) {
649 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
653 /* check to make sure the item is not already on this list */
654 for (last = list; *last; last = &(*last)->next) {
655 if (!ascii_strcasecmp(rx->pattern, (*last)->pattern) == 0) {
656 rx_t *tmp = rx_list_pop(last);
658 last = rx_list_last(last);
664 rx_set_template(rx, templ);
668 static int remove_from_spam_list (rx_t ** list, const char *pat)
673 if (!m_strcmp((*list)->pattern, pat)) {
674 rx_t *spam = rx_list_pop(list);
678 list = &(*list)->next;
686 static void remove_from_list (string_list_t ** l, const char *str)
688 string_list_t *p, *last = NULL;
690 if (m_strcmp("*", str) == 0)
691 string_list_wipe(l); /* ``unCMD *'' means delete all current entries */
696 if (ascii_strcasecmp (str, p->data) == 0) {
699 last->next = p->next;
712 static int remove_from_rx_list(rx_t **l, const char *str)
714 if (m_strcmp("*", str) == 0) {
719 l = rx_lookup(l, str);
721 rx_t *r = rx_list_pop(l);
729 static int parse_unignore (BUFFER * buf, BUFFER * s,
730 unsigned long data __attribute__ ((unused)),
731 BUFFER * err __attribute__ ((unused)))
734 mutt_extract_token (buf, s, 0);
736 /* don't add "*" to the unignore list */
737 if (m_strcmp (buf->data, "*"))
738 add_to_list (&UnIgnore, buf->data);
740 remove_from_list (&Ignore, buf->data);
741 } while (MoreArgs (s));
746 static int parse_ignore (BUFFER * buf, BUFFER * s,
747 unsigned long data __attribute__ ((unused)),
748 BUFFER * err __attribute__ ((unused)))
751 mutt_extract_token (buf, s, 0);
752 remove_from_list (&UnIgnore, buf->data);
753 add_to_list (&Ignore, buf->data);
754 } while (MoreArgs(s));
758 static int parse_list (BUFFER * buf, BUFFER * s,
759 unsigned long data __attribute__ ((unused)),
760 BUFFER * err __attribute__ ((unused)))
763 mutt_extract_token (buf, s, 0);
764 add_to_list ((string_list_t **) data, buf->data);
765 } while (MoreArgs(s));
769 static void _alternates_clean (void)
773 if (Context && Context->msgcount) {
774 for (i = 0; i < Context->msgcount; i++)
775 Context->hdrs[i]->recip_valid = 0;
779 static int parse_alternates (BUFFER * buf, BUFFER * s,
780 unsigned long data __attribute__ ((unused)),
781 BUFFER * err __attribute__ ((unused)))
783 _alternates_clean ();
785 mutt_extract_token (buf, s, 0);
786 remove_from_rx_list (&UnAlternates, buf->data);
788 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
791 while (MoreArgs (s));
796 static int parse_unalternates (BUFFER * buf, BUFFER * s,
797 unsigned long data __attribute__ ((unused)),
798 BUFFER * err __attribute__ ((unused)))
800 _alternates_clean ();
802 mutt_extract_token (buf, s, 0);
803 remove_from_rx_list (&Alternates, buf->data);
805 if (m_strcmp(buf->data, "*") &&
806 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
810 while (MoreArgs (s));
815 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
822 /* Insist on at least one parameter */
825 m_strcpy(err->data, err->dsize, _("spam: no matching pattern"));
827 m_strcpy(err->data, err->dsize, _("nospam: no matching pattern"));
831 /* Extract the first token, a regexp */
832 mutt_extract_token (buf, s, 0);
834 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
835 if (data == M_SPAM) {
836 /* If there's a second parameter, it's a template for the spam tag. */
838 mutt_extract_token (&templ, s, 0);
840 /* Add to the spam list. */
841 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
842 p_delete(&templ.data);
845 p_delete(&templ.data);
848 /* If not, try to remove from the nospam list. */
850 remove_from_rx_list (&NoSpamList, buf->data);
856 /* M_NOSPAM is for nospam commands. */
857 else if (data == M_NOSPAM) {
858 /* nospam only ever has one parameter. */
860 /* "*" is a special case. */
861 if (!m_strcmp(buf->data, "*")) {
862 rx_list_wipe(&SpamList);
863 rx_list_wipe(&NoSpamList);
867 /* If it's on the spam list, just remove it. */
868 if (remove_from_spam_list (&SpamList, buf->data) != 0)
871 /* Otherwise, add it to the nospam list. */
872 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
878 /* This should not happen. */
879 m_strcpy(err->data, err->dsize, "This is no good at all.");
883 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
884 BUFFER * err __attribute__ ((unused)))
887 mutt_extract_token (buf, s, 0);
889 * Check for deletion of entire list
891 if (m_strcmp(buf->data, "*") == 0) {
892 string_list_wipe((string_list_t **) data);
895 remove_from_list ((string_list_t **) data, buf->data);
897 while (MoreArgs (s));
902 static int parse_lists (BUFFER * buf, BUFFER * s,
903 unsigned long data __attribute__ ((unused)),
907 mutt_extract_token (buf, s, 0);
908 remove_from_rx_list (&UnMailLists, buf->data);
910 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
913 while (MoreArgs (s));
918 /* always wise to do what someone else did before */
919 static void _attachments_clean (void) {
921 if (Context && Context->msgcount) {
922 for (i = 0; i < Context->msgcount; i++)
923 Context->hdrs[i]->attach_valid = 0;
927 static int parse_attach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
928 BUFFER *err __attribute__ ((unused))) {
930 string_list_t *listp, *lastp;
935 /* Find the last item in the list that data points to. */
937 for (listp = *ldata; listp; listp = listp->next) {
938 a = (ATTACH_MATCH *)listp->data;
943 mutt_extract_token (buf, s, 0);
945 if (!buf->data || *buf->data == '\0')
948 a = p_new(ATTACH_MATCH, 1);
950 /* some cheap hacks that I expect to remove */
951 if (!m_strcasecmp(buf->data, "any"))
952 a->major = m_strdup("*/.*");
953 else if (!m_strcasecmp(buf->data, "none"))
954 a->major = m_strdup("cheap_hack/this_should_never_match");
956 a->major = m_strdup(buf->data);
958 if ((p = strchr(a->major, '/'))) {
963 a->minor = "unknown";
966 len = m_strlen(a->minor);
967 tmpminor = p_new(char, len + 3);
968 m_strcpy(&tmpminor[1], len + 3, a->minor);
970 tmpminor[len+1] = '$';
971 tmpminor[len+2] = '\0';
973 a->major_int = mutt_check_mime_type(a->major);
974 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
978 listp = p_new(string_list_t, 1);
979 listp->data = (char *)a;
988 while (MoreArgs (s));
990 _attachments_clean();
994 static int parse_unattach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
995 BUFFER *err __attribute__ ((unused))) {
997 string_list_t *lp, *lastp, *newlp;
1003 mutt_extract_token (buf, s, 0);
1005 if (!m_strcasecmp(buf->data, "any"))
1006 tmp = m_strdup("*/.*");
1007 else if (!m_strcasecmp(buf->data, "none"))
1008 tmp = m_strdup("cheap_hack/this_should_never_match");
1010 tmp = m_strdup(buf->data);
1012 if ((minor = strchr(tmp, '/'))) {
1016 minor = m_strdup("unknown");
1018 major = mutt_check_mime_type(tmp);
1020 /* We must do our own walk here because remove_from_list() will only
1021 * remove the string_list_t->data, not anything pointed to by the string_list_t->data. */
1023 for(lp = *ldata; lp; ) {
1024 a = (ATTACH_MATCH *)lp->data;
1025 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
1026 regfree(&a->minor_rx);
1027 p_delete(&a->major);
1029 /* Relink backward */
1031 lastp->next = lp->next;
1036 p_delete(&lp->data); /* same as a */
1046 while (MoreArgs (s));
1049 _attachments_clean();
1053 static int print_attach_list (string_list_t *lp, char op, const char *name) {
1055 printf("attachments %c%s %s/%s\n", op, name,
1056 ((ATTACH_MATCH *)lp->data)->major,
1057 ((ATTACH_MATCH *)lp->data)->minor);
1064 static int parse_attachments (BUFFER *buf, BUFFER *s,
1065 unsigned long data __attribute__ ((unused)),
1068 string_list_t **listp;
1070 mutt_extract_token(buf, s, 0);
1071 if (!buf->data || *buf->data == '\0') {
1072 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
1076 category = buf->data;
1082 printf("\nCurrent attachments settings:\n\n");
1083 print_attach_list(AttachAllow, '+', "A");
1084 print_attach_list(AttachExclude, '-', "A");
1085 print_attach_list(InlineAllow, '+', "I");
1086 print_attach_list(InlineExclude, '-', "I");
1087 set_option (OPTFORCEREDRAWINDEX);
1088 set_option (OPTFORCEREDRAWPAGER);
1089 mutt_any_key_to_continue (NULL);
1093 if (op != '+' && op != '-') {
1097 if (!m_strncasecmp(category, "attachment", strlen(category))) {
1099 listp = &AttachAllow;
1101 listp = &AttachExclude;
1103 else if (!m_strncasecmp(category, "inline", strlen(category))) {
1105 listp = &InlineAllow;
1107 listp = &InlineExclude;
1109 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
1113 return parse_attach_list(buf, s, listp, err);
1116 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
1118 string_list_t **listp;
1120 mutt_extract_token(buf, s, 0);
1121 if (!buf->data || *buf->data == '\0') {
1122 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
1128 if (op != '+' && op != '-') {
1132 if (!m_strncasecmp(p, "attachment", strlen(p))) {
1134 listp = &AttachAllow;
1136 listp = &AttachExclude;
1138 else if (!m_strncasecmp(p, "inline", strlen(p))) {
1140 listp = &InlineAllow;
1142 listp = &InlineExclude;
1145 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
1149 return parse_unattach_list(buf, s, listp, err);
1152 static int parse_unlists (BUFFER * buf, BUFFER * s,
1153 unsigned long data __attribute__ ((unused)),
1154 BUFFER * err __attribute__ ((unused)))
1157 mutt_extract_token (buf, s, 0);
1158 remove_from_rx_list (&SubscribedLists, buf->data);
1159 remove_from_rx_list (&MailLists, buf->data);
1161 if (m_strcmp(buf->data, "*") &&
1162 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1165 while (MoreArgs (s));
1170 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1174 mutt_extract_token (buf, s, 0);
1175 remove_from_rx_list (&UnMailLists, buf->data);
1176 remove_from_rx_list (&UnSubscribedLists, buf->data);
1178 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1180 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1183 while (MoreArgs (s));
1188 static int parse_unsubscribe (BUFFER * buf, BUFFER * s,
1189 unsigned long data __attribute__ ((unused)),
1190 BUFFER * err __attribute__ ((unused)))
1193 mutt_extract_token (buf, s, 0);
1194 remove_from_rx_list (&SubscribedLists, buf->data);
1196 if (m_strcmp(buf->data, "*") &&
1197 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1200 while (MoreArgs (s));
1205 static int parse_unalias (BUFFER * buf, BUFFER * s,
1206 unsigned long data __attribute__ ((unused)),
1207 BUFFER * err __attribute__ ((unused)))
1209 alias_t *tmp, *last = NULL;
1212 mutt_extract_token (buf, s, 0);
1214 if (m_strcmp("*", buf->data) == 0) {
1215 if (CurrentMenu == MENU_ALIAS) {
1216 for (tmp = Aliases; tmp; tmp = tmp->next)
1218 set_option (OPTFORCEREDRAWINDEX);
1221 alias_list_wipe(&Aliases);
1225 for (tmp = Aliases; tmp; tmp = tmp->next) {
1226 if (m_strcasecmp(buf->data, tmp->name) == 0) {
1227 if (CurrentMenu == MENU_ALIAS) {
1229 set_option (OPTFORCEREDRAWINDEX);
1234 last->next = tmp->next;
1236 Aliases = tmp->next;
1238 alias_list_wipe(&tmp);
1244 while (MoreArgs (s));
1248 static int parse_alias (BUFFER * buf, BUFFER * s,
1249 unsigned long data __attribute__ ((unused)),
1252 alias_t *tmp = Aliases;
1253 alias_t *last = NULL;
1256 if (!MoreArgs (s)) {
1257 m_strcpy(err->data, err->dsize, _("alias: no address"));
1261 mutt_extract_token (buf, s, 0);
1263 /* check to see if an alias with this name already exists */
1264 for (; tmp; tmp = tmp->next) {
1265 if (!m_strcasecmp(tmp->name, buf->data))
1271 /* create a new alias */
1273 tmp->name = m_strdup(buf->data);
1274 /* give the main addressbook code a chance */
1275 if (CurrentMenu == MENU_ALIAS)
1276 set_option (OPTMENUCALLER);
1279 /* override the previous value */
1280 address_list_wipe(&tmp->addr);
1281 if (CurrentMenu == MENU_ALIAS)
1282 set_option (OPTFORCEREDRAWINDEX);
1285 mutt_extract_token (buf, s,
1286 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1287 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1292 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1293 snprintf (err->data, err->dsize,
1294 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1303 parse_unmy_hdr (BUFFER * buf, BUFFER * s,
1304 unsigned long data __attribute__ ((unused)),
1305 BUFFER * err __attribute__ ((unused)))
1307 string_list_t *last = NULL;
1308 string_list_t *tmp = UserHeader;
1313 mutt_extract_token (buf, s, 0);
1314 if (m_strcmp("*", buf->data) == 0)
1315 string_list_wipe(&UserHeader);
1320 l = m_strlen(buf->data);
1321 if (buf->data[l - 1] == ':')
1325 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1326 && tmp->data[l] == ':') {
1329 last->next = tmp->next;
1331 UserHeader = tmp->next;
1334 string_list_wipe(&ptr);
1343 while (MoreArgs (s));
1347 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1354 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1355 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1356 m_strcpy(err->data, err->dsize, _("invalid header field"));
1359 keylen = p - buf->data + 1;
1362 for (tmp = UserHeader;; tmp = tmp->next) {
1363 /* see if there is already a field by this name */
1364 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1365 /* replace the old value */
1366 p_delete(&tmp->data);
1367 tmp->data = buf->data;
1374 tmp->next = string_item_new();
1378 tmp = string_item_new();
1381 tmp->data = buf->data;
1387 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1388 char* errbuf, ssize_t errlen) {
1391 if (m_strncmp("reverse-", s, 8) == 0) {
1393 flags = SORT_REVERSE;
1396 if (m_strncmp("last-", s, 5) == 0) {
1401 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1403 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1407 *((short*) dst->data) = i | flags;
1411 /* if additional data more == 1, we want to resolve synonyms */
1412 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
1414 char buf[LONG_STRING];
1415 struct option_t *ptr = p;
1417 if (DTYPE(ptr->type) == DT_SYN) {
1420 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1422 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_fromstr)
1425 mutt_option_value(ptr->option, buf, sizeof(buf));
1426 if (m_strlen(ptr->init) == 0 && buf && *buf)
1427 ptr->init = m_strdup(buf);
1430 static struct option_t* add_option (const char* name, const char* init,
1431 short type, short dodup) {
1432 struct option_t* option = p_new(struct option_t, 1);
1434 option->option = m_strdup(name);
1435 option->type = type;
1437 option->init = dodup ? m_strdup(init) : (char*) init;
1441 /* creates new option_t* of type DT_USER for $user_ var */
1442 static struct option_t* add_user_option (const char* name) {
1443 return (add_option (name, NULL, DT_USER, 1));
1446 /* free()'s option_t* */
1447 static void del_option (void* p) {
1448 struct option_t *ptr = (struct option_t*) p;
1449 char* s = (char*) ptr->data;
1450 p_delete(&ptr->option);
1452 p_delete(&ptr->init);
1456 static int init_expand (char** dst, struct option_t* src) {
1462 if (DTYPE(src->type) == DT_STR ||
1463 DTYPE(src->type) == DT_PATH) {
1464 /* only expand for string as it's the only place where
1465 * we want to expand vars right now */
1466 if (src->init && *src->init) {
1469 len = m_strlen(src->init) + 2;
1470 in.data = p_new(char, len + 1);
1471 snprintf (in.data, len, "\"%s\"", src->init);
1474 mutt_extract_token (&token, &in, 0);
1475 if (token.data && *token.data)
1476 *dst = m_strdup(token.data);
1478 *dst = m_strdup("");
1480 p_delete(&token.data);
1482 *dst = m_strdup("");
1484 /* for non-string: take value as is */
1485 *dst = m_strdup(src->init);
1489 /* if additional data more == 1, we want to resolve synonyms */
1490 static void mutt_restore_default (const char* name __attribute__ ((unused)),
1491 void* p, unsigned long more) {
1492 char errbuf[STRING];
1493 struct option_t* ptr = (struct option_t*) p;
1496 if (DTYPE (ptr->type) == DT_SYN) {
1499 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1503 if (FuncTable[DTYPE (ptr->type)].opt_fromstr) {
1504 init_expand (&init, ptr);
1505 if (!FuncTable[DTYPE (ptr->type)].opt_fromstr (ptr, init, errbuf,
1507 if (!option (OPTNOCURSES))
1509 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1510 "Please report this error: \"%s\"\n"),
1511 ptr->option, NONULL (init), errbuf);
1517 if (ptr->flags & R_INDEX)
1518 set_option (OPTFORCEREDRAWINDEX);
1519 if (ptr->flags & R_PAGER)
1520 set_option (OPTFORCEREDRAWPAGER);
1521 if (ptr->flags & R_RESORT_SUB)
1522 set_option (OPTSORTSUBTHREADS);
1523 if (ptr->flags & R_RESORT)
1524 set_option (OPTNEEDRESORT);
1525 if (ptr->flags & R_RESORT_INIT)
1526 set_option (OPTRESORTINIT);
1527 if (ptr->flags & R_TREE)
1528 set_option (OPTREDRAWTREE);
1531 /* check whether value for $dsn_return would be valid */
1532 static int check_dsn_return (const char* option __attribute__ ((unused)), unsigned long p,
1533 char* errbuf, ssize_t errlen) {
1534 char* val = (char*) p;
1535 if (val && *val && m_strncmp(val, "hdrs", 4) != 0 &&
1536 m_strncmp(val, "full", 4) != 0) {
1538 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1544 /* check whether value for $dsn_notify would be valid */
1546 check_dsn_notify (const char* option __attribute__ ((unused)),
1547 unsigned long val, char* errbuf, ssize_t errlen)
1549 const char *p = (const char*)val;
1552 const char *q = m_strchrnul(p, ',');
1555 if (!m_strncmp(p, "never", len) && !m_strncmp(p, "delay", len)
1556 && !m_strncmp(p, "failure", len) && !m_strncmp(p, "success", len))
1559 snprintf(errbuf, errlen, _("'%.*s' is invalid for $%s"),
1560 len, p, "dsn_notify");
1570 static int check_num (const char* option, unsigned long p,
1571 char* errbuf, ssize_t errlen) {
1574 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1580 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1581 char* errbuf, ssize_t errlen) {
1582 if (!check_num ("history", p, errbuf, errlen))
1584 mutt_init_history ();
1588 static int check_special (const char* name, unsigned long val,
1589 char* errbuf, ssize_t errlen) {
1592 for (i = 0; SpecialVars[i].name; i++) {
1593 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1594 return (SpecialVars[i].check (SpecialVars[i].name,
1595 val, errbuf, errlen));
1601 static const struct mapping_t* get_sortmap (struct option_t* option) {
1602 const struct mapping_t* map = NULL;
1604 switch (option->type & DT_SUBTYPE_MASK) {
1606 map = SortAliasMethods;
1608 case DT_SORT_BROWSER:
1609 map = SortBrowserMethods;
1612 map = SortKeyMethods;
1615 map = SortAuxMethods;
1624 #define CHECK_PAGER \
1625 if ((CurrentMenu == MENU_PAGER) && \
1626 (!option || (option->flags & R_RESORT))) \
1628 snprintf (err->data, err->dsize, \
1629 _("Not available in this menu.")); \
1633 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1636 int query, unset, inv, reset, r = 0;
1637 struct option_t* option = NULL;
1639 while (MoreArgs (s)) {
1640 /* reset state variables */
1642 unset = data & M_SET_UNSET;
1643 inv = data & M_SET_INV;
1644 reset = data & M_SET_RESET;
1646 if (*s->dptr == '?') {
1650 else if (m_strncmp("no", s->dptr, 2) == 0) {
1654 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1658 else if (*s->dptr == '&') {
1663 /* get the variable name */
1664 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1666 /* resolve synonyms */
1667 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1668 DTYPE (option->type == DT_SYN))
1670 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1671 syn_t* syn = syn_new();
1672 syn->f = m_strdup(CurRCFile);
1676 syn_list_push(&Synonyms, syn);
1680 /* see if we need to add $user_ var */
1681 if (!option && m_strncmp("user_", tmp->data, 5) == 0) {
1682 /* there's no option named like this yet so only add one
1683 * if the action isn't any of: reset, unset, query */
1684 if (!(reset || unset || query || *s->dptr != '=')) {
1685 option = add_user_option (tmp->data);
1686 hash_insert (ConfigOptions, option->option, option, 0);
1690 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1691 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1694 s->dptr = vskipspaces(s->dptr);
1697 if (query || unset || inv) {
1698 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1702 if (s && *s->dptr == '=') {
1703 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1707 if (!m_strcmp("all", tmp->data)) {
1708 if (CurrentMenu == MENU_PAGER) {
1709 snprintf (err->data, err->dsize, _("Not available in this menu."));
1712 hash_map (ConfigOptions, mutt_restore_default, 1);
1713 set_option (OPTFORCEREDRAWINDEX);
1714 set_option (OPTFORCEREDRAWPAGER);
1715 set_option (OPTSORTSUBTHREADS);
1716 set_option (OPTNEEDRESORT);
1717 set_option (OPTRESORTINIT);
1718 set_option (OPTREDRAWTREE);
1721 else if (!FuncTable[DTYPE (option->type)].opt_fromstr) {
1722 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1727 mutt_restore_default (NULL, option, 1);
1730 else if (DTYPE (option->type) == DT_BOOL) {
1731 /* XXX this currently ignores the function table
1732 * as we don't get invert and stuff into it */
1733 if (s && *s->dptr == '=') {
1734 if (unset || inv || query) {
1735 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1740 mutt_extract_token (tmp, s, 0);
1741 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1743 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1746 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1752 bool_to_string (err->data, err->dsize, option);
1758 unset_option (option->data);
1760 toggle_option (option->data);
1762 set_option (option->data);
1764 else if (DTYPE (option->type) == DT_STR ||
1765 DTYPE (option->type) == DT_PATH ||
1766 DTYPE (option->type) == DT_ADDR ||
1767 DTYPE (option->type) == DT_MAGIC ||
1768 DTYPE (option->type) == DT_NUM ||
1769 DTYPE (option->type) == DT_SORT ||
1770 DTYPE (option->type) == DT_RX ||
1771 DTYPE (option->type) == DT_USER ||
1772 DTYPE (option->type) == DT_SYS) {
1774 /* XXX maybe we need to get unset into handlers? */
1775 if (DTYPE (option->type) == DT_STR ||
1776 DTYPE (option->type) == DT_PATH ||
1777 DTYPE (option->type) == DT_ADDR ||
1778 DTYPE (option->type) == DT_USER ||
1779 DTYPE (option->type) == DT_SYS) {
1782 if (!FuncTable[DTYPE (option->type)].opt_fromstr) {
1783 snprintf (err->data, err->dsize, _("$%s is read-only"),
1787 } else if (DTYPE (option->type) == DT_ADDR)
1788 address_list_wipe((address_t **) option->data);
1789 else if (DTYPE (option->type) == DT_USER)
1790 /* to unset $user_ means remove */
1791 hash_delete (ConfigOptions, option->option,
1792 option, del_option);
1794 p_delete((void **)(void *)&option->data);
1799 if (query || *s->dptr != '=') {
1800 FuncTable[DTYPE (option->type)].opt_tostr
1801 (err->data, err->dsize, option);
1805 /* the $madmutt_ variables are read-only */
1806 if (!FuncTable[DTYPE (option->type)].opt_fromstr) {
1807 snprintf (err->data, err->dsize, _("$%s is read-only"),
1814 mutt_extract_token (tmp, s, 0);
1815 if (!FuncTable[DTYPE (option->type)].opt_fromstr
1816 (option, tmp->data, err->data, err->dsize))
1820 else if (DTYPE (option->type) == DT_QUAD) {
1823 quad_to_string (err->data, err->dsize, option);
1827 if (*s->dptr == '=') {
1830 mutt_extract_token (tmp, s, 0);
1831 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1832 set_quadoption (option->data, M_YES);
1833 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1834 set_quadoption (option->data, M_NO);
1835 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1836 set_quadoption (option->data, M_ASKYES);
1837 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1838 set_quadoption (option->data, M_ASKNO);
1840 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1841 tmp->data, option->option);
1848 toggle_quadoption (option->data);
1850 set_quadoption (option->data, M_NO);
1852 set_quadoption (option->data, M_YES);
1856 snprintf (err->data, err->dsize, _("%s: unknown type"),
1862 if (option->flags & R_INDEX)
1863 set_option (OPTFORCEREDRAWINDEX);
1864 if (option->flags & R_PAGER)
1865 set_option (OPTFORCEREDRAWPAGER);
1866 if (option->flags & R_RESORT_SUB)
1867 set_option (OPTSORTSUBTHREADS);
1868 if (option->flags & R_RESORT)
1869 set_option (OPTNEEDRESORT);
1870 if (option->flags & R_RESORT_INIT)
1871 set_option (OPTRESORTINIT);
1872 if (option->flags & R_TREE)
1873 set_option (OPTREDRAWTREE);
1880 /* reads the specified initialization file. returns -1 if errors were found
1881 so that we can pause to let the user know... */
1882 static int source_rc (const char *rcfile, BUFFER * err)
1885 int line = 0, rc = 0, conv = 0;
1887 char *linebuf = NULL;
1888 char *currentline = NULL;
1892 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1893 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1898 while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
1899 conv = ConfigCharset && (*ConfigCharset) && Charset;
1901 currentline = m_strdup(linebuf);
1904 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1907 currentline = linebuf;
1912 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1913 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1914 if (--rc < -MAXERRS) {
1916 p_delete(¤tline);
1925 p_delete(¤tline);
1927 p_delete(&token.data);
1931 mutt_wait_filter (pid);
1933 /* the muttrc source keyword */
1934 snprintf (err->data, err->dsize,
1935 rc >= -MAXERRS ? _("source: errors in %s")
1936 : _("source: reading aborted due too many errors in %s"),
1945 static int parse_source (BUFFER * tmp, BUFFER * s,
1946 unsigned long data __attribute__ ((unused)),
1949 char path[_POSIX_PATH_MAX];
1953 if (mutt_extract_token (tmp, s, 0) != 0) {
1954 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1958 m_strcpy(path, sizeof(path), tmp->data);
1959 mutt_expand_path (path, sizeof(path));
1961 rc += source_rc (path, err);
1963 while (MoreArgs (s));
1965 return ((rc < 0) ? -1 : 0);
1968 /* line command to execute
1970 token scratch buffer to be used by parser. caller should free
1971 token->data when finished. the reason for this variable is
1972 to avoid having to allocate and deallocate a lot of memory
1973 if we are parsing many lines. the caller can pass in the
1974 memory to use, which avoids having to create new space for
1975 every call to this function.
1977 err where to write error messages */
1978 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1984 expn.data = expn.dptr = line;
1985 expn.dsize = m_strlen(line);
1989 expn.dptr = vskipspaces(expn.dptr);
1990 while (*expn.dptr) {
1991 if (*expn.dptr == '#')
1992 break; /* rest of line is a comment */
1993 if (*expn.dptr == ';') {
1997 mutt_extract_token (token, &expn, 0);
1998 for (i = 0; Commands[i].name; i++) {
1999 if (!m_strcmp(token->data, Commands[i].name)) {
2000 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
2005 if (!Commands[i].name) {
2006 snprintf (err->data, err->dsize, _("%s: unknown command"),
2007 NONULL (token->data));
2014 p_delete(&expn.data);
2019 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
2020 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
2021 /* initial string that starts completion. No telling how much crap
2022 * the user has typed so far. Allocate LONG_STRING just to be sure! */
2023 char User_typed[LONG_STRING] = { 0 };
2025 int Num_matched = 0; /* Number of matches for completion */
2026 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
2027 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
2029 /* helper function for completion. Changes the dest buffer if
2030 necessary/possible to aid completion.
2031 dest == completion result gets here.
2032 src == candidate for completion.
2033 try == user entered data for completion.
2034 len == length of dest buffer.
2036 static void candidate (char *dest, char *try, const char *src, int len)
2040 if (strstr (src, try) == src) {
2041 Matches[Num_matched++] = src;
2043 m_strcpy(dest, len, src);
2045 for (l = 0; src[l] && src[l] == dest[l]; l++);
2051 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
2055 int spaces; /* keep track of the number of leading spaces on the line */
2057 buffer = vskipspaces(buffer);
2058 spaces = buffer - pt;
2060 pt = buffer + pos - spaces;
2061 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2064 if (pt == buffer) { /* complete cmd */
2065 /* first TAB. Collect all the matches */
2068 m_strcpy(User_typed, sizeof(User_typed), pt);
2069 p_clear(Matches, countof(Matches));
2070 p_clear(Completed, countof(Completed));
2071 for (num = 0; Commands[num].name; num++)
2072 candidate (Completed, User_typed, Commands[num].name,
2074 Matches[Num_matched++] = User_typed;
2076 /* All matches are stored. Longest non-ambiguous string is ""
2077 * i.e. dont change 'buffer'. Fake successful return this time */
2078 if (User_typed[0] == 0)
2082 if (Completed[0] == 0 && User_typed[0])
2085 /* Num_matched will _always_ be atleast 1 since the initial
2086 * user-typed string is always stored */
2087 if (numtabs == 1 && Num_matched == 2)
2088 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2089 else if (numtabs > 1 && Num_matched > 2)
2090 /* cycle thru all the matches */
2091 snprintf (Completed, sizeof(Completed), "%s",
2092 Matches[(numtabs - 2) % Num_matched]);
2094 /* return the completed command */
2095 m_strcpy(buffer, len - spaces, Completed);
2097 else if (!m_strncmp(buffer, "set", 3)
2098 || !m_strncmp(buffer, "unset", 5)
2099 || !m_strncmp(buffer, "reset", 5)
2100 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
2101 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
2104 /* loop through all the possible prefixes (no, inv, ...) */
2105 if (!m_strncmp(buffer, "set", 3)) {
2106 for (num = 0; prefixes[num]; num++) {
2107 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
2108 pt += m_strlen(prefixes[num]);
2114 /* first TAB. Collect all the matches */
2117 m_strcpy(User_typed, sizeof(User_typed), pt);
2118 p_clear(Matches, countof(Matches));
2119 p_clear(Completed, countof(Completed));
2120 for (num = 0; MuttVars[num].option; num++)
2121 candidate(Completed, User_typed, MuttVars[num].option,
2123 Matches[Num_matched++] = User_typed;
2125 /* All matches are stored. Longest non-ambiguous string is ""
2126 * i.e. dont change 'buffer'. Fake successful return this time */
2127 if (User_typed[0] == 0)
2131 if (Completed[0] == 0 && User_typed[0])
2134 /* Num_matched will _always_ be atleast 1 since the initial
2135 * user-typed string is always stored */
2136 if (numtabs == 1 && Num_matched == 2)
2137 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2138 else if (numtabs > 1 && Num_matched > 2)
2139 /* cycle thru all the matches */
2140 snprintf (Completed, sizeof(Completed), "%s",
2141 Matches[(numtabs - 2) % Num_matched]);
2143 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2145 else if (!m_strncmp(buffer, "exec", 4)) {
2146 struct binding_t *menu = km_get_table (CurrentMenu);
2148 if (!menu && CurrentMenu != MENU_PAGER)
2152 /* first TAB. Collect all the matches */
2155 m_strcpy(User_typed, sizeof(User_typed), pt);
2156 p_clear(Matches, countof(Matches));
2157 p_clear(Completed, countof(Completed));
2158 for (num = 0; menu[num].name; num++)
2159 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
2160 /* try the generic menu */
2161 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2163 for (num = 0; menu[num].name; num++)
2164 candidate (Completed, User_typed, menu[num].name,
2167 Matches[Num_matched++] = User_typed;
2169 /* All matches are stored. Longest non-ambiguous string is ""
2170 * i.e. dont change 'buffer'. Fake successful return this time */
2171 if (User_typed[0] == 0)
2175 if (Completed[0] == 0 && User_typed[0])
2178 /* Num_matched will _always_ be atleast 1 since the initial
2179 * user-typed string is always stored */
2180 if (numtabs == 1 && Num_matched == 2)
2181 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2182 else if (numtabs > 1 && Num_matched > 2)
2183 /* cycle thru all the matches */
2184 snprintf (Completed, sizeof(Completed), "%s",
2185 Matches[(numtabs - 2) % Num_matched]);
2187 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2195 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
2197 char var[STRING], *pt = buffer;
2199 struct option_t* option = NULL;
2204 buffer = vskipspaces(buffer);
2205 spaces = buffer - pt;
2207 pt = buffer + pos - spaces;
2208 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2210 pt++; /* move past the space */
2211 if (*pt == '=') /* abort if no var before the '=' */
2214 if (m_strncmp(buffer, "set", 3) == 0) {
2215 m_strcpy(var, sizeof(var), pt);
2216 /* ignore the trailing '=' when comparing */
2217 var[m_strlen(var) - 1] = 0;
2218 if (!(option = hash_find (ConfigOptions, var)))
2219 return 0; /* no such variable. */
2221 char tmp[LONG_STRING], tmp2[LONG_STRING];
2223 ssize_t dlen = buffer + len - pt - spaces;
2224 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2228 if ((DTYPE (option->type) == DT_STR) ||
2229 (DTYPE (option->type) == DT_PATH) ||
2230 (DTYPE (option->type) == DT_RX)) {
2231 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
2232 if (DTYPE (option->type) == DT_PATH)
2233 mutt_pretty_mailbox (tmp);
2235 else if (DTYPE (option->type) == DT_ADDR) {
2236 rfc822_addrcat(tmp, sizeof(tmp), *((address_t **) option->data), 0);
2238 else if (DTYPE (option->type) == DT_QUAD)
2239 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
2240 else if (DTYPE (option->type) == DT_NUM)
2241 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
2242 else if (DTYPE (option->type) == DT_SORT) {
2243 const struct mapping_t *map;
2246 switch (option->type & DT_SUBTYPE_MASK) {
2248 map = SortAliasMethods;
2250 case DT_SORT_BROWSER:
2251 map = SortBrowserMethods;
2254 map = SortKeyMethods;
2260 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2261 snprintf(tmp, sizeof(tmp), "%s%s%s",
2262 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2263 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2265 else if (DTYPE (option->type) == DT_MAGIC) {
2267 switch (DefaultMagic) {
2283 m_strcpy(tmp, sizeof(tmp), p);
2285 else if (DTYPE (option->type) == DT_BOOL)
2286 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
2290 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
2291 if (*s == '\\' || *s == '"')
2297 m_strcpy(tmp, sizeof(tmp), pt);
2298 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2306 /* Implement the -Q command line flag */
2307 int mutt_query_variables (string_list_t * queries)
2311 char errbuff[STRING];
2312 char command[STRING];
2320 err.dsize = sizeof(errbuff);
2322 for (p = queries; p; p = p->next) {
2323 snprintf (command, sizeof(command), "set ?%s\n", p->data);
2324 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2325 fprintf (stderr, "%s\n", err.data);
2326 p_delete(&token.data);
2329 printf ("%s\n", err.data);
2332 p_delete(&token.data);
2336 static int mutt_execute_commands (string_list_t * p)
2339 char errstr[STRING];
2343 err.dsize = sizeof(errstr);
2345 for (; p; p = p->next) {
2346 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2347 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2348 p_delete(&token.data);
2352 p_delete(&token.data);
2356 void mutt_init (int skip_sys_rc, string_list_t * commands)
2359 struct utsname utsname;
2361 char buffer[STRING], error[STRING];
2362 int default_rc = 0, need_pause = 0;
2368 err.dsize = sizeof(error);
2370 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2371 * to have some room for $user_ vars */
2372 ConfigOptions = hash_create (sizeof(MuttVars) * 3);
2373 for (i = 0; MuttVars[i].option; i++) {
2374 if (DTYPE (MuttVars[i].type) != DT_SYS)
2375 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2377 hash_insert (ConfigOptions, MuttVars[i].option,
2378 add_option (MuttVars[i].option, MuttVars[i].init,
2383 * XXX - use something even more difficult to predict?
2385 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2386 "\033]9;%ld\a", (long) time (NULL));
2388 /* on one of the systems I use, getcwd() does not return the same prefix
2389 as is listed in the passwd file */
2390 if ((p = getenv ("HOME")))
2391 Homedir = m_strdup(p);
2393 /* Get some information about the user */
2394 if ((pw = getpwuid (getuid ()))) {
2397 Username = m_strdup(pw->pw_name);
2399 Homedir = m_strdup(pw->pw_dir);
2401 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, GecosMask.rx);
2402 Realname = m_strdup(rnbuf);
2403 Shell = m_strdup(pw->pw_shell);
2409 fputs (_("unable to determine home directory"), stderr);
2412 if ((p = getenv ("USER")))
2413 Username = m_strdup(p);
2416 fputs (_("unable to determine username"), stderr);
2419 Shell = m_strdup((p = getenv ("SHELL")) ? p : "/bin/sh");
2422 /* And about the host... */
2424 /* some systems report the FQDN instead of just the hostname */
2425 if ((p = strchr (utsname.nodename, '.'))) {
2426 Hostname = p_dupstr(utsname.nodename, p - utsname.nodename);
2428 m_strcpy(buffer, sizeof(buffer), p); /* save the domain for below */
2431 Hostname = m_strdup(utsname.nodename);
2433 if (!p && getdnsdomainname(buffer, sizeof(buffer)) == -1)
2434 Fqdn = m_strdup("@");
2436 if (*buffer != '@') {
2437 Fqdn = p_new(char, m_strlen(buffer) + m_strlen(Hostname) + 2);
2438 sprintf (Fqdn, "%s.%s", NONULL(Hostname), buffer);
2441 Fqdn = m_strdup(NONULL (Hostname));
2448 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2450 fgets (buffer, sizeof(buffer), f);
2451 p = vskipspaces(buffer);
2453 while (*q && !isspace(*q))
2456 NewsServer = m_strdup(p);
2460 if ((p = getenv ("NNTPSERVER")))
2461 NewsServer = m_strdup(p);
2464 if ((p = getenv ("MAIL")))
2465 Spoolfile = m_strdup(p);
2466 else if ((p = getenv ("MAILDIR")))
2467 Spoolfile = m_strdup(p);
2470 mutt_concat_path(buffer, sizeof(buffer), NONULL(Homedir), MAILPATH);
2472 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(Username));
2474 Spoolfile = m_strdup(buffer);
2477 if ((p = getenv ("MAILCAPS")))
2478 MailcapPath = m_strdup(p);
2480 /* Default search path from RFC1524 */
2482 m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2483 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2486 Tempdir = m_strdup((p = getenv ("TMPDIR")) ? p : "/tmp");
2488 p = getenv ("VISUAL");
2490 p = getenv ("EDITOR");
2494 Editor = m_strdup(p);
2496 if ((p = getenv ("REPLYTO")) != NULL) {
2499 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2502 buf.data = buf.dptr = buffer;
2503 buf.dsize = m_strlen(buffer);
2506 parse_my_hdr (&token, &buf, 0, &err);
2507 p_delete(&token.data);
2510 if ((p = getenv ("EMAIL")) != NULL)
2511 From = rfc822_parse_adrlist (NULL, p);
2513 charset_initialize();
2515 /* Set standard defaults */
2516 hash_map (ConfigOptions, mutt_set_default, 0);
2517 hash_map (ConfigOptions, mutt_restore_default, 0);
2519 CurrentMenu = MENU_MAIN;
2522 /* Unset suspend by default if we're the session leader */
2523 if (getsid (0) == getpid ())
2524 unset_option (OPTSUSPEND);
2527 mutt_init_history ();
2530 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL (Homedir));
2531 if (access (buffer, F_OK) == -1)
2532 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
2536 Muttrc = m_strdup(buffer);
2539 m_strcpy(buffer, sizeof(buffer), Muttrc);
2541 mutt_expand_path (buffer, sizeof(buffer));
2542 Muttrc = m_strdup(buffer);
2544 p_delete(&AliasFile);
2545 AliasFile = m_strdup(NONULL (Muttrc));
2547 /* Process the global rc file if it exists and the user hasn't explicity
2548 requested not to via "-n". */
2550 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
2552 if (access (buffer, F_OK) == -1)
2553 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
2554 if (access (buffer, F_OK) == -1)
2555 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
2557 if (access (buffer, F_OK) == -1)
2558 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
2559 if (access (buffer, F_OK) != -1) {
2560 if (source_rc (buffer, &err) != 0) {
2561 fputs (err.data, stderr);
2562 fputc ('\n', stderr);
2568 /* Read the user's initialization file. */
2569 if (access (Muttrc, F_OK) != -1) {
2570 if (!option (OPTNOCURSES))
2572 if (source_rc (Muttrc, &err) != 0) {
2573 fputs (err.data, stderr);
2574 fputc ('\n', stderr);
2578 else if (!default_rc) {
2579 /* file specified by -F does not exist */
2580 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2581 mutt_endwin (buffer);
2585 if (mutt_execute_commands (commands) != 0)
2588 /* warn about synonym variables */
2592 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2594 for (syn = Synonyms; syn; syn = syn->next) {
2595 fprintf(stderr, "$%s ($%s should be used) (%s:%d)\n",
2596 syn->o ? NONULL(syn->o->option) : "",
2597 syn->n ? NONULL(syn->n->option) : "",
2598 NONULL(syn->f), syn->l);
2600 fprintf (stderr, _("Warning: synonym variables are scheduled"
2601 " for removal.\n"));
2602 syn_list_wipe(&Synonyms);
2606 if (need_pause && !option (OPTNOCURSES)) {
2607 if (mutt_any_key_to_continue (NULL) == -1)
2612 int mutt_get_hook_type (const char *name)
2614 struct command_t *c;
2616 for (c = Commands; c->name; c++)
2617 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2622 /* dump out the value of all the variables we have */
2623 int mutt_dump_variables (int full) {
2626 /* get all non-synonyms into list... */
2627 for (i = 0; MuttVars[i].option; i++) {
2628 struct option_t *option = MuttVars + i;
2629 char buf[LONG_STRING];
2631 if (DTYPE(option->type) == DT_SYN)
2635 mutt_option_value(option->option, buf, sizeof(buf));
2636 if (!m_strcmp(buf, option->init))
2641 FuncTable[DTYPE(option->type)].opt_tostr(buf, sizeof(buf), option);
2642 printf ("%s\n", buf);
2645 printf ("\n# vi""m:set ft=muttrc:\n");