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, unsigned long data,
736 BUFFER * err __attribute__ ((unused)))
739 mutt_extract_token (buf, s, 0);
740 add_to_list ((string_list_t **) data, buf->data);
741 } while (MoreArgs(s));
745 static void _alternates_clean (void)
749 if (Context && Context->msgcount) {
750 for (i = 0; i < Context->msgcount; i++)
751 Context->hdrs[i]->recip_valid = 0;
755 static int parse_alternates (BUFFER * buf, BUFFER * s,
756 unsigned long data __attribute__ ((unused)),
757 BUFFER * err __attribute__ ((unused)))
759 _alternates_clean ();
761 mutt_extract_token (buf, s, 0);
762 remove_from_rx_list (&UnAlternates, buf->data);
764 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
767 while (MoreArgs (s));
772 static int parse_unalternates (BUFFER * buf, BUFFER * s,
773 unsigned long data __attribute__ ((unused)),
774 BUFFER * err __attribute__ ((unused)))
776 _alternates_clean ();
778 mutt_extract_token (buf, s, 0);
779 remove_from_rx_list (&Alternates, buf->data);
781 if (m_strcmp(buf->data, "*") &&
782 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
786 while (MoreArgs (s));
791 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
798 /* Insist on at least one parameter */
801 m_strcpy(err->data, err->dsize, _("spam: no matching pattern"));
803 m_strcpy(err->data, err->dsize, _("nospam: no matching pattern"));
807 /* Extract the first token, a regexp */
808 mutt_extract_token (buf, s, 0);
810 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
811 if (data == M_SPAM) {
812 /* If there's a second parameter, it's a template for the spam tag. */
814 mutt_extract_token (&templ, s, 0);
816 /* Add to the spam list. */
817 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
818 p_delete(&templ.data);
821 p_delete(&templ.data);
824 /* If not, try to remove from the nospam list. */
826 remove_from_rx_list (&NoSpamList, buf->data);
832 /* M_NOSPAM is for nospam commands. */
833 else if (data == M_NOSPAM) {
834 /* nospam only ever has one parameter. */
836 /* "*" is a special case. */
837 if (!m_strcmp(buf->data, "*")) {
838 rx_list_wipe(&SpamList);
839 rx_list_wipe(&NoSpamList);
843 /* If it's on the spam list, just remove it. */
844 if (remove_from_spam_list (&SpamList, buf->data) != 0)
847 /* Otherwise, add it to the nospam list. */
848 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
854 /* This should not happen. */
855 m_strcpy(err->data, err->dsize, "This is no good at all.");
859 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
860 BUFFER * err __attribute__ ((unused)))
863 mutt_extract_token (buf, s, 0);
865 * Check for deletion of entire list
867 if (m_strcmp(buf->data, "*") == 0) {
868 string_list_wipe((string_list_t **) data);
871 remove_from_list ((string_list_t **) data, buf->data);
873 while (MoreArgs (s));
878 static int parse_lists (BUFFER * buf, BUFFER * s,
879 unsigned long data __attribute__ ((unused)),
883 mutt_extract_token (buf, s, 0);
884 remove_from_rx_list (&UnMailLists, buf->data);
886 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
889 while (MoreArgs (s));
894 /* always wise to do what someone else did before */
895 static void _attachments_clean (void) {
897 if (Context && Context->msgcount) {
898 for (i = 0; i < Context->msgcount; i++)
899 Context->hdrs[i]->attach_valid = 0;
903 static int parse_attach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
904 BUFFER *err __attribute__ ((unused))) {
906 string_list_t *listp, *lastp;
911 /* Find the last item in the list that data points to. */
913 for (listp = *ldata; listp; listp = listp->next) {
914 a = (ATTACH_MATCH *)listp->data;
919 mutt_extract_token (buf, s, 0);
921 if (!buf->data || *buf->data == '\0')
924 a = p_new(ATTACH_MATCH, 1);
926 /* some cheap hacks that I expect to remove */
927 if (!m_strcasecmp(buf->data, "any"))
928 a->major = m_strdup("*/.*");
929 else if (!m_strcasecmp(buf->data, "none"))
930 a->major = m_strdup("cheap_hack/this_should_never_match");
932 a->major = m_strdup(buf->data);
934 if ((p = strchr(a->major, '/'))) {
939 a->minor = "unknown";
942 len = m_strlen(a->minor);
943 tmpminor = p_new(char, len + 3);
944 m_strcpy(&tmpminor[1], len + 3, a->minor);
946 tmpminor[len+1] = '$';
947 tmpminor[len+2] = '\0';
949 a->major_int = mutt_check_mime_type(a->major);
950 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
954 listp = p_new(string_list_t, 1);
955 listp->data = (char *)a;
964 while (MoreArgs (s));
966 _attachments_clean();
970 static int parse_unattach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
971 BUFFER *err __attribute__ ((unused))) {
973 string_list_t *lp, *lastp, *newlp;
979 mutt_extract_token (buf, s, 0);
981 if (!m_strcasecmp(buf->data, "any"))
982 tmp = m_strdup("*/.*");
983 else if (!m_strcasecmp(buf->data, "none"))
984 tmp = m_strdup("cheap_hack/this_should_never_match");
986 tmp = m_strdup(buf->data);
988 if ((minor = strchr(tmp, '/'))) {
992 minor = m_strdup("unknown");
994 major = mutt_check_mime_type(tmp);
996 /* We must do our own walk here because remove_from_list() will only
997 * remove the string_list_t->data, not anything pointed to by the string_list_t->data. */
999 for(lp = *ldata; lp; ) {
1000 a = (ATTACH_MATCH *)lp->data;
1001 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
1002 regfree(&a->minor_rx);
1003 p_delete(&a->major);
1005 /* Relink backward */
1007 lastp->next = lp->next;
1012 p_delete(&lp->data); /* same as a */
1022 while (MoreArgs (s));
1025 _attachments_clean();
1029 static int print_attach_list (string_list_t *lp, char op, const char *name) {
1031 printf("attachments %c%s %s/%s\n", op, name,
1032 ((ATTACH_MATCH *)lp->data)->major,
1033 ((ATTACH_MATCH *)lp->data)->minor);
1040 static int parse_attachments (BUFFER *buf, BUFFER *s,
1041 unsigned long data __attribute__ ((unused)),
1044 string_list_t **listp;
1046 mutt_extract_token(buf, s, 0);
1047 if (!buf->data || *buf->data == '\0') {
1048 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
1052 category = buf->data;
1058 printf("\nCurrent attachments settings:\n\n");
1059 print_attach_list(AttachAllow, '+', "A");
1060 print_attach_list(AttachExclude, '-', "A");
1061 print_attach_list(InlineAllow, '+', "I");
1062 print_attach_list(InlineExclude, '-', "I");
1063 set_option (OPTFORCEREDRAWINDEX);
1064 set_option (OPTFORCEREDRAWPAGER);
1065 mutt_any_key_to_continue (NULL);
1069 if (op != '+' && op != '-') {
1073 if (!m_strncasecmp(category, "attachment", strlen(category))) {
1075 listp = &AttachAllow;
1077 listp = &AttachExclude;
1079 else if (!m_strncasecmp(category, "inline", strlen(category))) {
1081 listp = &InlineAllow;
1083 listp = &InlineExclude;
1085 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
1089 return parse_attach_list(buf, s, listp, err);
1092 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
1094 string_list_t **listp;
1096 mutt_extract_token(buf, s, 0);
1097 if (!buf->data || *buf->data == '\0') {
1098 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
1104 if (op != '+' && op != '-') {
1108 if (!m_strncasecmp(p, "attachment", strlen(p))) {
1110 listp = &AttachAllow;
1112 listp = &AttachExclude;
1114 else if (!m_strncasecmp(p, "inline", strlen(p))) {
1116 listp = &InlineAllow;
1118 listp = &InlineExclude;
1121 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
1125 return parse_unattach_list(buf, s, listp, err);
1128 static int parse_unlists (BUFFER * buf, BUFFER * s,
1129 unsigned long data __attribute__ ((unused)),
1130 BUFFER * err __attribute__ ((unused)))
1133 mutt_extract_token (buf, s, 0);
1134 remove_from_rx_list (&SubscribedLists, buf->data);
1135 remove_from_rx_list (&MailLists, buf->data);
1137 if (m_strcmp(buf->data, "*") &&
1138 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1141 while (MoreArgs (s));
1146 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1150 mutt_extract_token (buf, s, 0);
1151 remove_from_rx_list (&UnMailLists, buf->data);
1152 remove_from_rx_list (&UnSubscribedLists, buf->data);
1154 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1156 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1159 while (MoreArgs (s));
1164 static int parse_unsubscribe (BUFFER * buf, BUFFER * s,
1165 unsigned long data __attribute__ ((unused)),
1166 BUFFER * err __attribute__ ((unused)))
1169 mutt_extract_token (buf, s, 0);
1170 remove_from_rx_list (&SubscribedLists, buf->data);
1172 if (m_strcmp(buf->data, "*") &&
1173 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1176 while (MoreArgs (s));
1181 static int parse_unalias (BUFFER * buf, BUFFER * s,
1182 unsigned long data __attribute__ ((unused)),
1183 BUFFER * err __attribute__ ((unused)))
1185 alias_t *tmp, **last;
1188 mutt_extract_token (buf, s, 0);
1190 if (!m_strcmp("*", buf->data) == 0) {
1191 if (CurrentMenu == MENU_ALIAS) {
1192 for (tmp = Aliases; tmp; tmp = tmp->next)
1194 set_option(OPTFORCEREDRAWINDEX);
1196 alias_list_wipe(&Aliases);
1202 for (last = &Aliases; *last; last = &(*last)->next) {
1203 if (!m_strcasecmp(buf->data, (*last)->name)) {
1204 if (CurrentMenu == MENU_ALIAS) {
1206 set_option (OPTFORCEREDRAWINDEX);
1208 tmp = alias_list_pop(last);
1214 } while (MoreArgs(s));
1219 static int parse_alias (BUFFER * buf, BUFFER * s,
1220 unsigned long data __attribute__ ((unused)),
1226 if (!MoreArgs (s)) {
1227 m_strcpy(err->data, err->dsize, _("alias: no address"));
1231 mutt_extract_token (buf, s, 0);
1233 /* check to see if an alias with this name already exists */
1234 for (last = &Aliases; *last; last = &(*last)->next) {
1235 if (!m_strcasecmp((*last)->name, buf->data))
1240 /* create a new alias */
1241 *last = alias_new();
1242 (*last)->name = m_strdup(buf->data);
1243 /* give the main addressbook code a chance */
1244 if (CurrentMenu == MENU_ALIAS)
1245 set_option (OPTMENUCALLER);
1247 /* override the previous value */
1248 address_list_wipe(&(*last)->addr);
1249 if (CurrentMenu == MENU_ALIAS)
1250 set_option (OPTFORCEREDRAWINDEX);
1253 mutt_extract_token(buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE);
1254 (*last)->addr = mutt_parse_adrlist((*last)->addr, buf->data);
1255 if (mutt_addrlist_to_idna((*last)->addr, &estr)) {
1256 snprintf (err->data, err->dsize,
1257 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, (*last)->name);
1266 parse_unmy_hdr(BUFFER * buf, BUFFER * s,
1267 unsigned long data __attribute__ ((unused)),
1268 BUFFER * err __attribute__ ((unused)))
1271 mutt_extract_token (buf, s, 0);
1273 if (!m_strcmp("*", buf->data)) {
1274 string_list_wipe(&UserHeader);
1276 string_list_t **last = &UserHeader;
1277 ssize_t l = m_strlen(buf->data);
1279 if (buf->data[l - 1] == ':')
1283 if (!ascii_strncasecmp(buf->data, (*last)->data, l)
1284 && (*last)->data[l] == ':')
1286 string_list_t *tmp = string_list_pop(last);
1287 string_item_delete(&tmp);
1289 last = &(*last)->next;
1293 } while (MoreArgs(s));
1298 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1305 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1306 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1307 m_strcpy(err->data, err->dsize, _("invalid header field"));
1310 keylen = p - buf->data + 1;
1313 for (tmp = UserHeader;; tmp = tmp->next) {
1314 /* see if there is already a field by this name */
1315 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1316 /* replace the old value */
1317 p_delete(&tmp->data);
1318 tmp->data = buf->data;
1325 tmp->next = string_item_new();
1329 tmp = string_item_new();
1332 tmp->data = buf->data;
1338 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1339 char* errbuf, ssize_t errlen) {
1342 if (m_strncmp("reverse-", s, 8) == 0) {
1344 flags = SORT_REVERSE;
1347 if (m_strncmp("last-", s, 5) == 0) {
1352 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1354 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1358 *((short*) dst->data) = i | flags;
1362 /* if additional data more == 1, we want to resolve synonyms */
1363 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
1365 char buf[LONG_STRING];
1366 struct option_t *ptr = p;
1368 if (DTYPE(ptr->type) == DT_SYN) {
1371 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1373 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_fromstr)
1376 mutt_option_value(ptr->option, buf, sizeof(buf));
1377 if (m_strlen(ptr->init) == 0 && buf && *buf)
1378 ptr->init = m_strdup(buf);
1381 static struct option_t* add_option (const char* name, const char* init,
1382 short type, short dodup) {
1383 struct option_t* option = p_new(struct option_t, 1);
1385 option->option = m_strdup(name);
1386 option->type = type;
1388 option->init = dodup ? m_strdup(init) : (char*) init;
1392 /* creates new option_t* of type DT_USER for $user_ var */
1393 static struct option_t* add_user_option (const char* name) {
1394 return (add_option (name, NULL, DT_USER, 1));
1397 /* free()'s option_t* */
1398 static void del_option (void* p) {
1399 struct option_t *ptr = (struct option_t*) p;
1400 char* s = (char*) ptr->data;
1401 p_delete(&ptr->option);
1403 p_delete(&ptr->init);
1407 static int init_expand (char** dst, struct option_t* src) {
1413 if (DTYPE(src->type) == DT_STR ||
1414 DTYPE(src->type) == DT_PATH) {
1415 /* only expand for string as it's the only place where
1416 * we want to expand vars right now */
1417 if (src->init && *src->init) {
1420 len = m_strlen(src->init) + 2;
1421 in.data = p_new(char, len + 1);
1422 snprintf (in.data, len, "\"%s\"", src->init);
1425 mutt_extract_token (&token, &in, 0);
1426 if (token.data && *token.data)
1427 *dst = m_strdup(token.data);
1429 *dst = m_strdup("");
1431 p_delete(&token.data);
1433 *dst = m_strdup("");
1435 /* for non-string: take value as is */
1436 *dst = m_strdup(src->init);
1440 /* if additional data more == 1, we want to resolve synonyms */
1441 static void mutt_restore_default (const char* name __attribute__ ((unused)),
1442 void* p, unsigned long more) {
1443 char errbuf[STRING];
1444 struct option_t* ptr = (struct option_t*) p;
1447 if (DTYPE (ptr->type) == DT_SYN) {
1450 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1454 if (FuncTable[DTYPE (ptr->type)].opt_fromstr) {
1455 init_expand (&init, ptr);
1456 if (!FuncTable[DTYPE (ptr->type)].opt_fromstr (ptr, init, errbuf,
1458 if (!option (OPTNOCURSES))
1460 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1461 "Please report this error: \"%s\"\n"),
1462 ptr->option, NONULL (init), errbuf);
1468 if (ptr->flags & R_INDEX)
1469 set_option (OPTFORCEREDRAWINDEX);
1470 if (ptr->flags & R_PAGER)
1471 set_option (OPTFORCEREDRAWPAGER);
1472 if (ptr->flags & R_RESORT_SUB)
1473 set_option (OPTSORTSUBTHREADS);
1474 if (ptr->flags & R_RESORT)
1475 set_option (OPTNEEDRESORT);
1476 if (ptr->flags & R_RESORT_INIT)
1477 set_option (OPTRESORTINIT);
1478 if (ptr->flags & R_TREE)
1479 set_option (OPTREDRAWTREE);
1482 /* check whether value for $dsn_return would be valid */
1483 static int check_dsn_return (const char* option __attribute__ ((unused)), unsigned long p,
1484 char* errbuf, ssize_t errlen) {
1485 char* val = (char*) p;
1486 if (val && *val && m_strncmp(val, "hdrs", 4) != 0 &&
1487 m_strncmp(val, "full", 4) != 0) {
1489 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1495 /* check whether value for $dsn_notify would be valid */
1497 check_dsn_notify (const char* option __attribute__ ((unused)),
1498 unsigned long val, char* errbuf, ssize_t errlen)
1500 const char *p = (const char*)val;
1503 const char *q = m_strchrnul(p, ',');
1506 if (!m_strncmp(p, "never", len) && !m_strncmp(p, "delay", len)
1507 && !m_strncmp(p, "failure", len) && !m_strncmp(p, "success", len))
1510 snprintf(errbuf, errlen, _("'%.*s' is invalid for $%s"),
1511 len, p, "dsn_notify");
1521 static int check_num (const char* option, unsigned long p,
1522 char* errbuf, ssize_t errlen) {
1525 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1531 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1532 char* errbuf, ssize_t errlen) {
1533 if (!check_num ("history", p, errbuf, errlen))
1535 mutt_init_history ();
1539 static int check_special (const char* name, unsigned long val,
1540 char* errbuf, ssize_t errlen) {
1543 for (i = 0; SpecialVars[i].name; i++) {
1544 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1545 return (SpecialVars[i].check (SpecialVars[i].name,
1546 val, errbuf, errlen));
1552 static const struct mapping_t* get_sortmap (struct option_t* option) {
1553 const struct mapping_t* map = NULL;
1555 switch (option->type & DT_SUBTYPE_MASK) {
1557 map = SortAliasMethods;
1559 case DT_SORT_BROWSER:
1560 map = SortBrowserMethods;
1563 map = SortKeyMethods;
1566 map = SortAuxMethods;
1575 #define CHECK_PAGER \
1576 if ((CurrentMenu == MENU_PAGER) && \
1577 (!option || (option->flags & R_RESORT))) \
1579 snprintf (err->data, err->dsize, \
1580 _("Not available in this menu.")); \
1584 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1587 int query, unset, inv, reset, r = 0;
1588 struct option_t* option = NULL;
1590 while (MoreArgs (s)) {
1591 /* reset state variables */
1593 unset = data & M_SET_UNSET;
1594 inv = data & M_SET_INV;
1595 reset = data & M_SET_RESET;
1597 if (*s->dptr == '?') {
1601 else if (m_strncmp("no", s->dptr, 2) == 0) {
1605 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1609 else if (*s->dptr == '&') {
1614 /* get the variable name */
1615 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1617 /* resolve synonyms */
1618 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1619 DTYPE (option->type == DT_SYN))
1621 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1622 syn_t* syn = syn_new();
1623 syn->f = m_strdup(CurRCFile);
1627 syn_list_push(&Synonyms, syn);
1631 /* see if we need to add $user_ var */
1632 if (!option && m_strncmp("user_", tmp->data, 5) == 0) {
1633 /* there's no option named like this yet so only add one
1634 * if the action isn't any of: reset, unset, query */
1635 if (!(reset || unset || query || *s->dptr != '=')) {
1636 option = add_user_option (tmp->data);
1637 hash_insert (ConfigOptions, option->option, option, 0);
1641 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1642 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1645 s->dptr = vskipspaces(s->dptr);
1648 if (query || unset || inv) {
1649 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1653 if (s && *s->dptr == '=') {
1654 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1658 if (!m_strcmp("all", tmp->data)) {
1659 if (CurrentMenu == MENU_PAGER) {
1660 snprintf (err->data, err->dsize, _("Not available in this menu."));
1663 hash_map (ConfigOptions, mutt_restore_default, 1);
1664 set_option (OPTFORCEREDRAWINDEX);
1665 set_option (OPTFORCEREDRAWPAGER);
1666 set_option (OPTSORTSUBTHREADS);
1667 set_option (OPTNEEDRESORT);
1668 set_option (OPTRESORTINIT);
1669 set_option (OPTREDRAWTREE);
1672 else if (!FuncTable[DTYPE (option->type)].opt_fromstr) {
1673 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1678 mutt_restore_default (NULL, option, 1);
1681 else if (DTYPE (option->type) == DT_BOOL) {
1682 /* XXX this currently ignores the function table
1683 * as we don't get invert and stuff into it */
1684 if (s && *s->dptr == '=') {
1685 if (unset || inv || query) {
1686 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1691 mutt_extract_token (tmp, s, 0);
1692 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1694 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1697 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1703 bool_to_string (err->data, err->dsize, option);
1709 unset_option (option->data);
1711 toggle_option (option->data);
1713 set_option (option->data);
1715 else if (DTYPE (option->type) == DT_STR ||
1716 DTYPE (option->type) == DT_PATH ||
1717 DTYPE (option->type) == DT_ADDR ||
1718 DTYPE (option->type) == DT_MAGIC ||
1719 DTYPE (option->type) == DT_NUM ||
1720 DTYPE (option->type) == DT_SORT ||
1721 DTYPE (option->type) == DT_RX ||
1722 DTYPE (option->type) == DT_USER ||
1723 DTYPE (option->type) == DT_SYS) {
1725 /* XXX maybe we need to get unset into handlers? */
1726 if (DTYPE (option->type) == DT_STR ||
1727 DTYPE (option->type) == DT_PATH ||
1728 DTYPE (option->type) == DT_ADDR ||
1729 DTYPE (option->type) == DT_USER ||
1730 DTYPE (option->type) == DT_SYS) {
1733 if (!FuncTable[DTYPE (option->type)].opt_fromstr) {
1734 snprintf (err->data, err->dsize, _("$%s is read-only"),
1738 } else if (DTYPE (option->type) == DT_ADDR)
1739 address_list_wipe((address_t **) option->data);
1740 else if (DTYPE (option->type) == DT_USER)
1741 /* to unset $user_ means remove */
1742 hash_delete (ConfigOptions, option->option,
1743 option, del_option);
1745 p_delete((void **)(void *)&option->data);
1750 if (query || *s->dptr != '=') {
1751 FuncTable[DTYPE (option->type)].opt_tostr
1752 (err->data, err->dsize, option);
1756 /* the $madmutt_ variables are read-only */
1757 if (!FuncTable[DTYPE (option->type)].opt_fromstr) {
1758 snprintf (err->data, err->dsize, _("$%s is read-only"),
1765 mutt_extract_token (tmp, s, 0);
1766 if (!FuncTable[DTYPE (option->type)].opt_fromstr
1767 (option, tmp->data, err->data, err->dsize))
1771 else if (DTYPE (option->type) == DT_QUAD) {
1774 quad_to_string (err->data, err->dsize, option);
1778 if (*s->dptr == '=') {
1781 mutt_extract_token (tmp, s, 0);
1782 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1783 set_quadoption (option->data, M_YES);
1784 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1785 set_quadoption (option->data, M_NO);
1786 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1787 set_quadoption (option->data, M_ASKYES);
1788 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1789 set_quadoption (option->data, M_ASKNO);
1791 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1792 tmp->data, option->option);
1799 toggle_quadoption (option->data);
1801 set_quadoption (option->data, M_NO);
1803 set_quadoption (option->data, M_YES);
1807 snprintf (err->data, err->dsize, _("%s: unknown type"),
1813 if (option->flags & R_INDEX)
1814 set_option (OPTFORCEREDRAWINDEX);
1815 if (option->flags & R_PAGER)
1816 set_option (OPTFORCEREDRAWPAGER);
1817 if (option->flags & R_RESORT_SUB)
1818 set_option (OPTSORTSUBTHREADS);
1819 if (option->flags & R_RESORT)
1820 set_option (OPTNEEDRESORT);
1821 if (option->flags & R_RESORT_INIT)
1822 set_option (OPTRESORTINIT);
1823 if (option->flags & R_TREE)
1824 set_option (OPTREDRAWTREE);
1831 /* reads the specified initialization file. returns -1 if errors were found
1832 so that we can pause to let the user know... */
1833 static int source_rc (const char *rcfile, BUFFER * err)
1836 int line = 0, rc = 0, conv = 0;
1838 char *linebuf = NULL;
1839 char *currentline = NULL;
1843 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1844 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1849 while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
1850 conv = ConfigCharset && (*ConfigCharset) && Charset;
1852 currentline = m_strdup(linebuf);
1855 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1858 currentline = linebuf;
1863 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1864 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1865 if (--rc < -MAXERRS) {
1867 p_delete(¤tline);
1876 p_delete(¤tline);
1878 p_delete(&token.data);
1882 mutt_wait_filter (pid);
1884 /* the muttrc source keyword */
1885 snprintf (err->data, err->dsize,
1886 rc >= -MAXERRS ? _("source: errors in %s")
1887 : _("source: reading aborted due too many errors in %s"),
1896 static int parse_source (BUFFER * tmp, BUFFER * s,
1897 unsigned long data __attribute__ ((unused)),
1900 char path[_POSIX_PATH_MAX];
1904 if (mutt_extract_token (tmp, s, 0) != 0) {
1905 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1909 m_strcpy(path, sizeof(path), tmp->data);
1910 mutt_expand_path (path, sizeof(path));
1912 rc += source_rc (path, err);
1914 while (MoreArgs (s));
1916 return ((rc < 0) ? -1 : 0);
1919 /* line command to execute
1921 token scratch buffer to be used by parser. caller should free
1922 token->data when finished. the reason for this variable is
1923 to avoid having to allocate and deallocate a lot of memory
1924 if we are parsing many lines. the caller can pass in the
1925 memory to use, which avoids having to create new space for
1926 every call to this function.
1928 err where to write error messages */
1929 int mutt_parse_rc_line (const char *line, BUFFER * token, BUFFER * err)
1935 expn.data = expn.dptr = line;
1936 expn.dsize = m_strlen(line);
1940 expn.dptr = vskipspaces(expn.dptr);
1941 while (*expn.dptr) {
1942 if (*expn.dptr == '#')
1943 break; /* rest of line is a comment */
1944 if (*expn.dptr == ';') {
1948 mutt_extract_token (token, &expn, 0);
1949 for (i = 0; Commands[i].name; i++) {
1950 if (!m_strcmp(token->data, Commands[i].name)) {
1951 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1956 if (!Commands[i].name) {
1957 snprintf (err->data, err->dsize, _("%s: unknown command"),
1958 NONULL (token->data));
1965 p_delete(&expn.data);
1970 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
1971 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
1972 /* initial string that starts completion. No telling how much crap
1973 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1974 char User_typed[LONG_STRING] = { 0 };
1976 int Num_matched = 0; /* Number of matches for completion */
1977 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1978 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1980 /* helper function for completion. Changes the dest buffer if
1981 necessary/possible to aid completion.
1982 dest == completion result gets here.
1983 src == candidate for completion.
1984 try == user entered data for completion.
1985 len == length of dest buffer.
1987 static void candidate (char *dest, char *try, const char *src, int len)
1991 if (strstr (src, try) == src) {
1992 Matches[Num_matched++] = src;
1994 m_strcpy(dest, len, src);
1996 for (l = 0; src[l] && src[l] == dest[l]; l++);
2002 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
2006 int spaces; /* keep track of the number of leading spaces on the line */
2008 buffer = vskipspaces(buffer);
2009 spaces = buffer - pt;
2011 pt = buffer + pos - spaces;
2012 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2015 if (pt == buffer) { /* complete cmd */
2016 /* first TAB. Collect all the matches */
2019 m_strcpy(User_typed, sizeof(User_typed), pt);
2020 p_clear(Matches, countof(Matches));
2021 p_clear(Completed, countof(Completed));
2022 for (num = 0; Commands[num].name; num++)
2023 candidate (Completed, User_typed, Commands[num].name,
2025 Matches[Num_matched++] = User_typed;
2027 /* All matches are stored. Longest non-ambiguous string is ""
2028 * i.e. dont change 'buffer'. Fake successful return this time */
2029 if (User_typed[0] == 0)
2033 if (Completed[0] == 0 && User_typed[0])
2036 /* Num_matched will _always_ be atleast 1 since the initial
2037 * user-typed string is always stored */
2038 if (numtabs == 1 && Num_matched == 2)
2039 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2040 else if (numtabs > 1 && Num_matched > 2)
2041 /* cycle thru all the matches */
2042 snprintf (Completed, sizeof(Completed), "%s",
2043 Matches[(numtabs - 2) % Num_matched]);
2045 /* return the completed command */
2046 m_strcpy(buffer, len - spaces, Completed);
2048 else if (!m_strncmp(buffer, "set", 3)
2049 || !m_strncmp(buffer, "unset", 5)
2050 || !m_strncmp(buffer, "reset", 5)
2051 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
2052 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
2055 /* loop through all the possible prefixes (no, inv, ...) */
2056 if (!m_strncmp(buffer, "set", 3)) {
2057 for (num = 0; prefixes[num]; num++) {
2058 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
2059 pt += m_strlen(prefixes[num]);
2065 /* first TAB. Collect all the matches */
2068 m_strcpy(User_typed, sizeof(User_typed), pt);
2069 p_clear(Matches, countof(Matches));
2070 p_clear(Completed, countof(Completed));
2071 for (num = 0; MuttVars[num].option; num++)
2072 candidate(Completed, User_typed, MuttVars[num].option,
2074 Matches[Num_matched++] = User_typed;
2076 /* All matches are stored. Longest non-ambiguous string is ""
2077 * i.e. dont change 'buffer'. Fake successful return this time */
2078 if (User_typed[0] == 0)
2082 if (Completed[0] == 0 && User_typed[0])
2085 /* Num_matched will _always_ be atleast 1 since the initial
2086 * user-typed string is always stored */
2087 if (numtabs == 1 && Num_matched == 2)
2088 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2089 else if (numtabs > 1 && Num_matched > 2)
2090 /* cycle thru all the matches */
2091 snprintf (Completed, sizeof(Completed), "%s",
2092 Matches[(numtabs - 2) % Num_matched]);
2094 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2096 else if (!m_strncmp(buffer, "exec", 4)) {
2097 struct binding_t *menu = km_get_table (CurrentMenu);
2099 if (!menu && CurrentMenu != MENU_PAGER)
2103 /* first TAB. Collect all the matches */
2106 m_strcpy(User_typed, sizeof(User_typed), pt);
2107 p_clear(Matches, countof(Matches));
2108 p_clear(Completed, countof(Completed));
2109 for (num = 0; menu[num].name; num++)
2110 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
2111 /* try the generic menu */
2112 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2114 for (num = 0; menu[num].name; num++)
2115 candidate (Completed, User_typed, menu[num].name,
2118 Matches[Num_matched++] = User_typed;
2120 /* All matches are stored. Longest non-ambiguous string is ""
2121 * i.e. dont change 'buffer'. Fake successful return this time */
2122 if (User_typed[0] == 0)
2126 if (Completed[0] == 0 && User_typed[0])
2129 /* Num_matched will _always_ be atleast 1 since the initial
2130 * user-typed string is always stored */
2131 if (numtabs == 1 && Num_matched == 2)
2132 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2133 else if (numtabs > 1 && Num_matched > 2)
2134 /* cycle thru all the matches */
2135 snprintf (Completed, sizeof(Completed), "%s",
2136 Matches[(numtabs - 2) % Num_matched]);
2138 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2146 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
2148 char var[STRING], *pt = buffer;
2150 struct option_t* option = NULL;
2155 buffer = vskipspaces(buffer);
2156 spaces = buffer - pt;
2158 pt = buffer + pos - spaces;
2159 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2161 pt++; /* move past the space */
2162 if (*pt == '=') /* abort if no var before the '=' */
2165 if (m_strncmp(buffer, "set", 3) == 0) {
2166 m_strcpy(var, sizeof(var), pt);
2167 /* ignore the trailing '=' when comparing */
2168 var[m_strlen(var) - 1] = 0;
2169 if (!(option = hash_find (ConfigOptions, var)))
2170 return 0; /* no such variable. */
2172 char tmp[LONG_STRING], tmp2[LONG_STRING];
2174 ssize_t dlen = buffer + len - pt - spaces;
2175 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2179 if ((DTYPE (option->type) == DT_STR) ||
2180 (DTYPE (option->type) == DT_PATH) ||
2181 (DTYPE (option->type) == DT_RX)) {
2182 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
2183 if (DTYPE (option->type) == DT_PATH)
2184 mutt_pretty_mailbox (tmp);
2186 else if (DTYPE (option->type) == DT_ADDR) {
2187 rfc822_addrcat(tmp, sizeof(tmp), *((address_t **) option->data), 0);
2189 else if (DTYPE (option->type) == DT_QUAD)
2190 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
2191 else if (DTYPE (option->type) == DT_NUM)
2192 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
2193 else if (DTYPE (option->type) == DT_SORT) {
2194 const struct mapping_t *map;
2197 switch (option->type & DT_SUBTYPE_MASK) {
2199 map = SortAliasMethods;
2201 case DT_SORT_BROWSER:
2202 map = SortBrowserMethods;
2205 map = SortKeyMethods;
2211 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2212 snprintf(tmp, sizeof(tmp), "%s%s%s",
2213 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2214 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2216 else if (DTYPE (option->type) == DT_MAGIC) {
2218 switch (DefaultMagic) {
2234 m_strcpy(tmp, sizeof(tmp), p);
2236 else if (DTYPE (option->type) == DT_BOOL)
2237 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
2241 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
2242 if (*s == '\\' || *s == '"')
2248 m_strcpy(tmp, sizeof(tmp), pt);
2249 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2257 /* Implement the -Q command line flag */
2258 int mutt_query_variables (string_list_t * queries)
2262 char errbuff[STRING];
2263 char command[STRING];
2271 err.dsize = sizeof(errbuff);
2273 for (p = queries; p; p = p->next) {
2274 snprintf (command, sizeof(command), "set ?%s\n", p->data);
2275 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2276 fprintf (stderr, "%s\n", err.data);
2277 p_delete(&token.data);
2280 printf ("%s\n", err.data);
2283 p_delete(&token.data);
2287 static int mutt_execute_commands (string_list_t * p)
2290 char errstr[STRING];
2294 err.dsize = sizeof(errstr);
2296 for (; p; p = p->next) {
2297 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2298 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2299 p_delete(&token.data);
2303 p_delete(&token.data);
2307 void mutt_init (int skip_sys_rc, string_list_t * commands)
2310 struct utsname utsname;
2312 char buffer[STRING], error[STRING];
2313 int default_rc = 0, need_pause = 0;
2319 err.dsize = sizeof(error);
2321 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2322 * to have some room for $user_ vars */
2323 ConfigOptions = hash_create (sizeof(MuttVars) * 3);
2324 for (i = 0; MuttVars[i].option; i++) {
2325 if (DTYPE (MuttVars[i].type) != DT_SYS)
2326 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2328 hash_insert (ConfigOptions, MuttVars[i].option,
2329 add_option (MuttVars[i].option, MuttVars[i].init,
2334 * XXX - use something even more difficult to predict?
2336 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2337 "\033]9;%ld\a", (long) time (NULL));
2339 /* on one of the systems I use, getcwd() does not return the same prefix
2340 as is listed in the passwd file */
2341 if ((p = getenv ("HOME")))
2342 Homedir = m_strdup(p);
2344 /* Get some information about the user */
2345 if ((pw = getpwuid (getuid ()))) {
2348 Username = m_strdup(pw->pw_name);
2350 Homedir = m_strdup(pw->pw_dir);
2352 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, GecosMask.rx);
2353 Realname = m_strdup(rnbuf);
2354 Shell = m_strdup(pw->pw_shell);
2360 fputs (_("unable to determine home directory"), stderr);
2363 if ((p = getenv ("USER")))
2364 Username = m_strdup(p);
2367 fputs (_("unable to determine username"), stderr);
2370 Shell = m_strdup((p = getenv ("SHELL")) ? p : "/bin/sh");
2373 /* And about the host... */
2375 /* some systems report the FQDN instead of just the hostname */
2376 if ((p = strchr (utsname.nodename, '.'))) {
2377 Hostname = p_dupstr(utsname.nodename, p - utsname.nodename);
2379 m_strcpy(buffer, sizeof(buffer), p); /* save the domain for below */
2382 Hostname = m_strdup(utsname.nodename);
2384 if (!p && getdnsdomainname(buffer, sizeof(buffer)) == -1)
2385 Fqdn = m_strdup("@");
2387 if (*buffer != '@') {
2388 Fqdn = p_new(char, m_strlen(buffer) + m_strlen(Hostname) + 2);
2389 sprintf (Fqdn, "%s.%s", NONULL(Hostname), buffer);
2392 Fqdn = m_strdup(NONULL (Hostname));
2399 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2401 fgets (buffer, sizeof(buffer), f);
2402 p = vskipspaces(buffer);
2404 while (*q && !isspace(*q))
2407 NewsServer = m_strdup(p);
2411 if ((p = getenv ("NNTPSERVER")))
2412 NewsServer = m_strdup(p);
2415 if ((p = getenv ("MAIL")))
2416 Spoolfile = m_strdup(p);
2417 else if ((p = getenv ("MAILDIR")))
2418 Spoolfile = m_strdup(p);
2421 mutt_concat_path(buffer, sizeof(buffer), NONULL(Homedir), MAILPATH);
2423 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(Username));
2425 Spoolfile = m_strdup(buffer);
2428 if ((p = getenv ("MAILCAPS")))
2429 MailcapPath = m_strdup(p);
2431 /* Default search path from RFC1524 */
2433 m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2434 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2437 Tempdir = m_strdup((p = getenv ("TMPDIR")) ? p : "/tmp");
2439 p = getenv ("VISUAL");
2441 p = getenv ("EDITOR");
2445 Editor = m_strdup(p);
2447 if ((p = getenv ("REPLYTO")) != NULL) {
2450 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2453 buf.data = buf.dptr = buffer;
2454 buf.dsize = m_strlen(buffer);
2457 parse_my_hdr (&token, &buf, 0, &err);
2458 p_delete(&token.data);
2461 if ((p = getenv ("EMAIL")) != NULL)
2462 From = rfc822_parse_adrlist (NULL, p);
2464 charset_initialize();
2466 /* Set standard defaults */
2467 hash_map (ConfigOptions, mutt_set_default, 0);
2468 hash_map (ConfigOptions, mutt_restore_default, 0);
2470 CurrentMenu = MENU_MAIN;
2473 /* Unset suspend by default if we're the session leader */
2474 if (getsid (0) == getpid ())
2475 unset_option (OPTSUSPEND);
2478 mutt_init_history ();
2481 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL (Homedir));
2482 if (access (buffer, F_OK) == -1)
2483 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
2487 Muttrc = m_strdup(buffer);
2490 m_strcpy(buffer, sizeof(buffer), Muttrc);
2492 mutt_expand_path (buffer, sizeof(buffer));
2493 Muttrc = m_strdup(buffer);
2495 p_delete(&AliasFile);
2496 AliasFile = m_strdup(NONULL (Muttrc));
2498 /* Process the global rc file if it exists and the user hasn't explicity
2499 requested not to via "-n". */
2501 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
2503 if (access (buffer, F_OK) == -1)
2504 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
2505 if (access (buffer, F_OK) == -1)
2506 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
2508 if (access (buffer, F_OK) == -1)
2509 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
2510 if (access (buffer, F_OK) != -1) {
2511 if (source_rc (buffer, &err) != 0) {
2512 fputs (err.data, stderr);
2513 fputc ('\n', stderr);
2519 /* Read the user's initialization file. */
2520 if (access (Muttrc, F_OK) != -1) {
2521 if (!option (OPTNOCURSES))
2523 if (source_rc (Muttrc, &err) != 0) {
2524 fputs (err.data, stderr);
2525 fputc ('\n', stderr);
2529 else if (!default_rc) {
2530 /* file specified by -F does not exist */
2531 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2532 mutt_endwin (buffer);
2536 if (mutt_execute_commands (commands) != 0)
2539 /* warn about synonym variables */
2543 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2545 for (syn = Synonyms; syn; syn = syn->next) {
2546 fprintf(stderr, "$%s ($%s should be used) (%s:%d)\n",
2547 syn->o ? NONULL(syn->o->option) : "",
2548 syn->n ? NONULL(syn->n->option) : "",
2549 NONULL(syn->f), syn->l);
2551 fprintf (stderr, _("Warning: synonym variables are scheduled"
2552 " for removal.\n"));
2553 syn_list_wipe(&Synonyms);
2557 if (need_pause && !option (OPTNOCURSES)) {
2558 if (mutt_any_key_to_continue (NULL) == -1)
2563 int mutt_get_hook_type (const char *name)
2565 struct command_t *c;
2567 for (c = Commands; c->name; c++)
2568 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2573 /* dump out the value of all the variables we have */
2574 int mutt_dump_variables (int full) {
2577 /* get all non-synonyms into list... */
2578 for (i = 0; MuttVars[i].option; i++) {
2579 struct option_t *option = MuttVars + i;
2580 char buf[LONG_STRING];
2582 if (DTYPE(option->type) == DT_SYN)
2586 mutt_option_value(option->option, buf, sizeof(buf));
2587 if (!m_strcmp(buf, option->init))
2592 FuncTable[DTYPE(option->type)].opt_tostr(buf, sizeof(buf), option);
2593 printf ("%s\n", buf);
2596 printf ("\n# vi""m:set ft=muttrc:\n");