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-lua/lib-lua.h>
17 #include <lib-sys/unix.h>
18 #include <lib-sys/mutt_ssl.h>
19 #include <lib-ui/curses.h>
20 #include <lib-ui/history.h>
21 #include <lib-mx/mx.h>
22 #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 sys_to_string (char* dst, ssize_t dstlen, struct option_t* option);
117 /* protos for config type handles: convert to value from string */
118 static int bool_from_string (struct option_t* dst, const char* val,
119 char* errbuf, ssize_t errlen);
120 static int num_from_string (struct option_t* dst, const char* val,
121 char* errbuf, ssize_t errlen);
122 static int str_from_string (struct option_t* dst, const char* val,
123 char* errbuf, ssize_t errlen);
124 static int path_from_string (struct option_t* dst, const char* val,
125 char* errbuf, ssize_t errlen);
126 static int quad_from_string (struct option_t* dst, const char* val,
127 char* errbuf, ssize_t errlen);
128 static int sort_from_string (struct option_t* dst, const char* val,
129 char* errbuf, ssize_t errlen);
130 static int rx_from_string (struct option_t* dst, const char* val,
131 char* errbuf, ssize_t errlen);
132 static int magic_from_string (struct option_t* dst, const char* val,
133 char* errbuf, ssize_t errlen);
134 static int addr_from_string (struct option_t* dst, const char* val,
135 char* errbuf, ssize_t errlen);
139 void (*opt_tostr) (char* dst, ssize_t dstlen, struct option_t* option);
140 int (*opt_fromstr) (struct option_t* dst, const char* val,
141 char* errbuf, ssize_t errlen);
143 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
144 { DT_BOOL, bool_to_string, bool_from_string },
145 { DT_NUM, num_to_string, num_from_string },
146 { DT_STR, str_to_string, str_from_string },
147 { DT_PATH, str_to_string, path_from_string },
148 { DT_QUAD, quad_to_string, quad_from_string },
149 { DT_SORT, sort_to_string, sort_from_string },
150 { DT_RX, rx_to_string, rx_from_string },
151 { DT_MAGIC, magic_to_string, magic_from_string },
152 /* synonyms should be resolved already so we don't need this
153 * but must define it as DT_ is used for indexing */
154 { DT_SYN, NULL, NULL },
155 { DT_ADDR, addr_to_string, addr_from_string },
156 { DT_SYS, sys_to_string, NULL },
159 static void bool_to_string (char* dst, ssize_t dstlen,
160 struct option_t* option) {
161 snprintf (dst, dstlen, "%s=%s", option->option,
162 option (option->data) ? "yes" : "no");
165 static int bool_from_string (struct option_t* dst, const char* val,
166 char* errbuf __attribute__ ((unused)),
167 ssize_t errlen __attribute__ ((unused))) {
172 if (ascii_strncasecmp (val, "yes", 3) == 0)
174 else if (ascii_strncasecmp (val, "no", 2) == 0)
180 set_option (dst->data);
182 unset_option (dst->data);
186 static void num_to_string (char* dst, ssize_t dstlen,
187 struct option_t* option) {
189 const char* fmt = (m_strcmp(option->option, "umask") == 0) ?
191 snprintf (dst, dstlen, fmt, option->option,
192 *((short*) option->data));
195 static int num_from_string (struct option_t* dst, const char* val,
196 char* errbuf, ssize_t errlen) {
197 int num = 0, old = 0;
203 num = strtol (val, &t, 0);
205 if (m_strisempty(val) || *t || (short) num != num) {
207 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
213 /* just temporarily accept new val so that check_special for
214 * $history already has it when doing history's init() */
215 old = *((short*) dst->data);
216 *((short*) dst->data) = (short) num;
218 if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
219 *((short*) dst->data) = old;
226 static void str_to_string (char* dst, ssize_t dstlen,
227 struct option_t* option) {
228 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
229 NONULL (*((char**) option->data)));
232 static void sys_to_string (char* dst, ssize_t dstlen,
233 struct option_t* option) {
234 char *val = NULL, *t = NULL;
237 /* get some $madmutt_ values dynamically */
238 if (m_strcmp("madmutt_pwd", option->option) == 0) {
239 val = p_new(char, _POSIX_PATH_MAX);
240 val = getcwd (val, _POSIX_PATH_MAX-1);
242 } else if (m_strcmp("madmutt_folder_path", option->option) == 0 &&
243 CurrentFolder && *CurrentFolder) {
245 } else if (m_strcmp("madmutt_folder_name", option->option) == 0 &&
246 CurrentFolder && *CurrentFolder) {
248 ssize_t Maildirlength = m_strlen(Maildir);
251 * if name starts with $folder, just strip it to keep hierarchy
252 * $folder=imap://host, path=imap://host/inbox/b -> inbox/b
254 if (Maildirlength > 0 && m_strncmp(CurrentFolder, Maildir,
255 Maildirlength) == 0 &&
256 m_strlen(CurrentFolder) > Maildirlength) {
257 val = CurrentFolder + Maildirlength;
258 if (Maildir[Maildirlength]!='/')
260 /* if not $folder, just use everything after last / */
261 } else if ((t = strrchr (CurrentFolder, '/')) != NULL)
263 /* default: use as-is */
265 val = (char *) CurrentFolder;
268 val = (char *) option->init;
270 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (val));
275 static int path_from_string (struct option_t* dst, const char* val,
276 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
277 char path[_POSIX_PATH_MAX];
282 if (m_strisempty(val)) {
283 p_delete((char**) dst->data);
288 m_strcpy(path, sizeof(path), val);
289 mutt_expand_path (path, sizeof(path));
290 m_strreplace((char **) dst->data, path);
294 static int str_from_string (struct option_t* dst, const char* val,
295 char* errbuf, ssize_t errlen) {
299 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
302 m_strreplace((char**) dst->data, val);
306 static void quad_to_string (char* dst, ssize_t dstlen,
307 struct option_t* option) {
308 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
309 snprintf (dst, dstlen, "%s=%s", option->option,
310 vals[quadoption (option->data)]);
313 static int quad_from_string (struct option_t* dst, const char* val,
314 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
319 if (ascii_strncasecmp (val, "yes", 3) == 0)
321 else if (ascii_strncasecmp (val, "no", 2) == 0)
323 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
325 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
331 set_quadoption (dst->data, flag);
335 static void sort_to_string (char* dst, ssize_t dstlen,
336 struct option_t* option) {
337 const struct mapping_t *map = get_sortmap (option);
338 const char *p = NULL;
341 snprintf (dst, sizeof(dst), "%s=unknown", option->option);
345 p = mutt_getnamebyvalue(*((short *)option->data) & SORT_MASK, map);
347 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
348 (*((short *) option->data) & SORT_REVERSE) ?
350 (*((short *) option->data) & SORT_LAST) ? "last-" :
354 static int sort_from_string (struct option_t* dst, const char* val,
355 char* errbuf, ssize_t errlen) {
356 const struct mapping_t *map = NULL;
357 if (!(map = get_sortmap (dst))) {
359 snprintf (errbuf, errlen, _("%s: Unknown type."),
363 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
368 static void rx_to_string (char* dst, ssize_t dstlen,
369 struct option_t* option) {
370 rx_t* p = (rx_t*) option->data;
371 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
372 NONULL (p->pattern));
375 static int rx_from_string (struct option_t* dst, const char* val,
376 char* errbuf, ssize_t errlen) {
379 int flags = 0, e = 0, not = 0;
385 if (option (OPTATTACHMSG) && !m_strcmp(dst->option, "reply_regexp")) {
387 snprintf (errbuf, errlen,
388 "Operation not permitted when in attach-message mode.");
392 if (!((rx_t*) dst->data))
393 *((rx_t**) dst->data) = p_new(rx_t, 1);
395 p = (rx_t*) dst->data;
397 /* something to do? */
398 if (m_strisempty(val) || (p->pattern && m_strcmp(p->pattern, val) == 0))
401 if (m_strcmp(dst->option, "mask") != 0)
402 flags |= mutt_which_case (val);
405 if (m_strcmp(dst->option, "mask") == 0 && *s == '!') {
410 rx = p_new(regex_t, 1);
412 if ((e = REGCOMP (rx, s, flags)) != 0) {
413 regerror (e, rx, errbuf, errlen);
424 m_strreplace(&p->pattern, val);
428 if (m_strcmp(dst->option, "reply_regexp") == 0)
429 mutt_adjust_all_subjects ();
434 static void magic_to_string (char* dst, ssize_t dstlen,
435 struct option_t* option) {
436 const char* s = NULL;
437 switch (option->data) {
438 case M_MBOX: s = "mbox"; break;
439 case M_MMDF: s = "MMDF"; break;
440 case M_MH: s = "MH"; break;
441 case M_MAILDIR: s = "Maildir"; break;
442 default: s = "unknown"; break;
444 snprintf (dst, dstlen, "%s=%s", option->option, s);
447 static int magic_from_string (struct option_t* dst, const char* val,
448 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
451 if (!dst || m_strisempty(val))
453 if (ascii_strncasecmp (val, "mbox", 4) == 0)
455 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
457 else if (ascii_strncasecmp (val, "mh", 2) == 0)
459 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
465 *((short*) dst->data) = flag;
470 static void addr_to_string (char* dst, ssize_t dstlen,
471 struct option_t* option) {
474 rfc822_addrcat(s, sizeof(s), *((address_t**) option->data), 0);
475 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
478 static int addr_from_string (struct option_t* dst, const char* val,
479 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
482 address_list_wipe((address_t**) dst->data);
484 *((address_t**) dst->data) = rfc822_parse_adrlist (NULL, val);
488 int mutt_option_value (const char* val, char* dst, ssize_t dstlen) {
489 struct option_t* option = NULL;
490 char* tmp = NULL, *t = NULL;
493 if (!(option = hash_find (ConfigOptions, val))) {
497 tmp = p_new(char, dstlen+1);
498 FuncTable[DTYPE(option->type)].opt_tostr (tmp, dstlen, option);
500 /* as we get things of type $var=value and don't want to bloat the
501 * above "just" for expansion, we do the stripping here */
502 t = strchr (tmp, '=');
506 if (t[l-1] == '"' && *t == '"') {
511 memcpy (dst, t, l+1);
517 static void toggle_quadoption (int opt)
520 int b = (opt % 4) * 2;
522 QuadOptions[n] ^= (1 << b);
525 void set_quadoption (int opt, int flag)
528 int b = (opt % 4) * 2;
530 QuadOptions[n] &= ~(0x3 << b);
531 QuadOptions[n] |= (flag & 0x3) << b;
534 int quadoption (int opt)
537 int b = (opt % 4) * 2;
539 return (QuadOptions[n] >> b) & 0x3;
542 int query_quadoption (int opt, const char *prompt)
544 int v = quadoption (opt);
552 v = mutt_yesorno (prompt, (v == M_ASKYES));
553 CLEARLINE (LINES - 1);
560 static void add_to_list(string_list_t **list, const char *str)
562 /* don't add a NULL or empty string to the list */
563 if (m_strisempty(str))
566 /* check to make sure the item is not already on this list */
568 if (!ascii_strcasecmp(str, (*list)->data))
570 list = &(*list)->next;
573 *list = p_new(string_list_t, 1);
574 (*list)->data = m_strdup(str);
578 add_to_rx_list(rx_t **list, const char *s, int flags, BUFFER *err)
585 if (rx_lookup(list, s))
588 rx = rx_compile(s, flags);
590 snprintf(err->data, err->dsize, "Bad regexp: %s\n", s);
594 rx_list_append(list, rx);
598 static int add_to_spam_list(rx_t **list, const char *pat,
599 const char *templ, BUFFER * err)
603 if (m_strisempty(pat) || !templ)
606 if (!(rx = rx_compile (pat, REG_ICASE))) {
607 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
611 /* check to make sure the item is not already on this list */
613 if (!ascii_strcasecmp(rx->pattern, (*list)->pattern)) {
614 rx_t *tmp = rx_list_pop(list);
617 list = &(*list)->next;
622 rx_set_template(rx, templ);
626 static int remove_from_spam_list (rx_t ** list, const char *pat)
631 if (!m_strcmp((*list)->pattern, pat)) {
632 rx_t *spam = rx_list_pop(list);
636 list = &(*list)->next;
644 static void remove_from_list(string_list_t **l, const char *str)
646 if (!m_strcmp("*", str)) {
647 string_list_wipe(l); /* ``unCMD *'' means delete all current entries */
652 if (!ascii_strcasecmp(str, (*l)->data)) {
653 string_list_t *it = string_list_pop(l);
654 string_item_delete(&it);
661 static int remove_from_rx_list(rx_t **l, const char *str)
663 if (m_strcmp("*", str) == 0) {
668 l = rx_lookup(l, str);
670 rx_t *r = rx_list_pop(l);
678 static int parse_unignore (BUFFER * buf, BUFFER * s,
679 unsigned long data __attribute__ ((unused)),
680 BUFFER * err __attribute__ ((unused)))
683 mutt_extract_token (buf, s, 0);
685 /* don't add "*" to the unignore list */
686 if (m_strcmp (buf->data, "*"))
687 add_to_list (&UnIgnore, buf->data);
689 remove_from_list (&Ignore, buf->data);
690 } while (MoreArgs (s));
695 static int parse_ignore (BUFFER * buf, BUFFER * s,
696 unsigned long data __attribute__ ((unused)),
697 BUFFER * err __attribute__ ((unused)))
700 mutt_extract_token (buf, s, 0);
701 remove_from_list (&UnIgnore, buf->data);
702 add_to_list (&Ignore, buf->data);
703 } while (MoreArgs(s));
707 static int parse_list(BUFFER * buf, BUFFER * s, unsigned long data,
708 BUFFER * err __attribute__ ((unused)))
711 mutt_extract_token (buf, s, 0);
712 add_to_list ((string_list_t **) data, buf->data);
713 } while (MoreArgs(s));
717 static void _alternates_clean (void)
721 if (Context && Context->msgcount) {
722 for (i = 0; i < Context->msgcount; i++)
723 Context->hdrs[i]->recip_valid = 0;
727 static int parse_alternates (BUFFER * buf, BUFFER * s,
728 unsigned long data __attribute__ ((unused)),
729 BUFFER * err __attribute__ ((unused)))
731 _alternates_clean ();
733 mutt_extract_token (buf, s, 0);
734 remove_from_rx_list (&UnAlternates, buf->data);
736 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
739 while (MoreArgs (s));
744 static int parse_unalternates (BUFFER * buf, BUFFER * s,
745 unsigned long data __attribute__ ((unused)),
746 BUFFER * err __attribute__ ((unused)))
748 _alternates_clean ();
750 mutt_extract_token (buf, s, 0);
751 remove_from_rx_list (&Alternates, buf->data);
753 if (m_strcmp(buf->data, "*") &&
754 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
758 while (MoreArgs (s));
763 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
770 /* Insist on at least one parameter */
773 m_strcpy(err->data, err->dsize, _("spam: no matching pattern"));
775 m_strcpy(err->data, err->dsize, _("nospam: no matching pattern"));
779 /* Extract the first token, a regexp */
780 mutt_extract_token (buf, s, 0);
782 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
783 if (data == M_SPAM) {
784 /* If there's a second parameter, it's a template for the spam tag. */
786 mutt_extract_token (&templ, s, 0);
788 /* Add to the spam list. */
789 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
790 p_delete(&templ.data);
793 p_delete(&templ.data);
796 /* If not, try to remove from the nospam list. */
798 remove_from_rx_list (&NoSpamList, buf->data);
804 /* M_NOSPAM is for nospam commands. */
805 else if (data == M_NOSPAM) {
806 /* nospam only ever has one parameter. */
808 /* "*" is a special case. */
809 if (!m_strcmp(buf->data, "*")) {
810 rx_list_wipe(&SpamList);
811 rx_list_wipe(&NoSpamList);
815 /* If it's on the spam list, just remove it. */
816 if (remove_from_spam_list (&SpamList, buf->data) != 0)
819 /* Otherwise, add it to the nospam list. */
820 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
826 /* This should not happen. */
827 m_strcpy(err->data, err->dsize, "This is no good at all.");
831 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
832 BUFFER * err __attribute__ ((unused)))
835 mutt_extract_token (buf, s, 0);
837 * Check for deletion of entire list
839 if (m_strcmp(buf->data, "*") == 0) {
840 string_list_wipe((string_list_t **) data);
843 remove_from_list ((string_list_t **) data, buf->data);
845 while (MoreArgs (s));
850 static int parse_lists (BUFFER * buf, BUFFER * s,
851 unsigned long data __attribute__ ((unused)),
855 mutt_extract_token (buf, s, 0);
856 remove_from_rx_list (&UnMailLists, buf->data);
858 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
861 while (MoreArgs (s));
866 /* always wise to do what someone else did before */
867 static void _attachments_clean (void) {
869 if (Context && Context->msgcount) {
870 for (i = 0; i < Context->msgcount; i++)
871 Context->hdrs[i]->attach_valid = 0;
875 static int parse_attach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
876 BUFFER *err __attribute__ ((unused))) {
878 string_list_t *listp, *lastp;
883 /* Find the last item in the list that data points to. */
885 for (listp = *ldata; listp; listp = listp->next) {
886 a = (ATTACH_MATCH *)listp->data;
891 mutt_extract_token (buf, s, 0);
893 if (!buf->data || *buf->data == '\0')
896 a = p_new(ATTACH_MATCH, 1);
898 /* some cheap hacks that I expect to remove */
899 if (!m_strcasecmp(buf->data, "any"))
900 a->major = m_strdup("*/.*");
901 else if (!m_strcasecmp(buf->data, "none"))
902 a->major = m_strdup("cheap_hack/this_should_never_match");
904 a->major = m_strdup(buf->data);
906 if ((p = strchr(a->major, '/'))) {
911 a->minor = "unknown";
914 len = m_strlen(a->minor);
915 tmpminor = p_new(char, len + 3);
916 m_strcpy(&tmpminor[1], len + 3, a->minor);
918 tmpminor[len+1] = '$';
919 tmpminor[len+2] = '\0';
921 a->major_int = mutt_check_mime_type(a->major);
922 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
926 listp = p_new(string_list_t, 1);
927 listp->data = (char *)a;
936 while (MoreArgs (s));
938 _attachments_clean();
942 static int parse_unattach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
943 BUFFER *err __attribute__ ((unused))) {
945 string_list_t *lp, *lastp, *newlp;
951 mutt_extract_token (buf, s, 0);
953 if (!m_strcasecmp(buf->data, "any"))
954 tmp = m_strdup("*/.*");
955 else if (!m_strcasecmp(buf->data, "none"))
956 tmp = m_strdup("cheap_hack/this_should_never_match");
958 tmp = m_strdup(buf->data);
960 if ((minor = strchr(tmp, '/'))) {
964 minor = m_strdup("unknown");
966 major = mutt_check_mime_type(tmp);
968 /* We must do our own walk here because remove_from_list() will only
969 * remove the string_list_t->data, not anything pointed to by the string_list_t->data. */
971 for(lp = *ldata; lp; ) {
972 a = (ATTACH_MATCH *)lp->data;
973 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
974 regfree(&a->minor_rx);
977 /* Relink backward */
979 lastp->next = lp->next;
984 p_delete(&lp->data); /* same as a */
994 while (MoreArgs (s));
997 _attachments_clean();
1001 static int print_attach_list (string_list_t *lp, char op, const char *name) {
1003 printf("attachments %c%s %s/%s\n", op, name,
1004 ((ATTACH_MATCH *)lp->data)->major,
1005 ((ATTACH_MATCH *)lp->data)->minor);
1012 static int parse_attachments (BUFFER *buf, BUFFER *s,
1013 unsigned long data __attribute__ ((unused)),
1016 string_list_t **listp;
1018 mutt_extract_token(buf, s, 0);
1019 if (!buf->data || *buf->data == '\0') {
1020 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
1024 category = buf->data;
1030 printf("\nCurrent attachments settings:\n\n");
1031 print_attach_list(AttachAllow, '+', "A");
1032 print_attach_list(AttachExclude, '-', "A");
1033 print_attach_list(InlineAllow, '+', "I");
1034 print_attach_list(InlineExclude, '-', "I");
1035 set_option (OPTFORCEREDRAWINDEX);
1036 set_option (OPTFORCEREDRAWPAGER);
1037 mutt_any_key_to_continue (NULL);
1041 if (op != '+' && op != '-') {
1045 if (!m_strncasecmp(category, "attachment", strlen(category))) {
1047 listp = &AttachAllow;
1049 listp = &AttachExclude;
1051 else if (!m_strncasecmp(category, "inline", strlen(category))) {
1053 listp = &InlineAllow;
1055 listp = &InlineExclude;
1057 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
1061 return parse_attach_list(buf, s, listp, err);
1064 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
1066 string_list_t **listp;
1068 mutt_extract_token(buf, s, 0);
1069 if (!buf->data || *buf->data == '\0') {
1070 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
1076 if (op != '+' && op != '-') {
1080 if (!m_strncasecmp(p, "attachment", strlen(p))) {
1082 listp = &AttachAllow;
1084 listp = &AttachExclude;
1086 else if (!m_strncasecmp(p, "inline", strlen(p))) {
1088 listp = &InlineAllow;
1090 listp = &InlineExclude;
1093 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
1097 return parse_unattach_list(buf, s, listp, err);
1100 static int parse_unlists (BUFFER * buf, BUFFER * s,
1101 unsigned long data __attribute__ ((unused)),
1102 BUFFER * err __attribute__ ((unused)))
1105 mutt_extract_token (buf, s, 0);
1106 remove_from_rx_list (&SubscribedLists, buf->data);
1107 remove_from_rx_list (&MailLists, buf->data);
1109 if (m_strcmp(buf->data, "*") &&
1110 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1113 while (MoreArgs (s));
1118 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1122 mutt_extract_token (buf, s, 0);
1123 remove_from_rx_list (&UnMailLists, buf->data);
1124 remove_from_rx_list (&UnSubscribedLists, buf->data);
1126 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1128 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1131 while (MoreArgs (s));
1136 static int parse_unsubscribe (BUFFER * buf, BUFFER * s,
1137 unsigned long data __attribute__ ((unused)),
1138 BUFFER * err __attribute__ ((unused)))
1141 mutt_extract_token (buf, s, 0);
1142 remove_from_rx_list (&SubscribedLists, buf->data);
1144 if (m_strcmp(buf->data, "*") &&
1145 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1148 while (MoreArgs (s));
1153 static int parse_unalias (BUFFER * buf, BUFFER * s,
1154 unsigned long data __attribute__ ((unused)),
1155 BUFFER * err __attribute__ ((unused)))
1157 alias_t *tmp, **last;
1160 mutt_extract_token (buf, s, 0);
1162 if (!m_strcmp("*", buf->data) == 0) {
1163 if (CurrentMenu == MENU_ALIAS) {
1164 for (tmp = Aliases; tmp; tmp = tmp->next)
1166 set_option(OPTFORCEREDRAWINDEX);
1168 alias_list_wipe(&Aliases);
1174 for (last = &Aliases; *last; last = &(*last)->next) {
1175 if (!m_strcasecmp(buf->data, (*last)->name)) {
1176 if (CurrentMenu == MENU_ALIAS) {
1178 set_option (OPTFORCEREDRAWINDEX);
1180 tmp = alias_list_pop(last);
1186 } while (MoreArgs(s));
1191 static int parse_alias (BUFFER * buf, BUFFER * s,
1192 unsigned long data __attribute__ ((unused)),
1198 if (!MoreArgs (s)) {
1199 m_strcpy(err->data, err->dsize, _("alias: no address"));
1203 mutt_extract_token (buf, s, 0);
1205 /* check to see if an alias with this name already exists */
1206 for (last = &Aliases; *last; last = &(*last)->next) {
1207 if (!m_strcasecmp((*last)->name, buf->data))
1212 /* create a new alias */
1213 *last = alias_new();
1214 (*last)->name = m_strdup(buf->data);
1215 /* give the main addressbook code a chance */
1216 if (CurrentMenu == MENU_ALIAS)
1217 set_option (OPTMENUCALLER);
1219 /* override the previous value */
1220 address_list_wipe(&(*last)->addr);
1221 if (CurrentMenu == MENU_ALIAS)
1222 set_option (OPTFORCEREDRAWINDEX);
1225 mutt_extract_token(buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE);
1226 (*last)->addr = mutt_parse_adrlist((*last)->addr, buf->data);
1227 if (mutt_addrlist_to_idna((*last)->addr, &estr)) {
1228 snprintf (err->data, err->dsize,
1229 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, (*last)->name);
1238 parse_unmy_hdr(BUFFER * buf, BUFFER * s,
1239 unsigned long data __attribute__ ((unused)),
1240 BUFFER * err __attribute__ ((unused)))
1243 mutt_extract_token (buf, s, 0);
1245 if (!m_strcmp("*", buf->data)) {
1246 string_list_wipe(&UserHeader);
1248 string_list_t **last = &UserHeader;
1249 ssize_t l = m_strlen(buf->data);
1251 if (buf->data[l - 1] == ':')
1255 if (!ascii_strncasecmp(buf->data, (*last)->data, l)
1256 && (*last)->data[l] == ':')
1258 string_list_t *tmp = string_list_pop(last);
1259 string_item_delete(&tmp);
1261 last = &(*last)->next;
1265 } while (MoreArgs(s));
1270 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1277 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1278 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1279 m_strcpy(err->data, err->dsize, _("invalid header field"));
1282 keylen = p - buf->data + 1;
1285 for (tmp = UserHeader;; tmp = tmp->next) {
1286 /* see if there is already a field by this name */
1287 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1288 /* replace the old value */
1289 p_delete(&tmp->data);
1290 tmp->data = buf->data;
1297 tmp->next = string_item_new();
1301 tmp = string_item_new();
1304 tmp->data = buf->data;
1310 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1311 char* errbuf, ssize_t errlen) {
1314 if (m_strncmp("reverse-", s, 8) == 0) {
1316 flags = SORT_REVERSE;
1319 if (m_strncmp("last-", s, 5) == 0) {
1324 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1326 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1330 *((short*) dst->data) = i | flags;
1334 /* if additional data more == 1, we want to resolve synonyms */
1335 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
1337 char buf[LONG_STRING];
1338 struct option_t *ptr = p;
1340 if (DTYPE(ptr->type) == DT_SYN) {
1343 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1345 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_fromstr)
1348 mutt_option_value(ptr->option, buf, sizeof(buf));
1349 if (m_strlen(ptr->init) == 0 && buf && *buf)
1350 ptr->init = m_strdup(buf);
1353 static struct option_t* add_option (const char* name, const char* init,
1354 short type, short dodup) {
1355 struct option_t* option = p_new(struct option_t, 1);
1357 option->option = m_strdup(name);
1358 option->type = type;
1360 option->init = dodup ? m_strdup(init) : (char*) init;
1364 static int init_expand (char** dst, struct option_t* src) {
1370 if (DTYPE(src->type) == DT_STR ||
1371 DTYPE(src->type) == DT_PATH) {
1372 /* only expand for string as it's the only place where
1373 * we want to expand vars right now */
1374 if (src->init && *src->init) {
1377 len = m_strlen(src->init) + 2;
1378 in.data = p_new(char, len + 1);
1379 snprintf (in.data, len, "\"%s\"", src->init);
1382 mutt_extract_token (&token, &in, 0);
1383 if (token.data && *token.data)
1384 *dst = m_strdup(token.data);
1386 *dst = m_strdup("");
1388 p_delete(&token.data);
1390 *dst = m_strdup("");
1392 /* for non-string: take value as is */
1393 *dst = m_strdup(src->init);
1397 /* if additional data more == 1, we want to resolve synonyms */
1398 static void mutt_restore_default (const char* name __attribute__ ((unused)),
1399 void* p, unsigned long more) {
1400 char errbuf[STRING];
1401 struct option_t* ptr = (struct option_t*) p;
1404 if (DTYPE (ptr->type) == DT_SYN) {
1407 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1411 if (FuncTable[DTYPE (ptr->type)].opt_fromstr) {
1412 init_expand (&init, ptr);
1413 if (!FuncTable[DTYPE (ptr->type)].opt_fromstr (ptr, init, errbuf,
1415 if (!option (OPTNOCURSES))
1417 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1418 "Please report this error: \"%s\"\n"),
1419 ptr->option, NONULL (init), errbuf);
1425 if (ptr->flags & R_INDEX)
1426 set_option (OPTFORCEREDRAWINDEX);
1427 if (ptr->flags & R_PAGER)
1428 set_option (OPTFORCEREDRAWPAGER);
1429 if (ptr->flags & R_RESORT_SUB)
1430 set_option (OPTSORTSUBTHREADS);
1431 if (ptr->flags & R_RESORT)
1432 set_option (OPTNEEDRESORT);
1433 if (ptr->flags & R_RESORT_INIT)
1434 set_option (OPTRESORTINIT);
1435 if (ptr->flags & R_TREE)
1436 set_option (OPTREDRAWTREE);
1439 /* check whether value for $dsn_return would be valid */
1440 static int check_dsn_return (const char* option __attribute__ ((unused)), unsigned long p,
1441 char* errbuf, ssize_t errlen) {
1442 char* val = (char*) p;
1443 if (val && *val && m_strncmp(val, "hdrs", 4) != 0 &&
1444 m_strncmp(val, "full", 4) != 0) {
1446 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1452 /* check whether value for $dsn_notify would be valid */
1454 check_dsn_notify (const char* option __attribute__ ((unused)),
1455 unsigned long val, char* errbuf, ssize_t errlen)
1457 const char *p = (const char*)val;
1460 const char *q = m_strchrnul(p, ',');
1463 if (!m_strncmp(p, "never", len) && !m_strncmp(p, "delay", len)
1464 && !m_strncmp(p, "failure", len) && !m_strncmp(p, "success", len))
1467 snprintf(errbuf, errlen, _("'%.*s' is invalid for $%s"),
1468 len, p, "dsn_notify");
1478 static int check_num (const char* option, unsigned long p,
1479 char* errbuf, ssize_t errlen) {
1482 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1488 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1489 char* errbuf, ssize_t errlen) {
1490 if (!check_num ("history", p, errbuf, errlen))
1492 mutt_init_history ();
1496 static int check_special (const char* name, unsigned long val,
1497 char* errbuf, ssize_t errlen) {
1500 for (i = 0; SpecialVars[i].name; i++) {
1501 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1502 return (SpecialVars[i].check (SpecialVars[i].name,
1503 val, errbuf, errlen));
1509 static const struct mapping_t* get_sortmap (struct option_t* option) {
1510 const struct mapping_t* map = NULL;
1512 switch (option->type & DT_SUBTYPE_MASK) {
1514 map = SortAliasMethods;
1516 case DT_SORT_BROWSER:
1517 map = SortBrowserMethods;
1520 map = SortKeyMethods;
1523 map = SortAuxMethods;
1532 #define CHECK_PAGER \
1533 if ((CurrentMenu == MENU_PAGER) && \
1534 (!option || (option->flags & R_RESORT))) \
1536 snprintf (err->data, err->dsize, \
1537 _("Not available in this menu.")); \
1541 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1544 int query, unset, inv, reset, r = 0;
1545 struct option_t* option = NULL;
1547 while (MoreArgs (s)) {
1548 /* reset state variables */
1550 unset = data & M_SET_UNSET;
1551 inv = data & M_SET_INV;
1552 reset = data & M_SET_RESET;
1554 if (*s->dptr == '?') {
1558 else if (m_strncmp("no", s->dptr, 2) == 0) {
1562 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1566 else if (*s->dptr == '&') {
1571 /* get the variable name */
1572 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1574 /* resolve synonyms */
1575 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1576 DTYPE (option->type == DT_SYN))
1578 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1579 syn_t* syn = syn_new();
1580 syn->f = m_strdup(CurRCFile);
1584 syn_list_push(&Synonyms, syn);
1588 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1589 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1592 s->dptr = vskipspaces(s->dptr);
1595 if (query || unset || inv) {
1596 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1600 if (s && *s->dptr == '=') {
1601 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1605 if (!m_strcmp("all", tmp->data)) {
1606 if (CurrentMenu == MENU_PAGER) {
1607 snprintf (err->data, err->dsize, _("Not available in this menu."));
1610 hash_map (ConfigOptions, mutt_restore_default, 1);
1611 set_option (OPTFORCEREDRAWINDEX);
1612 set_option (OPTFORCEREDRAWPAGER);
1613 set_option (OPTSORTSUBTHREADS);
1614 set_option (OPTNEEDRESORT);
1615 set_option (OPTRESORTINIT);
1616 set_option (OPTREDRAWTREE);
1619 else if (!FuncTable[DTYPE (option->type)].opt_fromstr) {
1620 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1625 mutt_restore_default (NULL, option, 1);
1628 else if (DTYPE (option->type) == DT_BOOL) {
1629 /* XXX this currently ignores the function table
1630 * as we don't get invert and stuff into it */
1631 if (s && *s->dptr == '=') {
1632 if (unset || inv || query) {
1633 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1638 mutt_extract_token (tmp, s, 0);
1639 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1641 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1644 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1650 bool_to_string (err->data, err->dsize, option);
1656 unset_option (option->data);
1658 toggle_option (option->data);
1660 set_option (option->data);
1662 else if (DTYPE (option->type) == DT_STR ||
1663 DTYPE (option->type) == DT_PATH ||
1664 DTYPE (option->type) == DT_ADDR ||
1665 DTYPE (option->type) == DT_MAGIC ||
1666 DTYPE (option->type) == DT_NUM ||
1667 DTYPE (option->type) == DT_SORT ||
1668 DTYPE (option->type) == DT_RX ||
1669 DTYPE (option->type) == DT_SYS) {
1671 /* XXX maybe we need to get unset into handlers? */
1672 if (DTYPE (option->type) == DT_STR ||
1673 DTYPE (option->type) == DT_PATH ||
1674 DTYPE (option->type) == DT_ADDR ||
1675 DTYPE (option->type) == DT_SYS) {
1678 if (!FuncTable[DTYPE (option->type)].opt_fromstr) {
1679 snprintf (err->data, err->dsize, _("$%s is read-only"),
1683 } else if (DTYPE (option->type) == DT_ADDR)
1684 address_list_wipe((address_t **) option->data);
1686 p_delete((void **)(void *)&option->data);
1691 if (query || *s->dptr != '=') {
1692 FuncTable[DTYPE (option->type)].opt_tostr
1693 (err->data, err->dsize, option);
1697 /* the $madmutt_ variables are read-only */
1698 if (!FuncTable[DTYPE (option->type)].opt_fromstr) {
1699 snprintf (err->data, err->dsize, _("$%s is read-only"),
1706 mutt_extract_token (tmp, s, 0);
1707 if (!FuncTable[DTYPE (option->type)].opt_fromstr
1708 (option, tmp->data, err->data, err->dsize))
1712 else if (DTYPE (option->type) == DT_QUAD) {
1715 quad_to_string (err->data, err->dsize, option);
1719 if (*s->dptr == '=') {
1722 mutt_extract_token (tmp, s, 0);
1723 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1724 set_quadoption (option->data, M_YES);
1725 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1726 set_quadoption (option->data, M_NO);
1727 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1728 set_quadoption (option->data, M_ASKYES);
1729 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1730 set_quadoption (option->data, M_ASKNO);
1732 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1733 tmp->data, option->option);
1740 toggle_quadoption (option->data);
1742 set_quadoption (option->data, M_NO);
1744 set_quadoption (option->data, M_YES);
1748 snprintf (err->data, err->dsize, _("%s: unknown type"),
1754 if (option->flags & R_INDEX)
1755 set_option (OPTFORCEREDRAWINDEX);
1756 if (option->flags & R_PAGER)
1757 set_option (OPTFORCEREDRAWPAGER);
1758 if (option->flags & R_RESORT_SUB)
1759 set_option (OPTSORTSUBTHREADS);
1760 if (option->flags & R_RESORT)
1761 set_option (OPTNEEDRESORT);
1762 if (option->flags & R_RESORT_INIT)
1763 set_option (OPTRESORTINIT);
1764 if (option->flags & R_TREE)
1765 set_option (OPTREDRAWTREE);
1772 /* reads the specified initialization file. returns -1 if errors were found
1773 so that we can pause to let the user know... */
1774 static int source_rc (const char *rcfile, BUFFER * err)
1777 int line = 0, rc = 0, conv = 0;
1779 char *linebuf = NULL;
1780 char *currentline = NULL;
1784 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1785 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1790 while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
1791 conv = ConfigCharset && (*ConfigCharset) && Charset;
1793 currentline = m_strdup(linebuf);
1796 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1799 currentline = linebuf;
1804 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1805 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1806 if (--rc < -MAXERRS) {
1808 p_delete(¤tline);
1817 p_delete(¤tline);
1819 p_delete(&token.data);
1823 mutt_wait_filter (pid);
1825 /* the muttrc source keyword */
1826 snprintf (err->data, err->dsize,
1827 rc >= -MAXERRS ? _("source: errors in %s")
1828 : _("source: reading aborted due too many errors in %s"),
1837 static int parse_source (BUFFER * tmp, BUFFER * s,
1838 unsigned long data __attribute__ ((unused)),
1841 char path[_POSIX_PATH_MAX];
1845 if (mutt_extract_token (tmp, s, 0) != 0) {
1846 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1850 m_strcpy(path, sizeof(path), tmp->data);
1851 mutt_expand_path (path, sizeof(path));
1853 rc += source_rc (path, err);
1855 while (MoreArgs (s));
1857 return ((rc < 0) ? -1 : 0);
1860 /* line command to execute
1862 token scratch buffer to be used by parser. caller should free
1863 token->data when finished. the reason for this variable is
1864 to avoid having to allocate and deallocate a lot of memory
1865 if we are parsing many lines. the caller can pass in the
1866 memory to use, which avoids having to create new space for
1867 every call to this function.
1869 err where to write error messages */
1870 int mutt_parse_rc_line (const char *line, BUFFER * token, BUFFER * err)
1876 expn.data = expn.dptr = line;
1877 expn.dsize = m_strlen(line);
1881 expn.dptr = vskipspaces(expn.dptr);
1882 while (*expn.dptr) {
1883 if (*expn.dptr == '#')
1884 break; /* rest of line is a comment */
1885 if (*expn.dptr == ';') {
1889 mutt_extract_token (token, &expn, 0);
1890 for (i = 0; Commands[i].name; i++) {
1891 if (!m_strcmp(token->data, Commands[i].name)) {
1892 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1897 if (!Commands[i].name) {
1898 snprintf (err->data, err->dsize, _("%s: unknown command"),
1899 NONULL (token->data));
1906 p_delete(&expn.data);
1911 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
1912 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
1913 /* initial string that starts completion. No telling how much crap
1914 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1915 char User_typed[LONG_STRING] = { 0 };
1917 int Num_matched = 0; /* Number of matches for completion */
1918 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1919 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1921 /* helper function for completion. Changes the dest buffer if
1922 necessary/possible to aid completion.
1923 dest == completion result gets here.
1924 src == candidate for completion.
1925 try == user entered data for completion.
1926 len == length of dest buffer.
1928 static void candidate (char *dest, char *try, const char *src, int len)
1932 if (strstr (src, try) == src) {
1933 Matches[Num_matched++] = src;
1935 m_strcpy(dest, len, src);
1937 for (l = 0; src[l] && src[l] == dest[l]; l++);
1943 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
1947 int spaces; /* keep track of the number of leading spaces on the line */
1949 buffer = vskipspaces(buffer);
1950 spaces = buffer - pt;
1952 pt = buffer + pos - spaces;
1953 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1956 if (pt == buffer) { /* complete cmd */
1957 /* first TAB. Collect all the matches */
1960 m_strcpy(User_typed, sizeof(User_typed), pt);
1961 p_clear(Matches, countof(Matches));
1962 p_clear(Completed, countof(Completed));
1963 for (num = 0; Commands[num].name; num++)
1964 candidate (Completed, User_typed, Commands[num].name,
1966 Matches[Num_matched++] = User_typed;
1968 /* All matches are stored. Longest non-ambiguous string is ""
1969 * i.e. dont change 'buffer'. Fake successful return this time */
1970 if (User_typed[0] == 0)
1974 if (Completed[0] == 0 && User_typed[0])
1977 /* Num_matched will _always_ be atleast 1 since the initial
1978 * user-typed string is always stored */
1979 if (numtabs == 1 && Num_matched == 2)
1980 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1981 else if (numtabs > 1 && Num_matched > 2)
1982 /* cycle thru all the matches */
1983 snprintf (Completed, sizeof(Completed), "%s",
1984 Matches[(numtabs - 2) % Num_matched]);
1986 /* return the completed command */
1987 m_strcpy(buffer, len - spaces, Completed);
1989 else if (!m_strncmp(buffer, "set", 3)
1990 || !m_strncmp(buffer, "unset", 5)
1991 || !m_strncmp(buffer, "reset", 5)
1992 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
1993 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
1996 /* loop through all the possible prefixes (no, inv, ...) */
1997 if (!m_strncmp(buffer, "set", 3)) {
1998 for (num = 0; prefixes[num]; num++) {
1999 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
2000 pt += m_strlen(prefixes[num]);
2006 /* first TAB. Collect all the matches */
2009 m_strcpy(User_typed, sizeof(User_typed), pt);
2010 p_clear(Matches, countof(Matches));
2011 p_clear(Completed, countof(Completed));
2012 for (num = 0; MuttVars[num].option; num++)
2013 candidate(Completed, User_typed, MuttVars[num].option,
2015 Matches[Num_matched++] = User_typed;
2017 /* All matches are stored. Longest non-ambiguous string is ""
2018 * i.e. dont change 'buffer'. Fake successful return this time */
2019 if (User_typed[0] == 0)
2023 if (Completed[0] == 0 && User_typed[0])
2026 /* Num_matched will _always_ be atleast 1 since the initial
2027 * user-typed string is always stored */
2028 if (numtabs == 1 && Num_matched == 2)
2029 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2030 else if (numtabs > 1 && Num_matched > 2)
2031 /* cycle thru all the matches */
2032 snprintf (Completed, sizeof(Completed), "%s",
2033 Matches[(numtabs - 2) % Num_matched]);
2035 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2037 else if (!m_strncmp(buffer, "exec", 4)) {
2038 struct binding_t *menu = km_get_table (CurrentMenu);
2040 if (!menu && CurrentMenu != MENU_PAGER)
2044 /* first TAB. Collect all the matches */
2047 m_strcpy(User_typed, sizeof(User_typed), pt);
2048 p_clear(Matches, countof(Matches));
2049 p_clear(Completed, countof(Completed));
2050 for (num = 0; menu[num].name; num++)
2051 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
2052 /* try the generic menu */
2053 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2055 for (num = 0; menu[num].name; num++)
2056 candidate (Completed, User_typed, menu[num].name,
2059 Matches[Num_matched++] = User_typed;
2061 /* All matches are stored. Longest non-ambiguous string is ""
2062 * i.e. dont change 'buffer'. Fake successful return this time */
2063 if (User_typed[0] == 0)
2067 if (Completed[0] == 0 && User_typed[0])
2070 /* Num_matched will _always_ be atleast 1 since the initial
2071 * user-typed string is always stored */
2072 if (numtabs == 1 && Num_matched == 2)
2073 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2074 else if (numtabs > 1 && Num_matched > 2)
2075 /* cycle thru all the matches */
2076 snprintf (Completed, sizeof(Completed), "%s",
2077 Matches[(numtabs - 2) % Num_matched]);
2079 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2087 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
2089 char var[STRING], *pt = buffer;
2091 struct option_t* option = NULL;
2096 buffer = vskipspaces(buffer);
2097 spaces = buffer - pt;
2099 pt = buffer + pos - spaces;
2100 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2102 pt++; /* move past the space */
2103 if (*pt == '=') /* abort if no var before the '=' */
2106 if (m_strncmp(buffer, "set", 3) == 0) {
2107 m_strcpy(var, sizeof(var), pt);
2108 /* ignore the trailing '=' when comparing */
2109 var[m_strlen(var) - 1] = 0;
2110 if (!(option = hash_find (ConfigOptions, var)))
2111 return 0; /* no such variable. */
2113 char tmp[LONG_STRING], tmp2[LONG_STRING];
2115 ssize_t dlen = buffer + len - pt - spaces;
2116 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2120 if ((DTYPE (option->type) == DT_STR) ||
2121 (DTYPE (option->type) == DT_PATH) ||
2122 (DTYPE (option->type) == DT_RX)) {
2123 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
2124 if (DTYPE (option->type) == DT_PATH)
2125 mutt_pretty_mailbox (tmp);
2127 else if (DTYPE (option->type) == DT_ADDR) {
2128 rfc822_addrcat(tmp, sizeof(tmp), *((address_t **) option->data), 0);
2130 else if (DTYPE (option->type) == DT_QUAD)
2131 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
2132 else if (DTYPE (option->type) == DT_NUM)
2133 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
2134 else if (DTYPE (option->type) == DT_SORT) {
2135 const struct mapping_t *map;
2138 switch (option->type & DT_SUBTYPE_MASK) {
2140 map = SortAliasMethods;
2142 case DT_SORT_BROWSER:
2143 map = SortBrowserMethods;
2146 map = SortKeyMethods;
2152 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2153 snprintf(tmp, sizeof(tmp), "%s%s%s",
2154 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2155 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2157 else if (DTYPE (option->type) == DT_MAGIC) {
2159 switch (DefaultMagic) {
2175 m_strcpy(tmp, sizeof(tmp), p);
2177 else if (DTYPE (option->type) == DT_BOOL)
2178 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
2182 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
2183 if (*s == '\\' || *s == '"')
2189 m_strcpy(tmp, sizeof(tmp), pt);
2190 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2198 /* Implement the -Q command line flag */
2199 int mutt_query_variables (string_list_t * queries)
2203 char errbuff[STRING];
2204 char command[STRING];
2212 err.dsize = sizeof(errbuff);
2214 for (p = queries; p; p = p->next) {
2215 snprintf (command, sizeof(command), "set ?%s\n", p->data);
2216 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2217 fprintf (stderr, "%s\n", err.data);
2218 p_delete(&token.data);
2221 printf ("%s\n", err.data);
2224 p_delete(&token.data);
2228 static int mutt_execute_commands (string_list_t * p)
2231 char errstr[STRING];
2235 err.dsize = sizeof(errstr);
2237 for (; p; p = p->next) {
2238 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2239 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2240 p_delete(&token.data);
2244 p_delete(&token.data);
2248 void mutt_init (int skip_sys_rc, string_list_t * commands)
2251 struct utsname utsname;
2253 char buffer[STRING], error[STRING];
2254 int default_rc = 0, need_pause = 0;
2260 err.dsize = sizeof(error);
2262 ConfigOptions = hash_create (sizeof(MuttVars) * 2);
2263 for (i = 0; MuttVars[i].option; i++) {
2264 if (DTYPE (MuttVars[i].type) != DT_SYS)
2265 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2267 hash_insert (ConfigOptions, MuttVars[i].option,
2268 add_option (MuttVars[i].option, MuttVars[i].init,
2273 * XXX - use something even more difficult to predict?
2275 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2276 "\033]9;%ld\a", (long) time (NULL));
2278 /* on one of the systems I use, getcwd() does not return the same prefix
2279 as is listed in the passwd file */
2280 if ((p = getenv ("HOME")))
2281 Homedir = m_strdup(p);
2283 /* Get some information about the user */
2284 if ((pw = getpwuid (getuid ()))) {
2287 Username = m_strdup(pw->pw_name);
2289 Homedir = m_strdup(pw->pw_dir);
2291 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, GecosMask.rx);
2292 Realname = m_strdup(rnbuf);
2293 Shell = m_strdup(pw->pw_shell);
2299 fputs (_("unable to determine home directory"), stderr);
2302 if ((p = getenv ("USER")))
2303 Username = m_strdup(p);
2306 fputs (_("unable to determine username"), stderr);
2309 Shell = m_strdup((p = getenv ("SHELL")) ? p : "/bin/sh");
2312 /* And about the host... */
2314 /* some systems report the FQDN instead of just the hostname */
2315 if ((p = strchr (utsname.nodename, '.'))) {
2316 Hostname = p_dupstr(utsname.nodename, p - utsname.nodename);
2318 m_strcpy(buffer, sizeof(buffer), p); /* save the domain for below */
2321 Hostname = m_strdup(utsname.nodename);
2323 if (!p && getdnsdomainname(buffer, sizeof(buffer)) == -1)
2324 Fqdn = m_strdup("@");
2326 if (*buffer != '@') {
2327 Fqdn = p_new(char, m_strlen(buffer) + m_strlen(Hostname) + 2);
2328 sprintf (Fqdn, "%s.%s", NONULL(Hostname), buffer);
2331 Fqdn = m_strdup(NONULL (Hostname));
2338 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2340 fgets (buffer, sizeof(buffer), f);
2341 p = vskipspaces(buffer);
2343 while (*q && !isspace(*q))
2346 NewsServer = m_strdup(p);
2350 if ((p = getenv ("NNTPSERVER")))
2351 NewsServer = m_strdup(p);
2354 if ((p = getenv ("MAIL")))
2355 Spoolfile = m_strdup(p);
2356 else if ((p = getenv ("MAILDIR")))
2357 Spoolfile = m_strdup(p);
2360 mutt_concat_path(buffer, sizeof(buffer), NONULL(Homedir), MAILPATH);
2362 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(Username));
2364 Spoolfile = m_strdup(buffer);
2367 if ((p = getenv ("MAILCAPS")))
2368 MailcapPath = m_strdup(p);
2370 /* Default search path from RFC1524 */
2372 m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2373 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2376 Tempdir = m_strdup((p = getenv ("TMPDIR")) ? p : "/tmp");
2378 p = getenv ("VISUAL");
2380 p = getenv ("EDITOR");
2384 Editor = m_strdup(p);
2386 if ((p = getenv ("REPLYTO")) != NULL) {
2389 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2392 buf.data = buf.dptr = buffer;
2393 buf.dsize = m_strlen(buffer);
2396 parse_my_hdr (&token, &buf, 0, &err);
2397 p_delete(&token.data);
2400 if ((p = getenv ("EMAIL")) != NULL)
2401 From = rfc822_parse_adrlist (NULL, p);
2403 charset_initialize();
2406 /* Set standard defaults */
2407 hash_map (ConfigOptions, mutt_set_default, 0);
2408 hash_map (ConfigOptions, mutt_restore_default, 0);
2410 CurrentMenu = MENU_MAIN;
2413 /* Unset suspend by default if we're the session leader */
2414 if (getsid (0) == getpid ())
2415 unset_option (OPTSUSPEND);
2418 mutt_init_history ();
2421 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL (Homedir));
2422 if (access (buffer, F_OK) == -1)
2423 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
2427 Muttrc = m_strdup(buffer);
2430 m_strcpy(buffer, sizeof(buffer), Muttrc);
2432 mutt_expand_path (buffer, sizeof(buffer));
2433 Muttrc = m_strdup(buffer);
2435 p_delete(&AliasFile);
2436 AliasFile = m_strdup(NONULL (Muttrc));
2438 /* Process the global rc file if it exists and the user hasn't explicity
2439 requested not to via "-n". */
2441 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
2443 if (access (buffer, F_OK) == -1)
2444 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
2445 if (access (buffer, F_OK) == -1)
2446 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
2448 if (access (buffer, F_OK) == -1)
2449 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
2450 if (access (buffer, F_OK) != -1) {
2451 if (source_rc (buffer, &err) != 0) {
2452 fputs (err.data, stderr);
2453 fputc ('\n', stderr);
2459 /* Read the user's initialization file. */
2460 if (access (Muttrc, F_OK) != -1) {
2461 if (!option (OPTNOCURSES))
2463 if (source_rc (Muttrc, &err) != 0) {
2464 fputs (err.data, stderr);
2465 fputc ('\n', stderr);
2469 else if (!default_rc) {
2470 /* file specified by -F does not exist */
2471 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2472 mutt_endwin (buffer);
2477 snprintf(buffer, sizeof(buffer), "%s/.madmutt.lua", NONULL(Homedir));
2478 if (access(buffer, F_OK) < 0)
2479 snprintf(buffer, sizeof(buffer), "%s/.madmutt/cfg.lua", NONULL(Homedir));
2480 if (!access(buffer, F_OK)) {
2481 need_pause = mlua_wrap(mutt_error, mlua_dofile(buffer));
2485 if (mutt_execute_commands (commands) != 0)
2488 /* warn about synonym variables */
2492 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2494 for (syn = Synonyms; syn; syn = syn->next) {
2495 fprintf(stderr, "$%s ($%s should be used) (%s:%d)\n",
2496 syn->o ? NONULL(syn->o->option) : "",
2497 syn->n ? NONULL(syn->n->option) : "",
2498 NONULL(syn->f), syn->l);
2500 fprintf (stderr, _("Warning: synonym variables are scheduled"
2501 " for removal.\n"));
2502 syn_list_wipe(&Synonyms);
2506 if (need_pause && !option (OPTNOCURSES)) {
2507 if (mutt_any_key_to_continue (NULL) == -1)
2512 int mutt_get_hook_type (const char *name)
2514 struct command_t *c;
2516 for (c = Commands; c->name; c++)
2517 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2522 /* dump out the value of all the variables we have */
2523 int mutt_dump_variables (int full) {
2526 /* get all non-synonyms into list... */
2527 for (i = 0; MuttVars[i].option; i++) {
2528 struct option_t *option = MuttVars + i;
2529 char buf[LONG_STRING];
2531 if (DTYPE(option->type) == DT_SYN)
2535 mutt_option_value(option->option, buf, sizeof(buf));
2536 if (!m_strcmp(buf, option->init))
2541 FuncTable[DTYPE(option->type)].opt_tostr(buf, sizeof(buf), option);
2542 printf ("%s\n", buf);
2545 printf ("\n# vi""m:set ft=muttrc:\n");