2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
5 * Parts were written/modified by:
6 * Rocco Rutte <pdmef@cs.tu-berlin.de>
8 * This file is part of mutt-ng, see http://www.muttng.org/.
9 * It's licensed under the GNU General Public License,
10 * please see the file GPL in the top level source directory.
13 #include <lib-lib/lib-lib.h>
14 #include <sys/utsname.h>
16 #include <lib-sys/unix.h>
17 #include <lib-sys/mutt_ssl.h>
19 #include <lib-ui/curses.h>
20 #include <lib-ui/history.h>
21 #include <lib-mx/mx.h>
27 #include <lib-crypt/crypt.h>
28 #include "mutt_idna.h"
30 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
31 #include "mutt_libesmtp.h"
40 static const struct mapping_t* get_sortmap (struct option_t* option);
41 static int parse_sort (struct option_t* dst, const char *s,
42 const struct mapping_t *map,
43 char* errbuf, ssize_t errlen);
45 static HASH *ConfigOptions = NULL;
47 /* for synonym warning reports: synonym found during parsing */
48 typedef struct syn_t {
52 struct option_t* n; /* new */
53 struct option_t* o; /* old */
57 static void syn_wipe(syn_t *syn) {
61 DO_DELETE(syn_t, syn);
62 DO_SLIST(syn_t, syn, syn_delete);
64 /* for synonym warning reports: list of synonyms found */
65 static syn_t *Synonyms = NULL;
66 /* for synonym warning reports: current rc file */
67 static const char* CurRCFile = NULL;
68 /* for synonym warning reports: current rc line */
69 static int CurRCLine = 0;
71 /* prototypes for checking for special vars */
72 static int check_dsn_return (const char* option, unsigned long val,
73 char* errbuf, ssize_t errlen);
74 static int check_dsn_notify (const char* option, unsigned long val,
75 char* errbuf, ssize_t errlen);
76 static int check_history (const char* option, unsigned long val,
77 char* errbuf, ssize_t errlen);
78 /* this checks that numbers are >= 0 */
79 static int check_num (const char* option, unsigned long val,
80 char* errbuf, ssize_t errlen);
82 /* use this to check only */
83 static int check_special (const char* option, unsigned long val,
84 char* errbuf, ssize_t errlen);
86 /* variable <-> sanity check function mappings
87 * when changing these, make sure the proper _from_string handler
92 int (*check) (const char* option, unsigned long val,
93 char* errbuf, ssize_t errlen);
95 { "dsn_notify", check_dsn_notify },
96 { "dsn_return", check_dsn_return },
97 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
98 { "smtp_use_tls", mutt_libesmtp_check_usetls },
100 { "history", check_history },
101 { "pager_index_lines", check_num },
106 /* protos for config type handles: convert value to string */
107 static void bool_to_string (char* dst, ssize_t dstlen, struct option_t* option);
108 static void num_to_string (char* dst, ssize_t dstlen, struct option_t* option);
109 static void str_to_string (char* dst, ssize_t dstlen, struct option_t* option);
110 static void quad_to_string (char* dst, ssize_t dstlen, struct option_t* option);
111 static void sort_to_string (char* dst, ssize_t dstlen, struct option_t* option);
112 static void rx_to_string (char* dst, ssize_t dstlen, struct option_t* option);
113 static void magic_to_string (char* dst, ssize_t dstlen, struct option_t* option);
114 static void addr_to_string (char* dst, ssize_t dstlen, struct option_t* option);
115 static void user_to_string (char* dst, ssize_t dstlen, struct option_t* option);
116 static void sys_to_string (char* dst, ssize_t dstlen, struct option_t* option);
118 /* protos for config type handles: convert to value from string */
119 static int bool_from_string (struct option_t* dst, const char* val,
120 char* errbuf, ssize_t errlen);
121 static int num_from_string (struct option_t* dst, const char* val,
122 char* errbuf, ssize_t errlen);
123 static int str_from_string (struct option_t* dst, const char* val,
124 char* errbuf, ssize_t errlen);
125 static int path_from_string (struct option_t* dst, const char* val,
126 char* errbuf, ssize_t errlen);
127 static int quad_from_string (struct option_t* dst, const char* val,
128 char* errbuf, ssize_t errlen);
129 static int sort_from_string (struct option_t* dst, const char* val,
130 char* errbuf, ssize_t errlen);
131 static int rx_from_string (struct option_t* dst, const char* val,
132 char* errbuf, ssize_t errlen);
133 static int magic_from_string (struct option_t* dst, const char* val,
134 char* errbuf, ssize_t errlen);
135 static int addr_from_string (struct option_t* dst, const char* val,
136 char* errbuf, ssize_t errlen);
137 static int user_from_string (struct option_t* dst, const char* val,
138 char* errbuf, ssize_t errlen);
142 void (*opt_tostr) (char* dst, ssize_t dstlen, struct option_t* option);
143 int (*opt_fromstr) (struct option_t* dst, const char* val,
144 char* errbuf, ssize_t errlen);
146 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
147 { DT_BOOL, bool_to_string, bool_from_string },
148 { DT_NUM, num_to_string, num_from_string },
149 { DT_STR, str_to_string, str_from_string },
150 { DT_PATH, str_to_string, path_from_string },
151 { DT_QUAD, quad_to_string, quad_from_string },
152 { DT_SORT, sort_to_string, sort_from_string },
153 { DT_RX, rx_to_string, rx_from_string },
154 { DT_MAGIC, magic_to_string, magic_from_string },
155 /* synonyms should be resolved already so we don't need this
156 * but must define it as DT_ is used for indexing */
157 { DT_SYN, NULL, NULL },
158 { DT_ADDR, addr_to_string, addr_from_string },
159 { DT_USER, user_to_string, user_from_string },
160 { DT_SYS, sys_to_string, NULL },
163 static void bool_to_string (char* dst, ssize_t dstlen,
164 struct option_t* option) {
165 snprintf (dst, dstlen, "%s=%s", option->option,
166 option (option->data) ? "yes" : "no");
169 static int bool_from_string (struct option_t* dst, const char* val,
170 char* errbuf __attribute__ ((unused)),
171 ssize_t errlen __attribute__ ((unused))) {
176 if (ascii_strncasecmp (val, "yes", 3) == 0)
178 else if (ascii_strncasecmp (val, "no", 2) == 0)
184 set_option (dst->data);
186 unset_option (dst->data);
190 static void num_to_string (char* dst, ssize_t dstlen,
191 struct option_t* option) {
193 const char* fmt = (m_strcmp(option->option, "umask") == 0) ?
195 snprintf (dst, dstlen, fmt, option->option,
196 *((short*) option->data));
199 static int num_from_string (struct option_t* dst, const char* val,
200 char* errbuf, ssize_t errlen) {
201 int num = 0, old = 0;
207 num = strtol (val, &t, 0);
209 if (m_strisempty(val) || *t || (short) num != num) {
211 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
217 /* just temporarily accept new val so that check_special for
218 * $history already has it when doing history's init() */
219 old = *((short*) dst->data);
220 *((short*) dst->data) = (short) num;
222 if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
223 *((short*) dst->data) = old;
230 static void str_to_string (char* dst, ssize_t dstlen,
231 struct option_t* option) {
232 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
233 NONULL (*((char**) option->data)));
236 static void user_to_string (char* dst, ssize_t dstlen,
237 struct option_t* option) {
238 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
239 NONULL (((char*) option->data)));
242 static void sys_to_string (char* dst, ssize_t dstlen,
243 struct option_t* option) {
244 char *val = NULL, *t = NULL;
247 /* get some $madmutt_ values dynamically */
248 if (m_strcmp("madmutt_pwd", option->option) == 0) {
249 val = p_new(char, _POSIX_PATH_MAX);
250 val = getcwd (val, _POSIX_PATH_MAX-1);
252 } else if (m_strcmp("madmutt_folder_path", option->option) == 0 &&
253 CurrentFolder && *CurrentFolder) {
255 } else if (m_strcmp("madmutt_folder_name", option->option) == 0 &&
256 CurrentFolder && *CurrentFolder) {
258 ssize_t Maildirlength = m_strlen(Maildir);
261 * if name starts with $folder, just strip it to keep hierarchy
262 * $folder=imap://host, path=imap://host/inbox/b -> inbox/b
264 if (Maildirlength > 0 && m_strncmp(CurrentFolder, Maildir,
265 Maildirlength) == 0 &&
266 m_strlen(CurrentFolder) > Maildirlength) {
267 val = CurrentFolder + Maildirlength;
268 if (Maildir[Maildirlength]!='/')
270 /* if not $folder, just use everything after last / */
271 } else if ((t = strrchr (CurrentFolder, '/')) != NULL)
273 /* default: use as-is */
275 val = (char *) CurrentFolder;
278 val = (char *) option->init;
280 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (val));
285 static int path_from_string (struct option_t* dst, const char* val,
286 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
287 char path[_POSIX_PATH_MAX];
292 if (m_strisempty(val)) {
293 p_delete((char**) dst->data);
298 m_strcpy(path, sizeof(path), val);
299 mutt_expand_path (path, sizeof(path));
300 m_strreplace((char **) dst->data, path);
304 static int str_from_string (struct option_t* dst, const char* val,
305 char* errbuf, ssize_t errlen) {
309 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
312 m_strreplace((char**) dst->data, val);
316 static int user_from_string (struct option_t* dst, const char* val,
317 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
318 /* if dst == NULL, we may get here in case the user did unset it,
319 * see parse_set() where item is free()'d before coming here; so
320 * just silently ignore it */
323 if (m_strlen((char*) dst->data) == 0)
324 dst->data = (unsigned long) m_strdup(val);
326 char* s = (char*) dst->data;
327 m_strreplace(&s, val);
329 if (m_strlen(dst->init) == 0)
330 dst->init = m_strdup((char*) dst->data);
334 static void quad_to_string (char* dst, ssize_t dstlen,
335 struct option_t* option) {
336 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
337 snprintf (dst, dstlen, "%s=%s", option->option,
338 vals[quadoption (option->data)]);
341 static int quad_from_string (struct option_t* dst, const char* val,
342 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
347 if (ascii_strncasecmp (val, "yes", 3) == 0)
349 else if (ascii_strncasecmp (val, "no", 2) == 0)
351 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
353 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
359 set_quadoption (dst->data, flag);
363 static void sort_to_string (char* dst, ssize_t dstlen,
364 struct option_t* option) {
365 const struct mapping_t *map = get_sortmap (option);
366 const char *p = NULL;
369 snprintf (dst, sizeof(dst), "%s=unknown", option->option);
373 p = mutt_getnamebyvalue(*((short *)option->data) & SORT_MASK, map);
375 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
376 (*((short *) option->data) & SORT_REVERSE) ?
378 (*((short *) option->data) & SORT_LAST) ? "last-" :
382 static int sort_from_string (struct option_t* dst, const char* val,
383 char* errbuf, ssize_t errlen) {
384 const struct mapping_t *map = NULL;
385 if (!(map = get_sortmap (dst))) {
387 snprintf (errbuf, errlen, _("%s: Unknown type."),
391 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
396 static void rx_to_string (char* dst, ssize_t dstlen,
397 struct option_t* option) {
398 rx_t* p = (rx_t*) option->data;
399 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
400 NONULL (p->pattern));
403 static int rx_from_string (struct option_t* dst, const char* val,
404 char* errbuf, ssize_t errlen) {
407 int flags = 0, e = 0, not = 0;
413 if (option (OPTATTACHMSG) && !m_strcmp(dst->option, "reply_regexp")) {
415 snprintf (errbuf, errlen,
416 "Operation not permitted when in attach-message mode.");
420 if (!((rx_t*) dst->data))
421 *((rx_t**) dst->data) = p_new(rx_t, 1);
423 p = (rx_t*) dst->data;
425 /* something to do? */
426 if (m_strisempty(val) || (p->pattern && m_strcmp(p->pattern, val) == 0))
429 if (m_strcmp(dst->option, "mask") != 0)
430 flags |= mutt_which_case (val);
433 if (m_strcmp(dst->option, "mask") == 0 && *s == '!') {
438 rx = p_new(regex_t, 1);
440 if ((e = REGCOMP (rx, s, flags)) != 0) {
441 regerror (e, rx, errbuf, errlen);
452 m_strreplace(&p->pattern, val);
456 if (m_strcmp(dst->option, "reply_regexp") == 0)
457 mutt_adjust_all_subjects ();
462 static void magic_to_string (char* dst, ssize_t dstlen,
463 struct option_t* option) {
464 const char* s = NULL;
465 switch (option->data) {
466 case M_MBOX: s = "mbox"; break;
467 case M_MMDF: s = "MMDF"; break;
468 case M_MH: s = "MH"; break;
469 case M_MAILDIR: s = "Maildir"; break;
470 default: s = "unknown"; break;
472 snprintf (dst, dstlen, "%s=%s", option->option, s);
475 static int magic_from_string (struct option_t* dst, const char* val,
476 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
479 if (!dst || m_strisempty(val))
481 if (ascii_strncasecmp (val, "mbox", 4) == 0)
483 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
485 else if (ascii_strncasecmp (val, "mh", 2) == 0)
487 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
493 *((short*) dst->data) = flag;
498 static void addr_to_string (char* dst, ssize_t dstlen,
499 struct option_t* option) {
502 rfc822_addrcat(s, sizeof(s), *((address_t**) option->data), 0);
503 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
506 static int addr_from_string (struct option_t* dst, const char* val,
507 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
510 address_list_wipe((address_t**) dst->data);
512 *((address_t**) dst->data) = rfc822_parse_adrlist (NULL, val);
516 int mutt_option_value (const char* val, char* dst, ssize_t dstlen) {
517 struct option_t* option = NULL;
518 char* tmp = NULL, *t = NULL;
521 if (!(option = hash_find (ConfigOptions, val))) {
525 tmp = p_new(char, dstlen+1);
526 FuncTable[DTYPE(option->type)].opt_tostr (tmp, dstlen, option);
528 /* as we get things of type $var=value and don't want to bloat the
529 * above "just" for expansion, we do the stripping here */
530 t = strchr (tmp, '=');
534 if (t[l-1] == '"' && *t == '"') {
539 memcpy (dst, t, l+1);
545 static void toggle_quadoption (int opt)
548 int b = (opt % 4) * 2;
550 QuadOptions[n] ^= (1 << b);
553 void set_quadoption (int opt, int flag)
556 int b = (opt % 4) * 2;
558 QuadOptions[n] &= ~(0x3 << b);
559 QuadOptions[n] |= (flag & 0x3) << b;
562 int quadoption (int opt)
565 int b = (opt % 4) * 2;
567 return (QuadOptions[n] >> b) & 0x3;
570 int query_quadoption (int opt, const char *prompt)
572 int v = quadoption (opt);
580 v = mutt_yesorno (prompt, (v == M_ASKYES));
581 CLEARLINE (LINES - 1);
588 static void add_to_list(string_list_t **list, const char *str)
590 /* don't add a NULL or empty string to the list */
591 if (m_strisempty(str))
594 /* check to make sure the item is not already on this list */
596 if (!ascii_strcasecmp(str, (*list)->data))
598 list = &(*list)->next;
601 *list = p_new(string_list_t, 1);
602 (*list)->data = m_strdup(str);
606 add_to_rx_list(rx_t **list, const char *s, int flags, BUFFER *err)
613 if (rx_lookup(list, s))
616 rx = rx_compile(s, flags);
618 snprintf(err->data, err->dsize, "Bad regexp: %s\n", s);
622 rx_list_append(list, rx);
626 static int add_to_spam_list(rx_t **list, const char *pat,
627 const char *templ, BUFFER * err)
631 if (m_strisempty(pat) || !templ)
634 if (!(rx = rx_compile (pat, REG_ICASE))) {
635 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
639 /* check to make sure the item is not already on this list */
641 if (!ascii_strcasecmp(rx->pattern, (*list)->pattern)) {
642 rx_t *tmp = rx_list_pop(list);
645 list = &(*list)->next;
650 rx_set_template(rx, templ);
654 static int remove_from_spam_list (rx_t ** list, const char *pat)
659 if (!m_strcmp((*list)->pattern, pat)) {
660 rx_t *spam = rx_list_pop(list);
664 list = &(*list)->next;
672 static void remove_from_list(string_list_t **l, const char *str)
674 if (!m_strcmp("*", str)) {
675 string_list_wipe(l); /* ``unCMD *'' means delete all current entries */
680 if (!ascii_strcasecmp(str, (*l)->data)) {
681 string_list_t *it = string_list_pop(l);
682 string_item_delete(&it);
689 static int remove_from_rx_list(rx_t **l, const char *str)
691 if (m_strcmp("*", str) == 0) {
696 l = rx_lookup(l, str);
698 rx_t *r = rx_list_pop(l);
706 static int parse_unignore (BUFFER * buf, BUFFER * s,
707 unsigned long data __attribute__ ((unused)),
708 BUFFER * err __attribute__ ((unused)))
711 mutt_extract_token (buf, s, 0);
713 /* don't add "*" to the unignore list */
714 if (m_strcmp (buf->data, "*"))
715 add_to_list (&UnIgnore, buf->data);
717 remove_from_list (&Ignore, buf->data);
718 } while (MoreArgs (s));
723 static int parse_ignore (BUFFER * buf, BUFFER * s,
724 unsigned long data __attribute__ ((unused)),
725 BUFFER * err __attribute__ ((unused)))
728 mutt_extract_token (buf, s, 0);
729 remove_from_list (&UnIgnore, buf->data);
730 add_to_list (&Ignore, buf->data);
731 } while (MoreArgs(s));
735 static int parse_list (BUFFER * buf, BUFFER * s,
736 unsigned long data __attribute__ ((unused)),
737 BUFFER * err __attribute__ ((unused)))
740 mutt_extract_token (buf, s, 0);
741 add_to_list ((string_list_t **) data, buf->data);
742 } while (MoreArgs(s));
746 static void _alternates_clean (void)
750 if (Context && Context->msgcount) {
751 for (i = 0; i < Context->msgcount; i++)
752 Context->hdrs[i]->recip_valid = 0;
756 static int parse_alternates (BUFFER * buf, BUFFER * s,
757 unsigned long data __attribute__ ((unused)),
758 BUFFER * err __attribute__ ((unused)))
760 _alternates_clean ();
762 mutt_extract_token (buf, s, 0);
763 remove_from_rx_list (&UnAlternates, buf->data);
765 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
768 while (MoreArgs (s));
773 static int parse_unalternates (BUFFER * buf, BUFFER * s,
774 unsigned long data __attribute__ ((unused)),
775 BUFFER * err __attribute__ ((unused)))
777 _alternates_clean ();
779 mutt_extract_token (buf, s, 0);
780 remove_from_rx_list (&Alternates, buf->data);
782 if (m_strcmp(buf->data, "*") &&
783 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
787 while (MoreArgs (s));
792 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
799 /* Insist on at least one parameter */
802 m_strcpy(err->data, err->dsize, _("spam: no matching pattern"));
804 m_strcpy(err->data, err->dsize, _("nospam: no matching pattern"));
808 /* Extract the first token, a regexp */
809 mutt_extract_token (buf, s, 0);
811 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
812 if (data == M_SPAM) {
813 /* If there's a second parameter, it's a template for the spam tag. */
815 mutt_extract_token (&templ, s, 0);
817 /* Add to the spam list. */
818 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
819 p_delete(&templ.data);
822 p_delete(&templ.data);
825 /* If not, try to remove from the nospam list. */
827 remove_from_rx_list (&NoSpamList, buf->data);
833 /* M_NOSPAM is for nospam commands. */
834 else if (data == M_NOSPAM) {
835 /* nospam only ever has one parameter. */
837 /* "*" is a special case. */
838 if (!m_strcmp(buf->data, "*")) {
839 rx_list_wipe(&SpamList);
840 rx_list_wipe(&NoSpamList);
844 /* If it's on the spam list, just remove it. */
845 if (remove_from_spam_list (&SpamList, buf->data) != 0)
848 /* Otherwise, add it to the nospam list. */
849 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
855 /* This should not happen. */
856 m_strcpy(err->data, err->dsize, "This is no good at all.");
860 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
861 BUFFER * err __attribute__ ((unused)))
864 mutt_extract_token (buf, s, 0);
866 * Check for deletion of entire list
868 if (m_strcmp(buf->data, "*") == 0) {
869 string_list_wipe((string_list_t **) data);
872 remove_from_list ((string_list_t **) data, buf->data);
874 while (MoreArgs (s));
879 static int parse_lists (BUFFER * buf, BUFFER * s,
880 unsigned long data __attribute__ ((unused)),
884 mutt_extract_token (buf, s, 0);
885 remove_from_rx_list (&UnMailLists, buf->data);
887 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
890 while (MoreArgs (s));
895 /* always wise to do what someone else did before */
896 static void _attachments_clean (void) {
898 if (Context && Context->msgcount) {
899 for (i = 0; i < Context->msgcount; i++)
900 Context->hdrs[i]->attach_valid = 0;
904 static int parse_attach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
905 BUFFER *err __attribute__ ((unused))) {
907 string_list_t *listp, *lastp;
912 /* Find the last item in the list that data points to. */
914 for (listp = *ldata; listp; listp = listp->next) {
915 a = (ATTACH_MATCH *)listp->data;
920 mutt_extract_token (buf, s, 0);
922 if (!buf->data || *buf->data == '\0')
925 a = p_new(ATTACH_MATCH, 1);
927 /* some cheap hacks that I expect to remove */
928 if (!m_strcasecmp(buf->data, "any"))
929 a->major = m_strdup("*/.*");
930 else if (!m_strcasecmp(buf->data, "none"))
931 a->major = m_strdup("cheap_hack/this_should_never_match");
933 a->major = m_strdup(buf->data);
935 if ((p = strchr(a->major, '/'))) {
940 a->minor = "unknown";
943 len = m_strlen(a->minor);
944 tmpminor = p_new(char, len + 3);
945 m_strcpy(&tmpminor[1], len + 3, a->minor);
947 tmpminor[len+1] = '$';
948 tmpminor[len+2] = '\0';
950 a->major_int = mutt_check_mime_type(a->major);
951 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
955 listp = p_new(string_list_t, 1);
956 listp->data = (char *)a;
965 while (MoreArgs (s));
967 _attachments_clean();
971 static int parse_unattach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
972 BUFFER *err __attribute__ ((unused))) {
974 string_list_t *lp, *lastp, *newlp;
980 mutt_extract_token (buf, s, 0);
982 if (!m_strcasecmp(buf->data, "any"))
983 tmp = m_strdup("*/.*");
984 else if (!m_strcasecmp(buf->data, "none"))
985 tmp = m_strdup("cheap_hack/this_should_never_match");
987 tmp = m_strdup(buf->data);
989 if ((minor = strchr(tmp, '/'))) {
993 minor = m_strdup("unknown");
995 major = mutt_check_mime_type(tmp);
997 /* We must do our own walk here because remove_from_list() will only
998 * remove the string_list_t->data, not anything pointed to by the string_list_t->data. */
1000 for(lp = *ldata; lp; ) {
1001 a = (ATTACH_MATCH *)lp->data;
1002 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
1003 regfree(&a->minor_rx);
1004 p_delete(&a->major);
1006 /* Relink backward */
1008 lastp->next = lp->next;
1013 p_delete(&lp->data); /* same as a */
1023 while (MoreArgs (s));
1026 _attachments_clean();
1030 static int print_attach_list (string_list_t *lp, char op, const char *name) {
1032 printf("attachments %c%s %s/%s\n", op, name,
1033 ((ATTACH_MATCH *)lp->data)->major,
1034 ((ATTACH_MATCH *)lp->data)->minor);
1041 static int parse_attachments (BUFFER *buf, BUFFER *s,
1042 unsigned long data __attribute__ ((unused)),
1045 string_list_t **listp;
1047 mutt_extract_token(buf, s, 0);
1048 if (!buf->data || *buf->data == '\0') {
1049 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
1053 category = buf->data;
1059 printf("\nCurrent attachments settings:\n\n");
1060 print_attach_list(AttachAllow, '+', "A");
1061 print_attach_list(AttachExclude, '-', "A");
1062 print_attach_list(InlineAllow, '+', "I");
1063 print_attach_list(InlineExclude, '-', "I");
1064 set_option (OPTFORCEREDRAWINDEX);
1065 set_option (OPTFORCEREDRAWPAGER);
1066 mutt_any_key_to_continue (NULL);
1070 if (op != '+' && op != '-') {
1074 if (!m_strncasecmp(category, "attachment", strlen(category))) {
1076 listp = &AttachAllow;
1078 listp = &AttachExclude;
1080 else if (!m_strncasecmp(category, "inline", strlen(category))) {
1082 listp = &InlineAllow;
1084 listp = &InlineExclude;
1086 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
1090 return parse_attach_list(buf, s, listp, err);
1093 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
1095 string_list_t **listp;
1097 mutt_extract_token(buf, s, 0);
1098 if (!buf->data || *buf->data == '\0') {
1099 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
1105 if (op != '+' && op != '-') {
1109 if (!m_strncasecmp(p, "attachment", strlen(p))) {
1111 listp = &AttachAllow;
1113 listp = &AttachExclude;
1115 else if (!m_strncasecmp(p, "inline", strlen(p))) {
1117 listp = &InlineAllow;
1119 listp = &InlineExclude;
1122 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
1126 return parse_unattach_list(buf, s, listp, err);
1129 static int parse_unlists (BUFFER * buf, BUFFER * s,
1130 unsigned long data __attribute__ ((unused)),
1131 BUFFER * err __attribute__ ((unused)))
1134 mutt_extract_token (buf, s, 0);
1135 remove_from_rx_list (&SubscribedLists, buf->data);
1136 remove_from_rx_list (&MailLists, buf->data);
1138 if (m_strcmp(buf->data, "*") &&
1139 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1142 while (MoreArgs (s));
1147 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1151 mutt_extract_token (buf, s, 0);
1152 remove_from_rx_list (&UnMailLists, buf->data);
1153 remove_from_rx_list (&UnSubscribedLists, buf->data);
1155 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1157 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1160 while (MoreArgs (s));
1165 static int parse_unsubscribe (BUFFER * buf, BUFFER * s,
1166 unsigned long data __attribute__ ((unused)),
1167 BUFFER * err __attribute__ ((unused)))
1170 mutt_extract_token (buf, s, 0);
1171 remove_from_rx_list (&SubscribedLists, buf->data);
1173 if (m_strcmp(buf->data, "*") &&
1174 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1177 while (MoreArgs (s));
1182 static int parse_unalias (BUFFER * buf, BUFFER * s,
1183 unsigned long data __attribute__ ((unused)),
1184 BUFFER * err __attribute__ ((unused)))
1186 alias_t *tmp, **last;
1189 mutt_extract_token (buf, s, 0);
1191 if (!m_strcmp("*", buf->data) == 0) {
1192 if (CurrentMenu == MENU_ALIAS) {
1193 for (tmp = Aliases; tmp; tmp = tmp->next)
1195 set_option(OPTFORCEREDRAWINDEX);
1197 alias_list_wipe(&Aliases);
1203 for (last = &Aliases; *last; last = &(*last)->next) {
1204 if (!m_strcasecmp(buf->data, (*last)->name)) {
1205 if (CurrentMenu == MENU_ALIAS) {
1207 set_option (OPTFORCEREDRAWINDEX);
1209 tmp = alias_list_pop(last);
1215 } while (MoreArgs(s));
1220 static int parse_alias (BUFFER * buf, BUFFER * s,
1221 unsigned long data __attribute__ ((unused)),
1227 if (!MoreArgs (s)) {
1228 m_strcpy(err->data, err->dsize, _("alias: no address"));
1232 mutt_extract_token (buf, s, 0);
1234 /* check to see if an alias with this name already exists */
1235 for (last = &Aliases; *last; last = &(*last)->next) {
1236 if (!m_strcasecmp((*last)->name, buf->data))
1241 /* create a new alias */
1242 *last = alias_new();
1243 (*last)->name = m_strdup(buf->data);
1244 /* give the main addressbook code a chance */
1245 if (CurrentMenu == MENU_ALIAS)
1246 set_option (OPTMENUCALLER);
1248 /* override the previous value */
1249 address_list_wipe(&(*last)->addr);
1250 if (CurrentMenu == MENU_ALIAS)
1251 set_option (OPTFORCEREDRAWINDEX);
1254 mutt_extract_token(buf, s,
1255 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1256 (*last)->addr = mutt_parse_adrlist((*last)->addr, buf->data);
1257 if (mutt_addrlist_to_idna((*last)->addr, &estr)) {
1258 snprintf (err->data, err->dsize,
1259 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, (*last)->name);
1268 parse_unmy_hdr(BUFFER * buf, BUFFER * s,
1269 unsigned long data __attribute__ ((unused)),
1270 BUFFER * err __attribute__ ((unused)))
1273 mutt_extract_token (buf, s, 0);
1275 if (!m_strcmp("*", buf->data)) {
1276 string_list_wipe(&UserHeader);
1278 string_list_t **last = &UserHeader;
1279 ssize_t l = m_strlen(buf->data);
1281 if (buf->data[l - 1] == ':')
1285 if (!ascii_strncasecmp(buf->data, (*last)->data, l)
1286 && (*last)->data[l] == ':')
1288 string_list_t *tmp = string_list_pop(last);
1289 string_item_delete(&tmp);
1291 last = &(*last)->next;
1295 } while (MoreArgs(s));
1300 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1307 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1308 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1309 m_strcpy(err->data, err->dsize, _("invalid header field"));
1312 keylen = p - buf->data + 1;
1315 for (tmp = UserHeader;; tmp = tmp->next) {
1316 /* see if there is already a field by this name */
1317 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1318 /* replace the old value */
1319 p_delete(&tmp->data);
1320 tmp->data = buf->data;
1327 tmp->next = string_item_new();
1331 tmp = string_item_new();
1334 tmp->data = buf->data;
1340 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1341 char* errbuf, ssize_t errlen) {
1344 if (m_strncmp("reverse-", s, 8) == 0) {
1346 flags = SORT_REVERSE;
1349 if (m_strncmp("last-", s, 5) == 0) {
1354 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1356 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1360 *((short*) dst->data) = i | flags;
1364 /* if additional data more == 1, we want to resolve synonyms */
1365 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
1367 char buf[LONG_STRING];
1368 struct option_t *ptr = p;
1370 if (DTYPE(ptr->type) == DT_SYN) {
1373 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1375 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_fromstr)
1378 mutt_option_value(ptr->option, buf, sizeof(buf));
1379 if (m_strlen(ptr->init) == 0 && buf && *buf)
1380 ptr->init = m_strdup(buf);
1383 static struct option_t* add_option (const char* name, const char* init,
1384 short type, short dodup) {
1385 struct option_t* option = p_new(struct option_t, 1);
1387 option->option = m_strdup(name);
1388 option->type = type;
1390 option->init = dodup ? m_strdup(init) : (char*) init;
1394 /* creates new option_t* of type DT_USER for $user_ var */
1395 static struct option_t* add_user_option (const char* name) {
1396 return (add_option (name, NULL, DT_USER, 1));
1399 /* free()'s option_t* */
1400 static void del_option (void* p) {
1401 struct option_t *ptr = (struct option_t*) p;
1402 char* s = (char*) ptr->data;
1403 p_delete(&ptr->option);
1405 p_delete(&ptr->init);
1409 static int init_expand (char** dst, struct option_t* src) {
1415 if (DTYPE(src->type) == DT_STR ||
1416 DTYPE(src->type) == DT_PATH) {
1417 /* only expand for string as it's the only place where
1418 * we want to expand vars right now */
1419 if (src->init && *src->init) {
1422 len = m_strlen(src->init) + 2;
1423 in.data = p_new(char, len + 1);
1424 snprintf (in.data, len, "\"%s\"", src->init);
1427 mutt_extract_token (&token, &in, 0);
1428 if (token.data && *token.data)
1429 *dst = m_strdup(token.data);
1431 *dst = m_strdup("");
1433 p_delete(&token.data);
1435 *dst = m_strdup("");
1437 /* for non-string: take value as is */
1438 *dst = m_strdup(src->init);
1442 /* if additional data more == 1, we want to resolve synonyms */
1443 static void mutt_restore_default (const char* name __attribute__ ((unused)),
1444 void* p, unsigned long more) {
1445 char errbuf[STRING];
1446 struct option_t* ptr = (struct option_t*) p;
1449 if (DTYPE (ptr->type) == DT_SYN) {
1452 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1456 if (FuncTable[DTYPE (ptr->type)].opt_fromstr) {
1457 init_expand (&init, ptr);
1458 if (!FuncTable[DTYPE (ptr->type)].opt_fromstr (ptr, init, errbuf,
1460 if (!option (OPTNOCURSES))
1462 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1463 "Please report this error: \"%s\"\n"),
1464 ptr->option, NONULL (init), errbuf);
1470 if (ptr->flags & R_INDEX)
1471 set_option (OPTFORCEREDRAWINDEX);
1472 if (ptr->flags & R_PAGER)
1473 set_option (OPTFORCEREDRAWPAGER);
1474 if (ptr->flags & R_RESORT_SUB)
1475 set_option (OPTSORTSUBTHREADS);
1476 if (ptr->flags & R_RESORT)
1477 set_option (OPTNEEDRESORT);
1478 if (ptr->flags & R_RESORT_INIT)
1479 set_option (OPTRESORTINIT);
1480 if (ptr->flags & R_TREE)
1481 set_option (OPTREDRAWTREE);
1484 /* check whether value for $dsn_return would be valid */
1485 static int check_dsn_return (const char* option __attribute__ ((unused)), unsigned long p,
1486 char* errbuf, ssize_t errlen) {
1487 char* val = (char*) p;
1488 if (val && *val && m_strncmp(val, "hdrs", 4) != 0 &&
1489 m_strncmp(val, "full", 4) != 0) {
1491 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1497 /* check whether value for $dsn_notify would be valid */
1499 check_dsn_notify (const char* option __attribute__ ((unused)),
1500 unsigned long val, char* errbuf, ssize_t errlen)
1502 const char *p = (const char*)val;
1505 const char *q = m_strchrnul(p, ',');
1508 if (!m_strncmp(p, "never", len) && !m_strncmp(p, "delay", len)
1509 && !m_strncmp(p, "failure", len) && !m_strncmp(p, "success", len))
1512 snprintf(errbuf, errlen, _("'%.*s' is invalid for $%s"),
1513 len, p, "dsn_notify");
1523 static int check_num (const char* option, unsigned long p,
1524 char* errbuf, ssize_t errlen) {
1527 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1533 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1534 char* errbuf, ssize_t errlen) {
1535 if (!check_num ("history", p, errbuf, errlen))
1537 mutt_init_history ();
1541 static int check_special (const char* name, unsigned long val,
1542 char* errbuf, ssize_t errlen) {
1545 for (i = 0; SpecialVars[i].name; i++) {
1546 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1547 return (SpecialVars[i].check (SpecialVars[i].name,
1548 val, errbuf, errlen));
1554 static const struct mapping_t* get_sortmap (struct option_t* option) {
1555 const struct mapping_t* map = NULL;
1557 switch (option->type & DT_SUBTYPE_MASK) {
1559 map = SortAliasMethods;
1561 case DT_SORT_BROWSER:
1562 map = SortBrowserMethods;
1565 map = SortKeyMethods;
1568 map = SortAuxMethods;
1577 #define CHECK_PAGER \
1578 if ((CurrentMenu == MENU_PAGER) && \
1579 (!option || (option->flags & R_RESORT))) \
1581 snprintf (err->data, err->dsize, \
1582 _("Not available in this menu.")); \
1586 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1589 int query, unset, inv, reset, r = 0;
1590 struct option_t* option = NULL;
1592 while (MoreArgs (s)) {
1593 /* reset state variables */
1595 unset = data & M_SET_UNSET;
1596 inv = data & M_SET_INV;
1597 reset = data & M_SET_RESET;
1599 if (*s->dptr == '?') {
1603 else if (m_strncmp("no", s->dptr, 2) == 0) {
1607 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1611 else if (*s->dptr == '&') {
1616 /* get the variable name */
1617 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1619 /* resolve synonyms */
1620 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1621 DTYPE (option->type == DT_SYN))
1623 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1624 syn_t* syn = syn_new();
1625 syn->f = m_strdup(CurRCFile);
1629 syn_list_push(&Synonyms, syn);
1633 /* see if we need to add $user_ var */
1634 if (!option && m_strncmp("user_", tmp->data, 5) == 0) {
1635 /* there's no option named like this yet so only add one
1636 * if the action isn't any of: reset, unset, query */
1637 if (!(reset || unset || query || *s->dptr != '=')) {
1638 option = add_user_option (tmp->data);
1639 hash_insert (ConfigOptions, option->option, option, 0);
1643 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1644 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1647 s->dptr = vskipspaces(s->dptr);
1650 if (query || unset || inv) {
1651 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1655 if (s && *s->dptr == '=') {
1656 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1660 if (!m_strcmp("all", tmp->data)) {
1661 if (CurrentMenu == MENU_PAGER) {
1662 snprintf (err->data, err->dsize, _("Not available in this menu."));
1665 hash_map (ConfigOptions, mutt_restore_default, 1);
1666 set_option (OPTFORCEREDRAWINDEX);
1667 set_option (OPTFORCEREDRAWPAGER);
1668 set_option (OPTSORTSUBTHREADS);
1669 set_option (OPTNEEDRESORT);
1670 set_option (OPTRESORTINIT);
1671 set_option (OPTREDRAWTREE);
1674 else if (!FuncTable[DTYPE (option->type)].opt_fromstr) {
1675 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1680 mutt_restore_default (NULL, option, 1);
1683 else if (DTYPE (option->type) == DT_BOOL) {
1684 /* XXX this currently ignores the function table
1685 * as we don't get invert and stuff into it */
1686 if (s && *s->dptr == '=') {
1687 if (unset || inv || query) {
1688 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1693 mutt_extract_token (tmp, s, 0);
1694 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1696 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1699 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1705 bool_to_string (err->data, err->dsize, option);
1711 unset_option (option->data);
1713 toggle_option (option->data);
1715 set_option (option->data);
1717 else if (DTYPE (option->type) == DT_STR ||
1718 DTYPE (option->type) == DT_PATH ||
1719 DTYPE (option->type) == DT_ADDR ||
1720 DTYPE (option->type) == DT_MAGIC ||
1721 DTYPE (option->type) == DT_NUM ||
1722 DTYPE (option->type) == DT_SORT ||
1723 DTYPE (option->type) == DT_RX ||
1724 DTYPE (option->type) == DT_USER ||
1725 DTYPE (option->type) == DT_SYS) {
1727 /* XXX maybe we need to get unset into handlers? */
1728 if (DTYPE (option->type) == DT_STR ||
1729 DTYPE (option->type) == DT_PATH ||
1730 DTYPE (option->type) == DT_ADDR ||
1731 DTYPE (option->type) == DT_USER ||
1732 DTYPE (option->type) == DT_SYS) {
1735 if (!FuncTable[DTYPE (option->type)].opt_fromstr) {
1736 snprintf (err->data, err->dsize, _("$%s is read-only"),
1740 } else if (DTYPE (option->type) == DT_ADDR)
1741 address_list_wipe((address_t **) option->data);
1742 else if (DTYPE (option->type) == DT_USER)
1743 /* to unset $user_ means remove */
1744 hash_delete (ConfigOptions, option->option,
1745 option, del_option);
1747 p_delete((void **)(void *)&option->data);
1752 if (query || *s->dptr != '=') {
1753 FuncTable[DTYPE (option->type)].opt_tostr
1754 (err->data, err->dsize, option);
1758 /* the $madmutt_ variables are read-only */
1759 if (!FuncTable[DTYPE (option->type)].opt_fromstr) {
1760 snprintf (err->data, err->dsize, _("$%s is read-only"),
1767 mutt_extract_token (tmp, s, 0);
1768 if (!FuncTable[DTYPE (option->type)].opt_fromstr
1769 (option, tmp->data, err->data, err->dsize))
1773 else if (DTYPE (option->type) == DT_QUAD) {
1776 quad_to_string (err->data, err->dsize, option);
1780 if (*s->dptr == '=') {
1783 mutt_extract_token (tmp, s, 0);
1784 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1785 set_quadoption (option->data, M_YES);
1786 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1787 set_quadoption (option->data, M_NO);
1788 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1789 set_quadoption (option->data, M_ASKYES);
1790 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1791 set_quadoption (option->data, M_ASKNO);
1793 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1794 tmp->data, option->option);
1801 toggle_quadoption (option->data);
1803 set_quadoption (option->data, M_NO);
1805 set_quadoption (option->data, M_YES);
1809 snprintf (err->data, err->dsize, _("%s: unknown type"),
1815 if (option->flags & R_INDEX)
1816 set_option (OPTFORCEREDRAWINDEX);
1817 if (option->flags & R_PAGER)
1818 set_option (OPTFORCEREDRAWPAGER);
1819 if (option->flags & R_RESORT_SUB)
1820 set_option (OPTSORTSUBTHREADS);
1821 if (option->flags & R_RESORT)
1822 set_option (OPTNEEDRESORT);
1823 if (option->flags & R_RESORT_INIT)
1824 set_option (OPTRESORTINIT);
1825 if (option->flags & R_TREE)
1826 set_option (OPTREDRAWTREE);
1833 /* reads the specified initialization file. returns -1 if errors were found
1834 so that we can pause to let the user know... */
1835 static int source_rc (const char *rcfile, BUFFER * err)
1838 int line = 0, rc = 0, conv = 0;
1840 char *linebuf = NULL;
1841 char *currentline = NULL;
1845 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1846 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1851 while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
1852 conv = ConfigCharset && (*ConfigCharset) && Charset;
1854 currentline = m_strdup(linebuf);
1857 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1860 currentline = linebuf;
1865 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1866 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1867 if (--rc < -MAXERRS) {
1869 p_delete(¤tline);
1878 p_delete(¤tline);
1880 p_delete(&token.data);
1884 mutt_wait_filter (pid);
1886 /* the muttrc source keyword */
1887 snprintf (err->data, err->dsize,
1888 rc >= -MAXERRS ? _("source: errors in %s")
1889 : _("source: reading aborted due too many errors in %s"),
1898 static int parse_source (BUFFER * tmp, BUFFER * s,
1899 unsigned long data __attribute__ ((unused)),
1902 char path[_POSIX_PATH_MAX];
1906 if (mutt_extract_token (tmp, s, 0) != 0) {
1907 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1911 m_strcpy(path, sizeof(path), tmp->data);
1912 mutt_expand_path (path, sizeof(path));
1914 rc += source_rc (path, err);
1916 while (MoreArgs (s));
1918 return ((rc < 0) ? -1 : 0);
1921 /* line command to execute
1923 token scratch buffer to be used by parser. caller should free
1924 token->data when finished. the reason for this variable is
1925 to avoid having to allocate and deallocate a lot of memory
1926 if we are parsing many lines. the caller can pass in the
1927 memory to use, which avoids having to create new space for
1928 every call to this function.
1930 err where to write error messages */
1931 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1937 expn.data = expn.dptr = line;
1938 expn.dsize = m_strlen(line);
1942 expn.dptr = vskipspaces(expn.dptr);
1943 while (*expn.dptr) {
1944 if (*expn.dptr == '#')
1945 break; /* rest of line is a comment */
1946 if (*expn.dptr == ';') {
1950 mutt_extract_token (token, &expn, 0);
1951 for (i = 0; Commands[i].name; i++) {
1952 if (!m_strcmp(token->data, Commands[i].name)) {
1953 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1958 if (!Commands[i].name) {
1959 snprintf (err->data, err->dsize, _("%s: unknown command"),
1960 NONULL (token->data));
1967 p_delete(&expn.data);
1972 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
1973 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
1974 /* initial string that starts completion. No telling how much crap
1975 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1976 char User_typed[LONG_STRING] = { 0 };
1978 int Num_matched = 0; /* Number of matches for completion */
1979 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1980 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1982 /* helper function for completion. Changes the dest buffer if
1983 necessary/possible to aid completion.
1984 dest == completion result gets here.
1985 src == candidate for completion.
1986 try == user entered data for completion.
1987 len == length of dest buffer.
1989 static void candidate (char *dest, char *try, const char *src, int len)
1993 if (strstr (src, try) == src) {
1994 Matches[Num_matched++] = src;
1996 m_strcpy(dest, len, src);
1998 for (l = 0; src[l] && src[l] == dest[l]; l++);
2004 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
2008 int spaces; /* keep track of the number of leading spaces on the line */
2010 buffer = vskipspaces(buffer);
2011 spaces = buffer - pt;
2013 pt = buffer + pos - spaces;
2014 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2017 if (pt == buffer) { /* complete cmd */
2018 /* first TAB. Collect all the matches */
2021 m_strcpy(User_typed, sizeof(User_typed), pt);
2022 p_clear(Matches, countof(Matches));
2023 p_clear(Completed, countof(Completed));
2024 for (num = 0; Commands[num].name; num++)
2025 candidate (Completed, User_typed, Commands[num].name,
2027 Matches[Num_matched++] = User_typed;
2029 /* All matches are stored. Longest non-ambiguous string is ""
2030 * i.e. dont change 'buffer'. Fake successful return this time */
2031 if (User_typed[0] == 0)
2035 if (Completed[0] == 0 && User_typed[0])
2038 /* Num_matched will _always_ be atleast 1 since the initial
2039 * user-typed string is always stored */
2040 if (numtabs == 1 && Num_matched == 2)
2041 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2042 else if (numtabs > 1 && Num_matched > 2)
2043 /* cycle thru all the matches */
2044 snprintf (Completed, sizeof(Completed), "%s",
2045 Matches[(numtabs - 2) % Num_matched]);
2047 /* return the completed command */
2048 m_strcpy(buffer, len - spaces, Completed);
2050 else if (!m_strncmp(buffer, "set", 3)
2051 || !m_strncmp(buffer, "unset", 5)
2052 || !m_strncmp(buffer, "reset", 5)
2053 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
2054 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
2057 /* loop through all the possible prefixes (no, inv, ...) */
2058 if (!m_strncmp(buffer, "set", 3)) {
2059 for (num = 0; prefixes[num]; num++) {
2060 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
2061 pt += m_strlen(prefixes[num]);
2067 /* first TAB. Collect all the matches */
2070 m_strcpy(User_typed, sizeof(User_typed), pt);
2071 p_clear(Matches, countof(Matches));
2072 p_clear(Completed, countof(Completed));
2073 for (num = 0; MuttVars[num].option; num++)
2074 candidate(Completed, User_typed, MuttVars[num].option,
2076 Matches[Num_matched++] = User_typed;
2078 /* All matches are stored. Longest non-ambiguous string is ""
2079 * i.e. dont change 'buffer'. Fake successful return this time */
2080 if (User_typed[0] == 0)
2084 if (Completed[0] == 0 && User_typed[0])
2087 /* Num_matched will _always_ be atleast 1 since the initial
2088 * user-typed string is always stored */
2089 if (numtabs == 1 && Num_matched == 2)
2090 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2091 else if (numtabs > 1 && Num_matched > 2)
2092 /* cycle thru all the matches */
2093 snprintf (Completed, sizeof(Completed), "%s",
2094 Matches[(numtabs - 2) % Num_matched]);
2096 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2098 else if (!m_strncmp(buffer, "exec", 4)) {
2099 struct binding_t *menu = km_get_table (CurrentMenu);
2101 if (!menu && CurrentMenu != MENU_PAGER)
2105 /* first TAB. Collect all the matches */
2108 m_strcpy(User_typed, sizeof(User_typed), pt);
2109 p_clear(Matches, countof(Matches));
2110 p_clear(Completed, countof(Completed));
2111 for (num = 0; menu[num].name; num++)
2112 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
2113 /* try the generic menu */
2114 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2116 for (num = 0; menu[num].name; num++)
2117 candidate (Completed, User_typed, menu[num].name,
2120 Matches[Num_matched++] = User_typed;
2122 /* All matches are stored. Longest non-ambiguous string is ""
2123 * i.e. dont change 'buffer'. Fake successful return this time */
2124 if (User_typed[0] == 0)
2128 if (Completed[0] == 0 && User_typed[0])
2131 /* Num_matched will _always_ be atleast 1 since the initial
2132 * user-typed string is always stored */
2133 if (numtabs == 1 && Num_matched == 2)
2134 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2135 else if (numtabs > 1 && Num_matched > 2)
2136 /* cycle thru all the matches */
2137 snprintf (Completed, sizeof(Completed), "%s",
2138 Matches[(numtabs - 2) % Num_matched]);
2140 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2148 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
2150 char var[STRING], *pt = buffer;
2152 struct option_t* option = NULL;
2157 buffer = vskipspaces(buffer);
2158 spaces = buffer - pt;
2160 pt = buffer + pos - spaces;
2161 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2163 pt++; /* move past the space */
2164 if (*pt == '=') /* abort if no var before the '=' */
2167 if (m_strncmp(buffer, "set", 3) == 0) {
2168 m_strcpy(var, sizeof(var), pt);
2169 /* ignore the trailing '=' when comparing */
2170 var[m_strlen(var) - 1] = 0;
2171 if (!(option = hash_find (ConfigOptions, var)))
2172 return 0; /* no such variable. */
2174 char tmp[LONG_STRING], tmp2[LONG_STRING];
2176 ssize_t dlen = buffer + len - pt - spaces;
2177 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2181 if ((DTYPE (option->type) == DT_STR) ||
2182 (DTYPE (option->type) == DT_PATH) ||
2183 (DTYPE (option->type) == DT_RX)) {
2184 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
2185 if (DTYPE (option->type) == DT_PATH)
2186 mutt_pretty_mailbox (tmp);
2188 else if (DTYPE (option->type) == DT_ADDR) {
2189 rfc822_addrcat(tmp, sizeof(tmp), *((address_t **) option->data), 0);
2191 else if (DTYPE (option->type) == DT_QUAD)
2192 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
2193 else if (DTYPE (option->type) == DT_NUM)
2194 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
2195 else if (DTYPE (option->type) == DT_SORT) {
2196 const struct mapping_t *map;
2199 switch (option->type & DT_SUBTYPE_MASK) {
2201 map = SortAliasMethods;
2203 case DT_SORT_BROWSER:
2204 map = SortBrowserMethods;
2207 map = SortKeyMethods;
2213 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2214 snprintf(tmp, sizeof(tmp), "%s%s%s",
2215 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2216 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2218 else if (DTYPE (option->type) == DT_MAGIC) {
2220 switch (DefaultMagic) {
2236 m_strcpy(tmp, sizeof(tmp), p);
2238 else if (DTYPE (option->type) == DT_BOOL)
2239 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
2243 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
2244 if (*s == '\\' || *s == '"')
2250 m_strcpy(tmp, sizeof(tmp), pt);
2251 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2259 /* Implement the -Q command line flag */
2260 int mutt_query_variables (string_list_t * queries)
2264 char errbuff[STRING];
2265 char command[STRING];
2273 err.dsize = sizeof(errbuff);
2275 for (p = queries; p; p = p->next) {
2276 snprintf (command, sizeof(command), "set ?%s\n", p->data);
2277 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2278 fprintf (stderr, "%s\n", err.data);
2279 p_delete(&token.data);
2282 printf ("%s\n", err.data);
2285 p_delete(&token.data);
2289 static int mutt_execute_commands (string_list_t * p)
2292 char errstr[STRING];
2296 err.dsize = sizeof(errstr);
2298 for (; p; p = p->next) {
2299 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2300 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2301 p_delete(&token.data);
2305 p_delete(&token.data);
2309 void mutt_init (int skip_sys_rc, string_list_t * commands)
2312 struct utsname utsname;
2314 char buffer[STRING], error[STRING];
2315 int default_rc = 0, need_pause = 0;
2321 err.dsize = sizeof(error);
2323 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2324 * to have some room for $user_ vars */
2325 ConfigOptions = hash_create (sizeof(MuttVars) * 3);
2326 for (i = 0; MuttVars[i].option; i++) {
2327 if (DTYPE (MuttVars[i].type) != DT_SYS)
2328 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2330 hash_insert (ConfigOptions, MuttVars[i].option,
2331 add_option (MuttVars[i].option, MuttVars[i].init,
2336 * XXX - use something even more difficult to predict?
2338 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2339 "\033]9;%ld\a", (long) time (NULL));
2341 /* on one of the systems I use, getcwd() does not return the same prefix
2342 as is listed in the passwd file */
2343 if ((p = getenv ("HOME")))
2344 Homedir = m_strdup(p);
2346 /* Get some information about the user */
2347 if ((pw = getpwuid (getuid ()))) {
2350 Username = m_strdup(pw->pw_name);
2352 Homedir = m_strdup(pw->pw_dir);
2354 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, GecosMask.rx);
2355 Realname = m_strdup(rnbuf);
2356 Shell = m_strdup(pw->pw_shell);
2362 fputs (_("unable to determine home directory"), stderr);
2365 if ((p = getenv ("USER")))
2366 Username = m_strdup(p);
2369 fputs (_("unable to determine username"), stderr);
2372 Shell = m_strdup((p = getenv ("SHELL")) ? p : "/bin/sh");
2375 /* And about the host... */
2377 /* some systems report the FQDN instead of just the hostname */
2378 if ((p = strchr (utsname.nodename, '.'))) {
2379 Hostname = p_dupstr(utsname.nodename, p - utsname.nodename);
2381 m_strcpy(buffer, sizeof(buffer), p); /* save the domain for below */
2384 Hostname = m_strdup(utsname.nodename);
2386 if (!p && getdnsdomainname(buffer, sizeof(buffer)) == -1)
2387 Fqdn = m_strdup("@");
2389 if (*buffer != '@') {
2390 Fqdn = p_new(char, m_strlen(buffer) + m_strlen(Hostname) + 2);
2391 sprintf (Fqdn, "%s.%s", NONULL(Hostname), buffer);
2394 Fqdn = m_strdup(NONULL (Hostname));
2401 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2403 fgets (buffer, sizeof(buffer), f);
2404 p = vskipspaces(buffer);
2406 while (*q && !isspace(*q))
2409 NewsServer = m_strdup(p);
2413 if ((p = getenv ("NNTPSERVER")))
2414 NewsServer = m_strdup(p);
2417 if ((p = getenv ("MAIL")))
2418 Spoolfile = m_strdup(p);
2419 else if ((p = getenv ("MAILDIR")))
2420 Spoolfile = m_strdup(p);
2423 mutt_concat_path(buffer, sizeof(buffer), NONULL(Homedir), MAILPATH);
2425 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(Username));
2427 Spoolfile = m_strdup(buffer);
2430 if ((p = getenv ("MAILCAPS")))
2431 MailcapPath = m_strdup(p);
2433 /* Default search path from RFC1524 */
2435 m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2436 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2439 Tempdir = m_strdup((p = getenv ("TMPDIR")) ? p : "/tmp");
2441 p = getenv ("VISUAL");
2443 p = getenv ("EDITOR");
2447 Editor = m_strdup(p);
2449 if ((p = getenv ("REPLYTO")) != NULL) {
2452 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2455 buf.data = buf.dptr = buffer;
2456 buf.dsize = m_strlen(buffer);
2459 parse_my_hdr (&token, &buf, 0, &err);
2460 p_delete(&token.data);
2463 if ((p = getenv ("EMAIL")) != NULL)
2464 From = rfc822_parse_adrlist (NULL, p);
2466 charset_initialize();
2468 /* Set standard defaults */
2469 hash_map (ConfigOptions, mutt_set_default, 0);
2470 hash_map (ConfigOptions, mutt_restore_default, 0);
2472 CurrentMenu = MENU_MAIN;
2475 /* Unset suspend by default if we're the session leader */
2476 if (getsid (0) == getpid ())
2477 unset_option (OPTSUSPEND);
2480 mutt_init_history ();
2483 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL (Homedir));
2484 if (access (buffer, F_OK) == -1)
2485 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
2489 Muttrc = m_strdup(buffer);
2492 m_strcpy(buffer, sizeof(buffer), Muttrc);
2494 mutt_expand_path (buffer, sizeof(buffer));
2495 Muttrc = m_strdup(buffer);
2497 p_delete(&AliasFile);
2498 AliasFile = m_strdup(NONULL (Muttrc));
2500 /* Process the global rc file if it exists and the user hasn't explicity
2501 requested not to via "-n". */
2503 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
2505 if (access (buffer, F_OK) == -1)
2506 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
2507 if (access (buffer, F_OK) == -1)
2508 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
2510 if (access (buffer, F_OK) == -1)
2511 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
2512 if (access (buffer, F_OK) != -1) {
2513 if (source_rc (buffer, &err) != 0) {
2514 fputs (err.data, stderr);
2515 fputc ('\n', stderr);
2521 /* Read the user's initialization file. */
2522 if (access (Muttrc, F_OK) != -1) {
2523 if (!option (OPTNOCURSES))
2525 if (source_rc (Muttrc, &err) != 0) {
2526 fputs (err.data, stderr);
2527 fputc ('\n', stderr);
2531 else if (!default_rc) {
2532 /* file specified by -F does not exist */
2533 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2534 mutt_endwin (buffer);
2538 if (mutt_execute_commands (commands) != 0)
2541 /* warn about synonym variables */
2545 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2547 for (syn = Synonyms; syn; syn = syn->next) {
2548 fprintf(stderr, "$%s ($%s should be used) (%s:%d)\n",
2549 syn->o ? NONULL(syn->o->option) : "",
2550 syn->n ? NONULL(syn->n->option) : "",
2551 NONULL(syn->f), syn->l);
2553 fprintf (stderr, _("Warning: synonym variables are scheduled"
2554 " for removal.\n"));
2555 syn_list_wipe(&Synonyms);
2559 if (need_pause && !option (OPTNOCURSES)) {
2560 if (mutt_any_key_to_continue (NULL) == -1)
2565 int mutt_get_hook_type (const char *name)
2567 struct command_t *c;
2569 for (c = Commands; c->name; c++)
2570 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2575 /* dump out the value of all the variables we have */
2576 int mutt_dump_variables (int full) {
2579 /* get all non-synonyms into list... */
2580 for (i = 0; MuttVars[i].option; i++) {
2581 struct option_t *option = MuttVars + i;
2582 char buf[LONG_STRING];
2584 if (DTYPE(option->type) == DT_SYN)
2588 mutt_option_value(option->option, buf, sizeof(buf));
2589 if (!m_strcmp(buf, option->init))
2594 FuncTable[DTYPE(option->type)].opt_tostr(buf, sizeof(buf), option);
2595 printf ("%s\n", buf);
2598 printf ("\n# vi""m:set ft=muttrc:\n");