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>
26 #include <lib-crypt/crypt.h>
27 #include "mutt_idna.h"
29 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
30 #include "mutt_libesmtp.h"
42 static const struct mapping_t* get_sortmap (struct option_t* option);
43 static int parse_sort (struct option_t* dst, const char *s,
44 const struct mapping_t *map,
45 char* errbuf, ssize_t errlen);
47 static HASH *ConfigOptions = NULL;
49 /* for synonym warning reports: synonym found during parsing */
53 struct option_t* n; /* new */
54 struct option_t* o; /* old */
57 /* for synonym warning reports: list of synonyms found */
58 static list2_t* Synonyms;
59 /* for synonym warning reports: current rc file */
60 static const char* CurRCFile = NULL;
61 /* for synonym warning reports: current rc line */
62 static int CurRCLine = 0;
64 /* prototypes for checking for special vars */
65 static int check_dsn_return (const char* option, unsigned long val,
66 char* errbuf, ssize_t errlen);
67 static int check_dsn_notify (const char* option, unsigned long val,
68 char* errbuf, ssize_t errlen);
69 static int check_history (const char* option, unsigned long val,
70 char* errbuf, ssize_t errlen);
71 /* this checks that numbers are >= 0 */
72 static int check_num (const char* option, unsigned long val,
73 char* errbuf, ssize_t errlen);
75 /* use this to check only */
76 static int check_special (const char* option, unsigned long val,
77 char* errbuf, ssize_t errlen);
79 /* variable <-> sanity check function mappings
80 * when changing these, make sure the proper _from_string handler
85 int (*check) (const char* option, unsigned long val,
86 char* errbuf, ssize_t errlen);
88 { "dsn_notify", check_dsn_notify },
89 { "dsn_return", check_dsn_return },
90 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
91 { "smtp_use_tls", mutt_libesmtp_check_usetls },
93 { "history", check_history },
94 { "pager_index_lines", check_num },
99 /* protos for config type handles: convert value to string */
100 static void bool_to_string (char* dst, ssize_t dstlen, struct option_t* option);
101 static void num_to_string (char* dst, ssize_t dstlen, struct option_t* option);
102 static void str_to_string (char* dst, ssize_t dstlen, struct option_t* option);
103 static void quad_to_string (char* dst, ssize_t dstlen, struct option_t* option);
104 static void sort_to_string (char* dst, ssize_t dstlen, struct option_t* option);
105 static void rx_to_string (char* dst, ssize_t dstlen, struct option_t* option);
106 static void magic_to_string (char* dst, ssize_t dstlen, struct option_t* option);
107 static void addr_to_string (char* dst, ssize_t dstlen, struct option_t* option);
108 static void user_to_string (char* dst, ssize_t dstlen, struct option_t* option);
109 static void sys_to_string (char* dst, ssize_t dstlen, struct option_t* option);
111 /* protos for config type handles: convert to value from string */
112 static int bool_from_string (struct option_t* dst, const char* val,
113 char* errbuf, ssize_t errlen);
114 static int num_from_string (struct option_t* dst, const char* val,
115 char* errbuf, ssize_t errlen);
116 static int str_from_string (struct option_t* dst, const char* val,
117 char* errbuf, ssize_t errlen);
118 static int path_from_string (struct option_t* dst, const char* val,
119 char* errbuf, ssize_t errlen);
120 static int quad_from_string (struct option_t* dst, const char* val,
121 char* errbuf, ssize_t errlen);
122 static int sort_from_string (struct option_t* dst, const char* val,
123 char* errbuf, ssize_t errlen);
124 static int rx_from_string (struct option_t* dst, const char* val,
125 char* errbuf, ssize_t errlen);
126 static int magic_from_string (struct option_t* dst, const char* val,
127 char* errbuf, ssize_t errlen);
128 static int addr_from_string (struct option_t* dst, const char* val,
129 char* errbuf, ssize_t errlen);
130 static int user_from_string (struct option_t* dst, const char* val,
131 char* errbuf, ssize_t errlen);
135 void (*opt_to_string) (char* dst, ssize_t dstlen, struct option_t* option);
136 int (*opt_from_string) (struct option_t* dst, const char* val,
137 char* errbuf, ssize_t errlen);
139 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
140 { DT_BOOL, bool_to_string, bool_from_string },
141 { DT_NUM, num_to_string, num_from_string },
142 { DT_STR, str_to_string, str_from_string },
143 { DT_PATH, str_to_string, path_from_string },
144 { DT_QUAD, quad_to_string, quad_from_string },
145 { DT_SORT, sort_to_string, sort_from_string },
146 { DT_RX, rx_to_string, rx_from_string },
147 { DT_MAGIC, magic_to_string, magic_from_string },
148 /* synonyms should be resolved already so we don't need this
149 * but must define it as DT_ is used for indexing */
150 { DT_SYN, NULL, NULL },
151 { DT_ADDR, addr_to_string, addr_from_string },
152 { DT_USER, user_to_string, user_from_string },
153 { DT_SYS, sys_to_string, NULL },
156 static void bool_to_string (char* dst, ssize_t dstlen,
157 struct option_t* option) {
158 snprintf (dst, dstlen, "%s=%s", option->option,
159 option (option->data) ? "yes" : "no");
162 static int bool_from_string (struct option_t* dst, const char* val,
163 char* errbuf __attribute__ ((unused)),
164 ssize_t errlen __attribute__ ((unused))) {
169 if (ascii_strncasecmp (val, "yes", 3) == 0)
171 else if (ascii_strncasecmp (val, "no", 2) == 0)
177 set_option (dst->data);
179 unset_option (dst->data);
183 static void num_to_string (char* dst, ssize_t dstlen,
184 struct option_t* option) {
186 const char* fmt = (m_strcmp(option->option, "umask") == 0) ?
188 snprintf (dst, dstlen, fmt, option->option,
189 *((short*) option->data));
192 static int num_from_string (struct option_t* dst, const char* val,
193 char* errbuf, ssize_t errlen) {
194 int num = 0, old = 0;
200 num = strtol (val, &t, 0);
202 if (!*val || *t || (short) num != num) {
204 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
210 /* just temporarily accept new val so that check_special for
211 * $history already has it when doing history's init() */
212 old = *((short*) dst->data);
213 *((short*) dst->data) = (short) num;
215 if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
216 *((short*) dst->data) = old;
223 static void str_to_string (char* dst, ssize_t dstlen,
224 struct option_t* option) {
225 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
226 NONULL (*((char**) option->data)));
229 static void user_to_string (char* dst, ssize_t dstlen,
230 struct option_t* option) {
231 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
232 NONULL (((char*) option->data)));
235 static void sys_to_string (char* dst, ssize_t dstlen,
236 struct option_t* option) {
237 char *val = NULL, *t = NULL;
240 /* get some $madmutt_ values dynamically */
241 if (m_strcmp("madmutt_pwd", option->option) == 0) {
242 val = p_new(char, _POSIX_PATH_MAX);
243 val = getcwd (val, _POSIX_PATH_MAX-1);
245 } else if (m_strcmp("madmutt_folder_path", option->option) == 0 &&
246 CurrentFolder && *CurrentFolder) {
248 } else if (m_strcmp("madmutt_folder_name", option->option) == 0 &&
249 CurrentFolder && *CurrentFolder) {
251 ssize_t Maildirlength = m_strlen(Maildir);
254 * if name starts with $folder, just strip it to keep hierarchy
255 * $folder=imap://host, path=imap://host/inbox/b -> inbox/b
257 if (Maildirlength > 0 && m_strncmp(CurrentFolder, Maildir,
258 Maildirlength) == 0 &&
259 m_strlen(CurrentFolder) > Maildirlength) {
260 val = CurrentFolder + Maildirlength;
261 if (Maildir[Maildirlength]!='/')
263 /* if not $folder, just use everything after last / */
264 } else if ((t = strrchr (CurrentFolder, '/')) != NULL)
266 /* default: use as-is */
268 val = (char *) CurrentFolder;
271 val = (char *) option->init;
273 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (val));
278 static int path_from_string (struct option_t* dst, const char* val,
279 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
280 char path[_POSIX_PATH_MAX];
286 p_delete((char**) dst->data);
291 m_strcpy(path, sizeof(path), val);
292 mutt_expand_path (path, sizeof(path));
293 m_strreplace((char **) dst->data, path);
297 static int str_from_string (struct option_t* dst, const char* val,
298 char* errbuf, ssize_t errlen) {
302 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
305 m_strreplace((char**) dst->data, val);
309 static int user_from_string (struct option_t* dst, const char* val,
310 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
311 /* if dst == NULL, we may get here in case the user did unset it,
312 * see parse_set() where item is free()'d before coming here; so
313 * just silently ignore it */
316 if (m_strlen((char*) dst->data) == 0)
317 dst->data = (unsigned long) m_strdup(val);
319 char* s = (char*) dst->data;
320 m_strreplace(&s, val);
322 if (m_strlen(dst->init) == 0)
323 dst->init = m_strdup((char*) dst->data);
327 static void quad_to_string (char* dst, ssize_t dstlen,
328 struct option_t* option) {
329 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
330 snprintf (dst, dstlen, "%s=%s", option->option,
331 vals[quadoption (option->data)]);
334 static int quad_from_string (struct option_t* dst, const char* val,
335 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
340 if (ascii_strncasecmp (val, "yes", 3) == 0)
342 else if (ascii_strncasecmp (val, "no", 2) == 0)
344 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
346 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
352 set_quadoption (dst->data, flag);
356 static void sort_to_string (char* dst, ssize_t dstlen,
357 struct option_t* option) {
358 const struct mapping_t *map = get_sortmap (option);
359 const char *p = NULL;
362 snprintf (dst, sizeof(dst), "%s=unknown", option->option);
366 p = mutt_getnamebyvalue(*((short *)option->data) & SORT_MASK, map);
368 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
369 (*((short *) option->data) & SORT_REVERSE) ?
371 (*((short *) option->data) & SORT_LAST) ? "last-" :
375 static int sort_from_string (struct option_t* dst, const char* val,
376 char* errbuf, ssize_t errlen) {
377 const struct mapping_t *map = NULL;
378 if (!(map = get_sortmap (dst))) {
380 snprintf (errbuf, errlen, _("%s: Unknown type."),
384 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
389 static void rx_to_string (char* dst, ssize_t dstlen,
390 struct option_t* option) {
391 rx_t* p = (rx_t*) option->data;
392 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
393 NONULL (p->pattern));
396 static int rx_from_string (struct option_t* dst, const char* val,
397 char* errbuf, ssize_t errlen) {
400 int flags = 0, e = 0, not = 0;
406 if (option (OPTATTACHMSG) && !m_strcmp(dst->option, "reply_regexp")) {
408 snprintf (errbuf, errlen,
409 "Operation not permitted when in attach-message mode.");
413 if (!((rx_t*) dst->data))
414 *((rx_t**) dst->data) = p_new(rx_t, 1);
416 p = (rx_t*) dst->data;
418 /* something to do? */
419 if (!val || !*val || (p->pattern && m_strcmp(p->pattern, val) == 0))
422 if (m_strcmp(dst->option, "mask") != 0)
423 flags |= mutt_which_case (val);
426 if (m_strcmp(dst->option, "mask") == 0 && *s == '!') {
431 rx = p_new(regex_t, 1);
433 if ((e = REGCOMP (rx, s, flags)) != 0) {
434 regerror (e, rx, errbuf, errlen);
445 m_strreplace(&p->pattern, val);
449 if (m_strcmp(dst->option, "reply_regexp") == 0)
450 mutt_adjust_all_subjects ();
455 static void magic_to_string (char* dst, ssize_t dstlen,
456 struct option_t* option) {
457 const char* s = NULL;
458 switch (option->data) {
459 case M_MBOX: s = "mbox"; break;
460 case M_MMDF: s = "MMDF"; break;
461 case M_MH: s = "MH"; break;
462 case M_MAILDIR: s = "Maildir"; break;
463 default: s = "unknown"; break;
465 snprintf (dst, dstlen, "%s=%s", option->option, s);
468 static int magic_from_string (struct option_t* dst, const char* val,
469 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
472 if (!dst || !val || !*val)
474 if (ascii_strncasecmp (val, "mbox", 4) == 0)
476 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
478 else if (ascii_strncasecmp (val, "mh", 2) == 0)
480 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
486 *((short*) dst->data) = flag;
491 static void addr_to_string (char* dst, ssize_t dstlen,
492 struct option_t* option) {
495 rfc822_write_address (s, sizeof(s), *((address_t**) option->data), 0);
496 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
499 static int addr_from_string (struct option_t* dst, const char* val,
500 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
503 address_list_wipe((address_t**) dst->data);
505 *((address_t**) dst->data) = rfc822_parse_adrlist (NULL, val);
509 int mutt_option_value (const char* val, char* dst, ssize_t dstlen) {
510 struct option_t* option = NULL;
511 char* tmp = NULL, *t = NULL;
514 if (!(option = hash_find (ConfigOptions, val))) {
518 tmp = p_new(char, dstlen+1);
519 FuncTable[DTYPE (option->type)].opt_to_string (tmp, dstlen, option);
521 /* as we get things of type $var=value and don't want to bloat the
522 * above "just" for expansion, we do the stripping here */
523 t = strchr (tmp, '=');
527 if (t[l-1] == '"' && *t == '"') {
532 memcpy (dst, t, l+1);
538 /* for synonym warning reports: adds synonym to end of list */
539 static void syn_add (struct option_t* n, struct option_t* o) {
540 syn_t* tmp = p_new(syn_t, 1);
541 tmp->f = m_strdup(CurRCFile);
545 list_push_back (&Synonyms, tmp);
548 /* for synonym warning reports: free single item (for list_del()) */
549 static void syn_del (void** p) {
550 p_delete(&(*(syn_t**) p)->f);
554 static void toggle_quadoption (int opt)
557 int b = (opt % 4) * 2;
559 QuadOptions[n] ^= (1 << b);
562 void set_quadoption (int opt, int flag)
565 int b = (opt % 4) * 2;
567 QuadOptions[n] &= ~(0x3 << b);
568 QuadOptions[n] |= (flag & 0x3) << b;
571 int quadoption (int opt)
574 int b = (opt % 4) * 2;
576 return (QuadOptions[n] >> b) & 0x3;
579 int query_quadoption (int opt, const char *prompt)
581 int v = quadoption (opt);
589 v = mutt_yesorno (prompt, (v == M_ASKYES));
590 CLEARLINE (LINES - 1);
597 static void add_to_list (string_list_t ** list, const char *str)
599 string_list_t *t, *last = NULL;
601 /* don't add a NULL or empty string to the list */
602 if (!str || *str == '\0')
605 /* check to make sure the item is not already on this list */
606 for (last = *list; last; last = last->next) {
607 if (ascii_strcasecmp (str, last->data) == 0) {
608 /* already on the list, so just ignore it */
616 if (!*list || last) {
617 t = p_new(string_list_t, 1);
618 t->data = m_strdup(str);
629 add_to_rx_list(rx_t **list, const char *s, int flags, BUFFER *err)
636 if (rx_lookup(list, s))
639 rx = rx_compile(s, flags);
641 snprintf(err->data, err->dsize, "Bad regexp: %s\n", s);
645 rx_list_append(list, rx);
649 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
650 const char *templ, BUFFER * err)
652 SPAM_LIST *t = NULL, *last = NULL;
657 if (!pat || !*pat || !templ)
660 if (!(rx = rx_compile (pat, REG_ICASE))) {
661 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
665 /* check to make sure the item is not already on this list */
666 for (last = *list; last; last = last->next) {
667 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
668 /* Already on the list. Formerly we just skipped this case, but
669 * now we're supporting removals, which means we're supporting
670 * re-adds conceptually. So we probably want this to imply a
671 * removal, then do an add. We can achieve the removal by freeing
672 * the template, and leaving t pointed at the current item.
675 p_delete(&t->template);
682 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
683 * update. Otherwise we want to make a new one to link at the list's end.
686 t = mutt_new_spam_list ();
694 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
695 t->template = m_strdup(templ);
697 /* Find highest match number in template string */
699 for (p = templ; *p;) {
704 while (*p && isdigit ((int) *p))
710 t->nmatch++; /* match 0 is always the whole expr */
715 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
717 SPAM_LIST *spam, *prev;
720 /* Being first is a special case. */
724 if (spam->rx && !m_strcmp(spam->rx->pattern, pat)) {
726 rx_delete(&spam->rx);
727 p_delete(&spam->template);
733 for (spam = prev->next; spam;) {
734 if (!m_strcmp(spam->rx->pattern, pat)) {
735 prev->next = spam->next;
736 rx_delete(&spam->rx);
737 p_delete(&spam->template);
750 static void remove_from_list (string_list_t ** l, const char *str)
752 string_list_t *p, *last = NULL;
754 if (m_strcmp("*", str) == 0)
755 string_list_wipe(l); /* ``unCMD *'' means delete all current entries */
760 if (ascii_strcasecmp (str, p->data) == 0) {
763 last->next = p->next;
776 static int remove_from_rx_list(rx_t **l, const char *str)
778 if (m_strcmp("*", str) == 0) {
783 l = rx_lookup(l, str);
785 rx_t *r = rx_list_pop(l);
793 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
797 unsigned long res = 0;
799 struct option_t* option = NULL;
802 mutt_extract_token (tmp, s, 0);
804 /* is the item defined as a variable or a function? */
805 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL)
808 for (i = 0; !res && i < MENU_MAX; i++) {
809 struct binding_t *b = km_get_table (Menus[i].value);
814 for (j = 0; b[j].name; j++)
815 if (!ascii_strncasecmp (tmp->data, b[j].name, m_strlen(tmp->data))
816 && (m_strlen(b[j].name) == m_strlen(tmp->data))) {
822 /* check for feature_* */
823 if (!res && ascii_strncasecmp (tmp->data, "feature_", 8) == 0 &&
824 (j = m_strlen(tmp->data)) > 8) {
826 while (Features[i]) {
827 if (m_strlen(Features[i]) == j-8 &&
828 ascii_strncasecmp (Features[i], tmp->data+8, j-8) == 0) {
838 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
840 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
844 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
847 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
848 mutt_error ("Error: %s", err->data);
849 p_delete(&token.data);
852 p_delete(&token.data);
857 static int parse_unignore (BUFFER * buf, BUFFER * s,
858 unsigned long data __attribute__ ((unused)),
859 BUFFER * err __attribute__ ((unused)))
862 mutt_extract_token (buf, s, 0);
864 /* don't add "*" to the unignore list */
865 if (strcmp (buf->data, "*"))
866 add_to_list (&UnIgnore, buf->data);
868 remove_from_list (&Ignore, buf->data);
870 while (MoreArgs (s));
875 static int parse_ignore (BUFFER * buf, BUFFER * s,
876 unsigned long data __attribute__ ((unused)),
877 BUFFER * err __attribute__ ((unused)))
880 mutt_extract_token (buf, s, 0);
881 remove_from_list (&UnIgnore, buf->data);
882 add_to_list (&Ignore, buf->data);
884 while (MoreArgs (s));
889 static int parse_list (BUFFER * buf, BUFFER * s,
890 unsigned long data __attribute__ ((unused)),
891 BUFFER * err __attribute__ ((unused)))
894 mutt_extract_token (buf, s, 0);
895 add_to_list ((string_list_t **) data, buf->data);
897 while (MoreArgs (s));
902 static void _alternates_clean (void)
906 if (Context && Context->msgcount) {
907 for (i = 0; i < Context->msgcount; i++)
908 Context->hdrs[i]->recip_valid = 0;
912 static int parse_alternates (BUFFER * buf, BUFFER * s,
913 unsigned long data __attribute__ ((unused)),
914 BUFFER * err __attribute__ ((unused)))
916 _alternates_clean ();
918 mutt_extract_token (buf, s, 0);
919 remove_from_rx_list (&UnAlternates, buf->data);
921 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
924 while (MoreArgs (s));
929 static int parse_unalternates (BUFFER * buf, BUFFER * s,
930 unsigned long data __attribute__ ((unused)),
931 BUFFER * err __attribute__ ((unused)))
933 _alternates_clean ();
935 mutt_extract_token (buf, s, 0);
936 remove_from_rx_list (&Alternates, buf->data);
938 if (m_strcmp(buf->data, "*") &&
939 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
943 while (MoreArgs (s));
948 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
955 /* Insist on at least one parameter */
958 m_strcpy(err->data, err->dsize, _("spam: no matching pattern"));
960 m_strcpy(err->data, err->dsize, _("nospam: no matching pattern"));
964 /* Extract the first token, a regexp */
965 mutt_extract_token (buf, s, 0);
967 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
968 if (data == M_SPAM) {
969 /* If there's a second parameter, it's a template for the spam tag. */
971 mutt_extract_token (&templ, s, 0);
973 /* Add to the spam list. */
974 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
975 p_delete(&templ.data);
978 p_delete(&templ.data);
981 /* If not, try to remove from the nospam list. */
983 remove_from_rx_list (&NoSpamList, buf->data);
989 /* M_NOSPAM is for nospam commands. */
990 else if (data == M_NOSPAM) {
991 /* nospam only ever has one parameter. */
993 /* "*" is a special case. */
994 if (!m_strcmp(buf->data, "*")) {
995 mutt_free_spam_list (&SpamList);
996 rx_list_wipe(&NoSpamList);
1000 /* If it's on the spam list, just remove it. */
1001 if (remove_from_spam_list (&SpamList, buf->data) != 0)
1004 /* Otherwise, add it to the nospam list. */
1005 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
1011 /* This should not happen. */
1012 m_strcpy(err->data, err->dsize, "This is no good at all.");
1016 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
1017 BUFFER * err __attribute__ ((unused)))
1020 mutt_extract_token (buf, s, 0);
1022 * Check for deletion of entire list
1024 if (m_strcmp(buf->data, "*") == 0) {
1025 string_list_wipe((string_list_t **) data);
1028 remove_from_list ((string_list_t **) data, buf->data);
1030 while (MoreArgs (s));
1035 static int parse_lists (BUFFER * buf, BUFFER * s,
1036 unsigned long data __attribute__ ((unused)),
1040 mutt_extract_token (buf, s, 0);
1041 remove_from_rx_list (&UnMailLists, buf->data);
1043 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1046 while (MoreArgs (s));
1051 /* always wise to do what someone else did before */
1052 static void _attachments_clean (void) {
1054 if (Context && Context->msgcount) {
1055 for (i = 0; i < Context->msgcount; i++)
1056 Context->hdrs[i]->attach_valid = 0;
1060 static int parse_attach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
1061 BUFFER *err __attribute__ ((unused))) {
1063 string_list_t *listp, *lastp;
1068 /* Find the last item in the list that data points to. */
1070 for (listp = *ldata; listp; listp = listp->next) {
1071 a = (ATTACH_MATCH *)listp->data;
1076 mutt_extract_token (buf, s, 0);
1078 if (!buf->data || *buf->data == '\0')
1081 a = p_new(ATTACH_MATCH, 1);
1083 /* some cheap hacks that I expect to remove */
1084 if (!m_strcasecmp(buf->data, "any"))
1085 a->major = m_strdup("*/.*");
1086 else if (!m_strcasecmp(buf->data, "none"))
1087 a->major = m_strdup("cheap_hack/this_should_never_match");
1089 a->major = m_strdup(buf->data);
1091 if ((p = strchr(a->major, '/'))) {
1096 a->minor = "unknown";
1099 len = m_strlen(a->minor);
1100 tmpminor = p_new(char, len + 3);
1101 strcpy(&tmpminor[1], a->minor); /* __STRCPY_CHECKED__ */
1103 tmpminor[len+1] = '$';
1104 tmpminor[len+2] = '\0';
1106 a->major_int = mutt_check_mime_type(a->major);
1107 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
1109 p_delete(&tmpminor);
1111 listp = p_new(string_list_t, 1);
1112 listp->data = (char *)a;
1115 lastp->next = listp;
1121 while (MoreArgs (s));
1123 _attachments_clean();
1127 static int parse_unattach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
1128 BUFFER *err __attribute__ ((unused))) {
1130 string_list_t *lp, *lastp, *newlp;
1136 mutt_extract_token (buf, s, 0);
1138 if (!m_strcasecmp(buf->data, "any"))
1139 tmp = m_strdup("*/.*");
1140 else if (!m_strcasecmp(buf->data, "none"))
1141 tmp = m_strdup("cheap_hack/this_should_never_match");
1143 tmp = m_strdup(buf->data);
1145 if ((minor = strchr(tmp, '/'))) {
1149 minor = m_strdup("unknown");
1151 major = mutt_check_mime_type(tmp);
1153 /* We must do our own walk here because remove_from_list() will only
1154 * remove the string_list_t->data, not anything pointed to by the string_list_t->data. */
1156 for(lp = *ldata; lp; ) {
1157 a = (ATTACH_MATCH *)lp->data;
1158 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
1159 regfree(&a->minor_rx);
1160 p_delete(&a->major);
1162 /* Relink backward */
1164 lastp->next = lp->next;
1169 p_delete(&lp->data); /* same as a */
1179 while (MoreArgs (s));
1182 _attachments_clean();
1186 static int print_attach_list (string_list_t *lp, char op, const char *name) {
1188 printf("attachments %c%s %s/%s\n", op, name,
1189 ((ATTACH_MATCH *)lp->data)->major,
1190 ((ATTACH_MATCH *)lp->data)->minor);
1197 static int parse_attachments (BUFFER *buf, BUFFER *s,
1198 unsigned long data __attribute__ ((unused)),
1201 string_list_t **listp;
1203 mutt_extract_token(buf, s, 0);
1204 if (!buf->data || *buf->data == '\0') {
1205 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
1209 category = buf->data;
1215 printf("\nCurrent attachments settings:\n\n");
1216 print_attach_list(AttachAllow, '+', "A");
1217 print_attach_list(AttachExclude, '-', "A");
1218 print_attach_list(InlineAllow, '+', "I");
1219 print_attach_list(InlineExclude, '-', "I");
1220 set_option (OPTFORCEREDRAWINDEX);
1221 set_option (OPTFORCEREDRAWPAGER);
1222 mutt_any_key_to_continue (NULL);
1226 if (op != '+' && op != '-') {
1230 if (!m_strncasecmp(category, "attachment", strlen(category))) {
1232 listp = &AttachAllow;
1234 listp = &AttachExclude;
1236 else if (!m_strncasecmp(category, "inline", strlen(category))) {
1238 listp = &InlineAllow;
1240 listp = &InlineExclude;
1242 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
1246 return parse_attach_list(buf, s, listp, err);
1249 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
1251 string_list_t **listp;
1253 mutt_extract_token(buf, s, 0);
1254 if (!buf->data || *buf->data == '\0') {
1255 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
1261 if (op != '+' && op != '-') {
1265 if (!m_strncasecmp(p, "attachment", strlen(p))) {
1267 listp = &AttachAllow;
1269 listp = &AttachExclude;
1271 else if (!m_strncasecmp(p, "inline", strlen(p))) {
1273 listp = &InlineAllow;
1275 listp = &InlineExclude;
1278 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
1282 return parse_unattach_list(buf, s, listp, err);
1285 static int parse_unlists (BUFFER * buf, BUFFER * s,
1286 unsigned long data __attribute__ ((unused)),
1287 BUFFER * err __attribute__ ((unused)))
1290 mutt_extract_token (buf, s, 0);
1291 remove_from_rx_list (&SubscribedLists, buf->data);
1292 remove_from_rx_list (&MailLists, buf->data);
1294 if (m_strcmp(buf->data, "*") &&
1295 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1298 while (MoreArgs (s));
1303 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1307 mutt_extract_token (buf, s, 0);
1308 remove_from_rx_list (&UnMailLists, buf->data);
1309 remove_from_rx_list (&UnSubscribedLists, buf->data);
1311 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1313 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1316 while (MoreArgs (s));
1321 static int parse_unsubscribe (BUFFER * buf, BUFFER * s,
1322 unsigned long data __attribute__ ((unused)),
1323 BUFFER * err __attribute__ ((unused)))
1326 mutt_extract_token (buf, s, 0);
1327 remove_from_rx_list (&SubscribedLists, buf->data);
1329 if (m_strcmp(buf->data, "*") &&
1330 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1333 while (MoreArgs (s));
1338 static int parse_unalias (BUFFER * buf, BUFFER * s,
1339 unsigned long data __attribute__ ((unused)),
1340 BUFFER * err __attribute__ ((unused)))
1342 alias_t *tmp, *last = NULL;
1345 mutt_extract_token (buf, s, 0);
1347 if (m_strcmp("*", buf->data) == 0) {
1348 if (CurrentMenu == MENU_ALIAS) {
1349 for (tmp = Aliases; tmp; tmp = tmp->next)
1351 set_option (OPTFORCEREDRAWINDEX);
1354 alias_list_wipe(&Aliases);
1358 for (tmp = Aliases; tmp; tmp = tmp->next) {
1359 if (m_strcasecmp(buf->data, tmp->name) == 0) {
1360 if (CurrentMenu == MENU_ALIAS) {
1362 set_option (OPTFORCEREDRAWINDEX);
1367 last->next = tmp->next;
1369 Aliases = tmp->next;
1371 alias_list_wipe(&tmp);
1377 while (MoreArgs (s));
1381 static int parse_alias (BUFFER * buf, BUFFER * s,
1382 unsigned long data __attribute__ ((unused)),
1385 alias_t *tmp = Aliases;
1386 alias_t *last = NULL;
1389 if (!MoreArgs (s)) {
1390 m_strcpy(err->data, err->dsize, _("alias: no address"));
1394 mutt_extract_token (buf, s, 0);
1396 /* check to see if an alias with this name already exists */
1397 for (; tmp; tmp = tmp->next) {
1398 if (!m_strcasecmp(tmp->name, buf->data))
1404 /* create a new alias */
1406 tmp->name = m_strdup(buf->data);
1407 /* give the main addressbook code a chance */
1408 if (CurrentMenu == MENU_ALIAS)
1409 set_option (OPTMENUCALLER);
1412 /* override the previous value */
1413 address_list_wipe(&tmp->addr);
1414 if (CurrentMenu == MENU_ALIAS)
1415 set_option (OPTFORCEREDRAWINDEX);
1418 mutt_extract_token (buf, s,
1419 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1420 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1425 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1426 snprintf (err->data, err->dsize,
1427 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1436 parse_unmy_hdr (BUFFER * buf, BUFFER * s,
1437 unsigned long data __attribute__ ((unused)),
1438 BUFFER * err __attribute__ ((unused)))
1440 string_list_t *last = NULL;
1441 string_list_t *tmp = UserHeader;
1446 mutt_extract_token (buf, s, 0);
1447 if (m_strcmp("*", buf->data) == 0)
1448 string_list_wipe(&UserHeader);
1453 l = m_strlen(buf->data);
1454 if (buf->data[l - 1] == ':')
1458 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1459 && tmp->data[l] == ':') {
1462 last->next = tmp->next;
1464 UserHeader = tmp->next;
1467 string_list_wipe(&ptr);
1476 while (MoreArgs (s));
1480 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1487 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1488 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1489 m_strcpy(err->data, err->dsize, _("invalid header field"));
1492 keylen = p - buf->data + 1;
1495 for (tmp = UserHeader;; tmp = tmp->next) {
1496 /* see if there is already a field by this name */
1497 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1498 /* replace the old value */
1499 p_delete(&tmp->data);
1500 tmp->data = buf->data;
1507 tmp->next = string_item_new();
1511 tmp = string_item_new();
1514 tmp->data = buf->data;
1520 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1521 char* errbuf, ssize_t errlen) {
1524 if (m_strncmp("reverse-", s, 8) == 0) {
1526 flags = SORT_REVERSE;
1529 if (m_strncmp("last-", s, 5) == 0) {
1534 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1536 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1540 *((short*) dst->data) = i | flags;
1544 /* if additional data more == 1, we want to resolve synonyms */
1545 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
1547 char buf[LONG_STRING];
1548 struct option_t *ptr = p;
1550 if (DTYPE(ptr->type) == DT_SYN) {
1553 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1555 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_from_string)
1558 mutt_option_value(ptr->option, buf, sizeof(buf));
1559 if (m_strlen(ptr->init) == 0 && buf && *buf)
1560 ptr->init = m_strdup(buf);
1563 static struct option_t* add_option (const char* name, const char* init,
1564 short type, short dodup) {
1565 struct option_t* option = p_new(struct option_t, 1);
1567 option->option = m_strdup(name);
1568 option->type = type;
1570 option->init = dodup ? m_strdup(init) : (char*) init;
1574 /* creates new option_t* of type DT_USER for $user_ var */
1575 static struct option_t* add_user_option (const char* name) {
1576 return (add_option (name, NULL, DT_USER, 1));
1579 /* free()'s option_t* */
1580 static void del_option (void* p) {
1581 struct option_t *ptr = (struct option_t*) p;
1582 char* s = (char*) ptr->data;
1583 p_delete(&ptr->option);
1585 p_delete(&ptr->init);
1589 static int init_expand (char** dst, struct option_t* src) {
1595 if (DTYPE(src->type) == DT_STR ||
1596 DTYPE(src->type) == DT_PATH) {
1597 /* only expand for string as it's the only place where
1598 * we want to expand vars right now */
1599 if (src->init && *src->init) {
1602 len = m_strlen(src->init) + 2;
1603 in.data = p_new(char, len + 1);
1604 snprintf (in.data, len, "\"%s\"", src->init);
1607 mutt_extract_token (&token, &in, 0);
1608 if (token.data && *token.data)
1609 *dst = m_strdup(token.data);
1611 *dst = m_strdup("");
1613 p_delete(&token.data);
1615 *dst = m_strdup("");
1617 /* for non-string: take value as is */
1618 *dst = m_strdup(src->init);
1622 /* if additional data more == 1, we want to resolve synonyms */
1623 static void mutt_restore_default (const char* name __attribute__ ((unused)),
1624 void* p, unsigned long more) {
1625 char errbuf[STRING];
1626 struct option_t* ptr = (struct option_t*) p;
1629 if (DTYPE (ptr->type) == DT_SYN) {
1632 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1636 if (FuncTable[DTYPE (ptr->type)].opt_from_string) {
1637 init_expand (&init, ptr);
1638 if (!FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, init, errbuf,
1640 if (!option (OPTNOCURSES))
1642 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1643 "Please report this error: \"%s\"\n"),
1644 ptr->option, NONULL (init), errbuf);
1650 if (ptr->flags & R_INDEX)
1651 set_option (OPTFORCEREDRAWINDEX);
1652 if (ptr->flags & R_PAGER)
1653 set_option (OPTFORCEREDRAWPAGER);
1654 if (ptr->flags & R_RESORT_SUB)
1655 set_option (OPTSORTSUBTHREADS);
1656 if (ptr->flags & R_RESORT)
1657 set_option (OPTNEEDRESORT);
1658 if (ptr->flags & R_RESORT_INIT)
1659 set_option (OPTRESORTINIT);
1660 if (ptr->flags & R_TREE)
1661 set_option (OPTREDRAWTREE);
1664 /* check whether value for $dsn_return would be valid */
1665 static int check_dsn_return (const char* option __attribute__ ((unused)), unsigned long p,
1666 char* errbuf, ssize_t errlen) {
1667 char* val = (char*) p;
1668 if (val && *val && m_strncmp(val, "hdrs", 4) != 0 &&
1669 m_strncmp(val, "full", 4) != 0) {
1671 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1677 /* check whether value for $dsn_notify would be valid */
1679 check_dsn_notify (const char* option __attribute__ ((unused)),
1680 unsigned long val, char* errbuf, ssize_t errlen)
1682 const char *p = (const char*)val;
1685 const char *q = m_strchrnul(p, ',');
1688 if (!m_strncmp(p, "never", len) && !m_strncmp(p, "delay", len)
1689 && !m_strncmp(p, "failure", len) && !m_strncmp(p, "success", len))
1692 snprintf(errbuf, errlen, _("'%.*s' is invalid for $%s"),
1693 len, p, "dsn_notify");
1703 static int check_num (const char* option, unsigned long p,
1704 char* errbuf, ssize_t errlen) {
1707 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1713 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1714 char* errbuf, ssize_t errlen) {
1715 if (!check_num ("history", p, errbuf, errlen))
1717 mutt_init_history ();
1721 static int check_special (const char* name, unsigned long val,
1722 char* errbuf, ssize_t errlen) {
1725 for (i = 0; SpecialVars[i].name; i++) {
1726 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1727 return (SpecialVars[i].check (SpecialVars[i].name,
1728 val, errbuf, errlen));
1734 static const struct mapping_t* get_sortmap (struct option_t* option) {
1735 const struct mapping_t* map = NULL;
1737 switch (option->type & DT_SUBTYPE_MASK) {
1739 map = SortAliasMethods;
1741 case DT_SORT_BROWSER:
1742 map = SortBrowserMethods;
1745 map = SortKeyMethods;
1748 map = SortAuxMethods;
1757 #define CHECK_PAGER \
1758 if ((CurrentMenu == MENU_PAGER) && \
1759 (!option || (option->flags & R_RESORT))) \
1761 snprintf (err->data, err->dsize, \
1762 _("Not available in this menu.")); \
1766 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1769 int query, unset, inv, reset, r = 0;
1770 struct option_t* option = NULL;
1772 while (MoreArgs (s)) {
1773 /* reset state variables */
1775 unset = data & M_SET_UNSET;
1776 inv = data & M_SET_INV;
1777 reset = data & M_SET_RESET;
1779 if (*s->dptr == '?') {
1783 else if (m_strncmp("no", s->dptr, 2) == 0) {
1787 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1791 else if (*s->dptr == '&') {
1796 /* get the variable name */
1797 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1799 /* resolve synonyms */
1800 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1801 DTYPE (option->type == DT_SYN)) {
1802 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1803 syn_add (newopt, option);
1807 /* see if we need to add $user_ var */
1808 if (!option && m_strncmp("user_", tmp->data, 5) == 0) {
1809 /* there's no option named like this yet so only add one
1810 * if the action isn't any of: reset, unset, query */
1811 if (!(reset || unset || query || *s->dptr != '=')) {
1812 option = add_user_option (tmp->data);
1813 hash_insert (ConfigOptions, option->option, option, 0);
1817 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1818 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1821 s->dptr = vskipspaces(s->dptr);
1824 if (query || unset || inv) {
1825 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1829 if (s && *s->dptr == '=') {
1830 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1834 if (!m_strcmp("all", tmp->data)) {
1835 if (CurrentMenu == MENU_PAGER) {
1836 snprintf (err->data, err->dsize, _("Not available in this menu."));
1839 hash_map (ConfigOptions, mutt_restore_default, 1);
1840 set_option (OPTFORCEREDRAWINDEX);
1841 set_option (OPTFORCEREDRAWPAGER);
1842 set_option (OPTSORTSUBTHREADS);
1843 set_option (OPTNEEDRESORT);
1844 set_option (OPTRESORTINIT);
1845 set_option (OPTREDRAWTREE);
1848 else if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1849 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1854 mutt_restore_default (NULL, option, 1);
1857 else if (DTYPE (option->type) == DT_BOOL) {
1858 /* XXX this currently ignores the function table
1859 * as we don't get invert and stuff into it */
1860 if (s && *s->dptr == '=') {
1861 if (unset || inv || query) {
1862 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1867 mutt_extract_token (tmp, s, 0);
1868 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1870 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1873 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1879 bool_to_string (err->data, err->dsize, option);
1885 unset_option (option->data);
1887 toggle_option (option->data);
1889 set_option (option->data);
1891 else if (DTYPE (option->type) == DT_STR ||
1892 DTYPE (option->type) == DT_PATH ||
1893 DTYPE (option->type) == DT_ADDR ||
1894 DTYPE (option->type) == DT_MAGIC ||
1895 DTYPE (option->type) == DT_NUM ||
1896 DTYPE (option->type) == DT_SORT ||
1897 DTYPE (option->type) == DT_RX ||
1898 DTYPE (option->type) == DT_USER ||
1899 DTYPE (option->type) == DT_SYS) {
1901 /* XXX maybe we need to get unset into handlers? */
1902 if (DTYPE (option->type) == DT_STR ||
1903 DTYPE (option->type) == DT_PATH ||
1904 DTYPE (option->type) == DT_ADDR ||
1905 DTYPE (option->type) == DT_USER ||
1906 DTYPE (option->type) == DT_SYS) {
1909 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1910 snprintf (err->data, err->dsize, _("$%s is read-only"),
1914 } else if (DTYPE (option->type) == DT_ADDR)
1915 address_list_wipe((address_t **) option->data);
1916 else if (DTYPE (option->type) == DT_USER)
1917 /* to unset $user_ means remove */
1918 hash_delete (ConfigOptions, option->option,
1919 option, del_option);
1921 p_delete((void **)(void *)&option->data);
1926 if (query || *s->dptr != '=') {
1927 FuncTable[DTYPE (option->type)].opt_to_string
1928 (err->data, err->dsize, option);
1932 /* the $madmutt_ variables are read-only */
1933 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1934 snprintf (err->data, err->dsize, _("$%s is read-only"),
1941 mutt_extract_token (tmp, s, 0);
1942 if (!FuncTable[DTYPE (option->type)].opt_from_string
1943 (option, tmp->data, err->data, err->dsize))
1947 else if (DTYPE (option->type) == DT_QUAD) {
1950 quad_to_string (err->data, err->dsize, option);
1954 if (*s->dptr == '=') {
1957 mutt_extract_token (tmp, s, 0);
1958 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1959 set_quadoption (option->data, M_YES);
1960 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1961 set_quadoption (option->data, M_NO);
1962 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1963 set_quadoption (option->data, M_ASKYES);
1964 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1965 set_quadoption (option->data, M_ASKNO);
1967 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1968 tmp->data, option->option);
1975 toggle_quadoption (option->data);
1977 set_quadoption (option->data, M_NO);
1979 set_quadoption (option->data, M_YES);
1983 snprintf (err->data, err->dsize, _("%s: unknown type"),
1989 if (option->flags & R_INDEX)
1990 set_option (OPTFORCEREDRAWINDEX);
1991 if (option->flags & R_PAGER)
1992 set_option (OPTFORCEREDRAWPAGER);
1993 if (option->flags & R_RESORT_SUB)
1994 set_option (OPTSORTSUBTHREADS);
1995 if (option->flags & R_RESORT)
1996 set_option (OPTNEEDRESORT);
1997 if (option->flags & R_RESORT_INIT)
1998 set_option (OPTRESORTINIT);
1999 if (option->flags & R_TREE)
2000 set_option (OPTREDRAWTREE);
2007 /* reads the specified initialization file. returns -1 if errors were found
2008 so that we can pause to let the user know... */
2009 static int source_rc (const char *rcfile, BUFFER * err)
2012 int line = 0, rc = 0, conv = 0;
2014 char *linebuf = NULL;
2015 char *currentline = NULL;
2019 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
2020 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
2025 while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
2026 conv = ConfigCharset && (*ConfigCharset) && Charset;
2028 currentline = m_strdup(linebuf);
2031 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
2034 currentline = linebuf;
2039 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
2040 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
2041 if (--rc < -MAXERRS) {
2043 p_delete(¤tline);
2052 p_delete(¤tline);
2054 p_delete(&token.data);
2058 mutt_wait_filter (pid);
2060 /* the muttrc source keyword */
2061 snprintf (err->data, err->dsize,
2062 rc >= -MAXERRS ? _("source: errors in %s")
2063 : _("source: reading aborted due too many errors in %s"),
2072 static int parse_source (BUFFER * tmp, BUFFER * s,
2073 unsigned long data __attribute__ ((unused)),
2076 char path[_POSIX_PATH_MAX];
2080 if (mutt_extract_token (tmp, s, 0) != 0) {
2081 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
2085 m_strcpy(path, sizeof(path), tmp->data);
2086 mutt_expand_path (path, sizeof(path));
2088 rc += source_rc (path, err);
2090 while (MoreArgs (s));
2092 return ((rc < 0) ? -1 : 0);
2095 /* line command to execute
2097 token scratch buffer to be used by parser. caller should free
2098 token->data when finished. the reason for this variable is
2099 to avoid having to allocate and deallocate a lot of memory
2100 if we are parsing many lines. the caller can pass in the
2101 memory to use, which avoids having to create new space for
2102 every call to this function.
2104 err where to write error messages */
2105 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
2111 expn.data = expn.dptr = line;
2112 expn.dsize = m_strlen(line);
2116 expn.dptr = vskipspaces(expn.dptr);
2117 while (*expn.dptr) {
2118 if (*expn.dptr == '#')
2119 break; /* rest of line is a comment */
2120 if (*expn.dptr == ';') {
2124 mutt_extract_token (token, &expn, 0);
2125 for (i = 0; Commands[i].name; i++) {
2126 if (!m_strcmp(token->data, Commands[i].name)) {
2127 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
2132 if (!Commands[i].name) {
2133 snprintf (err->data, err->dsize, _("%s: unknown command"),
2134 NONULL (token->data));
2141 p_delete(&expn.data);
2146 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
2147 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
2148 /* initial string that starts completion. No telling how much crap
2149 * the user has typed so far. Allocate LONG_STRING just to be sure! */
2150 char User_typed[LONG_STRING] = { 0 };
2152 int Num_matched = 0; /* Number of matches for completion */
2153 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
2154 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
2156 /* helper function for completion. Changes the dest buffer if
2157 necessary/possible to aid completion.
2158 dest == completion result gets here.
2159 src == candidate for completion.
2160 try == user entered data for completion.
2161 len == length of dest buffer.
2163 static void candidate (char *dest, char *try, const char *src, int len)
2167 if (strstr (src, try) == src) {
2168 Matches[Num_matched++] = src;
2170 m_strcpy(dest, len, src);
2172 for (l = 0; src[l] && src[l] == dest[l]; l++);
2178 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
2182 int spaces; /* keep track of the number of leading spaces on the line */
2184 buffer = vskipspaces(buffer);
2185 spaces = buffer - pt;
2187 pt = buffer + pos - spaces;
2188 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2191 if (pt == buffer) { /* complete cmd */
2192 /* first TAB. Collect all the matches */
2195 m_strcpy(User_typed, sizeof(User_typed), pt);
2196 p_clear(Matches, countof(Matches));
2197 p_clear(Completed, countof(Completed));
2198 for (num = 0; Commands[num].name; num++)
2199 candidate (Completed, User_typed, Commands[num].name,
2201 Matches[Num_matched++] = User_typed;
2203 /* All matches are stored. Longest non-ambiguous string is ""
2204 * i.e. dont change 'buffer'. Fake successful return this time */
2205 if (User_typed[0] == 0)
2209 if (Completed[0] == 0 && User_typed[0])
2212 /* Num_matched will _always_ be atleast 1 since the initial
2213 * user-typed string is always stored */
2214 if (numtabs == 1 && Num_matched == 2)
2215 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2216 else if (numtabs > 1 && Num_matched > 2)
2217 /* cycle thru all the matches */
2218 snprintf (Completed, sizeof(Completed), "%s",
2219 Matches[(numtabs - 2) % Num_matched]);
2221 /* return the completed command */
2222 m_strcpy(buffer, len - spaces, Completed);
2224 else if (!m_strncmp(buffer, "set", 3)
2225 || !m_strncmp(buffer, "unset", 5)
2226 || !m_strncmp(buffer, "reset", 5)
2227 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
2228 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
2231 /* loop through all the possible prefixes (no, inv, ...) */
2232 if (!m_strncmp(buffer, "set", 3)) {
2233 for (num = 0; prefixes[num]; num++) {
2234 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
2235 pt += m_strlen(prefixes[num]);
2241 /* first TAB. Collect all the matches */
2244 m_strcpy(User_typed, sizeof(User_typed), pt);
2245 p_clear(Matches, countof(Matches));
2246 p_clear(Completed, countof(Completed));
2247 for (num = 0; MuttVars[num].option; num++)
2248 candidate(Completed, User_typed, MuttVars[num].option,
2250 Matches[Num_matched++] = User_typed;
2252 /* All matches are stored. Longest non-ambiguous string is ""
2253 * i.e. dont change 'buffer'. Fake successful return this time */
2254 if (User_typed[0] == 0)
2258 if (Completed[0] == 0 && User_typed[0])
2261 /* Num_matched will _always_ be atleast 1 since the initial
2262 * user-typed string is always stored */
2263 if (numtabs == 1 && Num_matched == 2)
2264 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2265 else if (numtabs > 1 && Num_matched > 2)
2266 /* cycle thru all the matches */
2267 snprintf (Completed, sizeof(Completed), "%s",
2268 Matches[(numtabs - 2) % Num_matched]);
2270 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2272 else if (!m_strncmp(buffer, "exec", 4)) {
2273 struct binding_t *menu = km_get_table (CurrentMenu);
2275 if (!menu && CurrentMenu != MENU_PAGER)
2279 /* first TAB. Collect all the matches */
2282 m_strcpy(User_typed, sizeof(User_typed), pt);
2283 p_clear(Matches, countof(Matches));
2284 p_clear(Completed, countof(Completed));
2285 for (num = 0; menu[num].name; num++)
2286 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
2287 /* try the generic menu */
2288 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2290 for (num = 0; menu[num].name; num++)
2291 candidate (Completed, User_typed, menu[num].name,
2294 Matches[Num_matched++] = User_typed;
2296 /* All matches are stored. Longest non-ambiguous string is ""
2297 * i.e. dont change 'buffer'. Fake successful return this time */
2298 if (User_typed[0] == 0)
2302 if (Completed[0] == 0 && User_typed[0])
2305 /* Num_matched will _always_ be atleast 1 since the initial
2306 * user-typed string is always stored */
2307 if (numtabs == 1 && Num_matched == 2)
2308 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2309 else if (numtabs > 1 && Num_matched > 2)
2310 /* cycle thru all the matches */
2311 snprintf (Completed, sizeof(Completed), "%s",
2312 Matches[(numtabs - 2) % Num_matched]);
2314 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2322 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
2324 char var[STRING], *pt = buffer;
2326 struct option_t* option = NULL;
2331 buffer = vskipspaces(buffer);
2332 spaces = buffer - pt;
2334 pt = buffer + pos - spaces;
2335 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2337 pt++; /* move past the space */
2338 if (*pt == '=') /* abort if no var before the '=' */
2341 if (m_strncmp(buffer, "set", 3) == 0) {
2342 m_strcpy(var, sizeof(var), pt);
2343 /* ignore the trailing '=' when comparing */
2344 var[m_strlen(var) - 1] = 0;
2345 if (!(option = hash_find (ConfigOptions, var)))
2346 return 0; /* no such variable. */
2348 char tmp[LONG_STRING], tmp2[LONG_STRING];
2350 ssize_t dlen = buffer + len - pt - spaces;
2351 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2355 if ((DTYPE (option->type) == DT_STR) ||
2356 (DTYPE (option->type) == DT_PATH) ||
2357 (DTYPE (option->type) == DT_RX)) {
2358 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
2359 if (DTYPE (option->type) == DT_PATH)
2360 mutt_pretty_mailbox (tmp);
2362 else if (DTYPE (option->type) == DT_ADDR) {
2363 rfc822_write_address (tmp, sizeof(tmp),
2364 *((address_t **) option->data), 0);
2366 else if (DTYPE (option->type) == DT_QUAD)
2367 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
2368 else if (DTYPE (option->type) == DT_NUM)
2369 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
2370 else if (DTYPE (option->type) == DT_SORT) {
2371 const struct mapping_t *map;
2374 switch (option->type & DT_SUBTYPE_MASK) {
2376 map = SortAliasMethods;
2378 case DT_SORT_BROWSER:
2379 map = SortBrowserMethods;
2382 map = SortKeyMethods;
2388 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2389 snprintf(tmp, sizeof(tmp), "%s%s%s",
2390 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2391 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2393 else if (DTYPE (option->type) == DT_MAGIC) {
2395 switch (DefaultMagic) {
2411 m_strcpy(tmp, sizeof(tmp), p);
2413 else if (DTYPE (option->type) == DT_BOOL)
2414 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
2418 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
2419 if (*s == '\\' || *s == '"')
2425 m_strcpy(tmp, sizeof(tmp), pt);
2426 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2434 /* Implement the -Q command line flag */
2435 int mutt_query_variables (string_list_t * queries)
2439 char errbuff[STRING];
2440 char command[STRING];
2448 err.dsize = sizeof(errbuff);
2450 for (p = queries; p; p = p->next) {
2451 snprintf (command, sizeof(command), "set ?%s\n", p->data);
2452 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2453 fprintf (stderr, "%s\n", err.data);
2454 p_delete(&token.data);
2457 printf ("%s\n", err.data);
2460 p_delete(&token.data);
2464 static int mutt_execute_commands (string_list_t * p)
2467 char errstr[SHORT_STRING];
2471 err.dsize = sizeof(errstr);
2473 for (; p; p = p->next) {
2474 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2475 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2476 p_delete(&token.data);
2480 p_delete(&token.data);
2484 void mutt_init (int skip_sys_rc, string_list_t * commands)
2487 struct utsname utsname;
2489 char buffer[STRING], error[STRING];
2490 int default_rc = 0, need_pause = 0;
2496 err.dsize = sizeof(error);
2498 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2499 * to have some room for $user_ vars */
2500 ConfigOptions = hash_create (sizeof(MuttVars) * 3);
2501 for (i = 0; MuttVars[i].option; i++) {
2502 if (DTYPE (MuttVars[i].type) != DT_SYS)
2503 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2505 hash_insert (ConfigOptions, MuttVars[i].option,
2506 add_option (MuttVars[i].option, MuttVars[i].init,
2511 * XXX - use something even more difficult to predict?
2513 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2514 "\033]9;%ld\a", (long) time (NULL));
2516 /* on one of the systems I use, getcwd() does not return the same prefix
2517 as is listed in the passwd file */
2518 if ((p = getenv ("HOME")))
2519 Homedir = m_strdup(p);
2521 /* Get some information about the user */
2522 if ((pw = getpwuid (getuid ()))) {
2525 Username = m_strdup(pw->pw_name);
2527 Homedir = m_strdup(pw->pw_dir);
2529 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, GecosMask.rx);
2530 Realname = m_strdup(rnbuf);
2531 Shell = m_strdup(pw->pw_shell);
2537 fputs (_("unable to determine home directory"), stderr);
2540 if ((p = getenv ("USER")))
2541 Username = m_strdup(p);
2544 fputs (_("unable to determine username"), stderr);
2547 Shell = m_strdup((p = getenv ("SHELL")) ? p : "/bin/sh");
2550 /* And about the host... */
2552 /* some systems report the FQDN instead of just the hostname */
2553 if ((p = strchr (utsname.nodename, '.'))) {
2554 Hostname = p_dupstr(utsname.nodename, p - utsname.nodename);
2556 m_strcpy(buffer, sizeof(buffer), p); /* save the domain for below */
2559 Hostname = m_strdup(utsname.nodename);
2561 if (!p && getdnsdomainname(buffer, sizeof(buffer)) == -1)
2562 Fqdn = m_strdup("@");
2564 if (*buffer != '@') {
2565 Fqdn = p_new(char, m_strlen(buffer) + m_strlen(Hostname) + 2);
2566 sprintf (Fqdn, "%s.%s", NONULL(Hostname), buffer); /* __SPRINTF_CHECKED__ */
2569 Fqdn = m_strdup(NONULL (Hostname));
2576 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2578 fgets (buffer, sizeof(buffer), f);
2579 p = vskipspaces(buffer);
2581 while (*q && !isspace(*q))
2584 NewsServer = m_strdup(p);
2588 if ((p = getenv ("NNTPSERVER")))
2589 NewsServer = m_strdup(p);
2592 if ((p = getenv ("MAIL")))
2593 Spoolfile = m_strdup(p);
2594 else if ((p = getenv ("MAILDIR")))
2595 Spoolfile = m_strdup(p);
2598 mutt_concat_path(buffer, sizeof(buffer), NONULL(Homedir), MAILPATH);
2600 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(Username));
2602 Spoolfile = m_strdup(buffer);
2605 if ((p = getenv ("MAILCAPS")))
2606 MailcapPath = m_strdup(p);
2608 /* Default search path from RFC1524 */
2610 m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2611 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2614 Tempdir = m_strdup((p = getenv ("TMPDIR")) ? p : "/tmp");
2616 p = getenv ("VISUAL");
2618 p = getenv ("EDITOR");
2622 Editor = m_strdup(p);
2624 if ((p = getenv ("REPLYTO")) != NULL) {
2627 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2630 buf.data = buf.dptr = buffer;
2631 buf.dsize = m_strlen(buffer);
2634 parse_my_hdr (&token, &buf, 0, &err);
2635 p_delete(&token.data);
2638 if ((p = getenv ("EMAIL")) != NULL)
2639 From = rfc822_parse_adrlist (NULL, p);
2641 charset_initialize();
2643 /* Set standard defaults */
2644 hash_map (ConfigOptions, mutt_set_default, 0);
2645 hash_map (ConfigOptions, mutt_restore_default, 0);
2647 CurrentMenu = MENU_MAIN;
2650 /* Unset suspend by default if we're the session leader */
2651 if (getsid (0) == getpid ())
2652 unset_option (OPTSUSPEND);
2655 mutt_init_history ();
2659 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc-%s", NONULL (Homedir),
2661 if (access (buffer, F_OK) == -1)
2663 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL (Homedir));
2664 if (access (buffer, F_OK) == -1)
2666 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc-%s",
2667 NONULL (Homedir), MUTT_VERSION);
2668 if (access (buffer, F_OK) == -1)
2670 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
2674 Muttrc = m_strdup(buffer);
2677 m_strcpy(buffer, sizeof(buffer), Muttrc);
2679 mutt_expand_path (buffer, sizeof(buffer));
2680 Muttrc = m_strdup(buffer);
2682 p_delete(&AliasFile);
2683 AliasFile = m_strdup(NONULL (Muttrc));
2685 /* Process the global rc file if it exists and the user hasn't explicity
2686 requested not to via "-n". */
2688 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
2690 if (access (buffer, F_OK) == -1)
2691 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
2692 if (access (buffer, F_OK) == -1)
2693 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
2695 if (access (buffer, F_OK) == -1)
2696 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
2697 if (access (buffer, F_OK) != -1) {
2698 if (source_rc (buffer, &err) != 0) {
2699 fputs (err.data, stderr);
2700 fputc ('\n', stderr);
2706 /* Read the user's initialization file. */
2707 if (access (Muttrc, F_OK) != -1) {
2708 if (!option (OPTNOCURSES))
2710 if (source_rc (Muttrc, &err) != 0) {
2711 fputs (err.data, stderr);
2712 fputc ('\n', stderr);
2716 else if (!default_rc) {
2717 /* file specified by -F does not exist */
2718 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2719 mutt_endwin (buffer);
2723 if (mutt_execute_commands (commands) != 0)
2726 /* warn about synonym variables */
2727 if (!list_empty(Synonyms)) {
2729 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2730 for (i = 0; i < Synonyms->length; i++) {
2731 struct option_t* newopt = NULL, *oldopt = NULL;
2732 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2733 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2734 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2735 oldopt ? NONULL (oldopt->option) : "",
2736 newopt ? NONULL (newopt->option) : "",
2737 NONULL(((syn_t*) Synonyms->data[i])->f),
2738 ((syn_t*) Synonyms->data[i])->l);
2740 fprintf (stderr, _("Warning: synonym variables are scheduled"
2741 " for removal.\n"));
2742 list_del (&Synonyms, syn_del);
2746 if (need_pause && !option (OPTNOCURSES)) {
2747 if (mutt_any_key_to_continue (NULL) == -1)
2752 set_option (OPTWEED); /* turn weeding on by default */
2756 int mutt_get_hook_type (const char *name)
2758 struct command_t *c;
2760 for (c = Commands; c->name; c++)
2761 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2766 /* dump out the value of all the variables we have */
2767 int mutt_dump_variables (int full) {
2770 /* get all non-synonyms into list... */
2771 for (i = 0; MuttVars[i].option; i++) {
2772 struct option_t *option = MuttVars + i;
2773 char buf[LONG_STRING];
2775 if (DTYPE(option->type) == DT_SYN)
2779 mutt_option_value(option->option, buf, sizeof(buf));
2780 if (!m_strcmp(buf, option->init))
2785 FuncTable[DTYPE(option->type)].opt_to_string
2786 (buf, sizeof(buf), option);
2787 printf ("%s\n", buf);
2790 printf ("\n# vi""m:set ft=muttrc:\n");