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 (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_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(rx_t **list, const char *pat,
641 const char *templ, BUFFER * err)
645 if (m_strisempty(pat) || !templ)
648 if (!(rx = rx_compile (pat, REG_ICASE))) {
649 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
653 /* check to make sure the item is not already on this list */
654 for (last = list; *last; last = &(*last)->next) {
655 if (!ascii_strcasecmp(rx->pattern, (*last)->pattern) == 0) {
656 rx_t *tmp = rx_list_pop(last);
658 last = rx_list_last(last);
664 rx_set_template(rx, templ);
668 static int remove_from_spam_list (rx_t ** list, const char *pat)
673 if (!m_strcmp((*list)->pattern, pat)) {
674 rx_t *spam = rx_list_pop(list);
678 list = &(*list)->next;
686 static void remove_from_list (string_list_t ** l, const char *str)
688 string_list_t *p, *last = NULL;
690 if (m_strcmp("*", str) == 0)
691 string_list_wipe(l); /* ``unCMD *'' means delete all current entries */
696 if (ascii_strcasecmp (str, p->data) == 0) {
699 last->next = p->next;
712 static int remove_from_rx_list(rx_t **l, const char *str)
714 if (m_strcmp("*", str) == 0) {
719 l = rx_lookup(l, str);
721 rx_t *r = rx_list_pop(l);
729 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
733 unsigned long res = 0;
735 struct option_t* option = NULL;
738 mutt_extract_token (tmp, s, 0);
740 /* is the item defined as a variable or a function? */
741 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL)
744 for (i = 0; !res && i < MENU_MAX; i++) {
745 struct binding_t *b = km_get_table (Menus[i].value);
750 for (j = 0; b[j].name; j++)
751 if (!ascii_strncasecmp (tmp->data, b[j].name, m_strlen(tmp->data))
752 && (m_strlen(b[j].name) == m_strlen(tmp->data))) {
758 /* check for feature_* */
759 if (!res && ascii_strncasecmp (tmp->data, "feature_", 8) == 0 &&
760 (j = m_strlen(tmp->data)) > 8) {
762 while (Features[i]) {
763 if (m_strlen(Features[i]) == j-8 &&
764 ascii_strncasecmp (Features[i], tmp->data+8, j-8) == 0) {
774 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
776 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
780 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
783 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
784 mutt_error ("Error: %s", err->data);
785 p_delete(&token.data);
788 p_delete(&token.data);
793 static int parse_unignore (BUFFER * buf, BUFFER * s,
794 unsigned long data __attribute__ ((unused)),
795 BUFFER * err __attribute__ ((unused)))
798 mutt_extract_token (buf, s, 0);
800 /* don't add "*" to the unignore list */
801 if (m_strcmp (buf->data, "*"))
802 add_to_list (&UnIgnore, buf->data);
804 remove_from_list (&Ignore, buf->data);
806 while (MoreArgs (s));
811 static int parse_ignore (BUFFER * buf, BUFFER * s,
812 unsigned long data __attribute__ ((unused)),
813 BUFFER * err __attribute__ ((unused)))
816 mutt_extract_token (buf, s, 0);
817 remove_from_list (&UnIgnore, buf->data);
818 add_to_list (&Ignore, buf->data);
820 while (MoreArgs (s));
825 static int parse_list (BUFFER * buf, BUFFER * s,
826 unsigned long data __attribute__ ((unused)),
827 BUFFER * err __attribute__ ((unused)))
830 mutt_extract_token (buf, s, 0);
831 add_to_list ((string_list_t **) data, buf->data);
833 while (MoreArgs (s));
838 static void _alternates_clean (void)
842 if (Context && Context->msgcount) {
843 for (i = 0; i < Context->msgcount; i++)
844 Context->hdrs[i]->recip_valid = 0;
848 static int parse_alternates (BUFFER * buf, BUFFER * s,
849 unsigned long data __attribute__ ((unused)),
850 BUFFER * err __attribute__ ((unused)))
852 _alternates_clean ();
854 mutt_extract_token (buf, s, 0);
855 remove_from_rx_list (&UnAlternates, buf->data);
857 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
860 while (MoreArgs (s));
865 static int parse_unalternates (BUFFER * buf, BUFFER * s,
866 unsigned long data __attribute__ ((unused)),
867 BUFFER * err __attribute__ ((unused)))
869 _alternates_clean ();
871 mutt_extract_token (buf, s, 0);
872 remove_from_rx_list (&Alternates, buf->data);
874 if (m_strcmp(buf->data, "*") &&
875 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
879 while (MoreArgs (s));
884 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
891 /* Insist on at least one parameter */
894 m_strcpy(err->data, err->dsize, _("spam: no matching pattern"));
896 m_strcpy(err->data, err->dsize, _("nospam: no matching pattern"));
900 /* Extract the first token, a regexp */
901 mutt_extract_token (buf, s, 0);
903 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
904 if (data == M_SPAM) {
905 /* If there's a second parameter, it's a template for the spam tag. */
907 mutt_extract_token (&templ, s, 0);
909 /* Add to the spam list. */
910 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
911 p_delete(&templ.data);
914 p_delete(&templ.data);
917 /* If not, try to remove from the nospam list. */
919 remove_from_rx_list (&NoSpamList, buf->data);
925 /* M_NOSPAM is for nospam commands. */
926 else if (data == M_NOSPAM) {
927 /* nospam only ever has one parameter. */
929 /* "*" is a special case. */
930 if (!m_strcmp(buf->data, "*")) {
931 rx_list_wipe(&SpamList);
932 rx_list_wipe(&NoSpamList);
936 /* If it's on the spam list, just remove it. */
937 if (remove_from_spam_list (&SpamList, buf->data) != 0)
940 /* Otherwise, add it to the nospam list. */
941 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
947 /* This should not happen. */
948 m_strcpy(err->data, err->dsize, "This is no good at all.");
952 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
953 BUFFER * err __attribute__ ((unused)))
956 mutt_extract_token (buf, s, 0);
958 * Check for deletion of entire list
960 if (m_strcmp(buf->data, "*") == 0) {
961 string_list_wipe((string_list_t **) data);
964 remove_from_list ((string_list_t **) data, buf->data);
966 while (MoreArgs (s));
971 static int parse_lists (BUFFER * buf, BUFFER * s,
972 unsigned long data __attribute__ ((unused)),
976 mutt_extract_token (buf, s, 0);
977 remove_from_rx_list (&UnMailLists, buf->data);
979 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
982 while (MoreArgs (s));
987 /* always wise to do what someone else did before */
988 static void _attachments_clean (void) {
990 if (Context && Context->msgcount) {
991 for (i = 0; i < Context->msgcount; i++)
992 Context->hdrs[i]->attach_valid = 0;
996 static int parse_attach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
997 BUFFER *err __attribute__ ((unused))) {
999 string_list_t *listp, *lastp;
1004 /* Find the last item in the list that data points to. */
1006 for (listp = *ldata; listp; listp = listp->next) {
1007 a = (ATTACH_MATCH *)listp->data;
1012 mutt_extract_token (buf, s, 0);
1014 if (!buf->data || *buf->data == '\0')
1017 a = p_new(ATTACH_MATCH, 1);
1019 /* some cheap hacks that I expect to remove */
1020 if (!m_strcasecmp(buf->data, "any"))
1021 a->major = m_strdup("*/.*");
1022 else if (!m_strcasecmp(buf->data, "none"))
1023 a->major = m_strdup("cheap_hack/this_should_never_match");
1025 a->major = m_strdup(buf->data);
1027 if ((p = strchr(a->major, '/'))) {
1032 a->minor = "unknown";
1035 len = m_strlen(a->minor);
1036 tmpminor = p_new(char, len + 3);
1037 m_strcpy(&tmpminor[1], len + 3, a->minor);
1039 tmpminor[len+1] = '$';
1040 tmpminor[len+2] = '\0';
1042 a->major_int = mutt_check_mime_type(a->major);
1043 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
1045 p_delete(&tmpminor);
1047 listp = p_new(string_list_t, 1);
1048 listp->data = (char *)a;
1051 lastp->next = listp;
1057 while (MoreArgs (s));
1059 _attachments_clean();
1063 static int parse_unattach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
1064 BUFFER *err __attribute__ ((unused))) {
1066 string_list_t *lp, *lastp, *newlp;
1072 mutt_extract_token (buf, s, 0);
1074 if (!m_strcasecmp(buf->data, "any"))
1075 tmp = m_strdup("*/.*");
1076 else if (!m_strcasecmp(buf->data, "none"))
1077 tmp = m_strdup("cheap_hack/this_should_never_match");
1079 tmp = m_strdup(buf->data);
1081 if ((minor = strchr(tmp, '/'))) {
1085 minor = m_strdup("unknown");
1087 major = mutt_check_mime_type(tmp);
1089 /* We must do our own walk here because remove_from_list() will only
1090 * remove the string_list_t->data, not anything pointed to by the string_list_t->data. */
1092 for(lp = *ldata; lp; ) {
1093 a = (ATTACH_MATCH *)lp->data;
1094 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
1095 regfree(&a->minor_rx);
1096 p_delete(&a->major);
1098 /* Relink backward */
1100 lastp->next = lp->next;
1105 p_delete(&lp->data); /* same as a */
1115 while (MoreArgs (s));
1118 _attachments_clean();
1122 static int print_attach_list (string_list_t *lp, char op, const char *name) {
1124 printf("attachments %c%s %s/%s\n", op, name,
1125 ((ATTACH_MATCH *)lp->data)->major,
1126 ((ATTACH_MATCH *)lp->data)->minor);
1133 static int parse_attachments (BUFFER *buf, BUFFER *s,
1134 unsigned long data __attribute__ ((unused)),
1137 string_list_t **listp;
1139 mutt_extract_token(buf, s, 0);
1140 if (!buf->data || *buf->data == '\0') {
1141 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
1145 category = buf->data;
1151 printf("\nCurrent attachments settings:\n\n");
1152 print_attach_list(AttachAllow, '+', "A");
1153 print_attach_list(AttachExclude, '-', "A");
1154 print_attach_list(InlineAllow, '+', "I");
1155 print_attach_list(InlineExclude, '-', "I");
1156 set_option (OPTFORCEREDRAWINDEX);
1157 set_option (OPTFORCEREDRAWPAGER);
1158 mutt_any_key_to_continue (NULL);
1162 if (op != '+' && op != '-') {
1166 if (!m_strncasecmp(category, "attachment", strlen(category))) {
1168 listp = &AttachAllow;
1170 listp = &AttachExclude;
1172 else if (!m_strncasecmp(category, "inline", strlen(category))) {
1174 listp = &InlineAllow;
1176 listp = &InlineExclude;
1178 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
1182 return parse_attach_list(buf, s, listp, err);
1185 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
1187 string_list_t **listp;
1189 mutt_extract_token(buf, s, 0);
1190 if (!buf->data || *buf->data == '\0') {
1191 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
1197 if (op != '+' && op != '-') {
1201 if (!m_strncasecmp(p, "attachment", strlen(p))) {
1203 listp = &AttachAllow;
1205 listp = &AttachExclude;
1207 else if (!m_strncasecmp(p, "inline", strlen(p))) {
1209 listp = &InlineAllow;
1211 listp = &InlineExclude;
1214 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
1218 return parse_unattach_list(buf, s, listp, err);
1221 static int parse_unlists (BUFFER * buf, BUFFER * s,
1222 unsigned long data __attribute__ ((unused)),
1223 BUFFER * err __attribute__ ((unused)))
1226 mutt_extract_token (buf, s, 0);
1227 remove_from_rx_list (&SubscribedLists, buf->data);
1228 remove_from_rx_list (&MailLists, buf->data);
1230 if (m_strcmp(buf->data, "*") &&
1231 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1234 while (MoreArgs (s));
1239 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1243 mutt_extract_token (buf, s, 0);
1244 remove_from_rx_list (&UnMailLists, buf->data);
1245 remove_from_rx_list (&UnSubscribedLists, buf->data);
1247 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1249 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1252 while (MoreArgs (s));
1257 static int parse_unsubscribe (BUFFER * buf, BUFFER * s,
1258 unsigned long data __attribute__ ((unused)),
1259 BUFFER * err __attribute__ ((unused)))
1262 mutt_extract_token (buf, s, 0);
1263 remove_from_rx_list (&SubscribedLists, buf->data);
1265 if (m_strcmp(buf->data, "*") &&
1266 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1269 while (MoreArgs (s));
1274 static int parse_unalias (BUFFER * buf, BUFFER * s,
1275 unsigned long data __attribute__ ((unused)),
1276 BUFFER * err __attribute__ ((unused)))
1278 alias_t *tmp, *last = NULL;
1281 mutt_extract_token (buf, s, 0);
1283 if (m_strcmp("*", buf->data) == 0) {
1284 if (CurrentMenu == MENU_ALIAS) {
1285 for (tmp = Aliases; tmp; tmp = tmp->next)
1287 set_option (OPTFORCEREDRAWINDEX);
1290 alias_list_wipe(&Aliases);
1294 for (tmp = Aliases; tmp; tmp = tmp->next) {
1295 if (m_strcasecmp(buf->data, tmp->name) == 0) {
1296 if (CurrentMenu == MENU_ALIAS) {
1298 set_option (OPTFORCEREDRAWINDEX);
1303 last->next = tmp->next;
1305 Aliases = tmp->next;
1307 alias_list_wipe(&tmp);
1313 while (MoreArgs (s));
1317 static int parse_alias (BUFFER * buf, BUFFER * s,
1318 unsigned long data __attribute__ ((unused)),
1321 alias_t *tmp = Aliases;
1322 alias_t *last = NULL;
1325 if (!MoreArgs (s)) {
1326 m_strcpy(err->data, err->dsize, _("alias: no address"));
1330 mutt_extract_token (buf, s, 0);
1332 /* check to see if an alias with this name already exists */
1333 for (; tmp; tmp = tmp->next) {
1334 if (!m_strcasecmp(tmp->name, buf->data))
1340 /* create a new alias */
1342 tmp->name = m_strdup(buf->data);
1343 /* give the main addressbook code a chance */
1344 if (CurrentMenu == MENU_ALIAS)
1345 set_option (OPTMENUCALLER);
1348 /* override the previous value */
1349 address_list_wipe(&tmp->addr);
1350 if (CurrentMenu == MENU_ALIAS)
1351 set_option (OPTFORCEREDRAWINDEX);
1354 mutt_extract_token (buf, s,
1355 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1356 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1361 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1362 snprintf (err->data, err->dsize,
1363 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1372 parse_unmy_hdr (BUFFER * buf, BUFFER * s,
1373 unsigned long data __attribute__ ((unused)),
1374 BUFFER * err __attribute__ ((unused)))
1376 string_list_t *last = NULL;
1377 string_list_t *tmp = UserHeader;
1382 mutt_extract_token (buf, s, 0);
1383 if (m_strcmp("*", buf->data) == 0)
1384 string_list_wipe(&UserHeader);
1389 l = m_strlen(buf->data);
1390 if (buf->data[l - 1] == ':')
1394 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1395 && tmp->data[l] == ':') {
1398 last->next = tmp->next;
1400 UserHeader = tmp->next;
1403 string_list_wipe(&ptr);
1412 while (MoreArgs (s));
1416 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1423 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1424 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1425 m_strcpy(err->data, err->dsize, _("invalid header field"));
1428 keylen = p - buf->data + 1;
1431 for (tmp = UserHeader;; tmp = tmp->next) {
1432 /* see if there is already a field by this name */
1433 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1434 /* replace the old value */
1435 p_delete(&tmp->data);
1436 tmp->data = buf->data;
1443 tmp->next = string_item_new();
1447 tmp = string_item_new();
1450 tmp->data = buf->data;
1456 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1457 char* errbuf, ssize_t errlen) {
1460 if (m_strncmp("reverse-", s, 8) == 0) {
1462 flags = SORT_REVERSE;
1465 if (m_strncmp("last-", s, 5) == 0) {
1470 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1472 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1476 *((short*) dst->data) = i | flags;
1480 /* if additional data more == 1, we want to resolve synonyms */
1481 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
1483 char buf[LONG_STRING];
1484 struct option_t *ptr = p;
1486 if (DTYPE(ptr->type) == DT_SYN) {
1489 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1491 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_from_string)
1494 mutt_option_value(ptr->option, buf, sizeof(buf));
1495 if (m_strlen(ptr->init) == 0 && buf && *buf)
1496 ptr->init = m_strdup(buf);
1499 static struct option_t* add_option (const char* name, const char* init,
1500 short type, short dodup) {
1501 struct option_t* option = p_new(struct option_t, 1);
1503 option->option = m_strdup(name);
1504 option->type = type;
1506 option->init = dodup ? m_strdup(init) : (char*) init;
1510 /* creates new option_t* of type DT_USER for $user_ var */
1511 static struct option_t* add_user_option (const char* name) {
1512 return (add_option (name, NULL, DT_USER, 1));
1515 /* free()'s option_t* */
1516 static void del_option (void* p) {
1517 struct option_t *ptr = (struct option_t*) p;
1518 char* s = (char*) ptr->data;
1519 p_delete(&ptr->option);
1521 p_delete(&ptr->init);
1525 static int init_expand (char** dst, struct option_t* src) {
1531 if (DTYPE(src->type) == DT_STR ||
1532 DTYPE(src->type) == DT_PATH) {
1533 /* only expand for string as it's the only place where
1534 * we want to expand vars right now */
1535 if (src->init && *src->init) {
1538 len = m_strlen(src->init) + 2;
1539 in.data = p_new(char, len + 1);
1540 snprintf (in.data, len, "\"%s\"", src->init);
1543 mutt_extract_token (&token, &in, 0);
1544 if (token.data && *token.data)
1545 *dst = m_strdup(token.data);
1547 *dst = m_strdup("");
1549 p_delete(&token.data);
1551 *dst = m_strdup("");
1553 /* for non-string: take value as is */
1554 *dst = m_strdup(src->init);
1558 /* if additional data more == 1, we want to resolve synonyms */
1559 static void mutt_restore_default (const char* name __attribute__ ((unused)),
1560 void* p, unsigned long more) {
1561 char errbuf[STRING];
1562 struct option_t* ptr = (struct option_t*) p;
1565 if (DTYPE (ptr->type) == DT_SYN) {
1568 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1572 if (FuncTable[DTYPE (ptr->type)].opt_from_string) {
1573 init_expand (&init, ptr);
1574 if (!FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, init, errbuf,
1576 if (!option (OPTNOCURSES))
1578 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1579 "Please report this error: \"%s\"\n"),
1580 ptr->option, NONULL (init), errbuf);
1586 if (ptr->flags & R_INDEX)
1587 set_option (OPTFORCEREDRAWINDEX);
1588 if (ptr->flags & R_PAGER)
1589 set_option (OPTFORCEREDRAWPAGER);
1590 if (ptr->flags & R_RESORT_SUB)
1591 set_option (OPTSORTSUBTHREADS);
1592 if (ptr->flags & R_RESORT)
1593 set_option (OPTNEEDRESORT);
1594 if (ptr->flags & R_RESORT_INIT)
1595 set_option (OPTRESORTINIT);
1596 if (ptr->flags & R_TREE)
1597 set_option (OPTREDRAWTREE);
1600 /* check whether value for $dsn_return would be valid */
1601 static int check_dsn_return (const char* option __attribute__ ((unused)), unsigned long p,
1602 char* errbuf, ssize_t errlen) {
1603 char* val = (char*) p;
1604 if (val && *val && m_strncmp(val, "hdrs", 4) != 0 &&
1605 m_strncmp(val, "full", 4) != 0) {
1607 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1613 /* check whether value for $dsn_notify would be valid */
1615 check_dsn_notify (const char* option __attribute__ ((unused)),
1616 unsigned long val, char* errbuf, ssize_t errlen)
1618 const char *p = (const char*)val;
1621 const char *q = m_strchrnul(p, ',');
1624 if (!m_strncmp(p, "never", len) && !m_strncmp(p, "delay", len)
1625 && !m_strncmp(p, "failure", len) && !m_strncmp(p, "success", len))
1628 snprintf(errbuf, errlen, _("'%.*s' is invalid for $%s"),
1629 len, p, "dsn_notify");
1639 static int check_num (const char* option, unsigned long p,
1640 char* errbuf, ssize_t errlen) {
1643 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1649 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1650 char* errbuf, ssize_t errlen) {
1651 if (!check_num ("history", p, errbuf, errlen))
1653 mutt_init_history ();
1657 static int check_special (const char* name, unsigned long val,
1658 char* errbuf, ssize_t errlen) {
1661 for (i = 0; SpecialVars[i].name; i++) {
1662 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1663 return (SpecialVars[i].check (SpecialVars[i].name,
1664 val, errbuf, errlen));
1670 static const struct mapping_t* get_sortmap (struct option_t* option) {
1671 const struct mapping_t* map = NULL;
1673 switch (option->type & DT_SUBTYPE_MASK) {
1675 map = SortAliasMethods;
1677 case DT_SORT_BROWSER:
1678 map = SortBrowserMethods;
1681 map = SortKeyMethods;
1684 map = SortAuxMethods;
1693 #define CHECK_PAGER \
1694 if ((CurrentMenu == MENU_PAGER) && \
1695 (!option || (option->flags & R_RESORT))) \
1697 snprintf (err->data, err->dsize, \
1698 _("Not available in this menu.")); \
1702 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1705 int query, unset, inv, reset, r = 0;
1706 struct option_t* option = NULL;
1708 while (MoreArgs (s)) {
1709 /* reset state variables */
1711 unset = data & M_SET_UNSET;
1712 inv = data & M_SET_INV;
1713 reset = data & M_SET_RESET;
1715 if (*s->dptr == '?') {
1719 else if (m_strncmp("no", s->dptr, 2) == 0) {
1723 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1727 else if (*s->dptr == '&') {
1732 /* get the variable name */
1733 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1735 /* resolve synonyms */
1736 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1737 DTYPE (option->type == DT_SYN))
1739 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1740 syn_t* syn = syn_new();
1741 syn->f = m_strdup(CurRCFile);
1745 syn_list_push(&Synonyms, syn);
1749 /* see if we need to add $user_ var */
1750 if (!option && m_strncmp("user_", tmp->data, 5) == 0) {
1751 /* there's no option named like this yet so only add one
1752 * if the action isn't any of: reset, unset, query */
1753 if (!(reset || unset || query || *s->dptr != '=')) {
1754 option = add_user_option (tmp->data);
1755 hash_insert (ConfigOptions, option->option, option, 0);
1759 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1760 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1763 s->dptr = vskipspaces(s->dptr);
1766 if (query || unset || inv) {
1767 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1771 if (s && *s->dptr == '=') {
1772 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1776 if (!m_strcmp("all", tmp->data)) {
1777 if (CurrentMenu == MENU_PAGER) {
1778 snprintf (err->data, err->dsize, _("Not available in this menu."));
1781 hash_map (ConfigOptions, mutt_restore_default, 1);
1782 set_option (OPTFORCEREDRAWINDEX);
1783 set_option (OPTFORCEREDRAWPAGER);
1784 set_option (OPTSORTSUBTHREADS);
1785 set_option (OPTNEEDRESORT);
1786 set_option (OPTRESORTINIT);
1787 set_option (OPTREDRAWTREE);
1790 else if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1791 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1796 mutt_restore_default (NULL, option, 1);
1799 else if (DTYPE (option->type) == DT_BOOL) {
1800 /* XXX this currently ignores the function table
1801 * as we don't get invert and stuff into it */
1802 if (s && *s->dptr == '=') {
1803 if (unset || inv || query) {
1804 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1809 mutt_extract_token (tmp, s, 0);
1810 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1812 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1815 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1821 bool_to_string (err->data, err->dsize, option);
1827 unset_option (option->data);
1829 toggle_option (option->data);
1831 set_option (option->data);
1833 else if (DTYPE (option->type) == DT_STR ||
1834 DTYPE (option->type) == DT_PATH ||
1835 DTYPE (option->type) == DT_ADDR ||
1836 DTYPE (option->type) == DT_MAGIC ||
1837 DTYPE (option->type) == DT_NUM ||
1838 DTYPE (option->type) == DT_SORT ||
1839 DTYPE (option->type) == DT_RX ||
1840 DTYPE (option->type) == DT_USER ||
1841 DTYPE (option->type) == DT_SYS) {
1843 /* XXX maybe we need to get unset into handlers? */
1844 if (DTYPE (option->type) == DT_STR ||
1845 DTYPE (option->type) == DT_PATH ||
1846 DTYPE (option->type) == DT_ADDR ||
1847 DTYPE (option->type) == DT_USER ||
1848 DTYPE (option->type) == DT_SYS) {
1851 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1852 snprintf (err->data, err->dsize, _("$%s is read-only"),
1856 } else if (DTYPE (option->type) == DT_ADDR)
1857 address_list_wipe((address_t **) option->data);
1858 else if (DTYPE (option->type) == DT_USER)
1859 /* to unset $user_ means remove */
1860 hash_delete (ConfigOptions, option->option,
1861 option, del_option);
1863 p_delete((void **)(void *)&option->data);
1868 if (query || *s->dptr != '=') {
1869 FuncTable[DTYPE (option->type)].opt_to_string
1870 (err->data, err->dsize, option);
1874 /* the $madmutt_ variables are read-only */
1875 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1876 snprintf (err->data, err->dsize, _("$%s is read-only"),
1883 mutt_extract_token (tmp, s, 0);
1884 if (!FuncTable[DTYPE (option->type)].opt_from_string
1885 (option, tmp->data, err->data, err->dsize))
1889 else if (DTYPE (option->type) == DT_QUAD) {
1892 quad_to_string (err->data, err->dsize, option);
1896 if (*s->dptr == '=') {
1899 mutt_extract_token (tmp, s, 0);
1900 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1901 set_quadoption (option->data, M_YES);
1902 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1903 set_quadoption (option->data, M_NO);
1904 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1905 set_quadoption (option->data, M_ASKYES);
1906 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1907 set_quadoption (option->data, M_ASKNO);
1909 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1910 tmp->data, option->option);
1917 toggle_quadoption (option->data);
1919 set_quadoption (option->data, M_NO);
1921 set_quadoption (option->data, M_YES);
1925 snprintf (err->data, err->dsize, _("%s: unknown type"),
1931 if (option->flags & R_INDEX)
1932 set_option (OPTFORCEREDRAWINDEX);
1933 if (option->flags & R_PAGER)
1934 set_option (OPTFORCEREDRAWPAGER);
1935 if (option->flags & R_RESORT_SUB)
1936 set_option (OPTSORTSUBTHREADS);
1937 if (option->flags & R_RESORT)
1938 set_option (OPTNEEDRESORT);
1939 if (option->flags & R_RESORT_INIT)
1940 set_option (OPTRESORTINIT);
1941 if (option->flags & R_TREE)
1942 set_option (OPTREDRAWTREE);
1949 /* reads the specified initialization file. returns -1 if errors were found
1950 so that we can pause to let the user know... */
1951 static int source_rc (const char *rcfile, BUFFER * err)
1954 int line = 0, rc = 0, conv = 0;
1956 char *linebuf = NULL;
1957 char *currentline = NULL;
1961 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1962 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1967 while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
1968 conv = ConfigCharset && (*ConfigCharset) && Charset;
1970 currentline = m_strdup(linebuf);
1973 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1976 currentline = linebuf;
1981 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1982 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1983 if (--rc < -MAXERRS) {
1985 p_delete(¤tline);
1994 p_delete(¤tline);
1996 p_delete(&token.data);
2000 mutt_wait_filter (pid);
2002 /* the muttrc source keyword */
2003 snprintf (err->data, err->dsize,
2004 rc >= -MAXERRS ? _("source: errors in %s")
2005 : _("source: reading aborted due too many errors in %s"),
2014 static int parse_source (BUFFER * tmp, BUFFER * s,
2015 unsigned long data __attribute__ ((unused)),
2018 char path[_POSIX_PATH_MAX];
2022 if (mutt_extract_token (tmp, s, 0) != 0) {
2023 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
2027 m_strcpy(path, sizeof(path), tmp->data);
2028 mutt_expand_path (path, sizeof(path));
2030 rc += source_rc (path, err);
2032 while (MoreArgs (s));
2034 return ((rc < 0) ? -1 : 0);
2037 /* line command to execute
2039 token scratch buffer to be used by parser. caller should free
2040 token->data when finished. the reason for this variable is
2041 to avoid having to allocate and deallocate a lot of memory
2042 if we are parsing many lines. the caller can pass in the
2043 memory to use, which avoids having to create new space for
2044 every call to this function.
2046 err where to write error messages */
2047 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
2053 expn.data = expn.dptr = line;
2054 expn.dsize = m_strlen(line);
2058 expn.dptr = vskipspaces(expn.dptr);
2059 while (*expn.dptr) {
2060 if (*expn.dptr == '#')
2061 break; /* rest of line is a comment */
2062 if (*expn.dptr == ';') {
2066 mutt_extract_token (token, &expn, 0);
2067 for (i = 0; Commands[i].name; i++) {
2068 if (!m_strcmp(token->data, Commands[i].name)) {
2069 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
2074 if (!Commands[i].name) {
2075 snprintf (err->data, err->dsize, _("%s: unknown command"),
2076 NONULL (token->data));
2083 p_delete(&expn.data);
2088 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
2089 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
2090 /* initial string that starts completion. No telling how much crap
2091 * the user has typed so far. Allocate LONG_STRING just to be sure! */
2092 char User_typed[LONG_STRING] = { 0 };
2094 int Num_matched = 0; /* Number of matches for completion */
2095 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
2096 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
2098 /* helper function for completion. Changes the dest buffer if
2099 necessary/possible to aid completion.
2100 dest == completion result gets here.
2101 src == candidate for completion.
2102 try == user entered data for completion.
2103 len == length of dest buffer.
2105 static void candidate (char *dest, char *try, const char *src, int len)
2109 if (strstr (src, try) == src) {
2110 Matches[Num_matched++] = src;
2112 m_strcpy(dest, len, src);
2114 for (l = 0; src[l] && src[l] == dest[l]; l++);
2120 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
2124 int spaces; /* keep track of the number of leading spaces on the line */
2126 buffer = vskipspaces(buffer);
2127 spaces = buffer - pt;
2129 pt = buffer + pos - spaces;
2130 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2133 if (pt == buffer) { /* complete cmd */
2134 /* first TAB. Collect all the matches */
2137 m_strcpy(User_typed, sizeof(User_typed), pt);
2138 p_clear(Matches, countof(Matches));
2139 p_clear(Completed, countof(Completed));
2140 for (num = 0; Commands[num].name; num++)
2141 candidate (Completed, User_typed, Commands[num].name,
2143 Matches[Num_matched++] = User_typed;
2145 /* All matches are stored. Longest non-ambiguous string is ""
2146 * i.e. dont change 'buffer'. Fake successful return this time */
2147 if (User_typed[0] == 0)
2151 if (Completed[0] == 0 && User_typed[0])
2154 /* Num_matched will _always_ be atleast 1 since the initial
2155 * user-typed string is always stored */
2156 if (numtabs == 1 && Num_matched == 2)
2157 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2158 else if (numtabs > 1 && Num_matched > 2)
2159 /* cycle thru all the matches */
2160 snprintf (Completed, sizeof(Completed), "%s",
2161 Matches[(numtabs - 2) % Num_matched]);
2163 /* return the completed command */
2164 m_strcpy(buffer, len - spaces, Completed);
2166 else if (!m_strncmp(buffer, "set", 3)
2167 || !m_strncmp(buffer, "unset", 5)
2168 || !m_strncmp(buffer, "reset", 5)
2169 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
2170 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
2173 /* loop through all the possible prefixes (no, inv, ...) */
2174 if (!m_strncmp(buffer, "set", 3)) {
2175 for (num = 0; prefixes[num]; num++) {
2176 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
2177 pt += m_strlen(prefixes[num]);
2183 /* first TAB. Collect all the matches */
2186 m_strcpy(User_typed, sizeof(User_typed), pt);
2187 p_clear(Matches, countof(Matches));
2188 p_clear(Completed, countof(Completed));
2189 for (num = 0; MuttVars[num].option; num++)
2190 candidate(Completed, User_typed, MuttVars[num].option,
2192 Matches[Num_matched++] = User_typed;
2194 /* All matches are stored. Longest non-ambiguous string is ""
2195 * i.e. dont change 'buffer'. Fake successful return this time */
2196 if (User_typed[0] == 0)
2200 if (Completed[0] == 0 && User_typed[0])
2203 /* Num_matched will _always_ be atleast 1 since the initial
2204 * user-typed string is always stored */
2205 if (numtabs == 1 && Num_matched == 2)
2206 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2207 else if (numtabs > 1 && Num_matched > 2)
2208 /* cycle thru all the matches */
2209 snprintf (Completed, sizeof(Completed), "%s",
2210 Matches[(numtabs - 2) % Num_matched]);
2212 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2214 else if (!m_strncmp(buffer, "exec", 4)) {
2215 struct binding_t *menu = km_get_table (CurrentMenu);
2217 if (!menu && CurrentMenu != MENU_PAGER)
2221 /* first TAB. Collect all the matches */
2224 m_strcpy(User_typed, sizeof(User_typed), pt);
2225 p_clear(Matches, countof(Matches));
2226 p_clear(Completed, countof(Completed));
2227 for (num = 0; menu[num].name; num++)
2228 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
2229 /* try the generic menu */
2230 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2232 for (num = 0; menu[num].name; num++)
2233 candidate (Completed, User_typed, menu[num].name,
2236 Matches[Num_matched++] = User_typed;
2238 /* All matches are stored. Longest non-ambiguous string is ""
2239 * i.e. dont change 'buffer'. Fake successful return this time */
2240 if (User_typed[0] == 0)
2244 if (Completed[0] == 0 && User_typed[0])
2247 /* Num_matched will _always_ be atleast 1 since the initial
2248 * user-typed string is always stored */
2249 if (numtabs == 1 && Num_matched == 2)
2250 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2251 else if (numtabs > 1 && Num_matched > 2)
2252 /* cycle thru all the matches */
2253 snprintf (Completed, sizeof(Completed), "%s",
2254 Matches[(numtabs - 2) % Num_matched]);
2256 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2264 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
2266 char var[STRING], *pt = buffer;
2268 struct option_t* option = NULL;
2273 buffer = vskipspaces(buffer);
2274 spaces = buffer - pt;
2276 pt = buffer + pos - spaces;
2277 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2279 pt++; /* move past the space */
2280 if (*pt == '=') /* abort if no var before the '=' */
2283 if (m_strncmp(buffer, "set", 3) == 0) {
2284 m_strcpy(var, sizeof(var), pt);
2285 /* ignore the trailing '=' when comparing */
2286 var[m_strlen(var) - 1] = 0;
2287 if (!(option = hash_find (ConfigOptions, var)))
2288 return 0; /* no such variable. */
2290 char tmp[LONG_STRING], tmp2[LONG_STRING];
2292 ssize_t dlen = buffer + len - pt - spaces;
2293 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2297 if ((DTYPE (option->type) == DT_STR) ||
2298 (DTYPE (option->type) == DT_PATH) ||
2299 (DTYPE (option->type) == DT_RX)) {
2300 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
2301 if (DTYPE (option->type) == DT_PATH)
2302 mutt_pretty_mailbox (tmp);
2304 else if (DTYPE (option->type) == DT_ADDR) {
2305 rfc822_addrcat(tmp, sizeof(tmp), *((address_t **) option->data), 0);
2307 else if (DTYPE (option->type) == DT_QUAD)
2308 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
2309 else if (DTYPE (option->type) == DT_NUM)
2310 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
2311 else if (DTYPE (option->type) == DT_SORT) {
2312 const struct mapping_t *map;
2315 switch (option->type & DT_SUBTYPE_MASK) {
2317 map = SortAliasMethods;
2319 case DT_SORT_BROWSER:
2320 map = SortBrowserMethods;
2323 map = SortKeyMethods;
2329 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2330 snprintf(tmp, sizeof(tmp), "%s%s%s",
2331 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2332 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2334 else if (DTYPE (option->type) == DT_MAGIC) {
2336 switch (DefaultMagic) {
2352 m_strcpy(tmp, sizeof(tmp), p);
2354 else if (DTYPE (option->type) == DT_BOOL)
2355 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
2359 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
2360 if (*s == '\\' || *s == '"')
2366 m_strcpy(tmp, sizeof(tmp), pt);
2367 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2375 /* Implement the -Q command line flag */
2376 int mutt_query_variables (string_list_t * queries)
2380 char errbuff[STRING];
2381 char command[STRING];
2389 err.dsize = sizeof(errbuff);
2391 for (p = queries; p; p = p->next) {
2392 snprintf (command, sizeof(command), "set ?%s\n", p->data);
2393 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2394 fprintf (stderr, "%s\n", err.data);
2395 p_delete(&token.data);
2398 printf ("%s\n", err.data);
2401 p_delete(&token.data);
2405 static int mutt_execute_commands (string_list_t * p)
2408 char errstr[STRING];
2412 err.dsize = sizeof(errstr);
2414 for (; p; p = p->next) {
2415 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2416 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2417 p_delete(&token.data);
2421 p_delete(&token.data);
2425 void mutt_init (int skip_sys_rc, string_list_t * commands)
2428 struct utsname utsname;
2430 char buffer[STRING], error[STRING];
2431 int default_rc = 0, need_pause = 0;
2437 err.dsize = sizeof(error);
2439 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2440 * to have some room for $user_ vars */
2441 ConfigOptions = hash_create (sizeof(MuttVars) * 3);
2442 for (i = 0; MuttVars[i].option; i++) {
2443 if (DTYPE (MuttVars[i].type) != DT_SYS)
2444 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2446 hash_insert (ConfigOptions, MuttVars[i].option,
2447 add_option (MuttVars[i].option, MuttVars[i].init,
2452 * XXX - use something even more difficult to predict?
2454 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2455 "\033]9;%ld\a", (long) time (NULL));
2457 /* on one of the systems I use, getcwd() does not return the same prefix
2458 as is listed in the passwd file */
2459 if ((p = getenv ("HOME")))
2460 Homedir = m_strdup(p);
2462 /* Get some information about the user */
2463 if ((pw = getpwuid (getuid ()))) {
2466 Username = m_strdup(pw->pw_name);
2468 Homedir = m_strdup(pw->pw_dir);
2470 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, GecosMask.rx);
2471 Realname = m_strdup(rnbuf);
2472 Shell = m_strdup(pw->pw_shell);
2478 fputs (_("unable to determine home directory"), stderr);
2481 if ((p = getenv ("USER")))
2482 Username = m_strdup(p);
2485 fputs (_("unable to determine username"), stderr);
2488 Shell = m_strdup((p = getenv ("SHELL")) ? p : "/bin/sh");
2491 /* And about the host... */
2493 /* some systems report the FQDN instead of just the hostname */
2494 if ((p = strchr (utsname.nodename, '.'))) {
2495 Hostname = p_dupstr(utsname.nodename, p - utsname.nodename);
2497 m_strcpy(buffer, sizeof(buffer), p); /* save the domain for below */
2500 Hostname = m_strdup(utsname.nodename);
2502 if (!p && getdnsdomainname(buffer, sizeof(buffer)) == -1)
2503 Fqdn = m_strdup("@");
2505 if (*buffer != '@') {
2506 Fqdn = p_new(char, m_strlen(buffer) + m_strlen(Hostname) + 2);
2507 sprintf (Fqdn, "%s.%s", NONULL(Hostname), buffer);
2510 Fqdn = m_strdup(NONULL (Hostname));
2517 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2519 fgets (buffer, sizeof(buffer), f);
2520 p = vskipspaces(buffer);
2522 while (*q && !isspace(*q))
2525 NewsServer = m_strdup(p);
2529 if ((p = getenv ("NNTPSERVER")))
2530 NewsServer = m_strdup(p);
2533 if ((p = getenv ("MAIL")))
2534 Spoolfile = m_strdup(p);
2535 else if ((p = getenv ("MAILDIR")))
2536 Spoolfile = m_strdup(p);
2539 mutt_concat_path(buffer, sizeof(buffer), NONULL(Homedir), MAILPATH);
2541 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(Username));
2543 Spoolfile = m_strdup(buffer);
2546 if ((p = getenv ("MAILCAPS")))
2547 MailcapPath = m_strdup(p);
2549 /* Default search path from RFC1524 */
2551 m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2552 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2555 Tempdir = m_strdup((p = getenv ("TMPDIR")) ? p : "/tmp");
2557 p = getenv ("VISUAL");
2559 p = getenv ("EDITOR");
2563 Editor = m_strdup(p);
2565 if ((p = getenv ("REPLYTO")) != NULL) {
2568 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2571 buf.data = buf.dptr = buffer;
2572 buf.dsize = m_strlen(buffer);
2575 parse_my_hdr (&token, &buf, 0, &err);
2576 p_delete(&token.data);
2579 if ((p = getenv ("EMAIL")) != NULL)
2580 From = rfc822_parse_adrlist (NULL, p);
2582 charset_initialize();
2584 /* Set standard defaults */
2585 hash_map (ConfigOptions, mutt_set_default, 0);
2586 hash_map (ConfigOptions, mutt_restore_default, 0);
2588 CurrentMenu = MENU_MAIN;
2591 /* Unset suspend by default if we're the session leader */
2592 if (getsid (0) == getpid ())
2593 unset_option (OPTSUSPEND);
2596 mutt_init_history ();
2599 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL (Homedir));
2600 if (access (buffer, F_OK) == -1)
2601 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
2605 Muttrc = m_strdup(buffer);
2608 m_strcpy(buffer, sizeof(buffer), Muttrc);
2610 mutt_expand_path (buffer, sizeof(buffer));
2611 Muttrc = m_strdup(buffer);
2613 p_delete(&AliasFile);
2614 AliasFile = m_strdup(NONULL (Muttrc));
2616 /* Process the global rc file if it exists and the user hasn't explicity
2617 requested not to via "-n". */
2619 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
2621 if (access (buffer, F_OK) == -1)
2622 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
2623 if (access (buffer, F_OK) == -1)
2624 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
2626 if (access (buffer, F_OK) == -1)
2627 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
2628 if (access (buffer, F_OK) != -1) {
2629 if (source_rc (buffer, &err) != 0) {
2630 fputs (err.data, stderr);
2631 fputc ('\n', stderr);
2637 /* Read the user's initialization file. */
2638 if (access (Muttrc, F_OK) != -1) {
2639 if (!option (OPTNOCURSES))
2641 if (source_rc (Muttrc, &err) != 0) {
2642 fputs (err.data, stderr);
2643 fputc ('\n', stderr);
2647 else if (!default_rc) {
2648 /* file specified by -F does not exist */
2649 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2650 mutt_endwin (buffer);
2654 if (mutt_execute_commands (commands) != 0)
2657 /* warn about synonym variables */
2661 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2663 for (syn = Synonyms; syn; syn = syn->next) {
2664 fprintf(stderr, "$%s ($%s should be used) (%s:%d)\n",
2665 syn->o ? NONULL(syn->o->option) : "",
2666 syn->n ? NONULL(syn->n->option) : "",
2667 NONULL(syn->f), syn->l);
2669 fprintf (stderr, _("Warning: synonym variables are scheduled"
2670 " for removal.\n"));
2671 syn_list_wipe(&Synonyms);
2675 if (need_pause && !option (OPTNOCURSES)) {
2676 if (mutt_any_key_to_continue (NULL) == -1)
2681 int mutt_get_hook_type (const char *name)
2683 struct command_t *c;
2685 for (c = Commands; c->name; c++)
2686 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2691 /* dump out the value of all the variables we have */
2692 int mutt_dump_variables (int full) {
2695 /* get all non-synonyms into list... */
2696 for (i = 0; MuttVars[i].option; i++) {
2697 struct option_t *option = MuttVars + i;
2698 char buf[LONG_STRING];
2700 if (DTYPE(option->type) == DT_SYN)
2704 mutt_option_value(option->option, buf, sizeof(buf));
2705 if (!m_strcmp(buf, option->init))
2710 FuncTable[DTYPE(option->type)].opt_to_string
2711 (buf, sizeof(buf), option);
2712 printf ("%s\n", buf);
2715 printf ("\n# vi""m:set ft=muttrc:\n");