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>
15 #include <lib-lua/lib-lua.h>
16 #include <lib-sys/unix.h>
17 #include <lib-sys/mutt_ssl.h>
18 #include <lib-ui/curses.h>
19 #include <lib-ui/history.h>
20 #include <lib-mx/mx.h>
21 #include <lib-crypt/crypt.h>
27 #include "mutt_idna.h"
29 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
30 #include "mutt_libesmtp.h"
39 static const struct mapping_t* get_sortmap (struct option_t* option);
40 static int parse_sort (struct option_t* dst, const char *s,
41 const struct mapping_t *map,
42 char* errbuf, ssize_t errlen);
44 static hash_t *ConfigOptions = NULL;
46 /* for synonym warning reports: synonym found during parsing */
47 typedef struct syn_t {
51 struct option_t* n; /* new */
52 struct option_t* o; /* old */
56 static void syn_wipe(syn_t *syn) {
60 DO_DELETE(syn_t, syn);
61 DO_SLIST(syn_t, syn, syn_delete);
63 /* for synonym warning reports: list of synonyms found */
64 static syn_t *Synonyms = NULL;
65 /* for synonym warning reports: current rc file */
66 static const char* CurRCFile = NULL;
67 /* for synonym warning reports: current rc line */
68 static int CurRCLine = 0;
70 /* prototypes for checking for special vars */
71 static int check_history (const char* option, unsigned long val,
72 char* errbuf, ssize_t errlen);
73 /* this checks that numbers are >= 0 */
74 static int check_num (const char* option, unsigned long val,
75 char* errbuf, ssize_t errlen);
77 /* use this to check only */
78 static int check_special (const char* option, unsigned long val,
79 char* errbuf, ssize_t errlen);
81 /* variable <-> sanity check function mappings
82 * when changing these, make sure the proper _from_string handler
87 int (*check) (const char* option, unsigned long val,
88 char* errbuf, ssize_t errlen);
90 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
91 { "smtp_use_tls", mutt_libesmtp_check_usetls },
93 { "history", check_history },
94 { "pager_index_lines", check_num },
99 /* protos for config type handles: convert value to string */
100 static void bool_to_string (char* dst, ssize_t dstlen, struct option_t* option);
101 static void num_to_string (char* dst, ssize_t dstlen, struct option_t* option);
102 static void str_to_string (char* dst, ssize_t dstlen, struct option_t* option);
103 static void quad_to_string (char* dst, ssize_t dstlen, struct option_t* option);
104 static void sort_to_string (char* dst, ssize_t dstlen, struct option_t* option);
105 static void rx_to_string (char* dst, ssize_t dstlen, struct option_t* option);
106 static void magic_to_string (char* dst, ssize_t dstlen, struct option_t* option);
107 static void addr_to_string (char* dst, ssize_t dstlen, struct option_t* option);
109 /* protos for config type handles: convert to value from string */
110 static int bool_from_string (struct option_t* dst, const char* val,
111 char* errbuf, ssize_t errlen);
112 static int num_from_string (struct option_t* dst, const char* val,
113 char* errbuf, ssize_t errlen);
114 static int str_from_string (struct option_t* dst, const char* val,
115 char* errbuf, ssize_t errlen);
116 static int path_from_string (struct option_t* dst, const char* val,
117 char* errbuf, ssize_t errlen);
118 static int quad_from_string (struct option_t* dst, const char* val,
119 char* errbuf, ssize_t errlen);
120 static int sort_from_string (struct option_t* dst, const char* val,
121 char* errbuf, ssize_t errlen);
122 static int rx_from_string (struct option_t* dst, const char* val,
123 char* errbuf, ssize_t errlen);
124 static int magic_from_string (struct option_t* dst, const char* val,
125 char* errbuf, ssize_t errlen);
126 static int addr_from_string (struct option_t* dst, const char* val,
127 char* errbuf, ssize_t errlen);
131 void (*opt_tostr) (char* dst, ssize_t dstlen, struct option_t* option);
132 int (*opt_fromstr) (struct option_t* dst, const char* val,
133 char* errbuf, ssize_t errlen);
135 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
136 { DT_BOOL, bool_to_string, bool_from_string },
137 { DT_NUM, num_to_string, num_from_string },
138 { DT_STR, str_to_string, str_from_string },
139 { DT_PATH, str_to_string, path_from_string },
140 { DT_QUAD, quad_to_string, quad_from_string },
141 { DT_SORT, sort_to_string, sort_from_string },
142 { DT_RX, rx_to_string, rx_from_string },
143 { DT_MAGIC, magic_to_string, magic_from_string },
144 /* synonyms should be resolved already so we don't need this
145 * but must define it as DT_ is used for indexing */
146 { DT_SYN, NULL, NULL },
147 { DT_ADDR, addr_to_string, addr_from_string },
150 static void bool_to_string (char* dst, ssize_t dstlen,
151 struct option_t* option) {
152 snprintf (dst, dstlen, "%s=%s", option->option,
153 option (option->data) ? "yes" : "no");
156 static int bool_from_string (struct option_t* dst, const char* val,
157 char* errbuf __attribute__ ((unused)),
158 ssize_t errlen __attribute__ ((unused))) {
163 if (ascii_strncasecmp (val, "yes", 3) == 0)
165 else if (ascii_strncasecmp (val, "no", 2) == 0)
171 set_option (dst->data);
173 unset_option (dst->data);
177 static void num_to_string (char* dst, ssize_t dstlen,
178 struct option_t* option) {
180 const char* fmt = (m_strcmp(option->option, "umask") == 0) ?
182 snprintf (dst, dstlen, fmt, option->option,
183 *((short*) option->data));
186 static int num_from_string (struct option_t* dst, const char* val,
187 char* errbuf, ssize_t errlen) {
188 int num = 0, old = 0;
194 num = strtol (val, &t, 0);
196 if (m_strisempty(val) || *t || (short) num != num) {
198 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
204 /* just temporarily accept new val so that check_special for
205 * $history already has it when doing history's init() */
206 old = *((short*) dst->data);
207 *((short*) dst->data) = (short) num;
209 if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
210 *((short*) dst->data) = old;
217 static void str_to_string (char* dst, ssize_t dstlen,
218 struct option_t* option) {
219 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
220 NONULL (*((char**) option->data)));
223 static int path_from_string (struct option_t* dst, const char* val,
224 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
225 char path[_POSIX_PATH_MAX];
230 if (m_strisempty(val)) {
231 p_delete((char**) dst->data);
236 m_strcpy(path, sizeof(path), val);
237 mutt_expand_path (path, sizeof(path));
238 m_strreplace((char **) dst->data, path);
242 static int str_from_string (struct option_t* dst, const char* val,
243 char* errbuf, ssize_t errlen) {
247 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
250 m_strreplace((char**) dst->data, val);
254 static void quad_to_string (char* dst, ssize_t dstlen,
255 struct option_t* option) {
256 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
257 snprintf (dst, dstlen, "%s=%s", option->option,
258 vals[quadoption (option->data)]);
261 static int quad_from_string (struct option_t* dst, const char* val,
262 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
267 if (ascii_strncasecmp (val, "yes", 3) == 0)
269 else if (ascii_strncasecmp (val, "no", 2) == 0)
271 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
273 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
279 set_quadoption (dst->data, flag);
283 static void sort_to_string (char* dst, ssize_t dstlen,
284 struct option_t* option) {
285 const struct mapping_t *map = get_sortmap (option);
286 const char *p = NULL;
289 snprintf (dst, sizeof(dst), "%s=unknown", option->option);
293 p = mutt_getnamebyvalue(*((short *)option->data) & SORT_MASK, map);
295 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
296 (*((short *) option->data) & SORT_REVERSE) ?
298 (*((short *) option->data) & SORT_LAST) ? "last-" :
302 static int sort_from_string (struct option_t* dst, const char* val,
303 char* errbuf, ssize_t errlen) {
304 const struct mapping_t *map = NULL;
305 if (!(map = get_sortmap (dst))) {
307 snprintf (errbuf, errlen, _("%s: Unknown type."),
311 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
316 static void rx_to_string (char* dst, ssize_t dstlen,
317 struct option_t* option) {
318 rx_t* p = (rx_t*) option->data;
319 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
320 NONULL (p->pattern));
323 static int rx_from_string (struct option_t* dst, const char* val,
324 char* errbuf, ssize_t errlen) {
327 int flags = 0, e = 0, not = 0;
333 if (option (OPTATTACHMSG) && !m_strcmp(dst->option, "reply_regexp")) {
335 snprintf (errbuf, errlen,
336 "Operation not permitted when in attach-message mode.");
340 if (!((rx_t*) dst->data))
341 *((rx_t**) dst->data) = p_new(rx_t, 1);
343 p = (rx_t*) dst->data;
345 /* something to do? */
346 if (m_strisempty(val) || (p->pattern && m_strcmp(p->pattern, val) == 0))
349 if (m_strcmp(dst->option, "mask") != 0)
350 flags |= mutt_which_case (val);
353 if (m_strcmp(dst->option, "mask") == 0 && *s == '!') {
358 rx = p_new(regex_t, 1);
360 if ((e = REGCOMP (rx, s, flags)) != 0) {
361 regerror (e, rx, errbuf, errlen);
372 m_strreplace(&p->pattern, val);
376 if (m_strcmp(dst->option, "reply_regexp") == 0)
377 mutt_adjust_all_subjects ();
382 static void magic_to_string (char* dst, ssize_t dstlen,
383 struct option_t* option) {
384 const char* s = NULL;
385 switch (option->data) {
386 case M_MBOX: s = "mbox"; break;
387 case M_MMDF: s = "MMDF"; break;
388 case M_MH: s = "MH"; break;
389 case M_MAILDIR: s = "Maildir"; break;
390 default: s = "unknown"; break;
392 snprintf (dst, dstlen, "%s=%s", option->option, s);
395 static int magic_from_string (struct option_t* dst, const char* val,
396 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
399 if (!dst || m_strisempty(val))
401 if (ascii_strncasecmp (val, "mbox", 4) == 0)
403 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
405 else if (ascii_strncasecmp (val, "mh", 2) == 0)
407 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
413 *((short*) dst->data) = flag;
418 static void addr_to_string (char* dst, ssize_t dstlen,
419 struct option_t* option) {
422 rfc822_addrcat(s, sizeof(s), *((address_t**) option->data), 0);
423 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
426 static int addr_from_string (struct option_t* dst, const char* val,
427 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
430 address_list_wipe((address_t**) dst->data);
432 *((address_t**) dst->data) = rfc822_parse_adrlist (NULL, val);
436 int mutt_option_value (const char* val, char* dst, ssize_t dstlen) {
437 struct option_t* option = NULL;
438 char* tmp = NULL, *t = NULL;
441 if (!(option = hash_find (ConfigOptions, val))) {
445 tmp = p_new(char, dstlen+1);
446 FuncTable[DTYPE(option->type)].opt_tostr (tmp, dstlen, option);
448 /* as we get things of type $var=value and don't want to bloat the
449 * above "just" for expansion, we do the stripping here */
450 t = strchr (tmp, '=');
454 if (t[l-1] == '"' && *t == '"') {
459 memcpy (dst, t, l+1);
465 static void toggle_quadoption (int opt)
468 int b = (opt % 4) * 2;
470 QuadOptions[n] ^= (1 << b);
473 void set_quadoption (int opt, int flag)
476 int b = (opt % 4) * 2;
478 QuadOptions[n] &= ~(0x3 << b);
479 QuadOptions[n] |= (flag & 0x3) << b;
482 int quadoption (int opt)
485 int b = (opt % 4) * 2;
487 return (QuadOptions[n] >> b) & 0x3;
490 int query_quadoption2(int v, const char *prompt)
498 v = mutt_yesorno(prompt, (v == M_ASKYES));
499 CLEARLINE (LINES - 1);
504 int query_quadoption (int opt, const char *prompt)
506 int v = quadoption (opt);
514 v = mutt_yesorno (prompt, (v == M_ASKYES));
515 CLEARLINE (LINES - 1);
522 static void add_to_list(string_list_t **list, const char *str)
524 /* don't add a NULL or empty string to the list */
525 if (m_strisempty(str))
528 /* check to make sure the item is not already on this list */
530 if (!ascii_strcasecmp(str, (*list)->data))
532 list = &(*list)->next;
535 *list = p_new(string_list_t, 1);
536 (*list)->data = m_strdup(str);
540 add_to_rx_list(rx_t **list, const char *s, int flags, BUFFER *err)
547 if (rx_lookup(list, s))
550 rx = rx_compile(s, flags);
552 snprintf(err->data, err->dsize, "Bad regexp: %s\n", s);
556 rx_list_append(list, rx);
560 static int add_to_spam_list(rx_t **list, const char *pat,
561 const char *templ, BUFFER * err)
565 if (m_strisempty(pat) || !templ)
568 if (!(rx = rx_compile (pat, REG_ICASE))) {
569 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
573 /* check to make sure the item is not already on this list */
575 if (!ascii_strcasecmp(rx->pattern, (*list)->pattern)) {
576 rx_t *tmp = rx_list_pop(list);
579 list = &(*list)->next;
584 rx_set_template(rx, templ);
588 static int remove_from_spam_list (rx_t ** list, const char *pat)
593 if (!m_strcmp((*list)->pattern, pat)) {
594 rx_t *spam = rx_list_pop(list);
598 list = &(*list)->next;
606 static void remove_from_list(string_list_t **l, const char *str)
608 if (!m_strcmp("*", str)) {
609 string_list_wipe(l); /* ``unCMD *'' means delete all current entries */
614 if (!ascii_strcasecmp(str, (*l)->data)) {
615 string_list_t *it = string_list_pop(l);
616 string_item_delete(&it);
623 static int remove_from_rx_list(rx_t **l, const char *str)
625 if (m_strcmp("*", str) == 0) {
630 l = rx_lookup(l, str);
632 rx_t *r = rx_list_pop(l);
640 static int parse_unignore (BUFFER * buf, BUFFER * s,
641 unsigned long data __attribute__ ((unused)),
642 BUFFER * err __attribute__ ((unused)))
645 mutt_extract_token (buf, s, 0);
647 /* don't add "*" to the unignore list */
648 if (m_strcmp (buf->data, "*"))
649 add_to_list (&UnIgnore, buf->data);
651 remove_from_list (&Ignore, buf->data);
652 } while (MoreArgs (s));
657 static int parse_ignore (BUFFER * buf, BUFFER * s,
658 unsigned long data __attribute__ ((unused)),
659 BUFFER * err __attribute__ ((unused)))
662 mutt_extract_token (buf, s, 0);
663 remove_from_list (&UnIgnore, buf->data);
664 add_to_list (&Ignore, buf->data);
665 } while (MoreArgs(s));
669 static int parse_list(BUFFER * buf, BUFFER * s, unsigned long data,
670 BUFFER * err __attribute__ ((unused)))
673 mutt_extract_token (buf, s, 0);
674 add_to_list ((string_list_t **) data, buf->data);
675 } while (MoreArgs(s));
679 static void _alternates_clean (void)
683 if (Context && Context->msgcount) {
684 for (i = 0; i < Context->msgcount; i++)
685 Context->hdrs[i]->recip_valid = 0;
689 static int parse_alternates (BUFFER * buf, BUFFER * s,
690 unsigned long data __attribute__ ((unused)),
691 BUFFER * err __attribute__ ((unused)))
693 _alternates_clean ();
695 mutt_extract_token (buf, s, 0);
696 remove_from_rx_list (&UnAlternates, buf->data);
698 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
701 while (MoreArgs (s));
706 static int parse_unalternates (BUFFER * buf, BUFFER * s,
707 unsigned long data __attribute__ ((unused)),
708 BUFFER * err __attribute__ ((unused)))
710 _alternates_clean ();
712 mutt_extract_token (buf, s, 0);
713 remove_from_rx_list (&Alternates, buf->data);
715 if (m_strcmp(buf->data, "*") &&
716 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
720 while (MoreArgs (s));
725 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
732 /* Insist on at least one parameter */
735 m_strcpy(err->data, err->dsize, _("spam: no matching pattern"));
737 m_strcpy(err->data, err->dsize, _("nospam: no matching pattern"));
741 /* Extract the first token, a regexp */
742 mutt_extract_token (buf, s, 0);
744 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
745 if (data == M_SPAM) {
746 /* If there's a second parameter, it's a template for the spam tag. */
748 mutt_extract_token (&templ, s, 0);
750 /* Add to the spam list. */
751 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
752 p_delete(&templ.data);
755 p_delete(&templ.data);
758 /* If not, try to remove from the nospam list. */
760 remove_from_rx_list (&NoSpamList, buf->data);
766 /* M_NOSPAM is for nospam commands. */
767 else if (data == M_NOSPAM) {
768 /* nospam only ever has one parameter. */
770 /* "*" is a special case. */
771 if (!m_strcmp(buf->data, "*")) {
772 rx_list_wipe(&SpamList);
773 rx_list_wipe(&NoSpamList);
777 /* If it's on the spam list, just remove it. */
778 if (remove_from_spam_list (&SpamList, buf->data) != 0)
781 /* Otherwise, add it to the nospam list. */
782 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
788 /* This should not happen. */
789 m_strcpy(err->data, err->dsize, "This is no good at all.");
793 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
794 BUFFER * err __attribute__ ((unused)))
797 mutt_extract_token (buf, s, 0);
799 * Check for deletion of entire list
801 if (m_strcmp(buf->data, "*") == 0) {
802 string_list_wipe((string_list_t **) data);
805 remove_from_list ((string_list_t **) data, buf->data);
807 while (MoreArgs (s));
812 static int parse_lists (BUFFER * buf, BUFFER * s,
813 unsigned long data __attribute__ ((unused)),
817 mutt_extract_token (buf, s, 0);
818 remove_from_rx_list (&UnMailLists, buf->data);
820 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
823 while (MoreArgs (s));
828 /* always wise to do what someone else did before */
829 static void _attachments_clean (void) {
831 if (Context && Context->msgcount) {
832 for (i = 0; i < Context->msgcount; i++)
833 Context->hdrs[i]->attach_valid = 0;
837 static int parse_attach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
838 BUFFER *err __attribute__ ((unused))) {
840 string_list_t *listp, *lastp;
845 /* Find the last item in the list that data points to. */
847 for (listp = *ldata; listp; listp = listp->next) {
848 a = (ATTACH_MATCH *)listp->data;
853 mutt_extract_token (buf, s, 0);
855 if (!buf->data || *buf->data == '\0')
858 a = p_new(ATTACH_MATCH, 1);
860 /* some cheap hacks that I expect to remove */
861 if (!m_strcasecmp(buf->data, "any"))
862 a->major = m_strdup("*/.*");
863 else if (!m_strcasecmp(buf->data, "none"))
864 a->major = m_strdup("cheap_hack/this_should_never_match");
866 a->major = m_strdup(buf->data);
868 if ((p = strchr(a->major, '/'))) {
873 a->minor = "unknown";
876 len = m_strlen(a->minor);
877 tmpminor = p_new(char, len + 3);
878 m_strcpy(&tmpminor[1], len + 3, a->minor);
880 tmpminor[len+1] = '$';
881 tmpminor[len+2] = '\0';
883 a->major_int = mutt_check_mime_type(a->major);
884 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
888 listp = p_new(string_list_t, 1);
889 listp->data = (char *)a;
898 while (MoreArgs (s));
900 _attachments_clean();
904 static int parse_unattach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
905 BUFFER *err __attribute__ ((unused))) {
907 string_list_t *lp, *lastp, *newlp;
913 mutt_extract_token (buf, s, 0);
915 if (!m_strcasecmp(buf->data, "any"))
916 tmp = m_strdup("*/.*");
917 else if (!m_strcasecmp(buf->data, "none"))
918 tmp = m_strdup("cheap_hack/this_should_never_match");
920 tmp = m_strdup(buf->data);
922 if ((minor = strchr(tmp, '/'))) {
926 minor = m_strdup("unknown");
928 major = mutt_check_mime_type(tmp);
930 /* We must do our own walk here because remove_from_list() will only
931 * remove the string_list_t->data, not anything pointed to by the string_list_t->data. */
933 for(lp = *ldata; lp; ) {
934 a = (ATTACH_MATCH *)lp->data;
935 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
936 regfree(&a->minor_rx);
939 /* Relink backward */
941 lastp->next = lp->next;
946 p_delete(&lp->data); /* same as a */
956 while (MoreArgs (s));
959 _attachments_clean();
963 static int print_attach_list (string_list_t *lp, char op, const char *name) {
965 printf("attachments %c%s %s/%s\n", op, name,
966 ((ATTACH_MATCH *)lp->data)->major,
967 ((ATTACH_MATCH *)lp->data)->minor);
974 static int parse_attachments (BUFFER *buf, BUFFER *s,
975 unsigned long data __attribute__ ((unused)),
978 string_list_t **listp;
980 mutt_extract_token(buf, s, 0);
981 if (!buf->data || *buf->data == '\0') {
982 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
986 category = buf->data;
992 printf("\nCurrent attachments settings:\n\n");
993 print_attach_list(AttachAllow, '+', "A");
994 print_attach_list(AttachExclude, '-', "A");
995 print_attach_list(InlineAllow, '+', "I");
996 print_attach_list(InlineExclude, '-', "I");
997 set_option (OPTFORCEREDRAWINDEX);
998 set_option (OPTFORCEREDRAWPAGER);
999 mutt_any_key_to_continue (NULL);
1003 if (op != '+' && op != '-') {
1007 if (!m_strncasecmp(category, "attachment", strlen(category))) {
1009 listp = &AttachAllow;
1011 listp = &AttachExclude;
1013 else if (!m_strncasecmp(category, "inline", strlen(category))) {
1015 listp = &InlineAllow;
1017 listp = &InlineExclude;
1019 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
1023 return parse_attach_list(buf, s, listp, err);
1026 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
1028 string_list_t **listp;
1030 mutt_extract_token(buf, s, 0);
1031 if (!buf->data || *buf->data == '\0') {
1032 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
1038 if (op != '+' && op != '-') {
1042 if (!m_strncasecmp(p, "attachment", strlen(p))) {
1044 listp = &AttachAllow;
1046 listp = &AttachExclude;
1048 else if (!m_strncasecmp(p, "inline", strlen(p))) {
1050 listp = &InlineAllow;
1052 listp = &InlineExclude;
1055 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
1059 return parse_unattach_list(buf, s, listp, err);
1062 static int parse_unlists (BUFFER * buf, BUFFER * s,
1063 unsigned long data __attribute__ ((unused)),
1064 BUFFER * err __attribute__ ((unused)))
1067 mutt_extract_token (buf, s, 0);
1068 remove_from_rx_list (&SubscribedLists, buf->data);
1069 remove_from_rx_list (&MailLists, buf->data);
1071 if (m_strcmp(buf->data, "*") &&
1072 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1075 while (MoreArgs (s));
1080 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1084 mutt_extract_token (buf, s, 0);
1085 remove_from_rx_list (&UnMailLists, buf->data);
1086 remove_from_rx_list (&UnSubscribedLists, buf->data);
1088 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1090 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1093 while (MoreArgs (s));
1098 static int parse_unsubscribe (BUFFER * buf, BUFFER * s,
1099 unsigned long data __attribute__ ((unused)),
1100 BUFFER * err __attribute__ ((unused)))
1103 mutt_extract_token (buf, s, 0);
1104 remove_from_rx_list (&SubscribedLists, buf->data);
1106 if (m_strcmp(buf->data, "*") &&
1107 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1110 while (MoreArgs (s));
1115 static int parse_unalias (BUFFER * buf, BUFFER * s,
1116 unsigned long data __attribute__ ((unused)),
1117 BUFFER * err __attribute__ ((unused)))
1119 alias_t *tmp, **last;
1122 mutt_extract_token (buf, s, 0);
1124 if (!m_strcmp("*", buf->data) == 0) {
1125 if (CurrentMenu == MENU_ALIAS) {
1126 for (tmp = Aliases; tmp; tmp = tmp->next)
1128 set_option(OPTFORCEREDRAWINDEX);
1130 alias_list_wipe(&Aliases);
1136 for (last = &Aliases; *last; last = &(*last)->next) {
1137 if (!m_strcasecmp(buf->data, (*last)->name)) {
1138 if (CurrentMenu == MENU_ALIAS) {
1140 set_option (OPTFORCEREDRAWINDEX);
1142 tmp = alias_list_pop(last);
1148 } while (MoreArgs(s));
1153 static int parse_alias (BUFFER * buf, BUFFER * s,
1154 unsigned long data __attribute__ ((unused)),
1160 if (!MoreArgs (s)) {
1161 m_strcpy(err->data, err->dsize, _("alias: no address"));
1165 mutt_extract_token (buf, s, 0);
1167 /* check to see if an alias with this name already exists */
1168 for (last = &Aliases; *last; last = &(*last)->next) {
1169 if (!m_strcasecmp((*last)->name, buf->data))
1174 /* create a new alias */
1175 *last = alias_new();
1176 (*last)->name = m_strdup(buf->data);
1177 /* give the main addressbook code a chance */
1178 if (CurrentMenu == MENU_ALIAS)
1179 set_option (OPTMENUCALLER);
1181 /* override the previous value */
1182 address_list_wipe(&(*last)->addr);
1183 if (CurrentMenu == MENU_ALIAS)
1184 set_option (OPTFORCEREDRAWINDEX);
1187 mutt_extract_token(buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE);
1188 (*last)->addr = mutt_parse_adrlist((*last)->addr, buf->data);
1189 if (mutt_addrlist_to_idna((*last)->addr, &estr)) {
1190 snprintf (err->data, err->dsize,
1191 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, (*last)->name);
1200 parse_unmy_hdr(BUFFER * buf, BUFFER * s,
1201 unsigned long data __attribute__ ((unused)),
1202 BUFFER * err __attribute__ ((unused)))
1205 mutt_extract_token (buf, s, 0);
1207 if (!m_strcmp("*", buf->data)) {
1208 string_list_wipe(&UserHeader);
1210 string_list_t **last = &UserHeader;
1211 ssize_t l = m_strlen(buf->data);
1213 if (buf->data[l - 1] == ':')
1217 if (!ascii_strncasecmp(buf->data, (*last)->data, l)
1218 && (*last)->data[l] == ':')
1220 string_list_t *tmp = string_list_pop(last);
1221 string_item_delete(&tmp);
1223 last = &(*last)->next;
1227 } while (MoreArgs(s));
1232 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1239 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1240 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1241 m_strcpy(err->data, err->dsize, _("invalid header field"));
1244 keylen = p - buf->data + 1;
1247 for (tmp = UserHeader;; tmp = tmp->next) {
1248 /* see if there is already a field by this name */
1249 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1250 /* replace the old value */
1251 p_delete(&tmp->data);
1252 tmp->data = buf->data;
1259 tmp->next = string_item_new();
1263 tmp = string_item_new();
1266 tmp->data = buf->data;
1272 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1273 char* errbuf, ssize_t errlen) {
1276 if (m_strncmp("reverse-", s, 8) == 0) {
1278 flags = SORT_REVERSE;
1281 if (m_strncmp("last-", s, 5) == 0) {
1286 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1288 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1292 *((short*) dst->data) = i | flags;
1296 /* if additional data more == 1, we want to resolve synonyms */
1297 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
1299 char buf[LONG_STRING];
1300 struct option_t *ptr = p;
1302 if (DTYPE(ptr->type) == DT_SYN) {
1305 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1307 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_fromstr)
1310 mutt_option_value(ptr->option, buf, sizeof(buf));
1311 if (m_strlen(ptr->init) == 0 && buf && *buf)
1312 ptr->init = m_strdup(buf);
1315 static int init_expand (char** dst, struct option_t* src) {
1321 if (DTYPE(src->type) == DT_STR ||
1322 DTYPE(src->type) == DT_PATH) {
1323 /* only expand for string as it's the only place where
1324 * we want to expand vars right now */
1325 if (src->init && *src->init) {
1328 len = m_strlen(src->init) + 2;
1329 in.data = p_new(char, len + 1);
1330 snprintf (in.data, len, "\"%s\"", src->init);
1333 mutt_extract_token (&token, &in, 0);
1334 if (token.data && *token.data)
1335 *dst = m_strdup(token.data);
1337 *dst = m_strdup("");
1339 p_delete(&token.data);
1341 *dst = m_strdup("");
1343 /* for non-string: take value as is */
1344 *dst = m_strdup(src->init);
1348 /* if additional data more == 1, we want to resolve synonyms */
1349 static void mutt_restore_default (const char* name __attribute__ ((unused)),
1350 void* p, unsigned long more) {
1351 char errbuf[STRING];
1352 struct option_t* ptr = (struct option_t*) p;
1355 if (DTYPE (ptr->type) == DT_SYN) {
1358 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1362 if (FuncTable[DTYPE (ptr->type)].opt_fromstr) {
1363 init_expand (&init, ptr);
1364 if (!FuncTable[DTYPE (ptr->type)].opt_fromstr (ptr, init, errbuf,
1366 if (!option (OPTNOCURSES))
1368 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1369 "Please report this error: \"%s\"\n"),
1370 ptr->option, NONULL (init), errbuf);
1376 if (ptr->flags & R_INDEX)
1377 set_option (OPTFORCEREDRAWINDEX);
1378 if (ptr->flags & R_PAGER)
1379 set_option (OPTFORCEREDRAWPAGER);
1380 if (ptr->flags & R_RESORT_SUB)
1381 set_option (OPTSORTSUBTHREADS);
1382 if (ptr->flags & R_RESORT)
1383 set_option (OPTNEEDRESORT);
1384 if (ptr->flags & R_RESORT_INIT)
1385 set_option (OPTRESORTINIT);
1386 if (ptr->flags & R_TREE)
1387 set_option (OPTREDRAWTREE);
1390 static int check_num (const char* option, unsigned long p,
1391 char* errbuf, ssize_t errlen) {
1394 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1400 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1401 char* errbuf, ssize_t errlen) {
1402 if (!check_num ("history", p, errbuf, errlen))
1404 mutt_init_history ();
1408 static int check_special (const char* name, unsigned long val,
1409 char* errbuf, ssize_t errlen) {
1412 for (i = 0; SpecialVars[i].name; i++) {
1413 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1414 return (SpecialVars[i].check (SpecialVars[i].name,
1415 val, errbuf, errlen));
1421 static const struct mapping_t* get_sortmap (struct option_t* option) {
1422 const struct mapping_t* map = NULL;
1424 switch (option->type & DT_SUBTYPE_MASK) {
1426 map = SortAliasMethods;
1428 case DT_SORT_BROWSER:
1429 map = SortBrowserMethods;
1432 map = SortKeyMethods;
1435 map = SortAuxMethods;
1444 #define CHECK_PAGER \
1445 if ((CurrentMenu == MENU_PAGER) && \
1446 (!option || (option->flags & R_RESORT))) \
1448 snprintf (err->data, err->dsize, \
1449 _("Not available in this menu.")); \
1453 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1456 int query, unset, inv, reset, r = 0;
1457 struct option_t* option = NULL;
1459 while (MoreArgs (s)) {
1460 /* reset state variables */
1462 unset = data & M_SET_UNSET;
1463 inv = data & M_SET_INV;
1464 reset = data & M_SET_RESET;
1466 if (*s->dptr == '?') {
1470 else if (m_strncmp("no", s->dptr, 2) == 0) {
1474 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1478 else if (*s->dptr == '&') {
1483 /* get the variable name */
1484 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1486 /* resolve synonyms */
1487 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1488 DTYPE (option->type == DT_SYN))
1490 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1491 syn_t* syn = syn_new();
1492 syn->f = m_strdup(CurRCFile);
1496 syn_list_push(&Synonyms, syn);
1500 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1501 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1504 s->dptr = vskipspaces(s->dptr);
1507 if (query || unset || inv) {
1508 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1512 if (s && *s->dptr == '=') {
1513 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1517 if (!m_strcmp("all", tmp->data)) {
1518 if (CurrentMenu == MENU_PAGER) {
1519 snprintf (err->data, err->dsize, _("Not available in this menu."));
1522 hash_map (ConfigOptions, mutt_restore_default, 1);
1523 set_option (OPTFORCEREDRAWINDEX);
1524 set_option (OPTFORCEREDRAWPAGER);
1525 set_option (OPTSORTSUBTHREADS);
1526 set_option (OPTNEEDRESORT);
1527 set_option (OPTRESORTINIT);
1528 set_option (OPTREDRAWTREE);
1532 mutt_restore_default (NULL, option, 1);
1535 else if (DTYPE (option->type) == DT_BOOL) {
1536 /* XXX this currently ignores the function table
1537 * as we don't get invert and stuff into it */
1538 if (s && *s->dptr == '=') {
1539 if (unset || inv || query) {
1540 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1545 mutt_extract_token (tmp, s, 0);
1546 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1548 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1551 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1557 bool_to_string (err->data, err->dsize, option);
1563 unset_option (option->data);
1565 toggle_option (option->data);
1567 set_option (option->data);
1569 else if (DTYPE (option->type) == DT_STR ||
1570 DTYPE (option->type) == DT_PATH ||
1571 DTYPE (option->type) == DT_ADDR ||
1572 DTYPE (option->type) == DT_MAGIC ||
1573 DTYPE (option->type) == DT_NUM ||
1574 DTYPE (option->type) == DT_SORT ||
1575 DTYPE (option->type) == DT_RX)
1577 /* XXX maybe we need to get unset into handlers? */
1578 if (DTYPE (option->type) == DT_STR ||
1579 DTYPE (option->type) == DT_PATH ||
1580 DTYPE (option->type) == DT_ADDR)
1584 if (DTYPE (option->type) == DT_ADDR)
1585 address_list_wipe((address_t **) option->data);
1587 p_delete((void **)(void *)&option->data);
1592 if (query || *s->dptr != '=') {
1593 FuncTable[DTYPE (option->type)].opt_tostr
1594 (err->data, err->dsize, option);
1600 mutt_extract_token (tmp, s, 0);
1601 if (!FuncTable[DTYPE (option->type)].opt_fromstr
1602 (option, tmp->data, err->data, err->dsize))
1605 else if (DTYPE (option->type) == DT_QUAD) {
1608 quad_to_string (err->data, err->dsize, option);
1612 if (*s->dptr == '=') {
1615 mutt_extract_token (tmp, s, 0);
1616 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1617 set_quadoption (option->data, M_YES);
1618 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1619 set_quadoption (option->data, M_NO);
1620 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1621 set_quadoption (option->data, M_ASKYES);
1622 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1623 set_quadoption (option->data, M_ASKNO);
1625 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1626 tmp->data, option->option);
1633 toggle_quadoption (option->data);
1635 set_quadoption (option->data, M_NO);
1637 set_quadoption (option->data, M_YES);
1641 snprintf (err->data, err->dsize, _("%s: unknown type"),
1647 if (option->flags & R_INDEX)
1648 set_option (OPTFORCEREDRAWINDEX);
1649 if (option->flags & R_PAGER)
1650 set_option (OPTFORCEREDRAWPAGER);
1651 if (option->flags & R_RESORT_SUB)
1652 set_option (OPTSORTSUBTHREADS);
1653 if (option->flags & R_RESORT)
1654 set_option (OPTNEEDRESORT);
1655 if (option->flags & R_RESORT_INIT)
1656 set_option (OPTRESORTINIT);
1657 if (option->flags & R_TREE)
1658 set_option (OPTREDRAWTREE);
1665 /* reads the specified initialization file. returns -1 if errors were found
1666 so that we can pause to let the user know... */
1667 static int source_rc (const char *rcfile, BUFFER * err)
1670 int line = 0, rc = 0, conv = 0;
1672 char *linebuf = NULL;
1673 char *currentline = NULL;
1677 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1678 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1683 while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
1684 conv = ConfigCharset && (*ConfigCharset) && MCharset.charset;
1686 currentline = m_strdup(linebuf);
1689 mutt_convert_string (¤tline, ConfigCharset, MCharset.charset, 0);
1692 currentline = linebuf;
1697 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1698 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1699 if (--rc < -MAXERRS) {
1701 p_delete(¤tline);
1710 p_delete(¤tline);
1712 p_delete(&token.data);
1716 mutt_wait_filter (pid);
1718 /* the muttrc source keyword */
1719 snprintf (err->data, err->dsize,
1720 rc >= -MAXERRS ? _("source: errors in %s")
1721 : _("source: reading aborted due too many errors in %s"),
1730 static int parse_source (BUFFER * tmp, BUFFER * s,
1731 unsigned long data __attribute__ ((unused)),
1734 char path[_POSIX_PATH_MAX];
1738 if (mutt_extract_token (tmp, s, 0) != 0) {
1739 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1743 m_strcpy(path, sizeof(path), tmp->data);
1744 mutt_expand_path (path, sizeof(path));
1746 rc += source_rc (path, err);
1748 while (MoreArgs (s));
1750 return ((rc < 0) ? -1 : 0);
1753 /* line command to execute
1755 token scratch buffer to be used by parser. caller should free
1756 token->data when finished. the reason for this variable is
1757 to avoid having to allocate and deallocate a lot of memory
1758 if we are parsing many lines. the caller can pass in the
1759 memory to use, which avoids having to create new space for
1760 every call to this function.
1762 err where to write error messages */
1763 int mutt_parse_rc_line (const char *line, BUFFER * token, BUFFER * err)
1769 expn.data = expn.dptr = line;
1770 expn.dsize = m_strlen(line);
1774 expn.dptr = vskipspaces(expn.dptr);
1775 while (*expn.dptr) {
1776 if (*expn.dptr == '#')
1777 break; /* rest of line is a comment */
1778 if (*expn.dptr == ';') {
1782 mutt_extract_token (token, &expn, 0);
1783 for (i = 0; Commands[i].name; i++) {
1784 if (!m_strcmp(token->data, Commands[i].name)) {
1785 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1790 if (!Commands[i].name) {
1791 snprintf (err->data, err->dsize, _("%s: unknown command"),
1792 NONULL (token->data));
1799 p_delete(&expn.data);
1804 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
1805 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
1806 /* initial string that starts completion. No telling how much crap
1807 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1808 char User_typed[LONG_STRING] = { 0 };
1810 int Num_matched = 0; /* Number of matches for completion */
1811 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1812 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1814 /* helper function for completion. Changes the dest buffer if
1815 necessary/possible to aid completion.
1816 dest == completion result gets here.
1817 src == candidate for completion.
1818 try == user entered data for completion.
1819 len == length of dest buffer.
1821 static void candidate (char *dest, char *try, const char *src, int len)
1825 if (strstr (src, try) == src) {
1826 Matches[Num_matched++] = src;
1828 m_strcpy(dest, len, src);
1830 for (l = 0; src[l] && src[l] == dest[l]; l++);
1836 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
1840 int spaces; /* keep track of the number of leading spaces on the line */
1842 buffer = vskipspaces(buffer);
1843 spaces = buffer - pt;
1845 pt = buffer + pos - spaces;
1846 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1849 if (pt == buffer) { /* complete cmd */
1850 /* first TAB. Collect all the matches */
1853 m_strcpy(User_typed, sizeof(User_typed), pt);
1854 p_clear(Matches, countof(Matches));
1855 p_clear(Completed, countof(Completed));
1856 for (num = 0; Commands[num].name; num++)
1857 candidate (Completed, User_typed, Commands[num].name,
1859 Matches[Num_matched++] = User_typed;
1861 /* All matches are stored. Longest non-ambiguous string is ""
1862 * i.e. dont change 'buffer'. Fake successful return this time */
1863 if (User_typed[0] == 0)
1867 if (Completed[0] == 0 && User_typed[0])
1870 /* Num_matched will _always_ be atleast 1 since the initial
1871 * user-typed string is always stored */
1872 if (numtabs == 1 && Num_matched == 2)
1873 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1874 else if (numtabs > 1 && Num_matched > 2)
1875 /* cycle thru all the matches */
1876 snprintf (Completed, sizeof(Completed), "%s",
1877 Matches[(numtabs - 2) % Num_matched]);
1879 /* return the completed command */
1880 m_strcpy(buffer, len - spaces, Completed);
1882 else if (!m_strncmp(buffer, "set", 3)
1883 || !m_strncmp(buffer, "unset", 5)
1884 || !m_strncmp(buffer, "reset", 5)
1885 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
1886 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
1889 /* loop through all the possible prefixes (no, inv, ...) */
1890 if (!m_strncmp(buffer, "set", 3)) {
1891 for (num = 0; prefixes[num]; num++) {
1892 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
1893 pt += m_strlen(prefixes[num]);
1899 /* first TAB. Collect all the matches */
1902 m_strcpy(User_typed, sizeof(User_typed), pt);
1903 p_clear(Matches, countof(Matches));
1904 p_clear(Completed, countof(Completed));
1905 for (num = 0; MuttVars[num].option; num++)
1906 candidate(Completed, User_typed, MuttVars[num].option,
1908 Matches[Num_matched++] = User_typed;
1910 /* All matches are stored. Longest non-ambiguous string is ""
1911 * i.e. dont change 'buffer'. Fake successful return this time */
1912 if (User_typed[0] == 0)
1916 if (Completed[0] == 0 && User_typed[0])
1919 /* Num_matched will _always_ be atleast 1 since the initial
1920 * user-typed string is always stored */
1921 if (numtabs == 1 && Num_matched == 2)
1922 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1923 else if (numtabs > 1 && Num_matched > 2)
1924 /* cycle thru all the matches */
1925 snprintf (Completed, sizeof(Completed), "%s",
1926 Matches[(numtabs - 2) % Num_matched]);
1928 m_strcpy(pt, buffer + len - pt - spaces, Completed);
1930 else if (!m_strncmp(buffer, "exec", 4)) {
1931 struct binding_t *menu = km_get_table (CurrentMenu);
1933 if (!menu && CurrentMenu != MENU_PAGER)
1937 /* first TAB. Collect all the matches */
1940 m_strcpy(User_typed, sizeof(User_typed), pt);
1941 p_clear(Matches, countof(Matches));
1942 p_clear(Completed, countof(Completed));
1943 for (num = 0; menu[num].name; num++)
1944 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
1945 /* try the generic menu */
1946 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1948 for (num = 0; menu[num].name; num++)
1949 candidate (Completed, User_typed, menu[num].name,
1952 Matches[Num_matched++] = User_typed;
1954 /* All matches are stored. Longest non-ambiguous string is ""
1955 * i.e. dont change 'buffer'. Fake successful return this time */
1956 if (User_typed[0] == 0)
1960 if (Completed[0] == 0 && User_typed[0])
1963 /* Num_matched will _always_ be atleast 1 since the initial
1964 * user-typed string is always stored */
1965 if (numtabs == 1 && Num_matched == 2)
1966 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1967 else if (numtabs > 1 && Num_matched > 2)
1968 /* cycle thru all the matches */
1969 snprintf (Completed, sizeof(Completed), "%s",
1970 Matches[(numtabs - 2) % Num_matched]);
1972 m_strcpy(pt, buffer + len - pt - spaces, Completed);
1980 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
1982 char var[STRING], *pt = buffer;
1984 struct option_t* option = NULL;
1989 buffer = vskipspaces(buffer);
1990 spaces = buffer - pt;
1992 pt = buffer + pos - spaces;
1993 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1995 pt++; /* move past the space */
1996 if (*pt == '=') /* abort if no var before the '=' */
1999 if (m_strncmp(buffer, "set", 3) == 0) {
2000 m_strcpy(var, sizeof(var), pt);
2001 /* ignore the trailing '=' when comparing */
2002 var[m_strlen(var) - 1] = 0;
2003 if (!(option = hash_find (ConfigOptions, var)))
2004 return 0; /* no such variable. */
2006 char tmp[LONG_STRING], tmp2[LONG_STRING];
2008 ssize_t dlen = buffer + len - pt - spaces;
2009 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2013 if ((DTYPE (option->type) == DT_STR) ||
2014 (DTYPE (option->type) == DT_PATH) ||
2015 (DTYPE (option->type) == DT_RX)) {
2016 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
2017 if (DTYPE (option->type) == DT_PATH)
2018 mutt_pretty_mailbox (tmp);
2020 else if (DTYPE (option->type) == DT_ADDR) {
2021 rfc822_addrcat(tmp, sizeof(tmp), *((address_t **) option->data), 0);
2023 else if (DTYPE (option->type) == DT_QUAD)
2024 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
2025 else if (DTYPE (option->type) == DT_NUM)
2026 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
2027 else if (DTYPE (option->type) == DT_SORT) {
2028 const struct mapping_t *map;
2031 switch (option->type & DT_SUBTYPE_MASK) {
2033 map = SortAliasMethods;
2035 case DT_SORT_BROWSER:
2036 map = SortBrowserMethods;
2039 map = SortKeyMethods;
2045 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2046 snprintf(tmp, sizeof(tmp), "%s%s%s",
2047 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2048 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2050 else if (DTYPE (option->type) == DT_MAGIC) {
2052 switch (DefaultMagic) {
2068 m_strcpy(tmp, sizeof(tmp), p);
2070 else if (DTYPE (option->type) == DT_BOOL)
2071 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
2075 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
2076 if (*s == '\\' || *s == '"')
2082 m_strcpy(tmp, sizeof(tmp), pt);
2083 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2091 /* Implement the -Q command line flag */
2092 int mutt_query_variables (string_list_t * queries)
2096 char errbuff[STRING];
2097 char command[STRING];
2105 err.dsize = sizeof(errbuff);
2107 for (p = queries; p; p = p->next) {
2108 snprintf (command, sizeof(command), "set ?%s\n", p->data);
2109 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2110 fprintf (stderr, "%s\n", err.data);
2111 p_delete(&token.data);
2114 printf ("%s\n", err.data);
2117 p_delete(&token.data);
2121 static int mutt_execute_commands (string_list_t * p)
2124 char errstr[STRING];
2128 err.dsize = sizeof(errstr);
2130 for (; p; p = p->next) {
2131 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2132 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2133 p_delete(&token.data);
2137 p_delete(&token.data);
2141 void mutt_init (int skip_sys_rc, string_list_t * commands)
2145 char buffer[STRING], error[STRING];
2146 int default_rc = 0, need_pause = 0;
2152 err.dsize = sizeof(error);
2154 ConfigOptions = hash_new (sizeof(MuttVars) * 2, 0);
2155 for (i = 0; MuttVars[i].option; i++) {
2156 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i]);
2160 * XXX - use something even more difficult to predict?
2162 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2163 "\033]9;%ld\a", (long) time (NULL));
2166 /* Get some information about the user */
2167 if ((pw = getpwuid (getuid ()))) {
2169 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, MCore.gecos_mask);
2170 Realname = m_strdup(rnbuf);
2178 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2180 fgets (buffer, sizeof(buffer), f);
2181 p = vskipspaces(buffer);
2183 while (*q && !isspace(*q))
2186 NewsServer = m_strdup(p);
2190 if ((p = getenv ("NNTPSERVER")))
2191 NewsServer = m_strdup(p);
2194 if ((p = getenv("MAIL") ?: getenv("MAILDIR"))) {
2195 Spoolfile = m_strdup(p);
2198 mutt_concat_path(buffer, sizeof(buffer), NONULL(MCore.homedir), MAILPATH);
2200 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(MCore.username));
2202 Spoolfile = m_strdup(buffer);
2205 if ((p = getenv ("MAILCAPS")))
2206 MailcapPath = m_strdup(p);
2208 /* Default search path from RFC1524 */
2210 m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2211 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2214 if ((p = getenv ("REPLYTO")) != NULL) {
2217 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2220 buf.data = buf.dptr = buffer;
2221 buf.dsize = m_strlen(buffer);
2224 parse_my_hdr (&token, &buf, 0, &err);
2225 p_delete(&token.data);
2228 if ((p = getenv ("EMAIL")) != NULL)
2229 From = rfc822_parse_adrlist (NULL, p);
2231 /* Set standard defaults */
2232 hash_map (ConfigOptions, mutt_set_default, 0);
2233 hash_map (ConfigOptions, mutt_restore_default, 0);
2235 CurrentMenu = MENU_MAIN;
2238 /* Unset suspend by default if we're the session leader */
2239 if (getsid (0) == getpid ())
2240 unset_option (OPTSUSPEND);
2243 mutt_init_history ();
2246 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL(MCore.homedir));
2247 if (access (buffer, F_OK) == -1)
2248 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
2249 NONULL(MCore.homedir));
2252 Muttrc = m_strdup(buffer);
2255 m_strcpy(buffer, sizeof(buffer), Muttrc);
2257 mutt_expand_path (buffer, sizeof(buffer));
2258 Muttrc = m_strdup(buffer);
2261 /* Process the global rc file if it exists and the user hasn't explicity
2262 requested not to via "-n". */
2264 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
2266 if (access (buffer, F_OK) == -1)
2267 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
2268 if (access (buffer, F_OK) == -1)
2269 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
2271 if (access (buffer, F_OK) == -1)
2272 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
2273 if (access (buffer, F_OK) != -1) {
2274 if (source_rc (buffer, &err) != 0) {
2275 fputs (err.data, stderr);
2276 fputc ('\n', stderr);
2282 /* Read the user's initialization file. */
2283 if (access (Muttrc, F_OK) != -1) {
2284 if (!option (OPTNOCURSES))
2286 if (source_rc (Muttrc, &err) != 0) {
2287 fputs (err.data, stderr);
2288 fputc ('\n', stderr);
2292 else if (!default_rc) {
2293 /* file specified by -F does not exist */
2294 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2295 mutt_endwin (buffer);
2300 snprintf(buffer, sizeof(buffer), "%s/.madmutt.lua", NONULL(MCore.homedir));
2301 if (access(buffer, F_OK) < 0)
2302 snprintf(buffer, sizeof(buffer), "%s/.madmutt/cfg.lua", NONULL(MCore.homedir));
2303 if (!access(buffer, F_OK)) {
2304 need_pause = luaM_wrap(mutt_error, luaM_dofile(buffer));
2308 if (mutt_execute_commands (commands) != 0)
2311 /* warn about synonym variables */
2315 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2317 for (syn = Synonyms; syn; syn = syn->next) {
2318 fprintf(stderr, "$%s ($%s should be used) (%s:%d)\n",
2319 syn->o ? NONULL(syn->o->option) : "",
2320 syn->n ? NONULL(syn->n->option) : "",
2321 NONULL(syn->f), syn->l);
2323 fprintf (stderr, _("Warning: synonym variables are scheduled"
2324 " for removal.\n"));
2325 syn_list_wipe(&Synonyms);
2329 if (need_pause && !option (OPTNOCURSES)) {
2330 if (mutt_any_key_to_continue (NULL) == -1)
2335 int mutt_get_hook_type (const char *name)
2337 struct command_t *c;
2339 for (c = Commands; c->name; c++)
2340 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2345 /* dump out the value of all the variables we have */
2346 int mutt_dump_variables (int full) {
2349 /* get all non-synonyms into list... */
2350 for (i = 0; MuttVars[i].option; i++) {
2351 struct option_t *option = MuttVars + i;
2352 char buf[LONG_STRING];
2354 if (DTYPE(option->type) == DT_SYN)
2358 mutt_option_value(option->option, buf, sizeof(buf));
2359 if (!m_strcmp(buf, option->init))
2364 FuncTable[DTYPE(option->type)].opt_tostr(buf, sizeof(buf), option);
2365 printf ("%s\n", buf);
2368 printf ("\n# vi""m:set ft=muttrc:\n");