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-ui/curses.h>
18 #include <lib-ui/history.h>
19 #include <lib-mx/mx.h>
26 #include "mutt_idna.h"
33 static const struct mapping_t* get_sortmap (struct option_t* option);
34 static int parse_sort (struct option_t* dst, const char *s,
35 const struct mapping_t *map,
36 char* errbuf, ssize_t errlen);
38 static hash_t *ConfigOptions = NULL;
40 /* for synonym warning reports: synonym found during parsing */
41 typedef struct syn_t {
45 struct option_t* n; /* new */
46 struct option_t* o; /* old */
50 static void syn_wipe(syn_t *syn) {
54 DO_DELETE(syn_t, syn);
55 DO_SLIST(syn_t, syn, syn_delete);
57 /* for synonym warning reports: list of synonyms found */
58 static syn_t *Synonyms = NULL;
59 /* for synonym warning reports: current rc file */
60 static const char* CurRCFile = NULL;
61 /* for synonym warning reports: current rc line */
62 static int CurRCLine = 0;
64 /* prototypes for checking for special vars */
65 static int check_history (const char* option, unsigned long val,
66 char* errbuf, ssize_t errlen);
67 /* this checks that numbers are >= 0 */
68 static int check_num (const char* option, unsigned long val,
69 char* errbuf, ssize_t errlen);
71 /* use this to check only */
72 static int check_special (const char* option, unsigned long val,
73 char* errbuf, ssize_t errlen);
75 /* variable <-> sanity check function mappings
76 * when changing these, make sure the proper _from_string handler
81 int (*check) (const char* option, unsigned long val,
82 char* errbuf, ssize_t errlen);
84 { "history", check_history },
85 { "pager_index_lines", check_num },
90 static void bool_to_string (char* dst, ssize_t dstlen,
91 struct option_t* option) {
92 snprintf (dst, dstlen, "%s=%s", option->option,
93 option (option->data) ? "yes" : "no");
96 static int bool_from_string (struct option_t* dst, const char* val,
97 char* errbuf __attribute__ ((unused)),
98 ssize_t errlen __attribute__ ((unused))) {
103 if (ascii_strncasecmp (val, "yes", 3) == 0)
105 else if (ascii_strncasecmp (val, "no", 2) == 0)
111 set_option (dst->data);
113 unset_option (dst->data);
117 static void num_to_string (char* dst, ssize_t dstlen,
118 struct option_t* option) {
120 const char* fmt = (m_strcmp(option->option, "umask") == 0) ?
122 snprintf (dst, dstlen, fmt, option->option,
123 *((short*) option->data));
126 static int num_from_string (struct option_t* dst, const char* val,
127 char* errbuf, ssize_t errlen) {
128 int num = 0, old = 0;
134 num = strtol (val, &t, 0);
136 if (m_strisempty(val) || *t || (short) num != num) {
138 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
144 /* just temporarily accept new val so that check_special for
145 * $history already has it when doing history's init() */
146 old = *((short*) dst->data);
147 *((short*) dst->data) = (short) num;
149 if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
150 *((short*) dst->data) = old;
157 static void str_to_string (char* dst, ssize_t dstlen,
158 struct option_t* option) {
159 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
160 NONULL (*((char**) option->data)));
163 static int path_from_string (struct option_t* dst, const char* val,
164 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
165 char path[_POSIX_PATH_MAX];
170 if (m_strisempty(val)) {
171 p_delete((char**) dst->data);
176 m_strcpy(path, sizeof(path), val);
177 mutt_expand_path (path, sizeof(path));
178 m_strreplace((char **) dst->data, path);
182 static int str_from_string (struct option_t* dst, const char* val,
183 char* errbuf, ssize_t errlen) {
187 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
190 m_strreplace((char**) dst->data, val);
194 static void quad_to_string (char* dst, ssize_t dstlen,
195 struct option_t* option) {
196 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
197 snprintf (dst, dstlen, "%s=%s", option->option,
198 vals[quadoption (option->data)]);
201 static int quad_from_string (struct option_t* dst, const char* val,
202 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
207 if (ascii_strncasecmp (val, "yes", 3) == 0)
209 else if (ascii_strncasecmp (val, "no", 2) == 0)
211 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
213 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
219 set_quadoption (dst->data, flag);
223 static void sort_to_string (char* dst, ssize_t dstlen,
224 struct option_t* option) {
225 const struct mapping_t *map = get_sortmap (option);
226 const char *p = NULL;
229 snprintf (dst, sizeof(dst), "%s=unknown", option->option);
233 p = mutt_getnamebyvalue(*((short *)option->data) & SORT_MASK, map);
235 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
236 (*((short *) option->data) & SORT_REVERSE) ?
238 (*((short *) option->data) & SORT_LAST) ? "last-" :
242 static int sort_from_string (struct option_t* dst, const char* val,
243 char* errbuf, ssize_t errlen) {
244 const struct mapping_t *map = NULL;
245 if (!(map = get_sortmap (dst))) {
247 snprintf (errbuf, errlen, _("%s: Unknown type."),
251 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
256 static void rx_to_string (char* dst, ssize_t dstlen,
257 struct option_t* option) {
258 rx_t* p = (rx_t*) option->data;
259 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
260 NONULL (p->pattern));
263 static int rx_from_string (struct option_t* dst, const char* val,
264 char* errbuf, ssize_t errlen) {
267 int flags = 0, e = 0, neg = 0;
273 if (option (OPTATTACHMSG) && !m_strcmp(dst->option, "reply_regexp")) {
275 snprintf (errbuf, errlen,
276 "Operation not permitted when in attach-message mode.");
280 if (!((rx_t*) dst->data))
281 *((rx_t**) dst->data) = p_new(rx_t, 1);
283 p = (rx_t*) dst->data;
285 /* something to do? */
286 if (m_strisempty(val) || (p->pattern && m_strcmp(p->pattern, val) == 0))
289 if (m_strcmp(dst->option, "mask") != 0)
290 flags |= mutt_which_case (val);
293 if (m_strcmp(dst->option, "mask") == 0 && *s == '!') {
298 rx = p_new(regex_t, 1);
300 if ((e = REGCOMP (rx, s, flags)) != 0) {
301 regerror (e, rx, errbuf, errlen);
312 m_strreplace(&p->pattern, val);
316 if (m_strcmp(dst->option, "reply_regexp") == 0)
317 mutt_adjust_all_subjects ();
322 static void magic_to_string (char* dst, ssize_t dstlen,
323 struct option_t* option) {
324 const char* s = NULL;
325 switch (option->data) {
326 case M_MBOX: s = "mbox"; break;
327 case M_MH: s = "MH"; break;
328 case M_MAILDIR: s = "Maildir"; break;
329 default: s = "unknown"; break;
331 snprintf (dst, dstlen, "%s=%s", option->option, s);
334 static int magic_from_string (struct option_t* dst, const char* val,
335 char *errbuf, ssize_t errlen)
339 if (!dst || m_strisempty(val))
341 if (ascii_strncasecmp (val, "mbox", 4) == 0)
343 else if (ascii_strncasecmp (val, "mh", 2) == 0)
345 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
351 *((short*) dst->data) = flag;
358 void (*opt_tostr) (char* dst, ssize_t dstlen, struct option_t* option);
359 int (*opt_fromstr) (struct option_t* dst, const char* val,
360 char* errbuf, ssize_t errlen);
362 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
363 { DT_BOOL, bool_to_string, bool_from_string },
364 { DT_NUM, num_to_string, num_from_string },
365 { DT_STR, str_to_string, str_from_string },
366 { DT_PATH, str_to_string, path_from_string },
367 { DT_QUAD, quad_to_string, quad_from_string },
368 { DT_SORT, sort_to_string, sort_from_string },
369 { DT_RX, rx_to_string, rx_from_string },
370 { DT_MAGIC, magic_to_string, magic_from_string },
374 int mutt_option_value (const char* val, char* dst, ssize_t dstlen) {
375 struct option_t* option = NULL;
376 char* tmp = NULL, *t = NULL;
379 if (!(option = hash_find (ConfigOptions, val))) {
383 tmp = p_new(char, dstlen+1);
384 FuncTable[DTYPE(option->type)].opt_tostr (tmp, dstlen, option);
386 /* as we get things of type $var=value and don't want to bloat the
387 * above "just" for expansion, we do the stripping here */
388 t = strchr (tmp, '=');
392 if (t[l-1] == '"' && *t == '"') {
397 memcpy (dst, t, l+1);
403 static void toggle_quadoption (int opt)
406 int b = (opt % 4) * 2;
408 QuadOptions[n] ^= (1 << b);
411 void set_quadoption (int opt, int flag)
414 int b = (opt % 4) * 2;
416 QuadOptions[n] &= ~(0x3 << b);
417 QuadOptions[n] |= (flag & 0x3) << b;
420 int quadoption (int opt)
423 int b = (opt % 4) * 2;
425 return (QuadOptions[n] >> b) & 0x3;
428 int query_quadoption2(int v, const char *prompt)
436 v = mutt_yesorno(prompt, (v == M_ASKYES));
437 CLEARLINE (LINES - 1);
442 int query_quadoption (int opt, const char *prompt)
444 int v = quadoption (opt);
452 v = mutt_yesorno (prompt, (v == M_ASKYES));
453 CLEARLINE (LINES - 1);
460 /* always wise to do what someone else did before */
461 static void _attachments_clean (void) {
463 if (Context && Context->msgcount) {
464 for (i = 0; i < Context->msgcount; i++)
465 Context->hdrs[i]->attach_valid = 0;
469 static int parse_attach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
470 BUFFER *err __attribute__ ((unused))) {
472 string_list_t *listp, *lastp;
477 /* Find the last item in the list that data points to. */
479 for (listp = *ldata; listp; listp = listp->next) {
480 a = (ATTACH_MATCH *)listp->data;
485 mutt_extract_token (buf, s, 0);
487 if (!buf->data || *buf->data == '\0')
490 a = p_new(ATTACH_MATCH, 1);
492 /* some cheap hacks that I expect to remove */
493 if (!m_strcasecmp(buf->data, "any"))
494 a->major = m_strdup("*/.*");
495 else if (!m_strcasecmp(buf->data, "none"))
496 a->major = m_strdup("cheap_hack/this_should_never_match");
498 a->major = m_strdup(buf->data);
500 if ((p = strchr(a->major, '/'))) {
505 a->minor = "unknown";
508 len = m_strlen(a->minor);
509 tmpminor = p_new(char, len + 3);
510 m_strcpy(&tmpminor[1], len + 3, a->minor);
512 tmpminor[len+1] = '$';
513 tmpminor[len+2] = '\0';
515 a->major_int = mutt_check_mime_type(a->major);
516 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
520 listp = p_new(string_list_t, 1);
521 listp->data = (char *)a;
530 while (MoreArgs (s));
532 _attachments_clean();
536 static int parse_unattach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
537 BUFFER *err __attribute__ ((unused))) {
539 string_list_t *lp, *lastp, *newlp;
545 mutt_extract_token (buf, s, 0);
547 if (!m_strcasecmp(buf->data, "any"))
548 tmp = m_strdup("*/.*");
549 else if (!m_strcasecmp(buf->data, "none"))
550 tmp = m_strdup("cheap_hack/this_should_never_match");
552 tmp = m_strdup(buf->data);
554 if ((minor = strchr(tmp, '/'))) {
558 minor = m_strdup("unknown");
560 major = mutt_check_mime_type(tmp);
562 /* We must do our own walk here because string_list_remove() will only
563 * remove the string_list_t->data, not anything pointed to by the string_list_t->data. */
565 for(lp = *ldata; lp; ) {
566 a = (ATTACH_MATCH *)lp->data;
567 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
568 regfree(&a->minor_rx);
571 /* Relink backward */
573 lastp->next = lp->next;
578 p_delete(&lp->data); /* same as a */
588 while (MoreArgs (s));
591 _attachments_clean();
595 static int print_attach_list (string_list_t *lp, char op, const char *name) {
597 printf("attachments %c%s %s/%s\n", op, name,
598 ((ATTACH_MATCH *)lp->data)->major,
599 ((ATTACH_MATCH *)lp->data)->minor);
606 static int parse_attachments (BUFFER *buf, BUFFER *s,
607 unsigned long data __attribute__ ((unused)),
610 string_list_t **listp;
612 mutt_extract_token(buf, s, 0);
613 if (!buf->data || *buf->data == '\0') {
614 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
618 category = buf->data;
624 printf("\nCurrent attachments settings:\n\n");
625 print_attach_list(AttachAllow, '+', "A");
626 print_attach_list(AttachExclude, '-', "A");
627 print_attach_list(InlineAllow, '+', "I");
628 print_attach_list(InlineExclude, '-', "I");
629 set_option (OPTFORCEREDRAWINDEX);
630 set_option (OPTFORCEREDRAWPAGER);
631 mutt_any_key_to_continue (NULL);
635 if (op != '+' && op != '-') {
639 if (!m_strncasecmp(category, "attachment", strlen(category))) {
641 listp = &AttachAllow;
643 listp = &AttachExclude;
645 else if (!m_strncasecmp(category, "inline", strlen(category))) {
647 listp = &InlineAllow;
649 listp = &InlineExclude;
651 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
655 return parse_attach_list(buf, s, listp, err);
658 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
660 string_list_t **listp;
662 mutt_extract_token(buf, s, 0);
663 if (!buf->data || *buf->data == '\0') {
664 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
670 if (op != '+' && op != '-') {
674 if (!m_strncasecmp(p, "attachment", strlen(p))) {
676 listp = &AttachAllow;
678 listp = &AttachExclude;
680 else if (!m_strncasecmp(p, "inline", strlen(p))) {
682 listp = &InlineAllow;
684 listp = &InlineExclude;
687 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
691 return parse_unattach_list(buf, s, listp, err);
694 static int parse_unalias (BUFFER * buf, BUFFER * s,
695 unsigned long data __attribute__ ((unused)),
696 BUFFER * err __attribute__ ((unused)))
698 alias_t *tmp, **last;
701 mutt_extract_token (buf, s, 0);
703 if (!m_strcmp("*", buf->data) == 0) {
704 if (CurrentMenu == MENU_ALIAS) {
705 for (tmp = Aliases; tmp; tmp = tmp->next)
707 set_option(OPTFORCEREDRAWINDEX);
709 alias_list_wipe(&Aliases);
715 for (last = &Aliases; *last; last = &(*last)->next) {
716 if (!m_strcasecmp(buf->data, (*last)->name)) {
717 if (CurrentMenu == MENU_ALIAS) {
719 set_option (OPTFORCEREDRAWINDEX);
721 tmp = alias_list_pop(last);
727 } while (MoreArgs(s));
732 static int parse_alias (BUFFER * buf, BUFFER * s,
733 unsigned long data __attribute__ ((unused)),
740 m_strcpy(err->data, err->dsize, _("alias: no address"));
744 mutt_extract_token (buf, s, 0);
746 /* check to see if an alias with this name already exists */
747 for (last = &Aliases; *last; last = &(*last)->next) {
748 if (!m_strcasecmp((*last)->name, buf->data))
753 /* create a new alias */
755 (*last)->name = m_strdup(buf->data);
756 /* give the main addressbook code a chance */
757 if (CurrentMenu == MENU_ALIAS)
758 set_option (OPTMENUCALLER);
760 /* override the previous value */
761 address_list_wipe(&(*last)->addr);
762 if (CurrentMenu == MENU_ALIAS)
763 set_option (OPTFORCEREDRAWINDEX);
766 mutt_extract_token(buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE);
767 (*last)->addr = mutt_parse_adrlist((*last)->addr, buf->data);
768 if (mutt_addrlist_to_idna((*last)->addr, &estr)) {
769 snprintf (err->data, err->dsize,
770 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, (*last)->name);
779 parse_unmy_hdr(BUFFER * buf, BUFFER * s,
780 unsigned long data __attribute__ ((unused)),
781 BUFFER * err __attribute__ ((unused)))
784 mutt_extract_token (buf, s, 0);
786 if (!m_strcmp("*", buf->data)) {
787 string_list_wipe(&UserHeader);
789 string_list_t **last = &UserHeader;
790 ssize_t l = m_strlen(buf->data);
792 if (buf->data[l - 1] == ':')
796 if (!ascii_strncasecmp(buf->data, (*last)->data, l)
797 && (*last)->data[l] == ':')
799 string_list_t *tmp = string_list_pop(last);
800 string_item_delete(&tmp);
802 last = &(*last)->next;
806 } while (MoreArgs(s));
811 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
818 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
819 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
820 m_strcpy(err->data, err->dsize, _("invalid header field"));
823 keylen = p - buf->data + 1;
826 for (tmp = UserHeader;; tmp = tmp->next) {
827 /* see if there is already a field by this name */
828 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
829 /* replace the old value */
830 p_delete(&tmp->data);
831 tmp->data = buf->data;
838 tmp->next = string_item_new();
842 tmp = string_item_new();
845 tmp->data = buf->data;
851 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
852 char* errbuf, ssize_t errlen) {
855 if (m_strncmp("reverse-", s, 8) == 0) {
857 flags = SORT_REVERSE;
860 if (m_strncmp("last-", s, 5) == 0) {
865 if ((i = mutt_getvaluebyname (s, map)) == -1) {
867 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
871 *((short*) dst->data) = i | flags;
875 /* if additional data more == 1, we want to resolve synonyms */
876 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
878 char buf[LONG_STRING];
879 struct option_t *ptr = p;
881 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_fromstr)
884 mutt_option_value(ptr->option, buf, sizeof(buf));
885 if (m_strlen(ptr->init) == 0 && buf && *buf)
886 ptr->init = m_strdup(buf);
889 static int init_expand (char** dst, struct option_t* src) {
895 if (DTYPE(src->type) == DT_STR || DTYPE(src->type) == DT_PATH) {
896 /* only expand for string as it's the only place where
897 * we want to expand vars right now */
898 if (src->init && *src->init) {
901 len = m_strlen(src->init) + 2;
902 in.data = p_new(char, len + 1);
903 snprintf (in.data, len, "\"%s\"", src->init);
906 mutt_extract_token (&token, &in, 0);
907 if (token.data && *token.data)
908 *dst = m_strdup(token.data);
912 p_delete(&token.data);
916 /* for non-string: take value as is */
917 *dst = m_strdup(src->init);
921 /* if additional data more == 1, we want to resolve synonyms */
922 static void mutt_restore_default (const char* name __attribute__ ((unused)),
923 void* p, unsigned long more) {
925 struct option_t* ptr = (struct option_t*) p;
930 if (FuncTable[DTYPE (ptr->type)].opt_fromstr) {
931 init_expand (&init, ptr);
932 if (!FuncTable[DTYPE (ptr->type)].opt_fromstr (ptr, init, errbuf,
934 if (!option (OPTNOCURSES))
936 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
937 "Please report this error: \"%s\"\n"),
938 ptr->option, NONULL (init), errbuf);
944 if (ptr->flags & R_INDEX)
945 set_option (OPTFORCEREDRAWINDEX);
946 if (ptr->flags & R_PAGER)
947 set_option (OPTFORCEREDRAWPAGER);
948 if (ptr->flags & R_RESORT_SUB)
949 set_option (OPTSORTSUBTHREADS);
950 if (ptr->flags & R_RESORT)
951 set_option (OPTNEEDRESORT);
952 if (ptr->flags & R_RESORT_INIT)
953 set_option (OPTRESORTINIT);
954 if (ptr->flags & R_TREE)
955 set_option (OPTREDRAWTREE);
958 static int check_num (const char* option, unsigned long p,
959 char* errbuf, ssize_t errlen) {
962 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
968 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
969 char* errbuf, ssize_t errlen) {
970 if (!check_num ("history", p, errbuf, errlen))
972 mutt_init_history ();
976 static int check_special (const char* name, unsigned long val,
977 char* errbuf, ssize_t errlen) {
980 for (i = 0; SpecialVars[i].name; i++) {
981 if (m_strcmp(SpecialVars[i].name, name) == 0) {
982 return (SpecialVars[i].check (SpecialVars[i].name,
983 val, errbuf, errlen));
989 static const struct mapping_t* get_sortmap (struct option_t* option) {
990 const struct mapping_t* map = NULL;
992 switch (option->type & DT_SUBTYPE_MASK) {
994 map = SortAliasMethods;
996 case DT_SORT_BROWSER:
997 map = SortBrowserMethods;
1000 map = SortKeyMethods;
1003 map = SortAuxMethods;
1012 #define CHECK_PAGER \
1013 if ((CurrentMenu == MENU_PAGER) && \
1014 (!option || (option->flags & R_RESORT))) \
1016 snprintf (err->data, err->dsize, \
1017 _("Not available in this menu.")); \
1021 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1024 int query, unset, inv, reset, r = 0;
1025 struct option_t* option = NULL;
1027 while (MoreArgs (s)) {
1028 /* reset state variables */
1030 unset = data & M_SET_UNSET;
1031 inv = data & M_SET_INV;
1032 reset = data & M_SET_RESET;
1034 if (*s->dptr == '?') {
1038 else if (m_strncmp("no", s->dptr, 2) == 0) {
1042 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1046 else if (*s->dptr == '&') {
1051 /* get the variable name */
1052 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1053 option = hash_find(ConfigOptions, tmp->data);
1054 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1055 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1058 s->dptr = vskipspaces(s->dptr);
1061 if (query || unset || inv) {
1062 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1066 if (s && *s->dptr == '=') {
1067 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1071 if (!m_strcmp("all", tmp->data)) {
1072 if (CurrentMenu == MENU_PAGER) {
1073 snprintf (err->data, err->dsize, _("Not available in this menu."));
1076 hash_map (ConfigOptions, mutt_restore_default, 1);
1077 set_option (OPTFORCEREDRAWINDEX);
1078 set_option (OPTFORCEREDRAWPAGER);
1079 set_option (OPTSORTSUBTHREADS);
1080 set_option (OPTNEEDRESORT);
1081 set_option (OPTRESORTINIT);
1082 set_option (OPTREDRAWTREE);
1086 mutt_restore_default (NULL, option, 1);
1089 else if (DTYPE (option->type) == DT_BOOL) {
1090 /* XXX this currently ignores the function table
1091 * as we don't get invert and stuff into it */
1092 if (s && *s->dptr == '=') {
1093 if (unset || inv || query) {
1094 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1099 mutt_extract_token (tmp, s, 0);
1100 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1102 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1105 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1111 bool_to_string (err->data, err->dsize, option);
1117 unset_option (option->data);
1119 toggle_option (option->data);
1121 set_option (option->data);
1123 else if (DTYPE (option->type) == DT_STR ||
1124 DTYPE (option->type) == DT_PATH ||
1125 DTYPE (option->type) == DT_MAGIC ||
1126 DTYPE (option->type) == DT_NUM ||
1127 DTYPE (option->type) == DT_SORT ||
1128 DTYPE (option->type) == DT_RX)
1130 /* XXX maybe we need to get unset into handlers? */
1131 if (DTYPE (option->type) == DT_STR || DTYPE (option->type) == DT_PATH) {
1134 p_delete((void **)(void *)&option->data);
1139 if (query || *s->dptr != '=') {
1140 FuncTable[DTYPE (option->type)].opt_tostr
1141 (err->data, err->dsize, option);
1147 mutt_extract_token (tmp, s, 0);
1148 if (!FuncTable[DTYPE (option->type)].opt_fromstr
1149 (option, tmp->data, err->data, err->dsize))
1152 else if (DTYPE (option->type) == DT_QUAD) {
1155 quad_to_string (err->data, err->dsize, option);
1159 if (*s->dptr == '=') {
1162 mutt_extract_token (tmp, s, 0);
1163 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1164 set_quadoption (option->data, M_YES);
1165 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1166 set_quadoption (option->data, M_NO);
1167 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1168 set_quadoption (option->data, M_ASKYES);
1169 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1170 set_quadoption (option->data, M_ASKNO);
1172 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1173 tmp->data, option->option);
1180 toggle_quadoption (option->data);
1182 set_quadoption (option->data, M_NO);
1184 set_quadoption (option->data, M_YES);
1188 snprintf (err->data, err->dsize, _("%s: unknown type"),
1194 if (option->flags & R_INDEX)
1195 set_option (OPTFORCEREDRAWINDEX);
1196 if (option->flags & R_PAGER)
1197 set_option (OPTFORCEREDRAWPAGER);
1198 if (option->flags & R_RESORT_SUB)
1199 set_option (OPTSORTSUBTHREADS);
1200 if (option->flags & R_RESORT)
1201 set_option (OPTNEEDRESORT);
1202 if (option->flags & R_RESORT_INIT)
1203 set_option (OPTRESORTINIT);
1204 if (option->flags & R_TREE)
1205 set_option (OPTREDRAWTREE);
1212 /* reads the specified initialization file. returns -1 if errors were found
1213 so that we can pause to let the user know... */
1214 static int source_rc (const char *rcfile, BUFFER * err)
1217 int line = 0, rc = 0, conv = 0;
1219 char *linebuf = NULL;
1220 char *currentline = NULL;
1224 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1225 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1230 while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
1231 conv = ConfigCharset && (*ConfigCharset) && mod_cset.charset;
1233 currentline = m_strdup(linebuf);
1236 mutt_convert_string (¤tline, ConfigCharset, mod_cset.charset, 0);
1239 currentline = linebuf;
1244 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1245 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1246 if (--rc < -MAXERRS) {
1248 p_delete(¤tline);
1257 p_delete(¤tline);
1259 p_delete(&token.data);
1263 mutt_wait_filter (pid);
1265 /* the muttrc source keyword */
1266 snprintf (err->data, err->dsize,
1267 rc >= -MAXERRS ? _("source: errors in %s")
1268 : _("source: reading aborted due too many errors in %s"),
1277 static int parse_source (BUFFER * tmp, BUFFER * s,
1278 unsigned long data __attribute__ ((unused)),
1281 char path[_POSIX_PATH_MAX];
1285 if (mutt_extract_token (tmp, s, 0) != 0) {
1286 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1290 m_strcpy(path, sizeof(path), tmp->data);
1291 mutt_expand_path (path, sizeof(path));
1293 rc += source_rc (path, err);
1295 while (MoreArgs (s));
1297 return ((rc < 0) ? -1 : 0);
1300 /* line command to execute
1302 token scratch buffer to be used by parser. caller should free
1303 token->data when finished. the reason for this variable is
1304 to avoid having to allocate and deallocate a lot of memory
1305 if we are parsing many lines. the caller can pass in the
1306 memory to use, which avoids having to create new space for
1307 every call to this function.
1309 err where to write error messages */
1310 int mutt_parse_rc_line (const char *line, BUFFER * token, BUFFER * err)
1316 expn.data = expn.dptr = line;
1317 expn.dsize = m_strlen(line);
1321 expn.dptr = vskipspaces(expn.dptr);
1322 while (*expn.dptr) {
1323 if (*expn.dptr == '#')
1324 break; /* rest of line is a comment */
1325 if (*expn.dptr == ';') {
1329 mutt_extract_token (token, &expn, 0);
1330 for (i = 0; Commands[i].name; i++) {
1331 if (!m_strcmp(token->data, Commands[i].name)) {
1332 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1337 if (!Commands[i].name) {
1338 snprintf (err->data, err->dsize, _("%s: unknown command"),
1339 NONULL (token->data));
1346 p_delete(&expn.data);
1351 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
1352 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
1353 /* initial string that starts completion. No telling how much crap
1354 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1355 char User_typed[LONG_STRING] = { 0 };
1357 int Num_matched = 0; /* Number of matches for completion */
1358 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1359 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1361 /* helper function for completion. Changes the dest buffer if
1362 necessary/possible to aid completion.
1363 dest == completion result gets here.
1364 src == candidate for completion.
1365 try == user entered data for completion.
1366 len == length of dest buffer.
1368 static void candidate (char *dest, char *try, const char *src, int len)
1372 if (strstr (src, try) == src) {
1373 Matches[Num_matched++] = src;
1375 m_strcpy(dest, len, src);
1377 for (l = 0; src[l] && src[l] == dest[l]; l++);
1383 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
1387 int spaces; /* keep track of the number of leading spaces on the line */
1389 buffer = vskipspaces(buffer);
1390 spaces = buffer - pt;
1392 pt = buffer + pos - spaces;
1393 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1396 if (pt == buffer) { /* complete cmd */
1397 /* first TAB. Collect all the matches */
1400 m_strcpy(User_typed, sizeof(User_typed), pt);
1401 p_clear(Matches, countof(Matches));
1402 p_clear(Completed, countof(Completed));
1403 for (num = 0; Commands[num].name; num++)
1404 candidate (Completed, User_typed, Commands[num].name,
1406 Matches[Num_matched++] = User_typed;
1408 /* All matches are stored. Longest non-ambiguous string is ""
1409 * i.e. dont change 'buffer'. Fake successful return this time */
1410 if (User_typed[0] == 0)
1414 if (Completed[0] == 0 && User_typed[0])
1417 /* Num_matched will _always_ be atleast 1 since the initial
1418 * user-typed string is always stored */
1419 if (numtabs == 1 && Num_matched == 2)
1420 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1421 else if (numtabs > 1 && Num_matched > 2)
1422 /* cycle thru all the matches */
1423 snprintf (Completed, sizeof(Completed), "%s",
1424 Matches[(numtabs - 2) % Num_matched]);
1426 /* return the completed command */
1427 m_strcpy(buffer, len - spaces, Completed);
1429 else if (!m_strncmp(buffer, "set", 3)
1430 || !m_strncmp(buffer, "unset", 5)
1431 || !m_strncmp(buffer, "reset", 5)
1432 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
1433 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
1436 /* loop through all the possible prefixes (no, inv, ...) */
1437 if (!m_strncmp(buffer, "set", 3)) {
1438 for (num = 0; prefixes[num]; num++) {
1439 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
1440 pt += m_strlen(prefixes[num]);
1446 /* first TAB. Collect all the matches */
1449 m_strcpy(User_typed, sizeof(User_typed), pt);
1450 p_clear(Matches, countof(Matches));
1451 p_clear(Completed, countof(Completed));
1452 for (num = 0; MuttVars[num].option; num++)
1453 candidate(Completed, User_typed, MuttVars[num].option,
1455 Matches[Num_matched++] = User_typed;
1457 /* All matches are stored. Longest non-ambiguous string is ""
1458 * i.e. dont change 'buffer'. Fake successful return this time */
1459 if (User_typed[0] == 0)
1463 if (Completed[0] == 0 && User_typed[0])
1466 /* Num_matched will _always_ be atleast 1 since the initial
1467 * user-typed string is always stored */
1468 if (numtabs == 1 && Num_matched == 2)
1469 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1470 else if (numtabs > 1 && Num_matched > 2)
1471 /* cycle thru all the matches */
1472 snprintf (Completed, sizeof(Completed), "%s",
1473 Matches[(numtabs - 2) % Num_matched]);
1475 m_strcpy(pt, buffer + len - pt - spaces, Completed);
1477 else if (!m_strncmp(buffer, "exec", 4)) {
1478 struct binding_t *menu = km_get_table (CurrentMenu);
1480 if (!menu && CurrentMenu != MENU_PAGER)
1484 /* first TAB. Collect all the matches */
1487 m_strcpy(User_typed, sizeof(User_typed), pt);
1488 p_clear(Matches, countof(Matches));
1489 p_clear(Completed, countof(Completed));
1490 for (num = 0; menu[num].name; num++)
1491 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
1492 /* try the generic menu */
1493 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1495 for (num = 0; menu[num].name; num++)
1496 candidate (Completed, User_typed, menu[num].name,
1499 Matches[Num_matched++] = User_typed;
1501 /* All matches are stored. Longest non-ambiguous string is ""
1502 * i.e. dont change 'buffer'. Fake successful return this time */
1503 if (User_typed[0] == 0)
1507 if (Completed[0] == 0 && User_typed[0])
1510 /* Num_matched will _always_ be atleast 1 since the initial
1511 * user-typed string is always stored */
1512 if (numtabs == 1 && Num_matched == 2)
1513 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1514 else if (numtabs > 1 && Num_matched > 2)
1515 /* cycle thru all the matches */
1516 snprintf (Completed, sizeof(Completed), "%s",
1517 Matches[(numtabs - 2) % Num_matched]);
1519 m_strcpy(pt, buffer + len - pt - spaces, Completed);
1527 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
1529 char var[STRING], *pt = buffer;
1531 struct option_t* option = NULL;
1536 buffer = vskipspaces(buffer);
1537 spaces = buffer - pt;
1539 pt = buffer + pos - spaces;
1540 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1542 pt++; /* move past the space */
1543 if (*pt == '=') /* abort if no var before the '=' */
1546 if (m_strncmp(buffer, "set", 3) == 0) {
1547 m_strcpy(var, sizeof(var), pt);
1548 /* ignore the trailing '=' when comparing */
1549 var[m_strlen(var) - 1] = 0;
1550 if (!(option = hash_find (ConfigOptions, var)))
1551 return 0; /* no such variable. */
1553 char tmp[LONG_STRING], tmp2[LONG_STRING];
1555 ssize_t dlen = buffer + len - pt - spaces;
1556 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1560 if ((DTYPE (option->type) == DT_STR) ||
1561 (DTYPE (option->type) == DT_PATH) ||
1562 (DTYPE (option->type) == DT_RX)) {
1563 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
1564 if (DTYPE (option->type) == DT_PATH)
1565 mutt_pretty_mailbox (tmp);
1567 else if (DTYPE (option->type) == DT_QUAD)
1568 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
1569 else if (DTYPE (option->type) == DT_NUM)
1570 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
1571 else if (DTYPE (option->type) == DT_SORT) {
1572 const struct mapping_t *map;
1575 switch (option->type & DT_SUBTYPE_MASK) {
1577 map = SortAliasMethods;
1579 case DT_SORT_BROWSER:
1580 map = SortBrowserMethods;
1583 map = SortKeyMethods;
1589 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
1590 snprintf(tmp, sizeof(tmp), "%s%s%s",
1591 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
1592 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
1594 else if (DTYPE (option->type) == DT_MAGIC) {
1596 switch (DefaultMagic) {
1609 m_strcpy(tmp, sizeof(tmp), p);
1611 else if (DTYPE (option->type) == DT_BOOL)
1612 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
1616 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
1617 if (*s == '\\' || *s == '"')
1623 m_strcpy(tmp, sizeof(tmp), pt);
1624 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
1632 /* Implement the -Q command line flag */
1633 int mutt_query_variables (string_list_t * queries)
1637 char errbuff[STRING];
1638 char command[STRING];
1646 err.dsize = sizeof(errbuff);
1648 for (p = queries; p; p = p->next) {
1649 snprintf (command, sizeof(command), "set ?%s\n", p->data);
1650 if (mutt_parse_rc_line (command, &token, &err) == -1) {
1651 fprintf (stderr, "%s\n", err.data);
1652 p_delete(&token.data);
1655 printf ("%s\n", err.data);
1658 p_delete(&token.data);
1662 static int mutt_execute_commands (string_list_t * p)
1665 char errstr[STRING];
1669 err.dsize = sizeof(errstr);
1671 for (; p; p = p->next) {
1672 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
1673 fprintf (stderr, _("Error in command line: %s\n"), err.data);
1674 p_delete(&token.data);
1678 p_delete(&token.data);
1682 void mutt_init (int skip_sys_rc, string_list_t * commands)
1686 char buffer[STRING], error[STRING];
1687 int default_rc = 0, need_pause = 0;
1693 err.dsize = sizeof(error);
1695 ConfigOptions = hash_new (sizeof(MuttVars) * 2, 0);
1696 for (i = 0; MuttVars[i].option; i++) {
1697 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i]);
1701 * XXX - use something even more difficult to predict?
1703 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
1704 "\033]9;%ld\a", (long) time (NULL));
1707 /* Get some information about the user */
1708 if ((pw = getpwuid (getuid ()))) {
1710 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, mod_core.gecos_mask);
1711 Realname = m_strdup(rnbuf);
1719 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
1721 fgets (buffer, sizeof(buffer), f);
1722 p = vskipspaces(buffer);
1724 while (*q && !isspace(*q))
1727 NewsServer = m_strdup(p);
1731 if ((p = getenv ("NNTPSERVER")))
1732 NewsServer = m_strdup(p);
1735 if ((p = getenv("MAIL") ?: getenv("MAILDIR"))) {
1736 Spoolfile = m_strdup(p);
1738 mutt_concat_path(buffer, sizeof(buffer), NONULL(mod_core.homedir), MAILPATH);
1739 Spoolfile = m_strdup(buffer);
1742 if ((p = getenv ("REPLYTO")) != NULL) {
1745 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
1748 buf.data = buf.dptr = buffer;
1749 buf.dsize = m_strlen(buffer);
1752 parse_my_hdr (&token, &buf, 0, &err);
1753 p_delete(&token.data);
1756 /* Set standard defaults */
1757 hash_map (ConfigOptions, mutt_set_default, 0);
1758 hash_map (ConfigOptions, mutt_restore_default, 0);
1760 CurrentMenu = MENU_MAIN;
1763 /* Unset suspend by default if we're the session leader */
1764 if (getsid (0) == getpid ())
1765 unset_option (OPTSUSPEND);
1768 mutt_init_history ();
1771 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL(mod_core.homedir));
1772 if (access (buffer, F_OK) == -1)
1773 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
1774 NONULL(mod_core.homedir));
1777 Muttrc = m_strdup(buffer);
1780 m_strcpy(buffer, sizeof(buffer), Muttrc);
1782 mutt_expand_path (buffer, sizeof(buffer));
1783 Muttrc = m_strdup(buffer);
1786 /* Process the global rc file if it exists and the user hasn't explicity
1787 requested not to via "-n". */
1789 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
1791 if (access (buffer, F_OK) == -1)
1792 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
1793 if (access (buffer, F_OK) == -1)
1794 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
1796 if (access (buffer, F_OK) == -1)
1797 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
1798 if (access (buffer, F_OK) != -1) {
1799 if (source_rc (buffer, &err) != 0) {
1800 fputs (err.data, stderr);
1801 fputc ('\n', stderr);
1807 /* Read the user's initialization file. */
1808 if (access (Muttrc, F_OK) != -1) {
1809 if (!option (OPTNOCURSES))
1811 if (source_rc (Muttrc, &err) != 0) {
1812 fputs (err.data, stderr);
1813 fputc ('\n', stderr);
1817 else if (!default_rc) {
1818 /* file specified by -F does not exist */
1819 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
1820 mutt_endwin (buffer);
1825 snprintf(buffer, sizeof(buffer), "%s/.madmutt.lua", NONULL(mod_core.homedir));
1826 if (access(buffer, F_OK) < 0)
1827 snprintf(buffer, sizeof(buffer), "%s/.madmutt/cfg.lua", NONULL(mod_core.homedir));
1828 if (!access(buffer, F_OK)) {
1829 need_pause = luaM_wrap(mutt_error, luaM_dofile(buffer));
1833 if (mutt_execute_commands (commands) != 0)
1836 /* warn about synonym variables */
1840 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
1842 for (syn = Synonyms; syn; syn = syn->next) {
1843 fprintf(stderr, "$%s ($%s should be used) (%s:%d)\n",
1844 syn->o ? NONULL(syn->o->option) : "",
1845 syn->n ? NONULL(syn->n->option) : "",
1846 NONULL(syn->f), syn->l);
1848 fprintf (stderr, _("Warning: synonym variables are scheduled"
1849 " for removal.\n"));
1850 syn_list_wipe(&Synonyms);
1854 if (need_pause && !option (OPTNOCURSES)) {
1855 if (mutt_any_key_to_continue (NULL) == -1)
1860 int mutt_get_hook_type (const char *name)
1862 struct command_t *c;
1864 for (c = Commands; c->name; c++)
1865 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
1870 /* dump out the value of all the variables we have */
1871 int mutt_dump_variables (int full) {
1874 /* get all non-synonyms into list... */
1875 for (i = 0; MuttVars[i].option; i++) {
1876 struct option_t *option = MuttVars + i;
1877 char buf[LONG_STRING];
1880 mutt_option_value(option->option, buf, sizeof(buf));
1881 if (!m_strcmp(buf, option->init))
1886 FuncTable[DTYPE(option->type)].opt_tostr(buf, sizeof(buf), option);
1887 printf ("%s\n", buf);
1890 printf ("\n# vi""m:set ft=muttrc:\n");