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/lib-ui.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 ();
324 void (*opt_tostr) (char* dst, ssize_t dstlen, struct option_t* option);
325 int (*opt_fromstr) (struct option_t* dst, const char* val,
326 char* errbuf, ssize_t errlen);
328 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
329 { DT_BOOL, bool_to_string, bool_from_string },
330 { DT_NUM, num_to_string, num_from_string },
331 { DT_STR, str_to_string, str_from_string },
332 { DT_PATH, str_to_string, path_from_string },
333 { DT_QUAD, quad_to_string, quad_from_string },
334 { DT_SORT, sort_to_string, sort_from_string },
335 { DT_RX, rx_to_string, rx_from_string },
339 int mutt_option_value (const char* val, char* dst, ssize_t dstlen) {
340 struct option_t* option = NULL;
341 char* tmp = NULL, *t = NULL;
344 if (!(option = hash_find (ConfigOptions, val))) {
348 tmp = p_new(char, dstlen+1);
349 FuncTable[DTYPE(option->type)].opt_tostr (tmp, dstlen, option);
351 /* as we get things of type $var=value and don't want to bloat the
352 * above "just" for expansion, we do the stripping here */
353 t = strchr (tmp, '=');
357 if (t[l-1] == '"' && *t == '"') {
362 memcpy (dst, t, l+1);
368 static void toggle_quadoption (int opt)
371 int b = (opt % 4) * 2;
373 QuadOptions[n] ^= (1 << b);
376 void set_quadoption (int opt, int flag)
379 int b = (opt % 4) * 2;
381 QuadOptions[n] &= ~(0x3 << b);
382 QuadOptions[n] |= (flag & 0x3) << b;
385 int quadoption (int opt)
388 int b = (opt % 4) * 2;
390 return (QuadOptions[n] >> b) & 0x3;
393 int query_quadoption2(int v, const char *prompt)
401 return mutt_yesorno(prompt, (v == M_ASKYES));
405 int query_quadoption (int opt, const char *prompt)
407 int v = quadoption (opt);
415 return mutt_yesorno(prompt, (v == M_ASKYES));
419 /* always wise to do what someone else did before */
420 static void _attachments_clean (void) {
422 if (Context && Context->msgcount) {
423 for (i = 0; i < Context->msgcount; i++)
424 Context->hdrs[i]->attach_valid = 0;
428 static int parse_attach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
429 BUFFER *err __attribute__ ((unused))) {
431 string_list_t *listp, *lastp;
436 /* Find the last item in the list that data points to. */
438 for (listp = *ldata; listp; listp = listp->next) {
439 a = (ATTACH_MATCH *)listp->data;
444 mutt_extract_token (buf, s, 0);
446 if (!buf->data || *buf->data == '\0')
449 a = p_new(ATTACH_MATCH, 1);
451 /* some cheap hacks that I expect to remove */
452 if (!m_strcasecmp(buf->data, "any"))
453 a->major = m_strdup("*/.*");
454 else if (!m_strcasecmp(buf->data, "none"))
455 a->major = m_strdup("cheap_hack/this_should_never_match");
457 a->major = m_strdup(buf->data);
459 if ((p = strchr(a->major, '/'))) {
464 a->minor = "unknown";
467 len = m_strlen(a->minor);
468 tmpminor = p_new(char, len + 3);
469 m_strcpy(&tmpminor[1], len + 3, a->minor);
471 tmpminor[len+1] = '$';
472 tmpminor[len+2] = '\0';
474 a->major_int = mutt_check_mime_type(a->major);
475 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
479 listp = p_new(string_list_t, 1);
480 listp->data = (char *)a;
489 while (MoreArgs (s));
491 _attachments_clean();
495 static int parse_unattach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
496 BUFFER *err __attribute__ ((unused))) {
498 string_list_t *lp, *lastp, *newlp;
504 mutt_extract_token (buf, s, 0);
506 if (!m_strcasecmp(buf->data, "any"))
507 tmp = m_strdup("*/.*");
508 else if (!m_strcasecmp(buf->data, "none"))
509 tmp = m_strdup("cheap_hack/this_should_never_match");
511 tmp = m_strdup(buf->data);
513 if ((minor = strchr(tmp, '/'))) {
517 minor = m_strdup("unknown");
519 major = mutt_check_mime_type(tmp);
521 /* We must do our own walk here because string_list_remove() will only
522 * remove the string_list_t->data, not anything pointed to by the string_list_t->data. */
524 for(lp = *ldata; lp; ) {
525 a = (ATTACH_MATCH *)lp->data;
526 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
527 regfree(&a->minor_rx);
530 /* Relink backward */
532 lastp->next = lp->next;
537 p_delete(&lp->data); /* same as a */
547 while (MoreArgs (s));
550 _attachments_clean();
554 static int print_attach_list (string_list_t *lp, char op, const char *name) {
556 printf("attachments %c%s %s/%s\n", op, name,
557 ((ATTACH_MATCH *)lp->data)->major,
558 ((ATTACH_MATCH *)lp->data)->minor);
565 static int parse_attachments (BUFFER *buf, BUFFER *s,
566 unsigned long data __attribute__ ((unused)),
569 string_list_t **listp;
571 mutt_extract_token(buf, s, 0);
572 if (!buf->data || *buf->data == '\0') {
573 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
577 category = buf->data;
583 printf("\nCurrent attachments settings:\n\n");
584 print_attach_list(AttachAllow, '+', "A");
585 print_attach_list(AttachExclude, '-', "A");
586 print_attach_list(InlineAllow, '+', "I");
587 print_attach_list(InlineExclude, '-', "I");
588 set_option (OPTFORCEREDRAWINDEX);
589 set_option (OPTFORCEREDRAWPAGER);
590 mutt_any_key_to_continue (NULL);
594 if (op != '+' && op != '-') {
598 if (!m_strncasecmp(category, "attachment", strlen(category))) {
600 listp = &AttachAllow;
602 listp = &AttachExclude;
604 else if (!m_strncasecmp(category, "inline", strlen(category))) {
606 listp = &InlineAllow;
608 listp = &InlineExclude;
610 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
614 return parse_attach_list(buf, s, listp, err);
617 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
619 string_list_t **listp;
621 mutt_extract_token(buf, s, 0);
622 if (!buf->data || *buf->data == '\0') {
623 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
629 if (op != '+' && op != '-') {
633 if (!m_strncasecmp(p, "attachment", strlen(p))) {
635 listp = &AttachAllow;
637 listp = &AttachExclude;
639 else if (!m_strncasecmp(p, "inline", strlen(p))) {
641 listp = &InlineAllow;
643 listp = &InlineExclude;
646 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
650 return parse_unattach_list(buf, s, listp, err);
653 static int parse_unalias (BUFFER * buf, BUFFER * s,
654 unsigned long data __attribute__ ((unused)),
655 BUFFER * err __attribute__ ((unused)))
657 alias_t *tmp, **last;
660 mutt_extract_token (buf, s, 0);
662 if (!m_strcmp("*", buf->data) == 0) {
663 if (CurrentMenu == MENU_ALIAS) {
664 for (tmp = Aliases; tmp; tmp = tmp->next)
666 set_option(OPTFORCEREDRAWINDEX);
668 alias_list_wipe(&Aliases);
674 for (last = &Aliases; *last; last = &(*last)->next) {
675 if (!m_strcasecmp(buf->data, (*last)->name)) {
676 if (CurrentMenu == MENU_ALIAS) {
678 set_option (OPTFORCEREDRAWINDEX);
680 tmp = alias_list_pop(last);
686 } while (MoreArgs(s));
691 static int parse_alias (BUFFER * buf, BUFFER * s,
692 unsigned long data __attribute__ ((unused)),
699 m_strcpy(err->data, err->dsize, _("alias: no address"));
703 mutt_extract_token (buf, s, 0);
705 /* check to see if an alias with this name already exists */
706 for (last = &Aliases; *last; last = &(*last)->next) {
707 if (!m_strcasecmp((*last)->name, buf->data))
712 /* create a new alias */
714 (*last)->name = m_strdup(buf->data);
715 /* give the main addressbook code a chance */
716 if (CurrentMenu == MENU_ALIAS)
717 set_option (OPTMENUCALLER);
719 /* override the previous value */
720 address_list_wipe(&(*last)->addr);
721 if (CurrentMenu == MENU_ALIAS)
722 set_option (OPTFORCEREDRAWINDEX);
725 mutt_extract_token(buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE);
726 (*last)->addr = mutt_parse_adrlist((*last)->addr, buf->data);
727 if (mutt_addrlist_to_idna((*last)->addr, &estr)) {
728 snprintf (err->data, err->dsize,
729 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, (*last)->name);
738 parse_unmy_hdr(BUFFER * buf, BUFFER * s,
739 unsigned long data __attribute__ ((unused)),
740 BUFFER * err __attribute__ ((unused)))
743 mutt_extract_token (buf, s, 0);
745 if (!m_strcmp("*", buf->data)) {
746 string_list_wipe(&UserHeader);
748 string_list_t **last = &UserHeader;
749 ssize_t l = m_strlen(buf->data);
751 if (buf->data[l - 1] == ':')
755 if (!ascii_strncasecmp(buf->data, (*last)->data, l)
756 && (*last)->data[l] == ':')
758 string_list_t *tmp = string_list_pop(last);
759 string_item_delete(&tmp);
761 last = &(*last)->next;
765 } while (MoreArgs(s));
770 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
777 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
778 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
779 m_strcpy(err->data, err->dsize, _("invalid header field"));
782 keylen = p - buf->data + 1;
785 for (tmp = UserHeader;; tmp = tmp->next) {
786 /* see if there is already a field by this name */
787 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
788 /* replace the old value */
789 p_delete(&tmp->data);
790 tmp->data = buf->data;
797 tmp->next = string_item_new();
801 tmp = string_item_new();
804 tmp->data = buf->data;
810 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
811 char* errbuf, ssize_t errlen) {
814 if (m_strncmp("reverse-", s, 8) == 0) {
816 flags = SORT_REVERSE;
819 if (m_strncmp("last-", s, 5) == 0) {
824 if ((i = mutt_getvaluebyname (s, map)) == -1) {
826 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
830 *((short*) dst->data) = i | flags;
834 /* if additional data more == 1, we want to resolve synonyms */
835 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
837 char buf[LONG_STRING];
838 struct option_t *ptr = p;
840 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_fromstr)
843 mutt_option_value(ptr->option, buf, sizeof(buf));
844 if (m_strlen(ptr->init) == 0 && *buf)
845 ptr->init = m_strdup(buf);
848 static int init_expand (char** dst, struct option_t* src) {
854 if (DTYPE(src->type) == DT_STR || DTYPE(src->type) == DT_PATH) {
855 /* only expand for string as it's the only place where
856 * we want to expand vars right now */
857 if (src->init && *src->init) {
860 len = m_strlen(src->init) + 2;
861 in.data = p_new(char, len + 1);
862 snprintf (in.data, len, "\"%s\"", src->init);
865 mutt_extract_token (&token, &in, 0);
866 if (token.data && *token.data)
867 *dst = m_strdup(token.data);
871 p_delete(&token.data);
875 /* for non-string: take value as is */
876 *dst = m_strdup(src->init);
880 /* if additional data more == 1, we want to resolve synonyms */
881 static void mutt_restore_default (const char* name __attribute__ ((unused)),
882 void* p, unsigned long more) {
884 struct option_t* ptr = (struct option_t*) p;
889 if (FuncTable[DTYPE (ptr->type)].opt_fromstr) {
890 init_expand (&init, ptr);
891 if (!FuncTable[DTYPE (ptr->type)].opt_fromstr (ptr, init, errbuf,
893 if (!option (OPTNOCURSES))
895 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
896 "Please report this error: \"%s\"\n"),
897 ptr->option, NONULL (init), errbuf);
903 set_option (OPTFORCEREDRAWINDEX);
904 set_option (OPTFORCEREDRAWPAGER);
905 set_option (OPTSORTSUBTHREADS);
906 set_option (OPTNEEDRESORT);
907 set_option (OPTRESORTINIT);
908 set_option (OPTREDRAWTREE);
911 static int check_num (const char* option, unsigned long p,
912 char* errbuf, ssize_t errlen) {
915 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
921 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
922 char* errbuf, ssize_t errlen) {
923 if (!check_num ("history", p, errbuf, errlen))
925 mutt_init_history ();
929 static int check_special (const char* name, unsigned long val,
930 char* errbuf, ssize_t errlen) {
933 for (i = 0; SpecialVars[i].name; i++) {
934 if (m_strcmp(SpecialVars[i].name, name) == 0) {
935 return (SpecialVars[i].check (SpecialVars[i].name,
936 val, errbuf, errlen));
942 static const struct mapping_t* get_sortmap (struct option_t* option) {
943 const struct mapping_t* map = NULL;
945 switch (option->type & DT_SUBTYPE_MASK) {
947 map = SortAliasMethods;
949 case DT_SORT_BROWSER:
950 map = SortBrowserMethods;
953 map = SortKeyMethods;
956 map = SortAuxMethods;
965 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
968 int query, unset, inv, reset, r = 0;
969 struct option_t* option = NULL;
971 while (MoreArgs (s)) {
972 /* reset state variables */
974 unset = data & M_SET_UNSET;
975 inv = data & M_SET_INV;
976 reset = data & M_SET_RESET;
978 if (*s->dptr == '?') {
982 else if (m_strncmp("no", s->dptr, 2) == 0) {
986 else if (m_strncmp("inv", s->dptr, 3) == 0) {
990 else if (*s->dptr == '&') {
995 /* get the variable name */
996 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
997 option = hash_find(ConfigOptions, tmp->data);
998 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
999 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1002 s->dptr = vskipspaces(s->dptr);
1005 if (query || unset || inv) {
1006 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1010 if (s && *s->dptr == '=') {
1011 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1015 if (!m_strcmp("all", tmp->data)) {
1016 if (CurrentMenu == MENU_PAGER) {
1017 snprintf (err->data, err->dsize, _("Not available in this menu."));
1020 hash_map (ConfigOptions, mutt_restore_default, 1);
1021 set_option (OPTFORCEREDRAWINDEX);
1022 set_option (OPTFORCEREDRAWPAGER);
1023 set_option (OPTSORTSUBTHREADS);
1024 set_option (OPTNEEDRESORT);
1025 set_option (OPTRESORTINIT);
1026 set_option (OPTREDRAWTREE);
1029 mutt_restore_default (NULL, option, 1);
1032 else if (DTYPE (option->type) == DT_BOOL) {
1033 /* XXX this currently ignores the function table
1034 * as we don't get invert and stuff into it */
1035 if (s && *s->dptr == '=') {
1036 if (unset || inv || query) {
1037 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1042 mutt_extract_token (tmp, s, 0);
1043 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1045 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1048 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1054 bool_to_string (err->data, err->dsize, option);
1059 unset_option (option->data);
1061 toggle_option (option->data);
1063 set_option (option->data);
1065 else if (DTYPE (option->type) == DT_STR ||
1066 DTYPE (option->type) == DT_PATH ||
1067 DTYPE (option->type) == DT_NUM ||
1068 DTYPE (option->type) == DT_SORT ||
1069 DTYPE (option->type) == DT_RX)
1071 /* XXX maybe we need to get unset into handlers? */
1072 if (DTYPE (option->type) == DT_STR || DTYPE (option->type) == DT_PATH) {
1074 p_delete((void **)(void *)&option->data);
1079 if (query || *s->dptr != '=') {
1080 FuncTable[DTYPE (option->type)].opt_tostr
1081 (err->data, err->dsize, option);
1086 mutt_extract_token (tmp, s, 0);
1087 if (!FuncTable[DTYPE (option->type)].opt_fromstr
1088 (option, tmp->data, err->data, err->dsize))
1091 else if (DTYPE (option->type) == DT_QUAD) {
1094 quad_to_string (err->data, err->dsize, option);
1098 if (*s->dptr == '=') {
1100 mutt_extract_token (tmp, s, 0);
1101 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1102 set_quadoption (option->data, M_YES);
1103 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1104 set_quadoption (option->data, M_NO);
1105 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1106 set_quadoption (option->data, M_ASKYES);
1107 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1108 set_quadoption (option->data, M_ASKNO);
1110 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1111 tmp->data, option->option);
1118 toggle_quadoption (option->data);
1120 set_quadoption (option->data, M_NO);
1122 set_quadoption (option->data, M_YES);
1126 snprintf (err->data, err->dsize, _("%s: unknown type"),
1132 set_option (OPTFORCEREDRAWINDEX);
1133 set_option (OPTFORCEREDRAWPAGER);
1134 set_option (OPTSORTSUBTHREADS);
1135 set_option (OPTNEEDRESORT);
1136 set_option (OPTRESORTINIT);
1137 set_option (OPTREDRAWTREE);
1144 /* reads the specified initialization file. returns -1 if errors were found
1145 so that we can pause to let the user know... */
1146 static int source_rc (const char *rcfile, BUFFER * err)
1149 int line = 0, rc = 0, conv = 0;
1151 char *linebuf = NULL;
1152 char *currentline = NULL;
1156 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1157 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1162 while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
1163 conv = ConfigCharset && (*ConfigCharset) && mod_cset.charset;
1165 currentline = m_strdup(linebuf);
1168 mutt_convert_string (¤tline, ConfigCharset, mod_cset.charset, 0);
1171 currentline = linebuf;
1176 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1177 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1178 if (--rc < -MAXERRS) {
1180 p_delete(¤tline);
1189 p_delete(¤tline);
1191 p_delete(&token.data);
1195 mutt_wait_filter (pid);
1197 /* the muttrc source keyword */
1198 snprintf (err->data, err->dsize,
1199 rc >= -MAXERRS ? _("source: errors in %s")
1200 : _("source: reading aborted due too many errors in %s"),
1209 static int parse_source (BUFFER * tmp, BUFFER * s,
1210 unsigned long data __attribute__ ((unused)),
1213 char path[_POSIX_PATH_MAX];
1217 if (mutt_extract_token (tmp, s, 0) != 0) {
1218 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1222 m_strcpy(path, sizeof(path), tmp->data);
1223 mutt_expand_path (path, sizeof(path));
1225 rc += source_rc (path, err);
1227 while (MoreArgs (s));
1229 return (rc < 0) ? -1 : 0;
1232 /* line command to execute
1234 token scratch buffer to be used by parser. caller should free
1235 token->data when finished. the reason for this variable is
1236 to avoid having to allocate and deallocate a lot of memory
1237 if we are parsing many lines. the caller can pass in the
1238 memory to use, which avoids having to create new space for
1239 every call to this function.
1241 err where to write error messages */
1242 int mutt_parse_rc_line (const char *line, BUFFER * token, BUFFER * err)
1248 expn.data = expn.dptr = line;
1249 expn.dsize = m_strlen(line);
1253 expn.dptr = vskipspaces(expn.dptr);
1254 while (*expn.dptr) {
1255 if (*expn.dptr == '#')
1256 break; /* rest of line is a comment */
1257 if (*expn.dptr == ';') {
1261 mutt_extract_token (token, &expn, 0);
1262 for (i = 0; Commands[i].name; i++) {
1263 if (!m_strcmp(token->data, Commands[i].name)) {
1264 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1269 if (!Commands[i].name) {
1270 snprintf (err->data, err->dsize, _("%s: unknown command"),
1271 NONULL (token->data));
1278 p_delete(&expn.data);
1283 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
1284 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
1285 /* initial string that starts completion. No telling how much crap
1286 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1287 char User_typed[LONG_STRING] = { 0 };
1289 int Num_matched = 0; /* Number of matches for completion */
1290 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1291 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1293 /* helper function for completion. Changes the dest buffer if
1294 necessary/possible to aid completion.
1295 dest == completion result gets here.
1296 src == candidate for completion.
1297 try == user entered data for completion.
1298 len == length of dest buffer.
1300 static void candidate (char *dest, char *try, const char *src, int len)
1304 if (strstr (src, try) == src) {
1305 Matches[Num_matched++] = src;
1307 m_strcpy(dest, len, src);
1309 for (l = 0; src[l] && src[l] == dest[l]; l++);
1315 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
1319 int spaces; /* keep track of the number of leading spaces on the line */
1321 buffer = vskipspaces(buffer);
1322 spaces = buffer - pt;
1324 pt = buffer + pos - spaces;
1325 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1328 if (pt == buffer) { /* complete cmd */
1329 /* first TAB. Collect all the matches */
1332 m_strcpy(User_typed, sizeof(User_typed), pt);
1333 p_clear(Matches, countof(Matches));
1334 p_clear(Completed, countof(Completed));
1335 for (num = 0; Commands[num].name; num++)
1336 candidate (Completed, User_typed, Commands[num].name,
1338 Matches[Num_matched++] = User_typed;
1340 /* All matches are stored. Longest non-ambiguous string is ""
1341 * i.e. dont change 'buffer'. Fake successful return this time */
1342 if (User_typed[0] == 0)
1346 if (Completed[0] == 0 && User_typed[0])
1349 /* Num_matched will _always_ be atleast 1 since the initial
1350 * user-typed string is always stored */
1351 if (numtabs == 1 && Num_matched == 2)
1352 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1353 else if (numtabs > 1 && Num_matched > 2)
1354 /* cycle thru all the matches */
1355 snprintf (Completed, sizeof(Completed), "%s",
1356 Matches[(numtabs - 2) % Num_matched]);
1358 /* return the completed command */
1359 m_strcpy(buffer, len - spaces, Completed);
1361 else if (!m_strncmp(buffer, "set", 3)
1362 || !m_strncmp(buffer, "unset", 5)
1363 || !m_strncmp(buffer, "reset", 5)
1364 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
1365 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
1368 /* loop through all the possible prefixes (no, inv, ...) */
1369 if (!m_strncmp(buffer, "set", 3)) {
1370 for (num = 0; prefixes[num]; num++) {
1371 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
1372 pt += m_strlen(prefixes[num]);
1378 /* first TAB. Collect all the matches */
1381 m_strcpy(User_typed, sizeof(User_typed), pt);
1382 p_clear(Matches, countof(Matches));
1383 p_clear(Completed, countof(Completed));
1384 for (num = 0; MuttVars[num].option; num++)
1385 candidate(Completed, User_typed, MuttVars[num].option,
1387 Matches[Num_matched++] = User_typed;
1389 /* All matches are stored. Longest non-ambiguous string is ""
1390 * i.e. dont change 'buffer'. Fake successful return this time */
1391 if (User_typed[0] == 0)
1395 if (Completed[0] == 0 && User_typed[0])
1398 /* Num_matched will _always_ be atleast 1 since the initial
1399 * user-typed string is always stored */
1400 if (numtabs == 1 && Num_matched == 2)
1401 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1402 else if (numtabs > 1 && Num_matched > 2)
1403 /* cycle thru all the matches */
1404 snprintf (Completed, sizeof(Completed), "%s",
1405 Matches[(numtabs - 2) % Num_matched]);
1407 m_strcpy(pt, buffer + len - pt - spaces, Completed);
1409 else if (!m_strncmp(buffer, "exec", 4)) {
1410 struct binding_t *menu = km_get_table (CurrentMenu);
1412 if (!menu && CurrentMenu != MENU_PAGER)
1416 /* first TAB. Collect all the matches */
1419 m_strcpy(User_typed, sizeof(User_typed), pt);
1420 p_clear(Matches, countof(Matches));
1421 p_clear(Completed, countof(Completed));
1422 for (num = 0; menu[num].name; num++)
1423 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
1424 /* try the generic menu */
1425 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1427 for (num = 0; menu[num].name; num++)
1428 candidate (Completed, User_typed, menu[num].name,
1431 Matches[Num_matched++] = User_typed;
1433 /* All matches are stored. Longest non-ambiguous string is ""
1434 * i.e. dont change 'buffer'. Fake successful return this time */
1435 if (User_typed[0] == 0)
1439 if (Completed[0] == 0 && User_typed[0])
1442 /* Num_matched will _always_ be atleast 1 since the initial
1443 * user-typed string is always stored */
1444 if (numtabs == 1 && Num_matched == 2)
1445 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1446 else if (numtabs > 1 && Num_matched > 2)
1447 /* cycle thru all the matches */
1448 snprintf (Completed, sizeof(Completed), "%s",
1449 Matches[(numtabs - 2) % Num_matched]);
1451 m_strcpy(pt, buffer + len - pt - spaces, Completed);
1459 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
1461 char var[STRING], *pt = buffer;
1463 struct option_t* option = NULL;
1468 buffer = vskipspaces(buffer);
1469 spaces = buffer - pt;
1471 pt = buffer + pos - spaces;
1472 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1474 pt++; /* move past the space */
1475 if (*pt == '=') /* abort if no var before the '=' */
1478 if (m_strncmp(buffer, "set", 3) == 0) {
1479 m_strcpy(var, sizeof(var), pt);
1480 /* ignore the trailing '=' when comparing */
1481 var[m_strlen(var) - 1] = 0;
1482 if (!(option = hash_find (ConfigOptions, var)))
1483 return 0; /* no such variable. */
1485 char tmp[LONG_STRING], tmp2[LONG_STRING];
1487 ssize_t dlen = buffer + len - pt - spaces;
1488 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1492 if ((DTYPE (option->type) == DT_STR) ||
1493 (DTYPE (option->type) == DT_PATH) ||
1494 (DTYPE (option->type) == DT_RX)) {
1495 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
1496 if (DTYPE (option->type) == DT_PATH)
1497 mutt_pretty_mailbox (tmp);
1499 else if (DTYPE (option->type) == DT_QUAD)
1500 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
1501 else if (DTYPE (option->type) == DT_NUM)
1502 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
1503 else if (DTYPE (option->type) == DT_SORT) {
1504 const struct mapping_t *map;
1507 switch (option->type & DT_SUBTYPE_MASK) {
1509 map = SortAliasMethods;
1511 case DT_SORT_BROWSER:
1512 map = SortBrowserMethods;
1515 map = SortKeyMethods;
1521 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
1522 snprintf(tmp, sizeof(tmp), "%s%s%s",
1523 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
1524 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
1526 else if (DTYPE (option->type) == DT_BOOL)
1527 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
1531 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
1532 if (*s == '\\' || *s == '"')
1538 m_strcpy(tmp, sizeof(tmp), pt);
1539 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
1547 static int mutt_execute_commands (string_list_t * p)
1550 char errstr[STRING];
1554 err.dsize = sizeof(errstr);
1556 for (; p; p = p->next) {
1557 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
1558 fprintf (stderr, _("Error in command line: %s\n"), err.data);
1559 p_delete(&token.data);
1563 p_delete(&token.data);
1567 void mutt_init (int skip_sys_rc, string_list_t * commands)
1571 char buffer[STRING], error[STRING];
1572 int default_rc = 0, need_pause = 0;
1578 err.dsize = sizeof(error);
1580 ConfigOptions = hash_new (sizeof(MuttVars) * 2, 0);
1581 for (i = 0; MuttVars[i].option; i++) {
1582 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i]);
1586 * XXX - use something even more difficult to predict?
1588 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
1589 "\033]9;%ld\a", (long) time (NULL));
1592 /* Get some information about the user */
1593 if ((pw = getpwuid (getuid ()))) {
1595 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, mod_core.gecos_mask);
1596 Realname = m_strdup(rnbuf);
1599 if ((p = getenv("MAIL") ?: getenv("MAILDIR"))) {
1600 Spoolfile = m_strdup(p);
1602 mutt_concat_path(buffer, sizeof(buffer), NONULL(mod_core.homedir), MAILPATH);
1603 Spoolfile = m_strdup(buffer);
1606 if ((p = getenv ("REPLYTO")) != NULL) {
1609 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
1612 buf.data = buf.dptr = buffer;
1613 buf.dsize = m_strlen(buffer);
1616 parse_my_hdr (&token, &buf, 0, &err);
1617 p_delete(&token.data);
1620 /* Set standard defaults */
1621 hash_map (ConfigOptions, mutt_set_default, 0);
1622 hash_map (ConfigOptions, mutt_restore_default, 0);
1624 CurrentMenu = MENU_MAIN;
1625 mutt_init_history ();
1628 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL(mod_core.homedir));
1629 if (access (buffer, F_OK) == -1)
1630 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
1631 NONULL(mod_core.homedir));
1634 Muttrc = m_strdup(buffer);
1637 m_strcpy(buffer, sizeof(buffer), Muttrc);
1639 mutt_expand_path (buffer, sizeof(buffer));
1640 Muttrc = m_strdup(buffer);
1643 /* Process the global rc file if it exists and the user hasn't explicity
1644 requested not to via "-n". */
1646 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
1648 if (access (buffer, F_OK) == -1)
1649 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
1650 if (access (buffer, F_OK) == -1)
1651 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
1653 if (access (buffer, F_OK) == -1)
1654 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
1655 if (access (buffer, F_OK) != -1) {
1656 if (source_rc (buffer, &err) != 0) {
1657 fputs (err.data, stderr);
1658 fputc ('\n', stderr);
1664 /* Read the user's initialization file. */
1665 if (access (Muttrc, F_OK) != -1) {
1666 if (!option (OPTNOCURSES))
1668 if (source_rc (Muttrc, &err) != 0) {
1669 fputs (err.data, stderr);
1670 fputc ('\n', stderr);
1674 else if (!default_rc) {
1675 /* file specified by -F does not exist */
1676 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
1677 mutt_endwin (buffer);
1682 snprintf(buffer, sizeof(buffer), "%s/.madmutt.lua", NONULL(mod_core.homedir));
1683 if (access(buffer, F_OK) < 0)
1684 snprintf(buffer, sizeof(buffer), "%s/.madmutt/cfg.lua", NONULL(mod_core.homedir));
1685 if (!access(buffer, F_OK)) {
1686 need_pause = luaM_wrap(mutt_error, luaM_dofile(buffer));
1690 if (mutt_execute_commands (commands) != 0)
1693 /* warn about synonym variables */
1697 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
1699 for (syn = Synonyms; syn; syn = syn->next) {
1700 fprintf(stderr, "$%s ($%s should be used) (%s:%d)\n",
1701 syn->o ? NONULL(syn->o->option) : "",
1702 syn->n ? NONULL(syn->n->option) : "",
1703 NONULL(syn->f), syn->l);
1705 fprintf (stderr, _("Warning: synonym variables are scheduled"
1706 " for removal.\n"));
1707 syn_list_wipe(&Synonyms);
1711 if (need_pause && !option (OPTNOCURSES)) {
1712 if (mutt_any_key_to_continue (NULL) == -1)