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 /* don't add a NULL or empty string to the list */
591 if (m_strisempty(str))
594 /* check to make sure the item is not already on this list */
596 if (!ascii_strcasecmp(str, (*list)->data))
598 list = &(*list)->next;
601 *list = p_new(string_list_t, 1);
602 (*list)->data = m_strdup(str);
606 add_to_rx_list(rx_t **list, const char *s, int flags, BUFFER *err)
613 if (rx_lookup(list, s))
616 rx = rx_compile(s, flags);
618 snprintf(err->data, err->dsize, "Bad regexp: %s\n", s);
622 rx_list_append(list, rx);
626 static int add_to_spam_list(rx_t **list, const char *pat,
627 const char *templ, BUFFER * err)
631 if (m_strisempty(pat) || !templ)
634 if (!(rx = rx_compile (pat, REG_ICASE))) {
635 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
639 /* check to make sure the item is not already on this list */
641 if (!ascii_strcasecmp(rx->pattern, (*list)->pattern)) {
642 rx_t *tmp = rx_list_pop(list);
645 list = &(*list)->next;
650 rx_set_template(rx, templ);
654 static int remove_from_spam_list (rx_t ** list, const char *pat)
659 if (!m_strcmp((*list)->pattern, pat)) {
660 rx_t *spam = rx_list_pop(list);
664 list = &(*list)->next;
672 static void remove_from_list(string_list_t **l, const char *str)
674 if (!m_strcmp("*", str)) {
675 string_list_wipe(l); /* ``unCMD *'' means delete all current entries */
680 if (!ascii_strcasecmp(str, (*l)->data)) {
681 string_list_t *it = string_list_pop(l);
682 string_item_delete(&it);
689 static int remove_from_rx_list(rx_t **l, const char *str)
691 if (m_strcmp("*", str) == 0) {
696 l = rx_lookup(l, str);
698 rx_t *r = rx_list_pop(l);
706 static int parse_unignore (BUFFER * buf, BUFFER * s,
707 unsigned long data __attribute__ ((unused)),
708 BUFFER * err __attribute__ ((unused)))
711 mutt_extract_token (buf, s, 0);
713 /* don't add "*" to the unignore list */
714 if (m_strcmp (buf->data, "*"))
715 add_to_list (&UnIgnore, buf->data);
717 remove_from_list (&Ignore, buf->data);
718 } while (MoreArgs (s));
723 static int parse_ignore (BUFFER * buf, BUFFER * s,
724 unsigned long data __attribute__ ((unused)),
725 BUFFER * err __attribute__ ((unused)))
728 mutt_extract_token (buf, s, 0);
729 remove_from_list (&UnIgnore, buf->data);
730 add_to_list (&Ignore, buf->data);
731 } while (MoreArgs(s));
735 static int parse_list (BUFFER * buf, BUFFER * s,
736 unsigned long data __attribute__ ((unused)),
737 BUFFER * err __attribute__ ((unused)))
740 mutt_extract_token (buf, s, 0);
741 add_to_list ((string_list_t **) data, buf->data);
742 } while (MoreArgs(s));
746 static void _alternates_clean (void)
750 if (Context && Context->msgcount) {
751 for (i = 0; i < Context->msgcount; i++)
752 Context->hdrs[i]->recip_valid = 0;
756 static int parse_alternates (BUFFER * buf, BUFFER * s,
757 unsigned long data __attribute__ ((unused)),
758 BUFFER * err __attribute__ ((unused)))
760 _alternates_clean ();
762 mutt_extract_token (buf, s, 0);
763 remove_from_rx_list (&UnAlternates, buf->data);
765 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
768 while (MoreArgs (s));
773 static int parse_unalternates (BUFFER * buf, BUFFER * s,
774 unsigned long data __attribute__ ((unused)),
775 BUFFER * err __attribute__ ((unused)))
777 _alternates_clean ();
779 mutt_extract_token (buf, s, 0);
780 remove_from_rx_list (&Alternates, buf->data);
782 if (m_strcmp(buf->data, "*") &&
783 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
787 while (MoreArgs (s));
792 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
799 /* Insist on at least one parameter */
802 m_strcpy(err->data, err->dsize, _("spam: no matching pattern"));
804 m_strcpy(err->data, err->dsize, _("nospam: no matching pattern"));
808 /* Extract the first token, a regexp */
809 mutt_extract_token (buf, s, 0);
811 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
812 if (data == M_SPAM) {
813 /* If there's a second parameter, it's a template for the spam tag. */
815 mutt_extract_token (&templ, s, 0);
817 /* Add to the spam list. */
818 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
819 p_delete(&templ.data);
822 p_delete(&templ.data);
825 /* If not, try to remove from the nospam list. */
827 remove_from_rx_list (&NoSpamList, buf->data);
833 /* M_NOSPAM is for nospam commands. */
834 else if (data == M_NOSPAM) {
835 /* nospam only ever has one parameter. */
837 /* "*" is a special case. */
838 if (!m_strcmp(buf->data, "*")) {
839 rx_list_wipe(&SpamList);
840 rx_list_wipe(&NoSpamList);
844 /* If it's on the spam list, just remove it. */
845 if (remove_from_spam_list (&SpamList, buf->data) != 0)
848 /* Otherwise, add it to the nospam list. */
849 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
855 /* This should not happen. */
856 m_strcpy(err->data, err->dsize, "This is no good at all.");
860 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
861 BUFFER * err __attribute__ ((unused)))
864 mutt_extract_token (buf, s, 0);
866 * Check for deletion of entire list
868 if (m_strcmp(buf->data, "*") == 0) {
869 string_list_wipe((string_list_t **) data);
872 remove_from_list ((string_list_t **) data, buf->data);
874 while (MoreArgs (s));
879 static int parse_lists (BUFFER * buf, BUFFER * s,
880 unsigned long data __attribute__ ((unused)),
884 mutt_extract_token (buf, s, 0);
885 remove_from_rx_list (&UnMailLists, buf->data);
887 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
890 while (MoreArgs (s));
895 /* always wise to do what someone else did before */
896 static void _attachments_clean (void) {
898 if (Context && Context->msgcount) {
899 for (i = 0; i < Context->msgcount; i++)
900 Context->hdrs[i]->attach_valid = 0;
904 static int parse_attach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
905 BUFFER *err __attribute__ ((unused))) {
907 string_list_t *listp, *lastp;
912 /* Find the last item in the list that data points to. */
914 for (listp = *ldata; listp; listp = listp->next) {
915 a = (ATTACH_MATCH *)listp->data;
920 mutt_extract_token (buf, s, 0);
922 if (!buf->data || *buf->data == '\0')
925 a = p_new(ATTACH_MATCH, 1);
927 /* some cheap hacks that I expect to remove */
928 if (!m_strcasecmp(buf->data, "any"))
929 a->major = m_strdup("*/.*");
930 else if (!m_strcasecmp(buf->data, "none"))
931 a->major = m_strdup("cheap_hack/this_should_never_match");
933 a->major = m_strdup(buf->data);
935 if ((p = strchr(a->major, '/'))) {
940 a->minor = "unknown";
943 len = m_strlen(a->minor);
944 tmpminor = p_new(char, len + 3);
945 m_strcpy(&tmpminor[1], len + 3, a->minor);
947 tmpminor[len+1] = '$';
948 tmpminor[len+2] = '\0';
950 a->major_int = mutt_check_mime_type(a->major);
951 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
955 listp = p_new(string_list_t, 1);
956 listp->data = (char *)a;
965 while (MoreArgs (s));
967 _attachments_clean();
971 static int parse_unattach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
972 BUFFER *err __attribute__ ((unused))) {
974 string_list_t *lp, *lastp, *newlp;
980 mutt_extract_token (buf, s, 0);
982 if (!m_strcasecmp(buf->data, "any"))
983 tmp = m_strdup("*/.*");
984 else if (!m_strcasecmp(buf->data, "none"))
985 tmp = m_strdup("cheap_hack/this_should_never_match");
987 tmp = m_strdup(buf->data);
989 if ((minor = strchr(tmp, '/'))) {
993 minor = m_strdup("unknown");
995 major = mutt_check_mime_type(tmp);
997 /* We must do our own walk here because remove_from_list() will only
998 * remove the string_list_t->data, not anything pointed to by the string_list_t->data. */
1000 for(lp = *ldata; lp; ) {
1001 a = (ATTACH_MATCH *)lp->data;
1002 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
1003 regfree(&a->minor_rx);
1004 p_delete(&a->major);
1006 /* Relink backward */
1008 lastp->next = lp->next;
1013 p_delete(&lp->data); /* same as a */
1023 while (MoreArgs (s));
1026 _attachments_clean();
1030 static int print_attach_list (string_list_t *lp, char op, const char *name) {
1032 printf("attachments %c%s %s/%s\n", op, name,
1033 ((ATTACH_MATCH *)lp->data)->major,
1034 ((ATTACH_MATCH *)lp->data)->minor);
1041 static int parse_attachments (BUFFER *buf, BUFFER *s,
1042 unsigned long data __attribute__ ((unused)),
1045 string_list_t **listp;
1047 mutt_extract_token(buf, s, 0);
1048 if (!buf->data || *buf->data == '\0') {
1049 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
1053 category = buf->data;
1059 printf("\nCurrent attachments settings:\n\n");
1060 print_attach_list(AttachAllow, '+', "A");
1061 print_attach_list(AttachExclude, '-', "A");
1062 print_attach_list(InlineAllow, '+', "I");
1063 print_attach_list(InlineExclude, '-', "I");
1064 set_option (OPTFORCEREDRAWINDEX);
1065 set_option (OPTFORCEREDRAWPAGER);
1066 mutt_any_key_to_continue (NULL);
1070 if (op != '+' && op != '-') {
1074 if (!m_strncasecmp(category, "attachment", strlen(category))) {
1076 listp = &AttachAllow;
1078 listp = &AttachExclude;
1080 else if (!m_strncasecmp(category, "inline", strlen(category))) {
1082 listp = &InlineAllow;
1084 listp = &InlineExclude;
1086 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
1090 return parse_attach_list(buf, s, listp, err);
1093 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
1095 string_list_t **listp;
1097 mutt_extract_token(buf, s, 0);
1098 if (!buf->data || *buf->data == '\0') {
1099 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
1105 if (op != '+' && op != '-') {
1109 if (!m_strncasecmp(p, "attachment", strlen(p))) {
1111 listp = &AttachAllow;
1113 listp = &AttachExclude;
1115 else if (!m_strncasecmp(p, "inline", strlen(p))) {
1117 listp = &InlineAllow;
1119 listp = &InlineExclude;
1122 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
1126 return parse_unattach_list(buf, s, listp, err);
1129 static int parse_unlists (BUFFER * buf, BUFFER * s,
1130 unsigned long data __attribute__ ((unused)),
1131 BUFFER * err __attribute__ ((unused)))
1134 mutt_extract_token (buf, s, 0);
1135 remove_from_rx_list (&SubscribedLists, buf->data);
1136 remove_from_rx_list (&MailLists, buf->data);
1138 if (m_strcmp(buf->data, "*") &&
1139 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1142 while (MoreArgs (s));
1147 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1151 mutt_extract_token (buf, s, 0);
1152 remove_from_rx_list (&UnMailLists, buf->data);
1153 remove_from_rx_list (&UnSubscribedLists, buf->data);
1155 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1157 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1160 while (MoreArgs (s));
1165 static int parse_unsubscribe (BUFFER * buf, BUFFER * s,
1166 unsigned long data __attribute__ ((unused)),
1167 BUFFER * err __attribute__ ((unused)))
1170 mutt_extract_token (buf, s, 0);
1171 remove_from_rx_list (&SubscribedLists, buf->data);
1173 if (m_strcmp(buf->data, "*") &&
1174 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1177 while (MoreArgs (s));
1182 static int parse_unalias (BUFFER * buf, BUFFER * s,
1183 unsigned long data __attribute__ ((unused)),
1184 BUFFER * err __attribute__ ((unused)))
1186 alias_t *tmp, *last = NULL;
1189 mutt_extract_token (buf, s, 0);
1191 if (m_strcmp("*", buf->data) == 0) {
1192 if (CurrentMenu == MENU_ALIAS) {
1193 for (tmp = Aliases; tmp; tmp = tmp->next)
1195 set_option (OPTFORCEREDRAWINDEX);
1198 alias_list_wipe(&Aliases);
1202 for (tmp = Aliases; tmp; tmp = tmp->next) {
1203 if (m_strcasecmp(buf->data, tmp->name) == 0) {
1204 if (CurrentMenu == MENU_ALIAS) {
1206 set_option (OPTFORCEREDRAWINDEX);
1211 last->next = tmp->next;
1213 Aliases = tmp->next;
1215 alias_list_wipe(&tmp);
1221 while (MoreArgs (s));
1225 static int parse_alias (BUFFER * buf, BUFFER * s,
1226 unsigned long data __attribute__ ((unused)),
1229 alias_t *tmp = Aliases;
1230 alias_t *last = NULL;
1233 if (!MoreArgs (s)) {
1234 m_strcpy(err->data, err->dsize, _("alias: no address"));
1238 mutt_extract_token (buf, s, 0);
1240 /* check to see if an alias with this name already exists */
1241 for (; tmp; tmp = tmp->next) {
1242 if (!m_strcasecmp(tmp->name, buf->data))
1248 /* create a new alias */
1250 tmp->name = m_strdup(buf->data);
1251 /* give the main addressbook code a chance */
1252 if (CurrentMenu == MENU_ALIAS)
1253 set_option (OPTMENUCALLER);
1256 /* override the previous value */
1257 address_list_wipe(&tmp->addr);
1258 if (CurrentMenu == MENU_ALIAS)
1259 set_option (OPTFORCEREDRAWINDEX);
1262 mutt_extract_token (buf, s,
1263 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1264 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1269 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1270 snprintf (err->data, err->dsize,
1271 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1280 parse_unmy_hdr (BUFFER * buf, BUFFER * s,
1281 unsigned long data __attribute__ ((unused)),
1282 BUFFER * err __attribute__ ((unused)))
1284 string_list_t *last = NULL;
1285 string_list_t *tmp = UserHeader;
1290 mutt_extract_token (buf, s, 0);
1291 if (m_strcmp("*", buf->data) == 0)
1292 string_list_wipe(&UserHeader);
1297 l = m_strlen(buf->data);
1298 if (buf->data[l - 1] == ':')
1302 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1303 && tmp->data[l] == ':') {
1306 last->next = tmp->next;
1308 UserHeader = tmp->next;
1311 string_list_wipe(&ptr);
1320 while (MoreArgs (s));
1324 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1331 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1332 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1333 m_strcpy(err->data, err->dsize, _("invalid header field"));
1336 keylen = p - buf->data + 1;
1339 for (tmp = UserHeader;; tmp = tmp->next) {
1340 /* see if there is already a field by this name */
1341 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1342 /* replace the old value */
1343 p_delete(&tmp->data);
1344 tmp->data = buf->data;
1351 tmp->next = string_item_new();
1355 tmp = string_item_new();
1358 tmp->data = buf->data;
1364 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1365 char* errbuf, ssize_t errlen) {
1368 if (m_strncmp("reverse-", s, 8) == 0) {
1370 flags = SORT_REVERSE;
1373 if (m_strncmp("last-", s, 5) == 0) {
1378 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1380 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1384 *((short*) dst->data) = i | flags;
1388 /* if additional data more == 1, we want to resolve synonyms */
1389 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
1391 char buf[LONG_STRING];
1392 struct option_t *ptr = p;
1394 if (DTYPE(ptr->type) == DT_SYN) {
1397 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1399 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_fromstr)
1402 mutt_option_value(ptr->option, buf, sizeof(buf));
1403 if (m_strlen(ptr->init) == 0 && buf && *buf)
1404 ptr->init = m_strdup(buf);
1407 static struct option_t* add_option (const char* name, const char* init,
1408 short type, short dodup) {
1409 struct option_t* option = p_new(struct option_t, 1);
1411 option->option = m_strdup(name);
1412 option->type = type;
1414 option->init = dodup ? m_strdup(init) : (char*) init;
1418 /* creates new option_t* of type DT_USER for $user_ var */
1419 static struct option_t* add_user_option (const char* name) {
1420 return (add_option (name, NULL, DT_USER, 1));
1423 /* free()'s option_t* */
1424 static void del_option (void* p) {
1425 struct option_t *ptr = (struct option_t*) p;
1426 char* s = (char*) ptr->data;
1427 p_delete(&ptr->option);
1429 p_delete(&ptr->init);
1433 static int init_expand (char** dst, struct option_t* src) {
1439 if (DTYPE(src->type) == DT_STR ||
1440 DTYPE(src->type) == DT_PATH) {
1441 /* only expand for string as it's the only place where
1442 * we want to expand vars right now */
1443 if (src->init && *src->init) {
1446 len = m_strlen(src->init) + 2;
1447 in.data = p_new(char, len + 1);
1448 snprintf (in.data, len, "\"%s\"", src->init);
1451 mutt_extract_token (&token, &in, 0);
1452 if (token.data && *token.data)
1453 *dst = m_strdup(token.data);
1455 *dst = m_strdup("");
1457 p_delete(&token.data);
1459 *dst = m_strdup("");
1461 /* for non-string: take value as is */
1462 *dst = m_strdup(src->init);
1466 /* if additional data more == 1, we want to resolve synonyms */
1467 static void mutt_restore_default (const char* name __attribute__ ((unused)),
1468 void* p, unsigned long more) {
1469 char errbuf[STRING];
1470 struct option_t* ptr = (struct option_t*) p;
1473 if (DTYPE (ptr->type) == DT_SYN) {
1476 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1480 if (FuncTable[DTYPE (ptr->type)].opt_fromstr) {
1481 init_expand (&init, ptr);
1482 if (!FuncTable[DTYPE (ptr->type)].opt_fromstr (ptr, init, errbuf,
1484 if (!option (OPTNOCURSES))
1486 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1487 "Please report this error: \"%s\"\n"),
1488 ptr->option, NONULL (init), errbuf);
1494 if (ptr->flags & R_INDEX)
1495 set_option (OPTFORCEREDRAWINDEX);
1496 if (ptr->flags & R_PAGER)
1497 set_option (OPTFORCEREDRAWPAGER);
1498 if (ptr->flags & R_RESORT_SUB)
1499 set_option (OPTSORTSUBTHREADS);
1500 if (ptr->flags & R_RESORT)
1501 set_option (OPTNEEDRESORT);
1502 if (ptr->flags & R_RESORT_INIT)
1503 set_option (OPTRESORTINIT);
1504 if (ptr->flags & R_TREE)
1505 set_option (OPTREDRAWTREE);
1508 /* check whether value for $dsn_return would be valid */
1509 static int check_dsn_return (const char* option __attribute__ ((unused)), unsigned long p,
1510 char* errbuf, ssize_t errlen) {
1511 char* val = (char*) p;
1512 if (val && *val && m_strncmp(val, "hdrs", 4) != 0 &&
1513 m_strncmp(val, "full", 4) != 0) {
1515 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1521 /* check whether value for $dsn_notify would be valid */
1523 check_dsn_notify (const char* option __attribute__ ((unused)),
1524 unsigned long val, char* errbuf, ssize_t errlen)
1526 const char *p = (const char*)val;
1529 const char *q = m_strchrnul(p, ',');
1532 if (!m_strncmp(p, "never", len) && !m_strncmp(p, "delay", len)
1533 && !m_strncmp(p, "failure", len) && !m_strncmp(p, "success", len))
1536 snprintf(errbuf, errlen, _("'%.*s' is invalid for $%s"),
1537 len, p, "dsn_notify");
1547 static int check_num (const char* option, unsigned long p,
1548 char* errbuf, ssize_t errlen) {
1551 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1557 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1558 char* errbuf, ssize_t errlen) {
1559 if (!check_num ("history", p, errbuf, errlen))
1561 mutt_init_history ();
1565 static int check_special (const char* name, unsigned long val,
1566 char* errbuf, ssize_t errlen) {
1569 for (i = 0; SpecialVars[i].name; i++) {
1570 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1571 return (SpecialVars[i].check (SpecialVars[i].name,
1572 val, errbuf, errlen));
1578 static const struct mapping_t* get_sortmap (struct option_t* option) {
1579 const struct mapping_t* map = NULL;
1581 switch (option->type & DT_SUBTYPE_MASK) {
1583 map = SortAliasMethods;
1585 case DT_SORT_BROWSER:
1586 map = SortBrowserMethods;
1589 map = SortKeyMethods;
1592 map = SortAuxMethods;
1601 #define CHECK_PAGER \
1602 if ((CurrentMenu == MENU_PAGER) && \
1603 (!option || (option->flags & R_RESORT))) \
1605 snprintf (err->data, err->dsize, \
1606 _("Not available in this menu.")); \
1610 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1613 int query, unset, inv, reset, r = 0;
1614 struct option_t* option = NULL;
1616 while (MoreArgs (s)) {
1617 /* reset state variables */
1619 unset = data & M_SET_UNSET;
1620 inv = data & M_SET_INV;
1621 reset = data & M_SET_RESET;
1623 if (*s->dptr == '?') {
1627 else if (m_strncmp("no", s->dptr, 2) == 0) {
1631 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1635 else if (*s->dptr == '&') {
1640 /* get the variable name */
1641 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1643 /* resolve synonyms */
1644 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1645 DTYPE (option->type == DT_SYN))
1647 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1648 syn_t* syn = syn_new();
1649 syn->f = m_strdup(CurRCFile);
1653 syn_list_push(&Synonyms, syn);
1657 /* see if we need to add $user_ var */
1658 if (!option && m_strncmp("user_", tmp->data, 5) == 0) {
1659 /* there's no option named like this yet so only add one
1660 * if the action isn't any of: reset, unset, query */
1661 if (!(reset || unset || query || *s->dptr != '=')) {
1662 option = add_user_option (tmp->data);
1663 hash_insert (ConfigOptions, option->option, option, 0);
1667 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1668 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1671 s->dptr = vskipspaces(s->dptr);
1674 if (query || unset || inv) {
1675 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1679 if (s && *s->dptr == '=') {
1680 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1684 if (!m_strcmp("all", tmp->data)) {
1685 if (CurrentMenu == MENU_PAGER) {
1686 snprintf (err->data, err->dsize, _("Not available in this menu."));
1689 hash_map (ConfigOptions, mutt_restore_default, 1);
1690 set_option (OPTFORCEREDRAWINDEX);
1691 set_option (OPTFORCEREDRAWPAGER);
1692 set_option (OPTSORTSUBTHREADS);
1693 set_option (OPTNEEDRESORT);
1694 set_option (OPTRESORTINIT);
1695 set_option (OPTREDRAWTREE);
1698 else if (!FuncTable[DTYPE (option->type)].opt_fromstr) {
1699 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1704 mutt_restore_default (NULL, option, 1);
1707 else if (DTYPE (option->type) == DT_BOOL) {
1708 /* XXX this currently ignores the function table
1709 * as we don't get invert and stuff into it */
1710 if (s && *s->dptr == '=') {
1711 if (unset || inv || query) {
1712 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1717 mutt_extract_token (tmp, s, 0);
1718 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1720 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1723 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1729 bool_to_string (err->data, err->dsize, option);
1735 unset_option (option->data);
1737 toggle_option (option->data);
1739 set_option (option->data);
1741 else if (DTYPE (option->type) == DT_STR ||
1742 DTYPE (option->type) == DT_PATH ||
1743 DTYPE (option->type) == DT_ADDR ||
1744 DTYPE (option->type) == DT_MAGIC ||
1745 DTYPE (option->type) == DT_NUM ||
1746 DTYPE (option->type) == DT_SORT ||
1747 DTYPE (option->type) == DT_RX ||
1748 DTYPE (option->type) == DT_USER ||
1749 DTYPE (option->type) == DT_SYS) {
1751 /* XXX maybe we need to get unset into handlers? */
1752 if (DTYPE (option->type) == DT_STR ||
1753 DTYPE (option->type) == DT_PATH ||
1754 DTYPE (option->type) == DT_ADDR ||
1755 DTYPE (option->type) == DT_USER ||
1756 DTYPE (option->type) == DT_SYS) {
1759 if (!FuncTable[DTYPE (option->type)].opt_fromstr) {
1760 snprintf (err->data, err->dsize, _("$%s is read-only"),
1764 } else if (DTYPE (option->type) == DT_ADDR)
1765 address_list_wipe((address_t **) option->data);
1766 else if (DTYPE (option->type) == DT_USER)
1767 /* to unset $user_ means remove */
1768 hash_delete (ConfigOptions, option->option,
1769 option, del_option);
1771 p_delete((void **)(void *)&option->data);
1776 if (query || *s->dptr != '=') {
1777 FuncTable[DTYPE (option->type)].opt_tostr
1778 (err->data, err->dsize, option);
1782 /* the $madmutt_ variables are read-only */
1783 if (!FuncTable[DTYPE (option->type)].opt_fromstr) {
1784 snprintf (err->data, err->dsize, _("$%s is read-only"),
1791 mutt_extract_token (tmp, s, 0);
1792 if (!FuncTable[DTYPE (option->type)].opt_fromstr
1793 (option, tmp->data, err->data, err->dsize))
1797 else if (DTYPE (option->type) == DT_QUAD) {
1800 quad_to_string (err->data, err->dsize, option);
1804 if (*s->dptr == '=') {
1807 mutt_extract_token (tmp, s, 0);
1808 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1809 set_quadoption (option->data, M_YES);
1810 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1811 set_quadoption (option->data, M_NO);
1812 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1813 set_quadoption (option->data, M_ASKYES);
1814 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1815 set_quadoption (option->data, M_ASKNO);
1817 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1818 tmp->data, option->option);
1825 toggle_quadoption (option->data);
1827 set_quadoption (option->data, M_NO);
1829 set_quadoption (option->data, M_YES);
1833 snprintf (err->data, err->dsize, _("%s: unknown type"),
1839 if (option->flags & R_INDEX)
1840 set_option (OPTFORCEREDRAWINDEX);
1841 if (option->flags & R_PAGER)
1842 set_option (OPTFORCEREDRAWPAGER);
1843 if (option->flags & R_RESORT_SUB)
1844 set_option (OPTSORTSUBTHREADS);
1845 if (option->flags & R_RESORT)
1846 set_option (OPTNEEDRESORT);
1847 if (option->flags & R_RESORT_INIT)
1848 set_option (OPTRESORTINIT);
1849 if (option->flags & R_TREE)
1850 set_option (OPTREDRAWTREE);
1857 /* reads the specified initialization file. returns -1 if errors were found
1858 so that we can pause to let the user know... */
1859 static int source_rc (const char *rcfile, BUFFER * err)
1862 int line = 0, rc = 0, conv = 0;
1864 char *linebuf = NULL;
1865 char *currentline = NULL;
1869 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1870 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1875 while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
1876 conv = ConfigCharset && (*ConfigCharset) && Charset;
1878 currentline = m_strdup(linebuf);
1881 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1884 currentline = linebuf;
1889 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1890 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1891 if (--rc < -MAXERRS) {
1893 p_delete(¤tline);
1902 p_delete(¤tline);
1904 p_delete(&token.data);
1908 mutt_wait_filter (pid);
1910 /* the muttrc source keyword */
1911 snprintf (err->data, err->dsize,
1912 rc >= -MAXERRS ? _("source: errors in %s")
1913 : _("source: reading aborted due too many errors in %s"),
1922 static int parse_source (BUFFER * tmp, BUFFER * s,
1923 unsigned long data __attribute__ ((unused)),
1926 char path[_POSIX_PATH_MAX];
1930 if (mutt_extract_token (tmp, s, 0) != 0) {
1931 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1935 m_strcpy(path, sizeof(path), tmp->data);
1936 mutt_expand_path (path, sizeof(path));
1938 rc += source_rc (path, err);
1940 while (MoreArgs (s));
1942 return ((rc < 0) ? -1 : 0);
1945 /* line command to execute
1947 token scratch buffer to be used by parser. caller should free
1948 token->data when finished. the reason for this variable is
1949 to avoid having to allocate and deallocate a lot of memory
1950 if we are parsing many lines. the caller can pass in the
1951 memory to use, which avoids having to create new space for
1952 every call to this function.
1954 err where to write error messages */
1955 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1961 expn.data = expn.dptr = line;
1962 expn.dsize = m_strlen(line);
1966 expn.dptr = vskipspaces(expn.dptr);
1967 while (*expn.dptr) {
1968 if (*expn.dptr == '#')
1969 break; /* rest of line is a comment */
1970 if (*expn.dptr == ';') {
1974 mutt_extract_token (token, &expn, 0);
1975 for (i = 0; Commands[i].name; i++) {
1976 if (!m_strcmp(token->data, Commands[i].name)) {
1977 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1982 if (!Commands[i].name) {
1983 snprintf (err->data, err->dsize, _("%s: unknown command"),
1984 NONULL (token->data));
1991 p_delete(&expn.data);
1996 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
1997 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
1998 /* initial string that starts completion. No telling how much crap
1999 * the user has typed so far. Allocate LONG_STRING just to be sure! */
2000 char User_typed[LONG_STRING] = { 0 };
2002 int Num_matched = 0; /* Number of matches for completion */
2003 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
2004 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
2006 /* helper function for completion. Changes the dest buffer if
2007 necessary/possible to aid completion.
2008 dest == completion result gets here.
2009 src == candidate for completion.
2010 try == user entered data for completion.
2011 len == length of dest buffer.
2013 static void candidate (char *dest, char *try, const char *src, int len)
2017 if (strstr (src, try) == src) {
2018 Matches[Num_matched++] = src;
2020 m_strcpy(dest, len, src);
2022 for (l = 0; src[l] && src[l] == dest[l]; l++);
2028 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
2032 int spaces; /* keep track of the number of leading spaces on the line */
2034 buffer = vskipspaces(buffer);
2035 spaces = buffer - pt;
2037 pt = buffer + pos - spaces;
2038 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2041 if (pt == buffer) { /* complete cmd */
2042 /* first TAB. Collect all the matches */
2045 m_strcpy(User_typed, sizeof(User_typed), pt);
2046 p_clear(Matches, countof(Matches));
2047 p_clear(Completed, countof(Completed));
2048 for (num = 0; Commands[num].name; num++)
2049 candidate (Completed, User_typed, Commands[num].name,
2051 Matches[Num_matched++] = User_typed;
2053 /* All matches are stored. Longest non-ambiguous string is ""
2054 * i.e. dont change 'buffer'. Fake successful return this time */
2055 if (User_typed[0] == 0)
2059 if (Completed[0] == 0 && User_typed[0])
2062 /* Num_matched will _always_ be atleast 1 since the initial
2063 * user-typed string is always stored */
2064 if (numtabs == 1 && Num_matched == 2)
2065 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2066 else if (numtabs > 1 && Num_matched > 2)
2067 /* cycle thru all the matches */
2068 snprintf (Completed, sizeof(Completed), "%s",
2069 Matches[(numtabs - 2) % Num_matched]);
2071 /* return the completed command */
2072 m_strcpy(buffer, len - spaces, Completed);
2074 else if (!m_strncmp(buffer, "set", 3)
2075 || !m_strncmp(buffer, "unset", 5)
2076 || !m_strncmp(buffer, "reset", 5)
2077 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
2078 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
2081 /* loop through all the possible prefixes (no, inv, ...) */
2082 if (!m_strncmp(buffer, "set", 3)) {
2083 for (num = 0; prefixes[num]; num++) {
2084 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
2085 pt += m_strlen(prefixes[num]);
2091 /* first TAB. Collect all the matches */
2094 m_strcpy(User_typed, sizeof(User_typed), pt);
2095 p_clear(Matches, countof(Matches));
2096 p_clear(Completed, countof(Completed));
2097 for (num = 0; MuttVars[num].option; num++)
2098 candidate(Completed, User_typed, MuttVars[num].option,
2100 Matches[Num_matched++] = User_typed;
2102 /* All matches are stored. Longest non-ambiguous string is ""
2103 * i.e. dont change 'buffer'. Fake successful return this time */
2104 if (User_typed[0] == 0)
2108 if (Completed[0] == 0 && User_typed[0])
2111 /* Num_matched will _always_ be atleast 1 since the initial
2112 * user-typed string is always stored */
2113 if (numtabs == 1 && Num_matched == 2)
2114 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2115 else if (numtabs > 1 && Num_matched > 2)
2116 /* cycle thru all the matches */
2117 snprintf (Completed, sizeof(Completed), "%s",
2118 Matches[(numtabs - 2) % Num_matched]);
2120 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2122 else if (!m_strncmp(buffer, "exec", 4)) {
2123 struct binding_t *menu = km_get_table (CurrentMenu);
2125 if (!menu && CurrentMenu != MENU_PAGER)
2129 /* first TAB. Collect all the matches */
2132 m_strcpy(User_typed, sizeof(User_typed), pt);
2133 p_clear(Matches, countof(Matches));
2134 p_clear(Completed, countof(Completed));
2135 for (num = 0; menu[num].name; num++)
2136 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
2137 /* try the generic menu */
2138 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2140 for (num = 0; menu[num].name; num++)
2141 candidate (Completed, User_typed, menu[num].name,
2144 Matches[Num_matched++] = User_typed;
2146 /* All matches are stored. Longest non-ambiguous string is ""
2147 * i.e. dont change 'buffer'. Fake successful return this time */
2148 if (User_typed[0] == 0)
2152 if (Completed[0] == 0 && User_typed[0])
2155 /* Num_matched will _always_ be atleast 1 since the initial
2156 * user-typed string is always stored */
2157 if (numtabs == 1 && Num_matched == 2)
2158 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2159 else if (numtabs > 1 && Num_matched > 2)
2160 /* cycle thru all the matches */
2161 snprintf (Completed, sizeof(Completed), "%s",
2162 Matches[(numtabs - 2) % Num_matched]);
2164 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2172 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
2174 char var[STRING], *pt = buffer;
2176 struct option_t* option = NULL;
2181 buffer = vskipspaces(buffer);
2182 spaces = buffer - pt;
2184 pt = buffer + pos - spaces;
2185 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2187 pt++; /* move past the space */
2188 if (*pt == '=') /* abort if no var before the '=' */
2191 if (m_strncmp(buffer, "set", 3) == 0) {
2192 m_strcpy(var, sizeof(var), pt);
2193 /* ignore the trailing '=' when comparing */
2194 var[m_strlen(var) - 1] = 0;
2195 if (!(option = hash_find (ConfigOptions, var)))
2196 return 0; /* no such variable. */
2198 char tmp[LONG_STRING], tmp2[LONG_STRING];
2200 ssize_t dlen = buffer + len - pt - spaces;
2201 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2205 if ((DTYPE (option->type) == DT_STR) ||
2206 (DTYPE (option->type) == DT_PATH) ||
2207 (DTYPE (option->type) == DT_RX)) {
2208 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
2209 if (DTYPE (option->type) == DT_PATH)
2210 mutt_pretty_mailbox (tmp);
2212 else if (DTYPE (option->type) == DT_ADDR) {
2213 rfc822_addrcat(tmp, sizeof(tmp), *((address_t **) option->data), 0);
2215 else if (DTYPE (option->type) == DT_QUAD)
2216 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
2217 else if (DTYPE (option->type) == DT_NUM)
2218 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
2219 else if (DTYPE (option->type) == DT_SORT) {
2220 const struct mapping_t *map;
2223 switch (option->type & DT_SUBTYPE_MASK) {
2225 map = SortAliasMethods;
2227 case DT_SORT_BROWSER:
2228 map = SortBrowserMethods;
2231 map = SortKeyMethods;
2237 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2238 snprintf(tmp, sizeof(tmp), "%s%s%s",
2239 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2240 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2242 else if (DTYPE (option->type) == DT_MAGIC) {
2244 switch (DefaultMagic) {
2260 m_strcpy(tmp, sizeof(tmp), p);
2262 else if (DTYPE (option->type) == DT_BOOL)
2263 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
2267 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
2268 if (*s == '\\' || *s == '"')
2274 m_strcpy(tmp, sizeof(tmp), pt);
2275 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2283 /* Implement the -Q command line flag */
2284 int mutt_query_variables (string_list_t * queries)
2288 char errbuff[STRING];
2289 char command[STRING];
2297 err.dsize = sizeof(errbuff);
2299 for (p = queries; p; p = p->next) {
2300 snprintf (command, sizeof(command), "set ?%s\n", p->data);
2301 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2302 fprintf (stderr, "%s\n", err.data);
2303 p_delete(&token.data);
2306 printf ("%s\n", err.data);
2309 p_delete(&token.data);
2313 static int mutt_execute_commands (string_list_t * p)
2316 char errstr[STRING];
2320 err.dsize = sizeof(errstr);
2322 for (; p; p = p->next) {
2323 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2324 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2325 p_delete(&token.data);
2329 p_delete(&token.data);
2333 void mutt_init (int skip_sys_rc, string_list_t * commands)
2336 struct utsname utsname;
2338 char buffer[STRING], error[STRING];
2339 int default_rc = 0, need_pause = 0;
2345 err.dsize = sizeof(error);
2347 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2348 * to have some room for $user_ vars */
2349 ConfigOptions = hash_create (sizeof(MuttVars) * 3);
2350 for (i = 0; MuttVars[i].option; i++) {
2351 if (DTYPE (MuttVars[i].type) != DT_SYS)
2352 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2354 hash_insert (ConfigOptions, MuttVars[i].option,
2355 add_option (MuttVars[i].option, MuttVars[i].init,
2360 * XXX - use something even more difficult to predict?
2362 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2363 "\033]9;%ld\a", (long) time (NULL));
2365 /* on one of the systems I use, getcwd() does not return the same prefix
2366 as is listed in the passwd file */
2367 if ((p = getenv ("HOME")))
2368 Homedir = m_strdup(p);
2370 /* Get some information about the user */
2371 if ((pw = getpwuid (getuid ()))) {
2374 Username = m_strdup(pw->pw_name);
2376 Homedir = m_strdup(pw->pw_dir);
2378 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, GecosMask.rx);
2379 Realname = m_strdup(rnbuf);
2380 Shell = m_strdup(pw->pw_shell);
2386 fputs (_("unable to determine home directory"), stderr);
2389 if ((p = getenv ("USER")))
2390 Username = m_strdup(p);
2393 fputs (_("unable to determine username"), stderr);
2396 Shell = m_strdup((p = getenv ("SHELL")) ? p : "/bin/sh");
2399 /* And about the host... */
2401 /* some systems report the FQDN instead of just the hostname */
2402 if ((p = strchr (utsname.nodename, '.'))) {
2403 Hostname = p_dupstr(utsname.nodename, p - utsname.nodename);
2405 m_strcpy(buffer, sizeof(buffer), p); /* save the domain for below */
2408 Hostname = m_strdup(utsname.nodename);
2410 if (!p && getdnsdomainname(buffer, sizeof(buffer)) == -1)
2411 Fqdn = m_strdup("@");
2413 if (*buffer != '@') {
2414 Fqdn = p_new(char, m_strlen(buffer) + m_strlen(Hostname) + 2);
2415 sprintf (Fqdn, "%s.%s", NONULL(Hostname), buffer);
2418 Fqdn = m_strdup(NONULL (Hostname));
2425 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2427 fgets (buffer, sizeof(buffer), f);
2428 p = vskipspaces(buffer);
2430 while (*q && !isspace(*q))
2433 NewsServer = m_strdup(p);
2437 if ((p = getenv ("NNTPSERVER")))
2438 NewsServer = m_strdup(p);
2441 if ((p = getenv ("MAIL")))
2442 Spoolfile = m_strdup(p);
2443 else if ((p = getenv ("MAILDIR")))
2444 Spoolfile = m_strdup(p);
2447 mutt_concat_path(buffer, sizeof(buffer), NONULL(Homedir), MAILPATH);
2449 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(Username));
2451 Spoolfile = m_strdup(buffer);
2454 if ((p = getenv ("MAILCAPS")))
2455 MailcapPath = m_strdup(p);
2457 /* Default search path from RFC1524 */
2459 m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2460 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2463 Tempdir = m_strdup((p = getenv ("TMPDIR")) ? p : "/tmp");
2465 p = getenv ("VISUAL");
2467 p = getenv ("EDITOR");
2471 Editor = m_strdup(p);
2473 if ((p = getenv ("REPLYTO")) != NULL) {
2476 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2479 buf.data = buf.dptr = buffer;
2480 buf.dsize = m_strlen(buffer);
2483 parse_my_hdr (&token, &buf, 0, &err);
2484 p_delete(&token.data);
2487 if ((p = getenv ("EMAIL")) != NULL)
2488 From = rfc822_parse_adrlist (NULL, p);
2490 charset_initialize();
2492 /* Set standard defaults */
2493 hash_map (ConfigOptions, mutt_set_default, 0);
2494 hash_map (ConfigOptions, mutt_restore_default, 0);
2496 CurrentMenu = MENU_MAIN;
2499 /* Unset suspend by default if we're the session leader */
2500 if (getsid (0) == getpid ())
2501 unset_option (OPTSUSPEND);
2504 mutt_init_history ();
2507 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL (Homedir));
2508 if (access (buffer, F_OK) == -1)
2509 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
2513 Muttrc = m_strdup(buffer);
2516 m_strcpy(buffer, sizeof(buffer), Muttrc);
2518 mutt_expand_path (buffer, sizeof(buffer));
2519 Muttrc = m_strdup(buffer);
2521 p_delete(&AliasFile);
2522 AliasFile = m_strdup(NONULL (Muttrc));
2524 /* Process the global rc file if it exists and the user hasn't explicity
2525 requested not to via "-n". */
2527 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
2529 if (access (buffer, F_OK) == -1)
2530 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
2531 if (access (buffer, F_OK) == -1)
2532 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
2534 if (access (buffer, F_OK) == -1)
2535 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
2536 if (access (buffer, F_OK) != -1) {
2537 if (source_rc (buffer, &err) != 0) {
2538 fputs (err.data, stderr);
2539 fputc ('\n', stderr);
2545 /* Read the user's initialization file. */
2546 if (access (Muttrc, F_OK) != -1) {
2547 if (!option (OPTNOCURSES))
2549 if (source_rc (Muttrc, &err) != 0) {
2550 fputs (err.data, stderr);
2551 fputc ('\n', stderr);
2555 else if (!default_rc) {
2556 /* file specified by -F does not exist */
2557 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2558 mutt_endwin (buffer);
2562 if (mutt_execute_commands (commands) != 0)
2565 /* warn about synonym variables */
2569 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2571 for (syn = Synonyms; syn; syn = syn->next) {
2572 fprintf(stderr, "$%s ($%s should be used) (%s:%d)\n",
2573 syn->o ? NONULL(syn->o->option) : "",
2574 syn->n ? NONULL(syn->n->option) : "",
2575 NONULL(syn->f), syn->l);
2577 fprintf (stderr, _("Warning: synonym variables are scheduled"
2578 " for removal.\n"));
2579 syn_list_wipe(&Synonyms);
2583 if (need_pause && !option (OPTNOCURSES)) {
2584 if (mutt_any_key_to_continue (NULL) == -1)
2589 int mutt_get_hook_type (const char *name)
2591 struct command_t *c;
2593 for (c = Commands; c->name; c++)
2594 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2599 /* dump out the value of all the variables we have */
2600 int mutt_dump_variables (int full) {
2603 /* get all non-synonyms into list... */
2604 for (i = 0; MuttVars[i].option; i++) {
2605 struct option_t *option = MuttVars + i;
2606 char buf[LONG_STRING];
2608 if (DTYPE(option->type) == DT_SYN)
2612 mutt_option_value(option->option, buf, sizeof(buf));
2613 if (!m_strcmp(buf, option->init))
2618 FuncTable[DTYPE(option->type)].opt_tostr(buf, sizeof(buf), option);
2619 printf ("%s\n", buf);
2622 printf ("\n# vi""m:set ft=muttrc:\n");