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_t *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_history (const char* option, unsigned long val,
73 char* errbuf, ssize_t errlen);
74 /* this checks that numbers are >= 0 */
75 static int check_num (const char* option, unsigned long val,
76 char* errbuf, ssize_t errlen);
78 /* use this to check only */
79 static int check_special (const char* option, unsigned long val,
80 char* errbuf, ssize_t errlen);
82 /* variable <-> sanity check function mappings
83 * when changing these, make sure the proper _from_string handler
88 int (*check) (const char* option, unsigned long val,
89 char* errbuf, ssize_t errlen);
91 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
92 { "smtp_use_tls", mutt_libesmtp_check_usetls },
94 { "history", check_history },
95 { "pager_index_lines", check_num },
100 /* protos for config type handles: convert value to string */
101 static void bool_to_string (char* dst, ssize_t dstlen, struct option_t* option);
102 static void num_to_string (char* dst, ssize_t dstlen, struct option_t* option);
103 static void str_to_string (char* dst, ssize_t dstlen, struct option_t* option);
104 static void quad_to_string (char* dst, ssize_t dstlen, struct option_t* option);
105 static void sort_to_string (char* dst, ssize_t dstlen, struct option_t* option);
106 static void rx_to_string (char* dst, ssize_t dstlen, struct option_t* option);
107 static void magic_to_string (char* dst, ssize_t dstlen, struct option_t* option);
108 static void addr_to_string (char* dst, ssize_t dstlen, struct option_t* option);
110 /* protos for config type handles: convert to value from string */
111 static int bool_from_string (struct option_t* dst, const char* val,
112 char* errbuf, ssize_t errlen);
113 static int num_from_string (struct option_t* dst, const char* val,
114 char* errbuf, ssize_t errlen);
115 static int str_from_string (struct option_t* dst, const char* val,
116 char* errbuf, ssize_t errlen);
117 static int path_from_string (struct option_t* dst, const char* val,
118 char* errbuf, ssize_t errlen);
119 static int quad_from_string (struct option_t* dst, const char* val,
120 char* errbuf, ssize_t errlen);
121 static int sort_from_string (struct option_t* dst, const char* val,
122 char* errbuf, ssize_t errlen);
123 static int rx_from_string (struct option_t* dst, const char* val,
124 char* errbuf, ssize_t errlen);
125 static int magic_from_string (struct option_t* dst, const char* val,
126 char* errbuf, ssize_t errlen);
127 static int addr_from_string (struct option_t* dst, const char* val,
128 char* errbuf, ssize_t errlen);
132 void (*opt_tostr) (char* dst, ssize_t dstlen, struct option_t* option);
133 int (*opt_fromstr) (struct option_t* dst, const char* val,
134 char* errbuf, ssize_t errlen);
136 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
137 { DT_BOOL, bool_to_string, bool_from_string },
138 { DT_NUM, num_to_string, num_from_string },
139 { DT_STR, str_to_string, str_from_string },
140 { DT_PATH, str_to_string, path_from_string },
141 { DT_QUAD, quad_to_string, quad_from_string },
142 { DT_SORT, sort_to_string, sort_from_string },
143 { DT_RX, rx_to_string, rx_from_string },
144 { DT_MAGIC, magic_to_string, magic_from_string },
145 /* synonyms should be resolved already so we don't need this
146 * but must define it as DT_ is used for indexing */
147 { DT_SYN, NULL, NULL },
148 { DT_ADDR, addr_to_string, addr_from_string },
151 static void bool_to_string (char* dst, ssize_t dstlen,
152 struct option_t* option) {
153 snprintf (dst, dstlen, "%s=%s", option->option,
154 option (option->data) ? "yes" : "no");
157 static int bool_from_string (struct option_t* dst, const char* val,
158 char* errbuf __attribute__ ((unused)),
159 ssize_t errlen __attribute__ ((unused))) {
164 if (ascii_strncasecmp (val, "yes", 3) == 0)
166 else if (ascii_strncasecmp (val, "no", 2) == 0)
172 set_option (dst->data);
174 unset_option (dst->data);
178 static void num_to_string (char* dst, ssize_t dstlen,
179 struct option_t* option) {
181 const char* fmt = (m_strcmp(option->option, "umask") == 0) ?
183 snprintf (dst, dstlen, fmt, option->option,
184 *((short*) option->data));
187 static int num_from_string (struct option_t* dst, const char* val,
188 char* errbuf, ssize_t errlen) {
189 int num = 0, old = 0;
195 num = strtol (val, &t, 0);
197 if (m_strisempty(val) || *t || (short) num != num) {
199 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
205 /* just temporarily accept new val so that check_special for
206 * $history already has it when doing history's init() */
207 old = *((short*) dst->data);
208 *((short*) dst->data) = (short) num;
210 if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
211 *((short*) dst->data) = old;
218 static void str_to_string (char* dst, ssize_t dstlen,
219 struct option_t* option) {
220 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
221 NONULL (*((char**) option->data)));
224 static int path_from_string (struct option_t* dst, const char* val,
225 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
226 char path[_POSIX_PATH_MAX];
231 if (m_strisempty(val)) {
232 p_delete((char**) dst->data);
237 m_strcpy(path, sizeof(path), val);
238 mutt_expand_path (path, sizeof(path));
239 m_strreplace((char **) dst->data, path);
243 static int str_from_string (struct option_t* dst, const char* val,
244 char* errbuf, ssize_t errlen) {
248 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
251 m_strreplace((char**) dst->data, val);
255 static void quad_to_string (char* dst, ssize_t dstlen,
256 struct option_t* option) {
257 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
258 snprintf (dst, dstlen, "%s=%s", option->option,
259 vals[quadoption (option->data)]);
262 static int quad_from_string (struct option_t* dst, const char* val,
263 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
268 if (ascii_strncasecmp (val, "yes", 3) == 0)
270 else if (ascii_strncasecmp (val, "no", 2) == 0)
272 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
274 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
280 set_quadoption (dst->data, flag);
284 static void sort_to_string (char* dst, ssize_t dstlen,
285 struct option_t* option) {
286 const struct mapping_t *map = get_sortmap (option);
287 const char *p = NULL;
290 snprintf (dst, sizeof(dst), "%s=unknown", option->option);
294 p = mutt_getnamebyvalue(*((short *)option->data) & SORT_MASK, map);
296 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
297 (*((short *) option->data) & SORT_REVERSE) ?
299 (*((short *) option->data) & SORT_LAST) ? "last-" :
303 static int sort_from_string (struct option_t* dst, const char* val,
304 char* errbuf, ssize_t errlen) {
305 const struct mapping_t *map = NULL;
306 if (!(map = get_sortmap (dst))) {
308 snprintf (errbuf, errlen, _("%s: Unknown type."),
312 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
317 static void rx_to_string (char* dst, ssize_t dstlen,
318 struct option_t* option) {
319 rx_t* p = (rx_t*) option->data;
320 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
321 NONULL (p->pattern));
324 static int rx_from_string (struct option_t* dst, const char* val,
325 char* errbuf, ssize_t errlen) {
328 int flags = 0, e = 0, not = 0;
334 if (option (OPTATTACHMSG) && !m_strcmp(dst->option, "reply_regexp")) {
336 snprintf (errbuf, errlen,
337 "Operation not permitted when in attach-message mode.");
341 if (!((rx_t*) dst->data))
342 *((rx_t**) dst->data) = p_new(rx_t, 1);
344 p = (rx_t*) dst->data;
346 /* something to do? */
347 if (m_strisempty(val) || (p->pattern && m_strcmp(p->pattern, val) == 0))
350 if (m_strcmp(dst->option, "mask") != 0)
351 flags |= mutt_which_case (val);
354 if (m_strcmp(dst->option, "mask") == 0 && *s == '!') {
359 rx = p_new(regex_t, 1);
361 if ((e = REGCOMP (rx, s, flags)) != 0) {
362 regerror (e, rx, errbuf, errlen);
373 m_strreplace(&p->pattern, val);
377 if (m_strcmp(dst->option, "reply_regexp") == 0)
378 mutt_adjust_all_subjects ();
383 static void magic_to_string (char* dst, ssize_t dstlen,
384 struct option_t* option) {
385 const char* s = NULL;
386 switch (option->data) {
387 case M_MBOX: s = "mbox"; break;
388 case M_MMDF: s = "MMDF"; break;
389 case M_MH: s = "MH"; break;
390 case M_MAILDIR: s = "Maildir"; break;
391 default: s = "unknown"; break;
393 snprintf (dst, dstlen, "%s=%s", option->option, s);
396 static int magic_from_string (struct option_t* dst, const char* val,
397 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
400 if (!dst || m_strisempty(val))
402 if (ascii_strncasecmp (val, "mbox", 4) == 0)
404 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
406 else if (ascii_strncasecmp (val, "mh", 2) == 0)
408 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
414 *((short*) dst->data) = flag;
419 static void addr_to_string (char* dst, ssize_t dstlen,
420 struct option_t* option) {
423 rfc822_addrcat(s, sizeof(s), *((address_t**) option->data), 0);
424 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
427 static int addr_from_string (struct option_t* dst, const char* val,
428 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
431 address_list_wipe((address_t**) dst->data);
433 *((address_t**) dst->data) = rfc822_parse_adrlist (NULL, val);
437 int mutt_option_value (const char* val, char* dst, ssize_t dstlen) {
438 struct option_t* option = NULL;
439 char* tmp = NULL, *t = NULL;
442 if (!(option = hash_find (ConfigOptions, val))) {
446 tmp = p_new(char, dstlen+1);
447 FuncTable[DTYPE(option->type)].opt_tostr (tmp, dstlen, option);
449 /* as we get things of type $var=value and don't want to bloat the
450 * above "just" for expansion, we do the stripping here */
451 t = strchr (tmp, '=');
455 if (t[l-1] == '"' && *t == '"') {
460 memcpy (dst, t, l+1);
466 static void toggle_quadoption (int opt)
469 int b = (opt % 4) * 2;
471 QuadOptions[n] ^= (1 << b);
474 void set_quadoption (int opt, int flag)
477 int b = (opt % 4) * 2;
479 QuadOptions[n] &= ~(0x3 << b);
480 QuadOptions[n] |= (flag & 0x3) << b;
483 int quadoption (int opt)
486 int b = (opt % 4) * 2;
488 return (QuadOptions[n] >> b) & 0x3;
491 int query_quadoption2(int v, const char *prompt)
499 v = mutt_yesorno(prompt, (v == M_ASKYES));
500 CLEARLINE (LINES - 1);
505 int query_quadoption (int opt, const char *prompt)
507 int v = quadoption (opt);
515 v = mutt_yesorno (prompt, (v == M_ASKYES));
516 CLEARLINE (LINES - 1);
523 static void add_to_list(string_list_t **list, const char *str)
525 /* don't add a NULL or empty string to the list */
526 if (m_strisempty(str))
529 /* check to make sure the item is not already on this list */
531 if (!ascii_strcasecmp(str, (*list)->data))
533 list = &(*list)->next;
536 *list = p_new(string_list_t, 1);
537 (*list)->data = m_strdup(str);
541 add_to_rx_list(rx_t **list, const char *s, int flags, BUFFER *err)
548 if (rx_lookup(list, s))
551 rx = rx_compile(s, flags);
553 snprintf(err->data, err->dsize, "Bad regexp: %s\n", s);
557 rx_list_append(list, rx);
561 static int add_to_spam_list(rx_t **list, const char *pat,
562 const char *templ, BUFFER * err)
566 if (m_strisempty(pat) || !templ)
569 if (!(rx = rx_compile (pat, REG_ICASE))) {
570 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
574 /* check to make sure the item is not already on this list */
576 if (!ascii_strcasecmp(rx->pattern, (*list)->pattern)) {
577 rx_t *tmp = rx_list_pop(list);
580 list = &(*list)->next;
585 rx_set_template(rx, templ);
589 static int remove_from_spam_list (rx_t ** list, const char *pat)
594 if (!m_strcmp((*list)->pattern, pat)) {
595 rx_t *spam = rx_list_pop(list);
599 list = &(*list)->next;
607 static void remove_from_list(string_list_t **l, const char *str)
609 if (!m_strcmp("*", str)) {
610 string_list_wipe(l); /* ``unCMD *'' means delete all current entries */
615 if (!ascii_strcasecmp(str, (*l)->data)) {
616 string_list_t *it = string_list_pop(l);
617 string_item_delete(&it);
624 static int remove_from_rx_list(rx_t **l, const char *str)
626 if (m_strcmp("*", str) == 0) {
631 l = rx_lookup(l, str);
633 rx_t *r = rx_list_pop(l);
641 static int parse_unignore (BUFFER * buf, BUFFER * s,
642 unsigned long data __attribute__ ((unused)),
643 BUFFER * err __attribute__ ((unused)))
646 mutt_extract_token (buf, s, 0);
648 /* don't add "*" to the unignore list */
649 if (m_strcmp (buf->data, "*"))
650 add_to_list (&UnIgnore, buf->data);
652 remove_from_list (&Ignore, buf->data);
653 } while (MoreArgs (s));
658 static int parse_ignore (BUFFER * buf, BUFFER * s,
659 unsigned long data __attribute__ ((unused)),
660 BUFFER * err __attribute__ ((unused)))
663 mutt_extract_token (buf, s, 0);
664 remove_from_list (&UnIgnore, buf->data);
665 add_to_list (&Ignore, buf->data);
666 } while (MoreArgs(s));
670 static int parse_list(BUFFER * buf, BUFFER * s, unsigned long data,
671 BUFFER * err __attribute__ ((unused)))
674 mutt_extract_token (buf, s, 0);
675 add_to_list ((string_list_t **) data, buf->data);
676 } while (MoreArgs(s));
680 static void _alternates_clean (void)
684 if (Context && Context->msgcount) {
685 for (i = 0; i < Context->msgcount; i++)
686 Context->hdrs[i]->recip_valid = 0;
690 static int parse_alternates (BUFFER * buf, BUFFER * s,
691 unsigned long data __attribute__ ((unused)),
692 BUFFER * err __attribute__ ((unused)))
694 _alternates_clean ();
696 mutt_extract_token (buf, s, 0);
697 remove_from_rx_list (&UnAlternates, buf->data);
699 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
702 while (MoreArgs (s));
707 static int parse_unalternates (BUFFER * buf, BUFFER * s,
708 unsigned long data __attribute__ ((unused)),
709 BUFFER * err __attribute__ ((unused)))
711 _alternates_clean ();
713 mutt_extract_token (buf, s, 0);
714 remove_from_rx_list (&Alternates, buf->data);
716 if (m_strcmp(buf->data, "*") &&
717 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
721 while (MoreArgs (s));
726 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
733 /* Insist on at least one parameter */
736 m_strcpy(err->data, err->dsize, _("spam: no matching pattern"));
738 m_strcpy(err->data, err->dsize, _("nospam: no matching pattern"));
742 /* Extract the first token, a regexp */
743 mutt_extract_token (buf, s, 0);
745 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
746 if (data == M_SPAM) {
747 /* If there's a second parameter, it's a template for the spam tag. */
749 mutt_extract_token (&templ, s, 0);
751 /* Add to the spam list. */
752 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
753 p_delete(&templ.data);
756 p_delete(&templ.data);
759 /* If not, try to remove from the nospam list. */
761 remove_from_rx_list (&NoSpamList, buf->data);
767 /* M_NOSPAM is for nospam commands. */
768 else if (data == M_NOSPAM) {
769 /* nospam only ever has one parameter. */
771 /* "*" is a special case. */
772 if (!m_strcmp(buf->data, "*")) {
773 rx_list_wipe(&SpamList);
774 rx_list_wipe(&NoSpamList);
778 /* If it's on the spam list, just remove it. */
779 if (remove_from_spam_list (&SpamList, buf->data) != 0)
782 /* Otherwise, add it to the nospam list. */
783 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
789 /* This should not happen. */
790 m_strcpy(err->data, err->dsize, "This is no good at all.");
794 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
795 BUFFER * err __attribute__ ((unused)))
798 mutt_extract_token (buf, s, 0);
800 * Check for deletion of entire list
802 if (m_strcmp(buf->data, "*") == 0) {
803 string_list_wipe((string_list_t **) data);
806 remove_from_list ((string_list_t **) data, buf->data);
808 while (MoreArgs (s));
813 static int parse_lists (BUFFER * buf, BUFFER * s,
814 unsigned long data __attribute__ ((unused)),
818 mutt_extract_token (buf, s, 0);
819 remove_from_rx_list (&UnMailLists, buf->data);
821 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
824 while (MoreArgs (s));
829 /* always wise to do what someone else did before */
830 static void _attachments_clean (void) {
832 if (Context && Context->msgcount) {
833 for (i = 0; i < Context->msgcount; i++)
834 Context->hdrs[i]->attach_valid = 0;
838 static int parse_attach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
839 BUFFER *err __attribute__ ((unused))) {
841 string_list_t *listp, *lastp;
846 /* Find the last item in the list that data points to. */
848 for (listp = *ldata; listp; listp = listp->next) {
849 a = (ATTACH_MATCH *)listp->data;
854 mutt_extract_token (buf, s, 0);
856 if (!buf->data || *buf->data == '\0')
859 a = p_new(ATTACH_MATCH, 1);
861 /* some cheap hacks that I expect to remove */
862 if (!m_strcasecmp(buf->data, "any"))
863 a->major = m_strdup("*/.*");
864 else if (!m_strcasecmp(buf->data, "none"))
865 a->major = m_strdup("cheap_hack/this_should_never_match");
867 a->major = m_strdup(buf->data);
869 if ((p = strchr(a->major, '/'))) {
874 a->minor = "unknown";
877 len = m_strlen(a->minor);
878 tmpminor = p_new(char, len + 3);
879 m_strcpy(&tmpminor[1], len + 3, a->minor);
881 tmpminor[len+1] = '$';
882 tmpminor[len+2] = '\0';
884 a->major_int = mutt_check_mime_type(a->major);
885 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
889 listp = p_new(string_list_t, 1);
890 listp->data = (char *)a;
899 while (MoreArgs (s));
901 _attachments_clean();
905 static int parse_unattach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
906 BUFFER *err __attribute__ ((unused))) {
908 string_list_t *lp, *lastp, *newlp;
914 mutt_extract_token (buf, s, 0);
916 if (!m_strcasecmp(buf->data, "any"))
917 tmp = m_strdup("*/.*");
918 else if (!m_strcasecmp(buf->data, "none"))
919 tmp = m_strdup("cheap_hack/this_should_never_match");
921 tmp = m_strdup(buf->data);
923 if ((minor = strchr(tmp, '/'))) {
927 minor = m_strdup("unknown");
929 major = mutt_check_mime_type(tmp);
931 /* We must do our own walk here because remove_from_list() will only
932 * remove the string_list_t->data, not anything pointed to by the string_list_t->data. */
934 for(lp = *ldata; lp; ) {
935 a = (ATTACH_MATCH *)lp->data;
936 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
937 regfree(&a->minor_rx);
940 /* Relink backward */
942 lastp->next = lp->next;
947 p_delete(&lp->data); /* same as a */
957 while (MoreArgs (s));
960 _attachments_clean();
964 static int print_attach_list (string_list_t *lp, char op, const char *name) {
966 printf("attachments %c%s %s/%s\n", op, name,
967 ((ATTACH_MATCH *)lp->data)->major,
968 ((ATTACH_MATCH *)lp->data)->minor);
975 static int parse_attachments (BUFFER *buf, BUFFER *s,
976 unsigned long data __attribute__ ((unused)),
979 string_list_t **listp;
981 mutt_extract_token(buf, s, 0);
982 if (!buf->data || *buf->data == '\0') {
983 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
987 category = buf->data;
993 printf("\nCurrent attachments settings:\n\n");
994 print_attach_list(AttachAllow, '+', "A");
995 print_attach_list(AttachExclude, '-', "A");
996 print_attach_list(InlineAllow, '+', "I");
997 print_attach_list(InlineExclude, '-', "I");
998 set_option (OPTFORCEREDRAWINDEX);
999 set_option (OPTFORCEREDRAWPAGER);
1000 mutt_any_key_to_continue (NULL);
1004 if (op != '+' && op != '-') {
1008 if (!m_strncasecmp(category, "attachment", strlen(category))) {
1010 listp = &AttachAllow;
1012 listp = &AttachExclude;
1014 else if (!m_strncasecmp(category, "inline", strlen(category))) {
1016 listp = &InlineAllow;
1018 listp = &InlineExclude;
1020 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
1024 return parse_attach_list(buf, s, listp, err);
1027 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
1029 string_list_t **listp;
1031 mutt_extract_token(buf, s, 0);
1032 if (!buf->data || *buf->data == '\0') {
1033 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
1039 if (op != '+' && op != '-') {
1043 if (!m_strncasecmp(p, "attachment", strlen(p))) {
1045 listp = &AttachAllow;
1047 listp = &AttachExclude;
1049 else if (!m_strncasecmp(p, "inline", strlen(p))) {
1051 listp = &InlineAllow;
1053 listp = &InlineExclude;
1056 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
1060 return parse_unattach_list(buf, s, listp, err);
1063 static int parse_unlists (BUFFER * buf, BUFFER * s,
1064 unsigned long data __attribute__ ((unused)),
1065 BUFFER * err __attribute__ ((unused)))
1068 mutt_extract_token (buf, s, 0);
1069 remove_from_rx_list (&SubscribedLists, buf->data);
1070 remove_from_rx_list (&MailLists, buf->data);
1072 if (m_strcmp(buf->data, "*") &&
1073 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1076 while (MoreArgs (s));
1081 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1085 mutt_extract_token (buf, s, 0);
1086 remove_from_rx_list (&UnMailLists, buf->data);
1087 remove_from_rx_list (&UnSubscribedLists, buf->data);
1089 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1091 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1094 while (MoreArgs (s));
1099 static int parse_unsubscribe (BUFFER * buf, BUFFER * s,
1100 unsigned long data __attribute__ ((unused)),
1101 BUFFER * err __attribute__ ((unused)))
1104 mutt_extract_token (buf, s, 0);
1105 remove_from_rx_list (&SubscribedLists, buf->data);
1107 if (m_strcmp(buf->data, "*") &&
1108 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1111 while (MoreArgs (s));
1116 static int parse_unalias (BUFFER * buf, BUFFER * s,
1117 unsigned long data __attribute__ ((unused)),
1118 BUFFER * err __attribute__ ((unused)))
1120 alias_t *tmp, **last;
1123 mutt_extract_token (buf, s, 0);
1125 if (!m_strcmp("*", buf->data) == 0) {
1126 if (CurrentMenu == MENU_ALIAS) {
1127 for (tmp = Aliases; tmp; tmp = tmp->next)
1129 set_option(OPTFORCEREDRAWINDEX);
1131 alias_list_wipe(&Aliases);
1137 for (last = &Aliases; *last; last = &(*last)->next) {
1138 if (!m_strcasecmp(buf->data, (*last)->name)) {
1139 if (CurrentMenu == MENU_ALIAS) {
1141 set_option (OPTFORCEREDRAWINDEX);
1143 tmp = alias_list_pop(last);
1149 } while (MoreArgs(s));
1154 static int parse_alias (BUFFER * buf, BUFFER * s,
1155 unsigned long data __attribute__ ((unused)),
1161 if (!MoreArgs (s)) {
1162 m_strcpy(err->data, err->dsize, _("alias: no address"));
1166 mutt_extract_token (buf, s, 0);
1168 /* check to see if an alias with this name already exists */
1169 for (last = &Aliases; *last; last = &(*last)->next) {
1170 if (!m_strcasecmp((*last)->name, buf->data))
1175 /* create a new alias */
1176 *last = alias_new();
1177 (*last)->name = m_strdup(buf->data);
1178 /* give the main addressbook code a chance */
1179 if (CurrentMenu == MENU_ALIAS)
1180 set_option (OPTMENUCALLER);
1182 /* override the previous value */
1183 address_list_wipe(&(*last)->addr);
1184 if (CurrentMenu == MENU_ALIAS)
1185 set_option (OPTFORCEREDRAWINDEX);
1188 mutt_extract_token(buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE);
1189 (*last)->addr = mutt_parse_adrlist((*last)->addr, buf->data);
1190 if (mutt_addrlist_to_idna((*last)->addr, &estr)) {
1191 snprintf (err->data, err->dsize,
1192 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, (*last)->name);
1201 parse_unmy_hdr(BUFFER * buf, BUFFER * s,
1202 unsigned long data __attribute__ ((unused)),
1203 BUFFER * err __attribute__ ((unused)))
1206 mutt_extract_token (buf, s, 0);
1208 if (!m_strcmp("*", buf->data)) {
1209 string_list_wipe(&UserHeader);
1211 string_list_t **last = &UserHeader;
1212 ssize_t l = m_strlen(buf->data);
1214 if (buf->data[l - 1] == ':')
1218 if (!ascii_strncasecmp(buf->data, (*last)->data, l)
1219 && (*last)->data[l] == ':')
1221 string_list_t *tmp = string_list_pop(last);
1222 string_item_delete(&tmp);
1224 last = &(*last)->next;
1228 } while (MoreArgs(s));
1233 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1240 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1241 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1242 m_strcpy(err->data, err->dsize, _("invalid header field"));
1245 keylen = p - buf->data + 1;
1248 for (tmp = UserHeader;; tmp = tmp->next) {
1249 /* see if there is already a field by this name */
1250 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1251 /* replace the old value */
1252 p_delete(&tmp->data);
1253 tmp->data = buf->data;
1260 tmp->next = string_item_new();
1264 tmp = string_item_new();
1267 tmp->data = buf->data;
1273 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1274 char* errbuf, ssize_t errlen) {
1277 if (m_strncmp("reverse-", s, 8) == 0) {
1279 flags = SORT_REVERSE;
1282 if (m_strncmp("last-", s, 5) == 0) {
1287 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1289 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1293 *((short*) dst->data) = i | flags;
1297 /* if additional data more == 1, we want to resolve synonyms */
1298 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
1300 char buf[LONG_STRING];
1301 struct option_t *ptr = p;
1303 if (DTYPE(ptr->type) == DT_SYN) {
1306 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1308 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_fromstr)
1311 mutt_option_value(ptr->option, buf, sizeof(buf));
1312 if (m_strlen(ptr->init) == 0 && buf && *buf)
1313 ptr->init = m_strdup(buf);
1316 static int init_expand (char** dst, struct option_t* src) {
1322 if (DTYPE(src->type) == DT_STR ||
1323 DTYPE(src->type) == DT_PATH) {
1324 /* only expand for string as it's the only place where
1325 * we want to expand vars right now */
1326 if (src->init && *src->init) {
1329 len = m_strlen(src->init) + 2;
1330 in.data = p_new(char, len + 1);
1331 snprintf (in.data, len, "\"%s\"", src->init);
1334 mutt_extract_token (&token, &in, 0);
1335 if (token.data && *token.data)
1336 *dst = m_strdup(token.data);
1338 *dst = m_strdup("");
1340 p_delete(&token.data);
1342 *dst = m_strdup("");
1344 /* for non-string: take value as is */
1345 *dst = m_strdup(src->init);
1349 /* if additional data more == 1, we want to resolve synonyms */
1350 static void mutt_restore_default (const char* name __attribute__ ((unused)),
1351 void* p, unsigned long more) {
1352 char errbuf[STRING];
1353 struct option_t* ptr = (struct option_t*) p;
1356 if (DTYPE (ptr->type) == DT_SYN) {
1359 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1363 if (FuncTable[DTYPE (ptr->type)].opt_fromstr) {
1364 init_expand (&init, ptr);
1365 if (!FuncTable[DTYPE (ptr->type)].opt_fromstr (ptr, init, errbuf,
1367 if (!option (OPTNOCURSES))
1369 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1370 "Please report this error: \"%s\"\n"),
1371 ptr->option, NONULL (init), errbuf);
1377 if (ptr->flags & R_INDEX)
1378 set_option (OPTFORCEREDRAWINDEX);
1379 if (ptr->flags & R_PAGER)
1380 set_option (OPTFORCEREDRAWPAGER);
1381 if (ptr->flags & R_RESORT_SUB)
1382 set_option (OPTSORTSUBTHREADS);
1383 if (ptr->flags & R_RESORT)
1384 set_option (OPTNEEDRESORT);
1385 if (ptr->flags & R_RESORT_INIT)
1386 set_option (OPTRESORTINIT);
1387 if (ptr->flags & R_TREE)
1388 set_option (OPTREDRAWTREE);
1391 static int check_num (const char* option, unsigned long p,
1392 char* errbuf, ssize_t errlen) {
1395 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1401 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1402 char* errbuf, ssize_t errlen) {
1403 if (!check_num ("history", p, errbuf, errlen))
1405 mutt_init_history ();
1409 static int check_special (const char* name, unsigned long val,
1410 char* errbuf, ssize_t errlen) {
1413 for (i = 0; SpecialVars[i].name; i++) {
1414 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1415 return (SpecialVars[i].check (SpecialVars[i].name,
1416 val, errbuf, errlen));
1422 static const struct mapping_t* get_sortmap (struct option_t* option) {
1423 const struct mapping_t* map = NULL;
1425 switch (option->type & DT_SUBTYPE_MASK) {
1427 map = SortAliasMethods;
1429 case DT_SORT_BROWSER:
1430 map = SortBrowserMethods;
1433 map = SortKeyMethods;
1436 map = SortAuxMethods;
1445 #define CHECK_PAGER \
1446 if ((CurrentMenu == MENU_PAGER) && \
1447 (!option || (option->flags & R_RESORT))) \
1449 snprintf (err->data, err->dsize, \
1450 _("Not available in this menu.")); \
1454 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1457 int query, unset, inv, reset, r = 0;
1458 struct option_t* option = NULL;
1460 while (MoreArgs (s)) {
1461 /* reset state variables */
1463 unset = data & M_SET_UNSET;
1464 inv = data & M_SET_INV;
1465 reset = data & M_SET_RESET;
1467 if (*s->dptr == '?') {
1471 else if (m_strncmp("no", s->dptr, 2) == 0) {
1475 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1479 else if (*s->dptr == '&') {
1484 /* get the variable name */
1485 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1487 /* resolve synonyms */
1488 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1489 DTYPE (option->type == DT_SYN))
1491 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1492 syn_t* syn = syn_new();
1493 syn->f = m_strdup(CurRCFile);
1497 syn_list_push(&Synonyms, syn);
1501 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1502 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1505 s->dptr = vskipspaces(s->dptr);
1508 if (query || unset || inv) {
1509 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1513 if (s && *s->dptr == '=') {
1514 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1518 if (!m_strcmp("all", tmp->data)) {
1519 if (CurrentMenu == MENU_PAGER) {
1520 snprintf (err->data, err->dsize, _("Not available in this menu."));
1523 hash_map (ConfigOptions, mutt_restore_default, 1);
1524 set_option (OPTFORCEREDRAWINDEX);
1525 set_option (OPTFORCEREDRAWPAGER);
1526 set_option (OPTSORTSUBTHREADS);
1527 set_option (OPTNEEDRESORT);
1528 set_option (OPTRESORTINIT);
1529 set_option (OPTREDRAWTREE);
1533 mutt_restore_default (NULL, option, 1);
1536 else if (DTYPE (option->type) == DT_BOOL) {
1537 /* XXX this currently ignores the function table
1538 * as we don't get invert and stuff into it */
1539 if (s && *s->dptr == '=') {
1540 if (unset || inv || query) {
1541 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1546 mutt_extract_token (tmp, s, 0);
1547 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1549 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1552 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1558 bool_to_string (err->data, err->dsize, option);
1564 unset_option (option->data);
1566 toggle_option (option->data);
1568 set_option (option->data);
1570 else if (DTYPE (option->type) == DT_STR ||
1571 DTYPE (option->type) == DT_PATH ||
1572 DTYPE (option->type) == DT_ADDR ||
1573 DTYPE (option->type) == DT_MAGIC ||
1574 DTYPE (option->type) == DT_NUM ||
1575 DTYPE (option->type) == DT_SORT ||
1576 DTYPE (option->type) == DT_RX)
1578 /* XXX maybe we need to get unset into handlers? */
1579 if (DTYPE (option->type) == DT_STR ||
1580 DTYPE (option->type) == DT_PATH ||
1581 DTYPE (option->type) == DT_ADDR)
1585 if (DTYPE (option->type) == DT_ADDR)
1586 address_list_wipe((address_t **) option->data);
1588 p_delete((void **)(void *)&option->data);
1593 if (query || *s->dptr != '=') {
1594 FuncTable[DTYPE (option->type)].opt_tostr
1595 (err->data, err->dsize, option);
1601 mutt_extract_token (tmp, s, 0);
1602 if (!FuncTable[DTYPE (option->type)].opt_fromstr
1603 (option, tmp->data, err->data, err->dsize))
1606 else if (DTYPE (option->type) == DT_QUAD) {
1609 quad_to_string (err->data, err->dsize, option);
1613 if (*s->dptr == '=') {
1616 mutt_extract_token (tmp, s, 0);
1617 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1618 set_quadoption (option->data, M_YES);
1619 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1620 set_quadoption (option->data, M_NO);
1621 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1622 set_quadoption (option->data, M_ASKYES);
1623 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1624 set_quadoption (option->data, M_ASKNO);
1626 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1627 tmp->data, option->option);
1634 toggle_quadoption (option->data);
1636 set_quadoption (option->data, M_NO);
1638 set_quadoption (option->data, M_YES);
1642 snprintf (err->data, err->dsize, _("%s: unknown type"),
1648 if (option->flags & R_INDEX)
1649 set_option (OPTFORCEREDRAWINDEX);
1650 if (option->flags & R_PAGER)
1651 set_option (OPTFORCEREDRAWPAGER);
1652 if (option->flags & R_RESORT_SUB)
1653 set_option (OPTSORTSUBTHREADS);
1654 if (option->flags & R_RESORT)
1655 set_option (OPTNEEDRESORT);
1656 if (option->flags & R_RESORT_INIT)
1657 set_option (OPTRESORTINIT);
1658 if (option->flags & R_TREE)
1659 set_option (OPTREDRAWTREE);
1666 /* reads the specified initialization file. returns -1 if errors were found
1667 so that we can pause to let the user know... */
1668 static int source_rc (const char *rcfile, BUFFER * err)
1671 int line = 0, rc = 0, conv = 0;
1673 char *linebuf = NULL;
1674 char *currentline = NULL;
1678 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1679 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1684 while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
1685 conv = ConfigCharset && (*ConfigCharset) && Charset;
1687 currentline = m_strdup(linebuf);
1690 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1693 currentline = linebuf;
1698 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1699 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1700 if (--rc < -MAXERRS) {
1702 p_delete(¤tline);
1711 p_delete(¤tline);
1713 p_delete(&token.data);
1717 mutt_wait_filter (pid);
1719 /* the muttrc source keyword */
1720 snprintf (err->data, err->dsize,
1721 rc >= -MAXERRS ? _("source: errors in %s")
1722 : _("source: reading aborted due too many errors in %s"),
1731 static int parse_source (BUFFER * tmp, BUFFER * s,
1732 unsigned long data __attribute__ ((unused)),
1735 char path[_POSIX_PATH_MAX];
1739 if (mutt_extract_token (tmp, s, 0) != 0) {
1740 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1744 m_strcpy(path, sizeof(path), tmp->data);
1745 mutt_expand_path (path, sizeof(path));
1747 rc += source_rc (path, err);
1749 while (MoreArgs (s));
1751 return ((rc < 0) ? -1 : 0);
1754 /* line command to execute
1756 token scratch buffer to be used by parser. caller should free
1757 token->data when finished. the reason for this variable is
1758 to avoid having to allocate and deallocate a lot of memory
1759 if we are parsing many lines. the caller can pass in the
1760 memory to use, which avoids having to create new space for
1761 every call to this function.
1763 err where to write error messages */
1764 int mutt_parse_rc_line (const char *line, BUFFER * token, BUFFER * err)
1770 expn.data = expn.dptr = line;
1771 expn.dsize = m_strlen(line);
1775 expn.dptr = vskipspaces(expn.dptr);
1776 while (*expn.dptr) {
1777 if (*expn.dptr == '#')
1778 break; /* rest of line is a comment */
1779 if (*expn.dptr == ';') {
1783 mutt_extract_token (token, &expn, 0);
1784 for (i = 0; Commands[i].name; i++) {
1785 if (!m_strcmp(token->data, Commands[i].name)) {
1786 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1791 if (!Commands[i].name) {
1792 snprintf (err->data, err->dsize, _("%s: unknown command"),
1793 NONULL (token->data));
1800 p_delete(&expn.data);
1805 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
1806 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
1807 /* initial string that starts completion. No telling how much crap
1808 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1809 char User_typed[LONG_STRING] = { 0 };
1811 int Num_matched = 0; /* Number of matches for completion */
1812 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1813 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1815 /* helper function for completion. Changes the dest buffer if
1816 necessary/possible to aid completion.
1817 dest == completion result gets here.
1818 src == candidate for completion.
1819 try == user entered data for completion.
1820 len == length of dest buffer.
1822 static void candidate (char *dest, char *try, const char *src, int len)
1826 if (strstr (src, try) == src) {
1827 Matches[Num_matched++] = src;
1829 m_strcpy(dest, len, src);
1831 for (l = 0; src[l] && src[l] == dest[l]; l++);
1837 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
1841 int spaces; /* keep track of the number of leading spaces on the line */
1843 buffer = vskipspaces(buffer);
1844 spaces = buffer - pt;
1846 pt = buffer + pos - spaces;
1847 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1850 if (pt == buffer) { /* complete cmd */
1851 /* first TAB. Collect all the matches */
1854 m_strcpy(User_typed, sizeof(User_typed), pt);
1855 p_clear(Matches, countof(Matches));
1856 p_clear(Completed, countof(Completed));
1857 for (num = 0; Commands[num].name; num++)
1858 candidate (Completed, User_typed, Commands[num].name,
1860 Matches[Num_matched++] = User_typed;
1862 /* All matches are stored. Longest non-ambiguous string is ""
1863 * i.e. dont change 'buffer'. Fake successful return this time */
1864 if (User_typed[0] == 0)
1868 if (Completed[0] == 0 && User_typed[0])
1871 /* Num_matched will _always_ be atleast 1 since the initial
1872 * user-typed string is always stored */
1873 if (numtabs == 1 && Num_matched == 2)
1874 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1875 else if (numtabs > 1 && Num_matched > 2)
1876 /* cycle thru all the matches */
1877 snprintf (Completed, sizeof(Completed), "%s",
1878 Matches[(numtabs - 2) % Num_matched]);
1880 /* return the completed command */
1881 m_strcpy(buffer, len - spaces, Completed);
1883 else if (!m_strncmp(buffer, "set", 3)
1884 || !m_strncmp(buffer, "unset", 5)
1885 || !m_strncmp(buffer, "reset", 5)
1886 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
1887 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
1890 /* loop through all the possible prefixes (no, inv, ...) */
1891 if (!m_strncmp(buffer, "set", 3)) {
1892 for (num = 0; prefixes[num]; num++) {
1893 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
1894 pt += m_strlen(prefixes[num]);
1900 /* first TAB. Collect all the matches */
1903 m_strcpy(User_typed, sizeof(User_typed), pt);
1904 p_clear(Matches, countof(Matches));
1905 p_clear(Completed, countof(Completed));
1906 for (num = 0; MuttVars[num].option; num++)
1907 candidate(Completed, User_typed, MuttVars[num].option,
1909 Matches[Num_matched++] = User_typed;
1911 /* All matches are stored. Longest non-ambiguous string is ""
1912 * i.e. dont change 'buffer'. Fake successful return this time */
1913 if (User_typed[0] == 0)
1917 if (Completed[0] == 0 && User_typed[0])
1920 /* Num_matched will _always_ be atleast 1 since the initial
1921 * user-typed string is always stored */
1922 if (numtabs == 1 && Num_matched == 2)
1923 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1924 else if (numtabs > 1 && Num_matched > 2)
1925 /* cycle thru all the matches */
1926 snprintf (Completed, sizeof(Completed), "%s",
1927 Matches[(numtabs - 2) % Num_matched]);
1929 m_strcpy(pt, buffer + len - pt - spaces, Completed);
1931 else if (!m_strncmp(buffer, "exec", 4)) {
1932 struct binding_t *menu = km_get_table (CurrentMenu);
1934 if (!menu && CurrentMenu != MENU_PAGER)
1938 /* first TAB. Collect all the matches */
1941 m_strcpy(User_typed, sizeof(User_typed), pt);
1942 p_clear(Matches, countof(Matches));
1943 p_clear(Completed, countof(Completed));
1944 for (num = 0; menu[num].name; num++)
1945 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
1946 /* try the generic menu */
1947 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1949 for (num = 0; menu[num].name; num++)
1950 candidate (Completed, User_typed, menu[num].name,
1953 Matches[Num_matched++] = User_typed;
1955 /* All matches are stored. Longest non-ambiguous string is ""
1956 * i.e. dont change 'buffer'. Fake successful return this time */
1957 if (User_typed[0] == 0)
1961 if (Completed[0] == 0 && User_typed[0])
1964 /* Num_matched will _always_ be atleast 1 since the initial
1965 * user-typed string is always stored */
1966 if (numtabs == 1 && Num_matched == 2)
1967 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1968 else if (numtabs > 1 && Num_matched > 2)
1969 /* cycle thru all the matches */
1970 snprintf (Completed, sizeof(Completed), "%s",
1971 Matches[(numtabs - 2) % Num_matched]);
1973 m_strcpy(pt, buffer + len - pt - spaces, Completed);
1981 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
1983 char var[STRING], *pt = buffer;
1985 struct option_t* option = NULL;
1990 buffer = vskipspaces(buffer);
1991 spaces = buffer - pt;
1993 pt = buffer + pos - spaces;
1994 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1996 pt++; /* move past the space */
1997 if (*pt == '=') /* abort if no var before the '=' */
2000 if (m_strncmp(buffer, "set", 3) == 0) {
2001 m_strcpy(var, sizeof(var), pt);
2002 /* ignore the trailing '=' when comparing */
2003 var[m_strlen(var) - 1] = 0;
2004 if (!(option = hash_find (ConfigOptions, var)))
2005 return 0; /* no such variable. */
2007 char tmp[LONG_STRING], tmp2[LONG_STRING];
2009 ssize_t dlen = buffer + len - pt - spaces;
2010 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2014 if ((DTYPE (option->type) == DT_STR) ||
2015 (DTYPE (option->type) == DT_PATH) ||
2016 (DTYPE (option->type) == DT_RX)) {
2017 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
2018 if (DTYPE (option->type) == DT_PATH)
2019 mutt_pretty_mailbox (tmp);
2021 else if (DTYPE (option->type) == DT_ADDR) {
2022 rfc822_addrcat(tmp, sizeof(tmp), *((address_t **) option->data), 0);
2024 else if (DTYPE (option->type) == DT_QUAD)
2025 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
2026 else if (DTYPE (option->type) == DT_NUM)
2027 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
2028 else if (DTYPE (option->type) == DT_SORT) {
2029 const struct mapping_t *map;
2032 switch (option->type & DT_SUBTYPE_MASK) {
2034 map = SortAliasMethods;
2036 case DT_SORT_BROWSER:
2037 map = SortBrowserMethods;
2040 map = SortKeyMethods;
2046 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2047 snprintf(tmp, sizeof(tmp), "%s%s%s",
2048 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2049 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2051 else if (DTYPE (option->type) == DT_MAGIC) {
2053 switch (DefaultMagic) {
2069 m_strcpy(tmp, sizeof(tmp), p);
2071 else if (DTYPE (option->type) == DT_BOOL)
2072 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
2076 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
2077 if (*s == '\\' || *s == '"')
2083 m_strcpy(tmp, sizeof(tmp), pt);
2084 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2092 /* Implement the -Q command line flag */
2093 int mutt_query_variables (string_list_t * queries)
2097 char errbuff[STRING];
2098 char command[STRING];
2106 err.dsize = sizeof(errbuff);
2108 for (p = queries; p; p = p->next) {
2109 snprintf (command, sizeof(command), "set ?%s\n", p->data);
2110 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2111 fprintf (stderr, "%s\n", err.data);
2112 p_delete(&token.data);
2115 printf ("%s\n", err.data);
2118 p_delete(&token.data);
2122 static int mutt_execute_commands (string_list_t * p)
2125 char errstr[STRING];
2129 err.dsize = sizeof(errstr);
2131 for (; p; p = p->next) {
2132 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2133 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2134 p_delete(&token.data);
2138 p_delete(&token.data);
2142 void mutt_init (int skip_sys_rc, string_list_t * commands)
2145 struct utsname utsname;
2147 char buffer[STRING], error[STRING];
2148 int default_rc = 0, need_pause = 0;
2154 err.dsize = sizeof(error);
2156 ConfigOptions = hash_new (sizeof(MuttVars) * 2, 0);
2157 for (i = 0; MuttVars[i].option; i++) {
2158 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i]);
2162 * XXX - use something even more difficult to predict?
2164 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2165 "\033]9;%ld\a", (long) time (NULL));
2168 /* Get some information about the user */
2169 if ((pw = getpwuid (getuid ()))) {
2171 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, MCore.gecos_mask);
2172 Realname = m_strdup(rnbuf);
2175 /* And about the host... */
2177 /* some systems report the FQDN instead of just the hostname */
2178 if ((p = strchr (utsname.nodename, '.'))) {
2179 Hostname = p_dupstr(utsname.nodename, p - utsname.nodename);
2181 m_strcpy(buffer, sizeof(buffer), p); /* save the domain for below */
2184 Hostname = m_strdup(utsname.nodename);
2186 if (!p && getdnsdomainname(buffer, sizeof(buffer)) == -1)
2187 Fqdn = m_strdup("@");
2189 if (*buffer != '@') {
2190 Fqdn = p_new(char, m_strlen(buffer) + m_strlen(Hostname) + 2);
2191 sprintf (Fqdn, "%s.%s", NONULL(Hostname), buffer);
2194 Fqdn = m_strdup(NONULL (Hostname));
2201 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2203 fgets (buffer, sizeof(buffer), f);
2204 p = vskipspaces(buffer);
2206 while (*q && !isspace(*q))
2209 NewsServer = m_strdup(p);
2213 if ((p = getenv ("NNTPSERVER")))
2214 NewsServer = m_strdup(p);
2217 if ((p = getenv ("MAIL")))
2218 Spoolfile = m_strdup(p);
2219 else if ((p = getenv ("MAILDIR")))
2220 Spoolfile = m_strdup(p);
2223 mutt_concat_path(buffer, sizeof(buffer), NONULL(MCore.homedir), MAILPATH);
2225 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(MCore.username));
2227 Spoolfile = m_strdup(buffer);
2230 if ((p = getenv ("MAILCAPS")))
2231 MailcapPath = m_strdup(p);
2233 /* Default search path from RFC1524 */
2235 m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2236 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2239 if ((p = getenv ("REPLYTO")) != NULL) {
2242 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2245 buf.data = buf.dptr = buffer;
2246 buf.dsize = m_strlen(buffer);
2249 parse_my_hdr (&token, &buf, 0, &err);
2250 p_delete(&token.data);
2253 if ((p = getenv ("EMAIL")) != NULL)
2254 From = rfc822_parse_adrlist (NULL, p);
2256 charset_initialize();
2258 /* Set standard defaults */
2259 hash_map (ConfigOptions, mutt_set_default, 0);
2260 hash_map (ConfigOptions, mutt_restore_default, 0);
2262 CurrentMenu = MENU_MAIN;
2265 /* Unset suspend by default if we're the session leader */
2266 if (getsid (0) == getpid ())
2267 unset_option (OPTSUSPEND);
2270 mutt_init_history ();
2273 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL(MCore.homedir));
2274 if (access (buffer, F_OK) == -1)
2275 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
2276 NONULL(MCore.homedir));
2279 Muttrc = m_strdup(buffer);
2282 m_strcpy(buffer, sizeof(buffer), Muttrc);
2284 mutt_expand_path (buffer, sizeof(buffer));
2285 Muttrc = m_strdup(buffer);
2288 /* Process the global rc file if it exists and the user hasn't explicity
2289 requested not to via "-n". */
2291 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
2293 if (access (buffer, F_OK) == -1)
2294 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
2295 if (access (buffer, F_OK) == -1)
2296 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
2298 if (access (buffer, F_OK) == -1)
2299 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
2300 if (access (buffer, F_OK) != -1) {
2301 if (source_rc (buffer, &err) != 0) {
2302 fputs (err.data, stderr);
2303 fputc ('\n', stderr);
2309 /* Read the user's initialization file. */
2310 if (access (Muttrc, F_OK) != -1) {
2311 if (!option (OPTNOCURSES))
2313 if (source_rc (Muttrc, &err) != 0) {
2314 fputs (err.data, stderr);
2315 fputc ('\n', stderr);
2319 else if (!default_rc) {
2320 /* file specified by -F does not exist */
2321 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2322 mutt_endwin (buffer);
2327 snprintf(buffer, sizeof(buffer), "%s/.madmutt.lua", NONULL(MCore.homedir));
2328 if (access(buffer, F_OK) < 0)
2329 snprintf(buffer, sizeof(buffer), "%s/.madmutt/cfg.lua", NONULL(MCore.homedir));
2330 if (!access(buffer, F_OK)) {
2331 need_pause = luaM_wrap(mutt_error, luaM_dofile(buffer));
2335 if (mutt_execute_commands (commands) != 0)
2338 /* warn about synonym variables */
2342 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2344 for (syn = Synonyms; syn; syn = syn->next) {
2345 fprintf(stderr, "$%s ($%s should be used) (%s:%d)\n",
2346 syn->o ? NONULL(syn->o->option) : "",
2347 syn->n ? NONULL(syn->n->option) : "",
2348 NONULL(syn->f), syn->l);
2350 fprintf (stderr, _("Warning: synonym variables are scheduled"
2351 " for removal.\n"));
2352 syn_list_wipe(&Synonyms);
2356 if (need_pause && !option (OPTNOCURSES)) {
2357 if (mutt_any_key_to_continue (NULL) == -1)
2362 int mutt_get_hook_type (const char *name)
2364 struct command_t *c;
2366 for (c = Commands; c->name; c++)
2367 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2372 /* dump out the value of all the variables we have */
2373 int mutt_dump_variables (int full) {
2376 /* get all non-synonyms into list... */
2377 for (i = 0; MuttVars[i].option; i++) {
2378 struct option_t *option = MuttVars + i;
2379 char buf[LONG_STRING];
2381 if (DTYPE(option->type) == DT_SYN)
2385 mutt_option_value(option->option, buf, sizeof(buf));
2386 if (!m_strcmp(buf, option->init))
2391 FuncTable[DTYPE(option->type)].opt_tostr(buf, sizeof(buf), option);
2392 printf ("%s\n", buf);
2395 printf ("\n# vi""m:set ft=muttrc:\n");