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_to_string) (char* dst, ssize_t dstlen, struct option_t* option);
143 int (*opt_from_string) (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 (!*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];
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 (!val || !*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 || !val || !*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_write_address (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_to_string (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 (SPAM_LIST ** list, const char *pat,
641 const char *templ, BUFFER * err)
643 SPAM_LIST *t = NULL, *last = NULL;
648 if (!pat || !*pat || !templ)
651 if (!(rx = rx_compile (pat, REG_ICASE))) {
652 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
656 /* check to make sure the item is not already on this list */
657 for (last = *list; last; last = last->next) {
658 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
659 /* Already on the list. Formerly we just skipped this case, but
660 * now we're supporting removals, which means we're supporting
661 * re-adds conceptually. So we probably want this to imply a
662 * removal, then do an add. We can achieve the removal by freeing
663 * the template, and leaving t pointed at the current item.
666 p_delete(&t->template);
673 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
674 * update. Otherwise we want to make a new one to link at the list's end.
677 t = mutt_new_spam_list ();
685 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
686 t->template = m_strdup(templ);
688 /* Find highest match number in template string */
690 for (p = templ; *p;) {
695 while (*p && isdigit ((int) *p))
701 t->nmatch++; /* match 0 is always the whole expr */
706 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
708 SPAM_LIST *spam, *prev;
711 /* Being first is a special case. */
715 if (spam->rx && !m_strcmp(spam->rx->pattern, pat)) {
717 rx_delete(&spam->rx);
718 p_delete(&spam->template);
724 for (spam = prev->next; spam;) {
725 if (!m_strcmp(spam->rx->pattern, pat)) {
726 prev->next = spam->next;
727 rx_delete(&spam->rx);
728 p_delete(&spam->template);
741 static void remove_from_list (string_list_t ** l, const char *str)
743 string_list_t *p, *last = NULL;
745 if (m_strcmp("*", str) == 0)
746 string_list_wipe(l); /* ``unCMD *'' means delete all current entries */
751 if (ascii_strcasecmp (str, p->data) == 0) {
754 last->next = p->next;
767 static int remove_from_rx_list(rx_t **l, const char *str)
769 if (m_strcmp("*", str) == 0) {
774 l = rx_lookup(l, str);
776 rx_t *r = rx_list_pop(l);
784 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
788 unsigned long res = 0;
790 struct option_t* option = NULL;
793 mutt_extract_token (tmp, s, 0);
795 /* is the item defined as a variable or a function? */
796 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL)
799 for (i = 0; !res && i < MENU_MAX; i++) {
800 struct binding_t *b = km_get_table (Menus[i].value);
805 for (j = 0; b[j].name; j++)
806 if (!ascii_strncasecmp (tmp->data, b[j].name, m_strlen(tmp->data))
807 && (m_strlen(b[j].name) == m_strlen(tmp->data))) {
813 /* check for feature_* */
814 if (!res && ascii_strncasecmp (tmp->data, "feature_", 8) == 0 &&
815 (j = m_strlen(tmp->data)) > 8) {
817 while (Features[i]) {
818 if (m_strlen(Features[i]) == j-8 &&
819 ascii_strncasecmp (Features[i], tmp->data+8, j-8) == 0) {
829 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
831 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
835 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
838 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
839 mutt_error ("Error: %s", err->data);
840 p_delete(&token.data);
843 p_delete(&token.data);
848 static int parse_unignore (BUFFER * buf, BUFFER * s,
849 unsigned long data __attribute__ ((unused)),
850 BUFFER * err __attribute__ ((unused)))
853 mutt_extract_token (buf, s, 0);
855 /* don't add "*" to the unignore list */
856 if (strcmp (buf->data, "*"))
857 add_to_list (&UnIgnore, buf->data);
859 remove_from_list (&Ignore, buf->data);
861 while (MoreArgs (s));
866 static int parse_ignore (BUFFER * buf, BUFFER * s,
867 unsigned long data __attribute__ ((unused)),
868 BUFFER * err __attribute__ ((unused)))
871 mutt_extract_token (buf, s, 0);
872 remove_from_list (&UnIgnore, buf->data);
873 add_to_list (&Ignore, buf->data);
875 while (MoreArgs (s));
880 static int parse_list (BUFFER * buf, BUFFER * s,
881 unsigned long data __attribute__ ((unused)),
882 BUFFER * err __attribute__ ((unused)))
885 mutt_extract_token (buf, s, 0);
886 add_to_list ((string_list_t **) data, buf->data);
888 while (MoreArgs (s));
893 static void _alternates_clean (void)
897 if (Context && Context->msgcount) {
898 for (i = 0; i < Context->msgcount; i++)
899 Context->hdrs[i]->recip_valid = 0;
903 static int parse_alternates (BUFFER * buf, BUFFER * s,
904 unsigned long data __attribute__ ((unused)),
905 BUFFER * err __attribute__ ((unused)))
907 _alternates_clean ();
909 mutt_extract_token (buf, s, 0);
910 remove_from_rx_list (&UnAlternates, buf->data);
912 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
915 while (MoreArgs (s));
920 static int parse_unalternates (BUFFER * buf, BUFFER * s,
921 unsigned long data __attribute__ ((unused)),
922 BUFFER * err __attribute__ ((unused)))
924 _alternates_clean ();
926 mutt_extract_token (buf, s, 0);
927 remove_from_rx_list (&Alternates, buf->data);
929 if (m_strcmp(buf->data, "*") &&
930 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
934 while (MoreArgs (s));
939 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
946 /* Insist on at least one parameter */
949 m_strcpy(err->data, err->dsize, _("spam: no matching pattern"));
951 m_strcpy(err->data, err->dsize, _("nospam: no matching pattern"));
955 /* Extract the first token, a regexp */
956 mutt_extract_token (buf, s, 0);
958 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
959 if (data == M_SPAM) {
960 /* If there's a second parameter, it's a template for the spam tag. */
962 mutt_extract_token (&templ, s, 0);
964 /* Add to the spam list. */
965 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
966 p_delete(&templ.data);
969 p_delete(&templ.data);
972 /* If not, try to remove from the nospam list. */
974 remove_from_rx_list (&NoSpamList, buf->data);
980 /* M_NOSPAM is for nospam commands. */
981 else if (data == M_NOSPAM) {
982 /* nospam only ever has one parameter. */
984 /* "*" is a special case. */
985 if (!m_strcmp(buf->data, "*")) {
986 mutt_free_spam_list (&SpamList);
987 rx_list_wipe(&NoSpamList);
991 /* If it's on the spam list, just remove it. */
992 if (remove_from_spam_list (&SpamList, buf->data) != 0)
995 /* Otherwise, add it to the nospam list. */
996 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
1002 /* This should not happen. */
1003 m_strcpy(err->data, err->dsize, "This is no good at all.");
1007 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
1008 BUFFER * err __attribute__ ((unused)))
1011 mutt_extract_token (buf, s, 0);
1013 * Check for deletion of entire list
1015 if (m_strcmp(buf->data, "*") == 0) {
1016 string_list_wipe((string_list_t **) data);
1019 remove_from_list ((string_list_t **) data, buf->data);
1021 while (MoreArgs (s));
1026 static int parse_lists (BUFFER * buf, BUFFER * s,
1027 unsigned long data __attribute__ ((unused)),
1031 mutt_extract_token (buf, s, 0);
1032 remove_from_rx_list (&UnMailLists, buf->data);
1034 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1037 while (MoreArgs (s));
1042 /* always wise to do what someone else did before */
1043 static void _attachments_clean (void) {
1045 if (Context && Context->msgcount) {
1046 for (i = 0; i < Context->msgcount; i++)
1047 Context->hdrs[i]->attach_valid = 0;
1051 static int parse_attach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
1052 BUFFER *err __attribute__ ((unused))) {
1054 string_list_t *listp, *lastp;
1059 /* Find the last item in the list that data points to. */
1061 for (listp = *ldata; listp; listp = listp->next) {
1062 a = (ATTACH_MATCH *)listp->data;
1067 mutt_extract_token (buf, s, 0);
1069 if (!buf->data || *buf->data == '\0')
1072 a = p_new(ATTACH_MATCH, 1);
1074 /* some cheap hacks that I expect to remove */
1075 if (!m_strcasecmp(buf->data, "any"))
1076 a->major = m_strdup("*/.*");
1077 else if (!m_strcasecmp(buf->data, "none"))
1078 a->major = m_strdup("cheap_hack/this_should_never_match");
1080 a->major = m_strdup(buf->data);
1082 if ((p = strchr(a->major, '/'))) {
1087 a->minor = "unknown";
1090 len = m_strlen(a->minor);
1091 tmpminor = p_new(char, len + 3);
1092 strcpy(&tmpminor[1], a->minor); /* __STRCPY_CHECKED__ */
1094 tmpminor[len+1] = '$';
1095 tmpminor[len+2] = '\0';
1097 a->major_int = mutt_check_mime_type(a->major);
1098 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
1100 p_delete(&tmpminor);
1102 listp = p_new(string_list_t, 1);
1103 listp->data = (char *)a;
1106 lastp->next = listp;
1112 while (MoreArgs (s));
1114 _attachments_clean();
1118 static int parse_unattach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
1119 BUFFER *err __attribute__ ((unused))) {
1121 string_list_t *lp, *lastp, *newlp;
1127 mutt_extract_token (buf, s, 0);
1129 if (!m_strcasecmp(buf->data, "any"))
1130 tmp = m_strdup("*/.*");
1131 else if (!m_strcasecmp(buf->data, "none"))
1132 tmp = m_strdup("cheap_hack/this_should_never_match");
1134 tmp = m_strdup(buf->data);
1136 if ((minor = strchr(tmp, '/'))) {
1140 minor = m_strdup("unknown");
1142 major = mutt_check_mime_type(tmp);
1144 /* We must do our own walk here because remove_from_list() will only
1145 * remove the string_list_t->data, not anything pointed to by the string_list_t->data. */
1147 for(lp = *ldata; lp; ) {
1148 a = (ATTACH_MATCH *)lp->data;
1149 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
1150 regfree(&a->minor_rx);
1151 p_delete(&a->major);
1153 /* Relink backward */
1155 lastp->next = lp->next;
1160 p_delete(&lp->data); /* same as a */
1170 while (MoreArgs (s));
1173 _attachments_clean();
1177 static int print_attach_list (string_list_t *lp, char op, const char *name) {
1179 printf("attachments %c%s %s/%s\n", op, name,
1180 ((ATTACH_MATCH *)lp->data)->major,
1181 ((ATTACH_MATCH *)lp->data)->minor);
1188 static int parse_attachments (BUFFER *buf, BUFFER *s,
1189 unsigned long data __attribute__ ((unused)),
1192 string_list_t **listp;
1194 mutt_extract_token(buf, s, 0);
1195 if (!buf->data || *buf->data == '\0') {
1196 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
1200 category = buf->data;
1206 printf("\nCurrent attachments settings:\n\n");
1207 print_attach_list(AttachAllow, '+', "A");
1208 print_attach_list(AttachExclude, '-', "A");
1209 print_attach_list(InlineAllow, '+', "I");
1210 print_attach_list(InlineExclude, '-', "I");
1211 set_option (OPTFORCEREDRAWINDEX);
1212 set_option (OPTFORCEREDRAWPAGER);
1213 mutt_any_key_to_continue (NULL);
1217 if (op != '+' && op != '-') {
1221 if (!m_strncasecmp(category, "attachment", strlen(category))) {
1223 listp = &AttachAllow;
1225 listp = &AttachExclude;
1227 else if (!m_strncasecmp(category, "inline", strlen(category))) {
1229 listp = &InlineAllow;
1231 listp = &InlineExclude;
1233 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
1237 return parse_attach_list(buf, s, listp, err);
1240 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
1242 string_list_t **listp;
1244 mutt_extract_token(buf, s, 0);
1245 if (!buf->data || *buf->data == '\0') {
1246 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
1252 if (op != '+' && op != '-') {
1256 if (!m_strncasecmp(p, "attachment", strlen(p))) {
1258 listp = &AttachAllow;
1260 listp = &AttachExclude;
1262 else if (!m_strncasecmp(p, "inline", strlen(p))) {
1264 listp = &InlineAllow;
1266 listp = &InlineExclude;
1269 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
1273 return parse_unattach_list(buf, s, listp, err);
1276 static int parse_unlists (BUFFER * buf, BUFFER * s,
1277 unsigned long data __attribute__ ((unused)),
1278 BUFFER * err __attribute__ ((unused)))
1281 mutt_extract_token (buf, s, 0);
1282 remove_from_rx_list (&SubscribedLists, buf->data);
1283 remove_from_rx_list (&MailLists, buf->data);
1285 if (m_strcmp(buf->data, "*") &&
1286 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1289 while (MoreArgs (s));
1294 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1298 mutt_extract_token (buf, s, 0);
1299 remove_from_rx_list (&UnMailLists, buf->data);
1300 remove_from_rx_list (&UnSubscribedLists, buf->data);
1302 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1304 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1307 while (MoreArgs (s));
1312 static int parse_unsubscribe (BUFFER * buf, BUFFER * s,
1313 unsigned long data __attribute__ ((unused)),
1314 BUFFER * err __attribute__ ((unused)))
1317 mutt_extract_token (buf, s, 0);
1318 remove_from_rx_list (&SubscribedLists, buf->data);
1320 if (m_strcmp(buf->data, "*") &&
1321 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1324 while (MoreArgs (s));
1329 static int parse_unalias (BUFFER * buf, BUFFER * s,
1330 unsigned long data __attribute__ ((unused)),
1331 BUFFER * err __attribute__ ((unused)))
1333 alias_t *tmp, *last = NULL;
1336 mutt_extract_token (buf, s, 0);
1338 if (m_strcmp("*", buf->data) == 0) {
1339 if (CurrentMenu == MENU_ALIAS) {
1340 for (tmp = Aliases; tmp; tmp = tmp->next)
1342 set_option (OPTFORCEREDRAWINDEX);
1345 alias_list_wipe(&Aliases);
1349 for (tmp = Aliases; tmp; tmp = tmp->next) {
1350 if (m_strcasecmp(buf->data, tmp->name) == 0) {
1351 if (CurrentMenu == MENU_ALIAS) {
1353 set_option (OPTFORCEREDRAWINDEX);
1358 last->next = tmp->next;
1360 Aliases = tmp->next;
1362 alias_list_wipe(&tmp);
1368 while (MoreArgs (s));
1372 static int parse_alias (BUFFER * buf, BUFFER * s,
1373 unsigned long data __attribute__ ((unused)),
1376 alias_t *tmp = Aliases;
1377 alias_t *last = NULL;
1380 if (!MoreArgs (s)) {
1381 m_strcpy(err->data, err->dsize, _("alias: no address"));
1385 mutt_extract_token (buf, s, 0);
1387 /* check to see if an alias with this name already exists */
1388 for (; tmp; tmp = tmp->next) {
1389 if (!m_strcasecmp(tmp->name, buf->data))
1395 /* create a new alias */
1397 tmp->name = m_strdup(buf->data);
1398 /* give the main addressbook code a chance */
1399 if (CurrentMenu == MENU_ALIAS)
1400 set_option (OPTMENUCALLER);
1403 /* override the previous value */
1404 address_list_wipe(&tmp->addr);
1405 if (CurrentMenu == MENU_ALIAS)
1406 set_option (OPTFORCEREDRAWINDEX);
1409 mutt_extract_token (buf, s,
1410 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1411 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1416 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1417 snprintf (err->data, err->dsize,
1418 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1427 parse_unmy_hdr (BUFFER * buf, BUFFER * s,
1428 unsigned long data __attribute__ ((unused)),
1429 BUFFER * err __attribute__ ((unused)))
1431 string_list_t *last = NULL;
1432 string_list_t *tmp = UserHeader;
1437 mutt_extract_token (buf, s, 0);
1438 if (m_strcmp("*", buf->data) == 0)
1439 string_list_wipe(&UserHeader);
1444 l = m_strlen(buf->data);
1445 if (buf->data[l - 1] == ':')
1449 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1450 && tmp->data[l] == ':') {
1453 last->next = tmp->next;
1455 UserHeader = tmp->next;
1458 string_list_wipe(&ptr);
1467 while (MoreArgs (s));
1471 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1478 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1479 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1480 m_strcpy(err->data, err->dsize, _("invalid header field"));
1483 keylen = p - buf->data + 1;
1486 for (tmp = UserHeader;; tmp = tmp->next) {
1487 /* see if there is already a field by this name */
1488 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1489 /* replace the old value */
1490 p_delete(&tmp->data);
1491 tmp->data = buf->data;
1498 tmp->next = string_item_new();
1502 tmp = string_item_new();
1505 tmp->data = buf->data;
1511 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1512 char* errbuf, ssize_t errlen) {
1515 if (m_strncmp("reverse-", s, 8) == 0) {
1517 flags = SORT_REVERSE;
1520 if (m_strncmp("last-", s, 5) == 0) {
1525 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1527 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1531 *((short*) dst->data) = i | flags;
1535 /* if additional data more == 1, we want to resolve synonyms */
1536 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
1538 char buf[LONG_STRING];
1539 struct option_t *ptr = p;
1541 if (DTYPE(ptr->type) == DT_SYN) {
1544 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1546 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_from_string)
1549 mutt_option_value(ptr->option, buf, sizeof(buf));
1550 if (m_strlen(ptr->init) == 0 && buf && *buf)
1551 ptr->init = m_strdup(buf);
1554 static struct option_t* add_option (const char* name, const char* init,
1555 short type, short dodup) {
1556 struct option_t* option = p_new(struct option_t, 1);
1558 option->option = m_strdup(name);
1559 option->type = type;
1561 option->init = dodup ? m_strdup(init) : (char*) init;
1565 /* creates new option_t* of type DT_USER for $user_ var */
1566 static struct option_t* add_user_option (const char* name) {
1567 return (add_option (name, NULL, DT_USER, 1));
1570 /* free()'s option_t* */
1571 static void del_option (void* p) {
1572 struct option_t *ptr = (struct option_t*) p;
1573 char* s = (char*) ptr->data;
1574 p_delete(&ptr->option);
1576 p_delete(&ptr->init);
1580 static int init_expand (char** dst, struct option_t* src) {
1586 if (DTYPE(src->type) == DT_STR ||
1587 DTYPE(src->type) == DT_PATH) {
1588 /* only expand for string as it's the only place where
1589 * we want to expand vars right now */
1590 if (src->init && *src->init) {
1593 len = m_strlen(src->init) + 2;
1594 in.data = p_new(char, len + 1);
1595 snprintf (in.data, len, "\"%s\"", src->init);
1598 mutt_extract_token (&token, &in, 0);
1599 if (token.data && *token.data)
1600 *dst = m_strdup(token.data);
1602 *dst = m_strdup("");
1604 p_delete(&token.data);
1606 *dst = m_strdup("");
1608 /* for non-string: take value as is */
1609 *dst = m_strdup(src->init);
1613 /* if additional data more == 1, we want to resolve synonyms */
1614 static void mutt_restore_default (const char* name __attribute__ ((unused)),
1615 void* p, unsigned long more) {
1616 char errbuf[STRING];
1617 struct option_t* ptr = (struct option_t*) p;
1620 if (DTYPE (ptr->type) == DT_SYN) {
1623 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1627 if (FuncTable[DTYPE (ptr->type)].opt_from_string) {
1628 init_expand (&init, ptr);
1629 if (!FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, init, errbuf,
1631 if (!option (OPTNOCURSES))
1633 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1634 "Please report this error: \"%s\"\n"),
1635 ptr->option, NONULL (init), errbuf);
1641 if (ptr->flags & R_INDEX)
1642 set_option (OPTFORCEREDRAWINDEX);
1643 if (ptr->flags & R_PAGER)
1644 set_option (OPTFORCEREDRAWPAGER);
1645 if (ptr->flags & R_RESORT_SUB)
1646 set_option (OPTSORTSUBTHREADS);
1647 if (ptr->flags & R_RESORT)
1648 set_option (OPTNEEDRESORT);
1649 if (ptr->flags & R_RESORT_INIT)
1650 set_option (OPTRESORTINIT);
1651 if (ptr->flags & R_TREE)
1652 set_option (OPTREDRAWTREE);
1655 /* check whether value for $dsn_return would be valid */
1656 static int check_dsn_return (const char* option __attribute__ ((unused)), unsigned long p,
1657 char* errbuf, ssize_t errlen) {
1658 char* val = (char*) p;
1659 if (val && *val && m_strncmp(val, "hdrs", 4) != 0 &&
1660 m_strncmp(val, "full", 4) != 0) {
1662 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1668 /* check whether value for $dsn_notify would be valid */
1670 check_dsn_notify (const char* option __attribute__ ((unused)),
1671 unsigned long val, char* errbuf, ssize_t errlen)
1673 const char *p = (const char*)val;
1676 const char *q = m_strchrnul(p, ',');
1679 if (!m_strncmp(p, "never", len) && !m_strncmp(p, "delay", len)
1680 && !m_strncmp(p, "failure", len) && !m_strncmp(p, "success", len))
1683 snprintf(errbuf, errlen, _("'%.*s' is invalid for $%s"),
1684 len, p, "dsn_notify");
1694 static int check_num (const char* option, unsigned long p,
1695 char* errbuf, ssize_t errlen) {
1698 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1704 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1705 char* errbuf, ssize_t errlen) {
1706 if (!check_num ("history", p, errbuf, errlen))
1708 mutt_init_history ();
1712 static int check_special (const char* name, unsigned long val,
1713 char* errbuf, ssize_t errlen) {
1716 for (i = 0; SpecialVars[i].name; i++) {
1717 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1718 return (SpecialVars[i].check (SpecialVars[i].name,
1719 val, errbuf, errlen));
1725 static const struct mapping_t* get_sortmap (struct option_t* option) {
1726 const struct mapping_t* map = NULL;
1728 switch (option->type & DT_SUBTYPE_MASK) {
1730 map = SortAliasMethods;
1732 case DT_SORT_BROWSER:
1733 map = SortBrowserMethods;
1736 map = SortKeyMethods;
1739 map = SortAuxMethods;
1748 #define CHECK_PAGER \
1749 if ((CurrentMenu == MENU_PAGER) && \
1750 (!option || (option->flags & R_RESORT))) \
1752 snprintf (err->data, err->dsize, \
1753 _("Not available in this menu.")); \
1757 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1760 int query, unset, inv, reset, r = 0;
1761 struct option_t* option = NULL;
1763 while (MoreArgs (s)) {
1764 /* reset state variables */
1766 unset = data & M_SET_UNSET;
1767 inv = data & M_SET_INV;
1768 reset = data & M_SET_RESET;
1770 if (*s->dptr == '?') {
1774 else if (m_strncmp("no", s->dptr, 2) == 0) {
1778 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1782 else if (*s->dptr == '&') {
1787 /* get the variable name */
1788 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1790 /* resolve synonyms */
1791 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1792 DTYPE (option->type == DT_SYN))
1794 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1795 syn_t* tmp = syn_new();
1796 tmp->f = m_strdup(CurRCFile);
1800 syn_list_push(&Synonyms, tmp);
1804 /* see if we need to add $user_ var */
1805 if (!option && m_strncmp("user_", tmp->data, 5) == 0) {
1806 /* there's no option named like this yet so only add one
1807 * if the action isn't any of: reset, unset, query */
1808 if (!(reset || unset || query || *s->dptr != '=')) {
1809 option = add_user_option (tmp->data);
1810 hash_insert (ConfigOptions, option->option, option, 0);
1814 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1815 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1818 s->dptr = vskipspaces(s->dptr);
1821 if (query || unset || inv) {
1822 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1826 if (s && *s->dptr == '=') {
1827 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1831 if (!m_strcmp("all", tmp->data)) {
1832 if (CurrentMenu == MENU_PAGER) {
1833 snprintf (err->data, err->dsize, _("Not available in this menu."));
1836 hash_map (ConfigOptions, mutt_restore_default, 1);
1837 set_option (OPTFORCEREDRAWINDEX);
1838 set_option (OPTFORCEREDRAWPAGER);
1839 set_option (OPTSORTSUBTHREADS);
1840 set_option (OPTNEEDRESORT);
1841 set_option (OPTRESORTINIT);
1842 set_option (OPTREDRAWTREE);
1845 else if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1846 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1851 mutt_restore_default (NULL, option, 1);
1854 else if (DTYPE (option->type) == DT_BOOL) {
1855 /* XXX this currently ignores the function table
1856 * as we don't get invert and stuff into it */
1857 if (s && *s->dptr == '=') {
1858 if (unset || inv || query) {
1859 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1864 mutt_extract_token (tmp, s, 0);
1865 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1867 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1870 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1876 bool_to_string (err->data, err->dsize, option);
1882 unset_option (option->data);
1884 toggle_option (option->data);
1886 set_option (option->data);
1888 else if (DTYPE (option->type) == DT_STR ||
1889 DTYPE (option->type) == DT_PATH ||
1890 DTYPE (option->type) == DT_ADDR ||
1891 DTYPE (option->type) == DT_MAGIC ||
1892 DTYPE (option->type) == DT_NUM ||
1893 DTYPE (option->type) == DT_SORT ||
1894 DTYPE (option->type) == DT_RX ||
1895 DTYPE (option->type) == DT_USER ||
1896 DTYPE (option->type) == DT_SYS) {
1898 /* XXX maybe we need to get unset into handlers? */
1899 if (DTYPE (option->type) == DT_STR ||
1900 DTYPE (option->type) == DT_PATH ||
1901 DTYPE (option->type) == DT_ADDR ||
1902 DTYPE (option->type) == DT_USER ||
1903 DTYPE (option->type) == DT_SYS) {
1906 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1907 snprintf (err->data, err->dsize, _("$%s is read-only"),
1911 } else if (DTYPE (option->type) == DT_ADDR)
1912 address_list_wipe((address_t **) option->data);
1913 else if (DTYPE (option->type) == DT_USER)
1914 /* to unset $user_ means remove */
1915 hash_delete (ConfigOptions, option->option,
1916 option, del_option);
1918 p_delete((void **)(void *)&option->data);
1923 if (query || *s->dptr != '=') {
1924 FuncTable[DTYPE (option->type)].opt_to_string
1925 (err->data, err->dsize, option);
1929 /* the $madmutt_ variables are read-only */
1930 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1931 snprintf (err->data, err->dsize, _("$%s is read-only"),
1938 mutt_extract_token (tmp, s, 0);
1939 if (!FuncTable[DTYPE (option->type)].opt_from_string
1940 (option, tmp->data, err->data, err->dsize))
1944 else if (DTYPE (option->type) == DT_QUAD) {
1947 quad_to_string (err->data, err->dsize, option);
1951 if (*s->dptr == '=') {
1954 mutt_extract_token (tmp, s, 0);
1955 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1956 set_quadoption (option->data, M_YES);
1957 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1958 set_quadoption (option->data, M_NO);
1959 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1960 set_quadoption (option->data, M_ASKYES);
1961 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1962 set_quadoption (option->data, M_ASKNO);
1964 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1965 tmp->data, option->option);
1972 toggle_quadoption (option->data);
1974 set_quadoption (option->data, M_NO);
1976 set_quadoption (option->data, M_YES);
1980 snprintf (err->data, err->dsize, _("%s: unknown type"),
1986 if (option->flags & R_INDEX)
1987 set_option (OPTFORCEREDRAWINDEX);
1988 if (option->flags & R_PAGER)
1989 set_option (OPTFORCEREDRAWPAGER);
1990 if (option->flags & R_RESORT_SUB)
1991 set_option (OPTSORTSUBTHREADS);
1992 if (option->flags & R_RESORT)
1993 set_option (OPTNEEDRESORT);
1994 if (option->flags & R_RESORT_INIT)
1995 set_option (OPTRESORTINIT);
1996 if (option->flags & R_TREE)
1997 set_option (OPTREDRAWTREE);
2004 /* reads the specified initialization file. returns -1 if errors were found
2005 so that we can pause to let the user know... */
2006 static int source_rc (const char *rcfile, BUFFER * err)
2009 int line = 0, rc = 0, conv = 0;
2011 char *linebuf = NULL;
2012 char *currentline = NULL;
2016 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
2017 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
2022 while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
2023 conv = ConfigCharset && (*ConfigCharset) && Charset;
2025 currentline = m_strdup(linebuf);
2028 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
2031 currentline = linebuf;
2036 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
2037 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
2038 if (--rc < -MAXERRS) {
2040 p_delete(¤tline);
2049 p_delete(¤tline);
2051 p_delete(&token.data);
2055 mutt_wait_filter (pid);
2057 /* the muttrc source keyword */
2058 snprintf (err->data, err->dsize,
2059 rc >= -MAXERRS ? _("source: errors in %s")
2060 : _("source: reading aborted due too many errors in %s"),
2069 static int parse_source (BUFFER * tmp, BUFFER * s,
2070 unsigned long data __attribute__ ((unused)),
2073 char path[_POSIX_PATH_MAX];
2077 if (mutt_extract_token (tmp, s, 0) != 0) {
2078 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
2082 m_strcpy(path, sizeof(path), tmp->data);
2083 mutt_expand_path (path, sizeof(path));
2085 rc += source_rc (path, err);
2087 while (MoreArgs (s));
2089 return ((rc < 0) ? -1 : 0);
2092 /* line command to execute
2094 token scratch buffer to be used by parser. caller should free
2095 token->data when finished. the reason for this variable is
2096 to avoid having to allocate and deallocate a lot of memory
2097 if we are parsing many lines. the caller can pass in the
2098 memory to use, which avoids having to create new space for
2099 every call to this function.
2101 err where to write error messages */
2102 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
2108 expn.data = expn.dptr = line;
2109 expn.dsize = m_strlen(line);
2113 expn.dptr = vskipspaces(expn.dptr);
2114 while (*expn.dptr) {
2115 if (*expn.dptr == '#')
2116 break; /* rest of line is a comment */
2117 if (*expn.dptr == ';') {
2121 mutt_extract_token (token, &expn, 0);
2122 for (i = 0; Commands[i].name; i++) {
2123 if (!m_strcmp(token->data, Commands[i].name)) {
2124 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
2129 if (!Commands[i].name) {
2130 snprintf (err->data, err->dsize, _("%s: unknown command"),
2131 NONULL (token->data));
2138 p_delete(&expn.data);
2143 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
2144 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
2145 /* initial string that starts completion. No telling how much crap
2146 * the user has typed so far. Allocate LONG_STRING just to be sure! */
2147 char User_typed[LONG_STRING] = { 0 };
2149 int Num_matched = 0; /* Number of matches for completion */
2150 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
2151 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
2153 /* helper function for completion. Changes the dest buffer if
2154 necessary/possible to aid completion.
2155 dest == completion result gets here.
2156 src == candidate for completion.
2157 try == user entered data for completion.
2158 len == length of dest buffer.
2160 static void candidate (char *dest, char *try, const char *src, int len)
2164 if (strstr (src, try) == src) {
2165 Matches[Num_matched++] = src;
2167 m_strcpy(dest, len, src);
2169 for (l = 0; src[l] && src[l] == dest[l]; l++);
2175 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
2179 int spaces; /* keep track of the number of leading spaces on the line */
2181 buffer = vskipspaces(buffer);
2182 spaces = buffer - pt;
2184 pt = buffer + pos - spaces;
2185 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2188 if (pt == buffer) { /* complete cmd */
2189 /* first TAB. Collect all the matches */
2192 m_strcpy(User_typed, sizeof(User_typed), pt);
2193 p_clear(Matches, countof(Matches));
2194 p_clear(Completed, countof(Completed));
2195 for (num = 0; Commands[num].name; num++)
2196 candidate (Completed, User_typed, Commands[num].name,
2198 Matches[Num_matched++] = User_typed;
2200 /* All matches are stored. Longest non-ambiguous string is ""
2201 * i.e. dont change 'buffer'. Fake successful return this time */
2202 if (User_typed[0] == 0)
2206 if (Completed[0] == 0 && User_typed[0])
2209 /* Num_matched will _always_ be atleast 1 since the initial
2210 * user-typed string is always stored */
2211 if (numtabs == 1 && Num_matched == 2)
2212 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2213 else if (numtabs > 1 && Num_matched > 2)
2214 /* cycle thru all the matches */
2215 snprintf (Completed, sizeof(Completed), "%s",
2216 Matches[(numtabs - 2) % Num_matched]);
2218 /* return the completed command */
2219 m_strcpy(buffer, len - spaces, Completed);
2221 else if (!m_strncmp(buffer, "set", 3)
2222 || !m_strncmp(buffer, "unset", 5)
2223 || !m_strncmp(buffer, "reset", 5)
2224 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
2225 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
2228 /* loop through all the possible prefixes (no, inv, ...) */
2229 if (!m_strncmp(buffer, "set", 3)) {
2230 for (num = 0; prefixes[num]; num++) {
2231 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
2232 pt += m_strlen(prefixes[num]);
2238 /* first TAB. Collect all the matches */
2241 m_strcpy(User_typed, sizeof(User_typed), pt);
2242 p_clear(Matches, countof(Matches));
2243 p_clear(Completed, countof(Completed));
2244 for (num = 0; MuttVars[num].option; num++)
2245 candidate(Completed, User_typed, MuttVars[num].option,
2247 Matches[Num_matched++] = User_typed;
2249 /* All matches are stored. Longest non-ambiguous string is ""
2250 * i.e. dont change 'buffer'. Fake successful return this time */
2251 if (User_typed[0] == 0)
2255 if (Completed[0] == 0 && User_typed[0])
2258 /* Num_matched will _always_ be atleast 1 since the initial
2259 * user-typed string is always stored */
2260 if (numtabs == 1 && Num_matched == 2)
2261 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2262 else if (numtabs > 1 && Num_matched > 2)
2263 /* cycle thru all the matches */
2264 snprintf (Completed, sizeof(Completed), "%s",
2265 Matches[(numtabs - 2) % Num_matched]);
2267 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2269 else if (!m_strncmp(buffer, "exec", 4)) {
2270 struct binding_t *menu = km_get_table (CurrentMenu);
2272 if (!menu && CurrentMenu != MENU_PAGER)
2276 /* first TAB. Collect all the matches */
2279 m_strcpy(User_typed, sizeof(User_typed), pt);
2280 p_clear(Matches, countof(Matches));
2281 p_clear(Completed, countof(Completed));
2282 for (num = 0; menu[num].name; num++)
2283 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
2284 /* try the generic menu */
2285 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2287 for (num = 0; menu[num].name; num++)
2288 candidate (Completed, User_typed, menu[num].name,
2291 Matches[Num_matched++] = User_typed;
2293 /* All matches are stored. Longest non-ambiguous string is ""
2294 * i.e. dont change 'buffer'. Fake successful return this time */
2295 if (User_typed[0] == 0)
2299 if (Completed[0] == 0 && User_typed[0])
2302 /* Num_matched will _always_ be atleast 1 since the initial
2303 * user-typed string is always stored */
2304 if (numtabs == 1 && Num_matched == 2)
2305 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2306 else if (numtabs > 1 && Num_matched > 2)
2307 /* cycle thru all the matches */
2308 snprintf (Completed, sizeof(Completed), "%s",
2309 Matches[(numtabs - 2) % Num_matched]);
2311 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2319 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
2321 char var[STRING], *pt = buffer;
2323 struct option_t* option = NULL;
2328 buffer = vskipspaces(buffer);
2329 spaces = buffer - pt;
2331 pt = buffer + pos - spaces;
2332 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2334 pt++; /* move past the space */
2335 if (*pt == '=') /* abort if no var before the '=' */
2338 if (m_strncmp(buffer, "set", 3) == 0) {
2339 m_strcpy(var, sizeof(var), pt);
2340 /* ignore the trailing '=' when comparing */
2341 var[m_strlen(var) - 1] = 0;
2342 if (!(option = hash_find (ConfigOptions, var)))
2343 return 0; /* no such variable. */
2345 char tmp[LONG_STRING], tmp2[LONG_STRING];
2347 ssize_t dlen = buffer + len - pt - spaces;
2348 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2352 if ((DTYPE (option->type) == DT_STR) ||
2353 (DTYPE (option->type) == DT_PATH) ||
2354 (DTYPE (option->type) == DT_RX)) {
2355 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
2356 if (DTYPE (option->type) == DT_PATH)
2357 mutt_pretty_mailbox (tmp);
2359 else if (DTYPE (option->type) == DT_ADDR) {
2360 rfc822_write_address (tmp, sizeof(tmp),
2361 *((address_t **) option->data), 0);
2363 else if (DTYPE (option->type) == DT_QUAD)
2364 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
2365 else if (DTYPE (option->type) == DT_NUM)
2366 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
2367 else if (DTYPE (option->type) == DT_SORT) {
2368 const struct mapping_t *map;
2371 switch (option->type & DT_SUBTYPE_MASK) {
2373 map = SortAliasMethods;
2375 case DT_SORT_BROWSER:
2376 map = SortBrowserMethods;
2379 map = SortKeyMethods;
2385 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2386 snprintf(tmp, sizeof(tmp), "%s%s%s",
2387 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2388 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2390 else if (DTYPE (option->type) == DT_MAGIC) {
2392 switch (DefaultMagic) {
2408 m_strcpy(tmp, sizeof(tmp), p);
2410 else if (DTYPE (option->type) == DT_BOOL)
2411 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
2415 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
2416 if (*s == '\\' || *s == '"')
2422 m_strcpy(tmp, sizeof(tmp), pt);
2423 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2431 /* Implement the -Q command line flag */
2432 int mutt_query_variables (string_list_t * queries)
2436 char errbuff[STRING];
2437 char command[STRING];
2445 err.dsize = sizeof(errbuff);
2447 for (p = queries; p; p = p->next) {
2448 snprintf (command, sizeof(command), "set ?%s\n", p->data);
2449 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2450 fprintf (stderr, "%s\n", err.data);
2451 p_delete(&token.data);
2454 printf ("%s\n", err.data);
2457 p_delete(&token.data);
2461 static int mutt_execute_commands (string_list_t * p)
2464 char errstr[SHORT_STRING];
2468 err.dsize = sizeof(errstr);
2470 for (; p; p = p->next) {
2471 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2472 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2473 p_delete(&token.data);
2477 p_delete(&token.data);
2481 void mutt_init (int skip_sys_rc, string_list_t * commands)
2484 struct utsname utsname;
2486 char buffer[STRING], error[STRING];
2487 int default_rc = 0, need_pause = 0;
2493 err.dsize = sizeof(error);
2495 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2496 * to have some room for $user_ vars */
2497 ConfigOptions = hash_create (sizeof(MuttVars) * 3);
2498 for (i = 0; MuttVars[i].option; i++) {
2499 if (DTYPE (MuttVars[i].type) != DT_SYS)
2500 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2502 hash_insert (ConfigOptions, MuttVars[i].option,
2503 add_option (MuttVars[i].option, MuttVars[i].init,
2508 * XXX - use something even more difficult to predict?
2510 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2511 "\033]9;%ld\a", (long) time (NULL));
2513 /* on one of the systems I use, getcwd() does not return the same prefix
2514 as is listed in the passwd file */
2515 if ((p = getenv ("HOME")))
2516 Homedir = m_strdup(p);
2518 /* Get some information about the user */
2519 if ((pw = getpwuid (getuid ()))) {
2522 Username = m_strdup(pw->pw_name);
2524 Homedir = m_strdup(pw->pw_dir);
2526 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, GecosMask.rx);
2527 Realname = m_strdup(rnbuf);
2528 Shell = m_strdup(pw->pw_shell);
2534 fputs (_("unable to determine home directory"), stderr);
2537 if ((p = getenv ("USER")))
2538 Username = m_strdup(p);
2541 fputs (_("unable to determine username"), stderr);
2544 Shell = m_strdup((p = getenv ("SHELL")) ? p : "/bin/sh");
2547 /* And about the host... */
2549 /* some systems report the FQDN instead of just the hostname */
2550 if ((p = strchr (utsname.nodename, '.'))) {
2551 Hostname = p_dupstr(utsname.nodename, p - utsname.nodename);
2553 m_strcpy(buffer, sizeof(buffer), p); /* save the domain for below */
2556 Hostname = m_strdup(utsname.nodename);
2558 if (!p && getdnsdomainname(buffer, sizeof(buffer)) == -1)
2559 Fqdn = m_strdup("@");
2561 if (*buffer != '@') {
2562 Fqdn = p_new(char, m_strlen(buffer) + m_strlen(Hostname) + 2);
2563 sprintf (Fqdn, "%s.%s", NONULL(Hostname), buffer); /* __SPRINTF_CHECKED__ */
2566 Fqdn = m_strdup(NONULL (Hostname));
2573 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2575 fgets (buffer, sizeof(buffer), f);
2576 p = vskipspaces(buffer);
2578 while (*q && !isspace(*q))
2581 NewsServer = m_strdup(p);
2585 if ((p = getenv ("NNTPSERVER")))
2586 NewsServer = m_strdup(p);
2589 if ((p = getenv ("MAIL")))
2590 Spoolfile = m_strdup(p);
2591 else if ((p = getenv ("MAILDIR")))
2592 Spoolfile = m_strdup(p);
2595 mutt_concat_path(buffer, sizeof(buffer), NONULL(Homedir), MAILPATH);
2597 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(Username));
2599 Spoolfile = m_strdup(buffer);
2602 if ((p = getenv ("MAILCAPS")))
2603 MailcapPath = m_strdup(p);
2605 /* Default search path from RFC1524 */
2607 m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2608 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2611 Tempdir = m_strdup((p = getenv ("TMPDIR")) ? p : "/tmp");
2613 p = getenv ("VISUAL");
2615 p = getenv ("EDITOR");
2619 Editor = m_strdup(p);
2621 if ((p = getenv ("REPLYTO")) != NULL) {
2624 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2627 buf.data = buf.dptr = buffer;
2628 buf.dsize = m_strlen(buffer);
2631 parse_my_hdr (&token, &buf, 0, &err);
2632 p_delete(&token.data);
2635 if ((p = getenv ("EMAIL")) != NULL)
2636 From = rfc822_parse_adrlist (NULL, p);
2638 charset_initialize();
2640 /* Set standard defaults */
2641 hash_map (ConfigOptions, mutt_set_default, 0);
2642 hash_map (ConfigOptions, mutt_restore_default, 0);
2644 CurrentMenu = MENU_MAIN;
2647 /* Unset suspend by default if we're the session leader */
2648 if (getsid (0) == getpid ())
2649 unset_option (OPTSUSPEND);
2652 mutt_init_history ();
2656 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc-%s", NONULL (Homedir),
2658 if (access (buffer, F_OK) == -1)
2660 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL (Homedir));
2661 if (access (buffer, F_OK) == -1)
2663 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc-%s",
2664 NONULL (Homedir), MUTT_VERSION);
2665 if (access (buffer, F_OK) == -1)
2667 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
2671 Muttrc = m_strdup(buffer);
2674 m_strcpy(buffer, sizeof(buffer), Muttrc);
2676 mutt_expand_path (buffer, sizeof(buffer));
2677 Muttrc = m_strdup(buffer);
2679 p_delete(&AliasFile);
2680 AliasFile = m_strdup(NONULL (Muttrc));
2682 /* Process the global rc file if it exists and the user hasn't explicity
2683 requested not to via "-n". */
2685 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
2687 if (access (buffer, F_OK) == -1)
2688 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
2689 if (access (buffer, F_OK) == -1)
2690 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
2692 if (access (buffer, F_OK) == -1)
2693 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
2694 if (access (buffer, F_OK) != -1) {
2695 if (source_rc (buffer, &err) != 0) {
2696 fputs (err.data, stderr);
2697 fputc ('\n', stderr);
2703 /* Read the user's initialization file. */
2704 if (access (Muttrc, F_OK) != -1) {
2705 if (!option (OPTNOCURSES))
2707 if (source_rc (Muttrc, &err) != 0) {
2708 fputs (err.data, stderr);
2709 fputc ('\n', stderr);
2713 else if (!default_rc) {
2714 /* file specified by -F does not exist */
2715 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2716 mutt_endwin (buffer);
2720 if (mutt_execute_commands (commands) != 0)
2723 /* warn about synonym variables */
2727 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2729 for (syn = Synonyms; syn; syn = syn->next) {
2730 fprintf(stderr, "$%s ($%s should be used) (%s:%d)\n",
2731 syn->o ? NONULL(syn->o->option) : "",
2732 syn->n ? NONULL(syn->n->option) : "",
2733 NONULL(syn->f), syn->l);
2735 fprintf (stderr, _("Warning: synonym variables are scheduled"
2736 " for removal.\n"));
2737 syn_list_wipe(&Synonyms);
2741 if (need_pause && !option (OPTNOCURSES)) {
2742 if (mutt_any_key_to_continue (NULL) == -1)
2747 int mutt_get_hook_type (const char *name)
2749 struct command_t *c;
2751 for (c = Commands; c->name; c++)
2752 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2757 /* dump out the value of all the variables we have */
2758 int mutt_dump_variables (int full) {
2761 /* get all non-synonyms into list... */
2762 for (i = 0; MuttVars[i].option; i++) {
2763 struct option_t *option = MuttVars + i;
2764 char buf[LONG_STRING];
2766 if (DTYPE(option->type) == DT_SYN)
2770 mutt_option_value(option->option, buf, sizeof(buf));
2771 if (!m_strcmp(buf, option->init))
2776 FuncTable[DTYPE(option->type)].opt_to_string
2777 (buf, sizeof(buf), option);
2778 printf ("%s\n", buf);
2781 printf ("\n# vi""m:set ft=muttrc:\n");