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_dsn_return (const char* option, unsigned long val,
73 char* errbuf, ssize_t errlen);
74 static int check_dsn_notify (const char* option, unsigned long val,
75 char* errbuf, ssize_t errlen);
76 static int check_history (const char* option, unsigned long val,
77 char* errbuf, ssize_t errlen);
78 /* this checks that numbers are >= 0 */
79 static int check_num (const char* option, unsigned long val,
80 char* errbuf, ssize_t errlen);
82 /* use this to check only */
83 static int check_special (const char* option, unsigned long val,
84 char* errbuf, ssize_t errlen);
86 /* variable <-> sanity check function mappings
87 * when changing these, make sure the proper _from_string handler
92 int (*check) (const char* option, unsigned long val,
93 char* errbuf, ssize_t errlen);
95 { "dsn_notify", check_dsn_notify },
96 { "dsn_return", check_dsn_return },
97 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
98 { "smtp_use_tls", mutt_libesmtp_check_usetls },
100 { "history", check_history },
101 { "pager_index_lines", check_num },
106 /* protos for config type handles: convert value to string */
107 static void bool_to_string (char* dst, ssize_t dstlen, struct option_t* option);
108 static void num_to_string (char* dst, ssize_t dstlen, struct option_t* option);
109 static void str_to_string (char* dst, ssize_t dstlen, struct option_t* option);
110 static void quad_to_string (char* dst, ssize_t dstlen, struct option_t* option);
111 static void sort_to_string (char* dst, ssize_t dstlen, struct option_t* option);
112 static void rx_to_string (char* dst, ssize_t dstlen, struct option_t* option);
113 static void magic_to_string (char* dst, ssize_t dstlen, struct option_t* option);
114 static void addr_to_string (char* dst, ssize_t dstlen, struct option_t* option);
116 /* protos for config type handles: convert to value from string */
117 static int bool_from_string (struct option_t* dst, const char* val,
118 char* errbuf, ssize_t errlen);
119 static int num_from_string (struct option_t* dst, const char* val,
120 char* errbuf, ssize_t errlen);
121 static int str_from_string (struct option_t* dst, const char* val,
122 char* errbuf, ssize_t errlen);
123 static int path_from_string (struct option_t* dst, const char* val,
124 char* errbuf, ssize_t errlen);
125 static int quad_from_string (struct option_t* dst, const char* val,
126 char* errbuf, ssize_t errlen);
127 static int sort_from_string (struct option_t* dst, const char* val,
128 char* errbuf, ssize_t errlen);
129 static int rx_from_string (struct option_t* dst, const char* val,
130 char* errbuf, ssize_t errlen);
131 static int magic_from_string (struct option_t* dst, const char* val,
132 char* errbuf, ssize_t errlen);
133 static int addr_from_string (struct option_t* dst, const char* val,
134 char* errbuf, ssize_t errlen);
138 void (*opt_tostr) (char* dst, ssize_t dstlen, struct option_t* option);
139 int (*opt_fromstr) (struct option_t* dst, const char* val,
140 char* errbuf, ssize_t errlen);
142 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
143 { DT_BOOL, bool_to_string, bool_from_string },
144 { DT_NUM, num_to_string, num_from_string },
145 { DT_STR, str_to_string, str_from_string },
146 { DT_PATH, str_to_string, path_from_string },
147 { DT_QUAD, quad_to_string, quad_from_string },
148 { DT_SORT, sort_to_string, sort_from_string },
149 { DT_RX, rx_to_string, rx_from_string },
150 { DT_MAGIC, magic_to_string, magic_from_string },
151 /* synonyms should be resolved already so we don't need this
152 * but must define it as DT_ is used for indexing */
153 { DT_SYN, NULL, NULL },
154 { DT_ADDR, addr_to_string, addr_from_string },
157 static void bool_to_string (char* dst, ssize_t dstlen,
158 struct option_t* option) {
159 snprintf (dst, dstlen, "%s=%s", option->option,
160 option (option->data) ? "yes" : "no");
163 static int bool_from_string (struct option_t* dst, const char* val,
164 char* errbuf __attribute__ ((unused)),
165 ssize_t errlen __attribute__ ((unused))) {
170 if (ascii_strncasecmp (val, "yes", 3) == 0)
172 else if (ascii_strncasecmp (val, "no", 2) == 0)
178 set_option (dst->data);
180 unset_option (dst->data);
184 static void num_to_string (char* dst, ssize_t dstlen,
185 struct option_t* option) {
187 const char* fmt = (m_strcmp(option->option, "umask") == 0) ?
189 snprintf (dst, dstlen, fmt, option->option,
190 *((short*) option->data));
193 static int num_from_string (struct option_t* dst, const char* val,
194 char* errbuf, ssize_t errlen) {
195 int num = 0, old = 0;
201 num = strtol (val, &t, 0);
203 if (m_strisempty(val) || *t || (short) num != num) {
205 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
211 /* just temporarily accept new val so that check_special for
212 * $history already has it when doing history's init() */
213 old = *((short*) dst->data);
214 *((short*) dst->data) = (short) num;
216 if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
217 *((short*) dst->data) = old;
224 static void str_to_string (char* dst, ssize_t dstlen,
225 struct option_t* option) {
226 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
227 NONULL (*((char**) option->data)));
230 static int path_from_string (struct option_t* dst, const char* val,
231 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
232 char path[_POSIX_PATH_MAX];
237 if (m_strisempty(val)) {
238 p_delete((char**) dst->data);
243 m_strcpy(path, sizeof(path), val);
244 mutt_expand_path (path, sizeof(path));
245 m_strreplace((char **) dst->data, path);
249 static int str_from_string (struct option_t* dst, const char* val,
250 char* errbuf, ssize_t errlen) {
254 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
257 m_strreplace((char**) dst->data, val);
261 static void quad_to_string (char* dst, ssize_t dstlen,
262 struct option_t* option) {
263 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
264 snprintf (dst, dstlen, "%s=%s", option->option,
265 vals[quadoption (option->data)]);
268 static int quad_from_string (struct option_t* dst, const char* val,
269 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
274 if (ascii_strncasecmp (val, "yes", 3) == 0)
276 else if (ascii_strncasecmp (val, "no", 2) == 0)
278 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
280 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
286 set_quadoption (dst->data, flag);
290 static void sort_to_string (char* dst, ssize_t dstlen,
291 struct option_t* option) {
292 const struct mapping_t *map = get_sortmap (option);
293 const char *p = NULL;
296 snprintf (dst, sizeof(dst), "%s=unknown", option->option);
300 p = mutt_getnamebyvalue(*((short *)option->data) & SORT_MASK, map);
302 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
303 (*((short *) option->data) & SORT_REVERSE) ?
305 (*((short *) option->data) & SORT_LAST) ? "last-" :
309 static int sort_from_string (struct option_t* dst, const char* val,
310 char* errbuf, ssize_t errlen) {
311 const struct mapping_t *map = NULL;
312 if (!(map = get_sortmap (dst))) {
314 snprintf (errbuf, errlen, _("%s: Unknown type."),
318 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
323 static void rx_to_string (char* dst, ssize_t dstlen,
324 struct option_t* option) {
325 rx_t* p = (rx_t*) option->data;
326 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
327 NONULL (p->pattern));
330 static int rx_from_string (struct option_t* dst, const char* val,
331 char* errbuf, ssize_t errlen) {
334 int flags = 0, e = 0, not = 0;
340 if (option (OPTATTACHMSG) && !m_strcmp(dst->option, "reply_regexp")) {
342 snprintf (errbuf, errlen,
343 "Operation not permitted when in attach-message mode.");
347 if (!((rx_t*) dst->data))
348 *((rx_t**) dst->data) = p_new(rx_t, 1);
350 p = (rx_t*) dst->data;
352 /* something to do? */
353 if (m_strisempty(val) || (p->pattern && m_strcmp(p->pattern, val) == 0))
356 if (m_strcmp(dst->option, "mask") != 0)
357 flags |= mutt_which_case (val);
360 if (m_strcmp(dst->option, "mask") == 0 && *s == '!') {
365 rx = p_new(regex_t, 1);
367 if ((e = REGCOMP (rx, s, flags)) != 0) {
368 regerror (e, rx, errbuf, errlen);
379 m_strreplace(&p->pattern, val);
383 if (m_strcmp(dst->option, "reply_regexp") == 0)
384 mutt_adjust_all_subjects ();
389 static void magic_to_string (char* dst, ssize_t dstlen,
390 struct option_t* option) {
391 const char* s = NULL;
392 switch (option->data) {
393 case M_MBOX: s = "mbox"; break;
394 case M_MMDF: s = "MMDF"; break;
395 case M_MH: s = "MH"; break;
396 case M_MAILDIR: s = "Maildir"; break;
397 default: s = "unknown"; break;
399 snprintf (dst, dstlen, "%s=%s", option->option, s);
402 static int magic_from_string (struct option_t* dst, const char* val,
403 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
406 if (!dst || m_strisempty(val))
408 if (ascii_strncasecmp (val, "mbox", 4) == 0)
410 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
412 else if (ascii_strncasecmp (val, "mh", 2) == 0)
414 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
420 *((short*) dst->data) = flag;
425 static void addr_to_string (char* dst, ssize_t dstlen,
426 struct option_t* option) {
429 rfc822_addrcat(s, sizeof(s), *((address_t**) option->data), 0);
430 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
433 static int addr_from_string (struct option_t* dst, const char* val,
434 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
437 address_list_wipe((address_t**) dst->data);
439 *((address_t**) dst->data) = rfc822_parse_adrlist (NULL, val);
443 int mutt_option_value (const char* val, char* dst, ssize_t dstlen) {
444 struct option_t* option = NULL;
445 char* tmp = NULL, *t = NULL;
448 if (!(option = hash_find (ConfigOptions, val))) {
452 tmp = p_new(char, dstlen+1);
453 FuncTable[DTYPE(option->type)].opt_tostr (tmp, dstlen, option);
455 /* as we get things of type $var=value and don't want to bloat the
456 * above "just" for expansion, we do the stripping here */
457 t = strchr (tmp, '=');
461 if (t[l-1] == '"' && *t == '"') {
466 memcpy (dst, t, l+1);
472 static void toggle_quadoption (int opt)
475 int b = (opt % 4) * 2;
477 QuadOptions[n] ^= (1 << b);
480 void set_quadoption (int opt, int flag)
483 int b = (opt % 4) * 2;
485 QuadOptions[n] &= ~(0x3 << b);
486 QuadOptions[n] |= (flag & 0x3) << b;
489 int quadoption (int opt)
492 int b = (opt % 4) * 2;
494 return (QuadOptions[n] >> b) & 0x3;
497 int query_quadoption2(int v, const char *prompt)
505 v = mutt_yesorno(prompt, (v == M_ASKYES));
506 CLEARLINE (LINES - 1);
511 int query_quadoption (int opt, const char *prompt)
513 int v = quadoption (opt);
521 v = mutt_yesorno (prompt, (v == M_ASKYES));
522 CLEARLINE (LINES - 1);
529 static void add_to_list(string_list_t **list, const char *str)
531 /* don't add a NULL or empty string to the list */
532 if (m_strisempty(str))
535 /* check to make sure the item is not already on this list */
537 if (!ascii_strcasecmp(str, (*list)->data))
539 list = &(*list)->next;
542 *list = p_new(string_list_t, 1);
543 (*list)->data = m_strdup(str);
547 add_to_rx_list(rx_t **list, const char *s, int flags, BUFFER *err)
554 if (rx_lookup(list, s))
557 rx = rx_compile(s, flags);
559 snprintf(err->data, err->dsize, "Bad regexp: %s\n", s);
563 rx_list_append(list, rx);
567 static int add_to_spam_list(rx_t **list, const char *pat,
568 const char *templ, BUFFER * err)
572 if (m_strisempty(pat) || !templ)
575 if (!(rx = rx_compile (pat, REG_ICASE))) {
576 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
580 /* check to make sure the item is not already on this list */
582 if (!ascii_strcasecmp(rx->pattern, (*list)->pattern)) {
583 rx_t *tmp = rx_list_pop(list);
586 list = &(*list)->next;
591 rx_set_template(rx, templ);
595 static int remove_from_spam_list (rx_t ** list, const char *pat)
600 if (!m_strcmp((*list)->pattern, pat)) {
601 rx_t *spam = rx_list_pop(list);
605 list = &(*list)->next;
613 static void remove_from_list(string_list_t **l, const char *str)
615 if (!m_strcmp("*", str)) {
616 string_list_wipe(l); /* ``unCMD *'' means delete all current entries */
621 if (!ascii_strcasecmp(str, (*l)->data)) {
622 string_list_t *it = string_list_pop(l);
623 string_item_delete(&it);
630 static int remove_from_rx_list(rx_t **l, const char *str)
632 if (m_strcmp("*", str) == 0) {
637 l = rx_lookup(l, str);
639 rx_t *r = rx_list_pop(l);
647 static int parse_unignore (BUFFER * buf, BUFFER * s,
648 unsigned long data __attribute__ ((unused)),
649 BUFFER * err __attribute__ ((unused)))
652 mutt_extract_token (buf, s, 0);
654 /* don't add "*" to the unignore list */
655 if (m_strcmp (buf->data, "*"))
656 add_to_list (&UnIgnore, buf->data);
658 remove_from_list (&Ignore, buf->data);
659 } while (MoreArgs (s));
664 static int parse_ignore (BUFFER * buf, BUFFER * s,
665 unsigned long data __attribute__ ((unused)),
666 BUFFER * err __attribute__ ((unused)))
669 mutt_extract_token (buf, s, 0);
670 remove_from_list (&UnIgnore, buf->data);
671 add_to_list (&Ignore, buf->data);
672 } while (MoreArgs(s));
676 static int parse_list(BUFFER * buf, BUFFER * s, unsigned long data,
677 BUFFER * err __attribute__ ((unused)))
680 mutt_extract_token (buf, s, 0);
681 add_to_list ((string_list_t **) data, buf->data);
682 } while (MoreArgs(s));
686 static void _alternates_clean (void)
690 if (Context && Context->msgcount) {
691 for (i = 0; i < Context->msgcount; i++)
692 Context->hdrs[i]->recip_valid = 0;
696 static int parse_alternates (BUFFER * buf, BUFFER * s,
697 unsigned long data __attribute__ ((unused)),
698 BUFFER * err __attribute__ ((unused)))
700 _alternates_clean ();
702 mutt_extract_token (buf, s, 0);
703 remove_from_rx_list (&UnAlternates, buf->data);
705 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
708 while (MoreArgs (s));
713 static int parse_unalternates (BUFFER * buf, BUFFER * s,
714 unsigned long data __attribute__ ((unused)),
715 BUFFER * err __attribute__ ((unused)))
717 _alternates_clean ();
719 mutt_extract_token (buf, s, 0);
720 remove_from_rx_list (&Alternates, buf->data);
722 if (m_strcmp(buf->data, "*") &&
723 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
727 while (MoreArgs (s));
732 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
739 /* Insist on at least one parameter */
742 m_strcpy(err->data, err->dsize, _("spam: no matching pattern"));
744 m_strcpy(err->data, err->dsize, _("nospam: no matching pattern"));
748 /* Extract the first token, a regexp */
749 mutt_extract_token (buf, s, 0);
751 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
752 if (data == M_SPAM) {
753 /* If there's a second parameter, it's a template for the spam tag. */
755 mutt_extract_token (&templ, s, 0);
757 /* Add to the spam list. */
758 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
759 p_delete(&templ.data);
762 p_delete(&templ.data);
765 /* If not, try to remove from the nospam list. */
767 remove_from_rx_list (&NoSpamList, buf->data);
773 /* M_NOSPAM is for nospam commands. */
774 else if (data == M_NOSPAM) {
775 /* nospam only ever has one parameter. */
777 /* "*" is a special case. */
778 if (!m_strcmp(buf->data, "*")) {
779 rx_list_wipe(&SpamList);
780 rx_list_wipe(&NoSpamList);
784 /* If it's on the spam list, just remove it. */
785 if (remove_from_spam_list (&SpamList, buf->data) != 0)
788 /* Otherwise, add it to the nospam list. */
789 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
795 /* This should not happen. */
796 m_strcpy(err->data, err->dsize, "This is no good at all.");
800 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
801 BUFFER * err __attribute__ ((unused)))
804 mutt_extract_token (buf, s, 0);
806 * Check for deletion of entire list
808 if (m_strcmp(buf->data, "*") == 0) {
809 string_list_wipe((string_list_t **) data);
812 remove_from_list ((string_list_t **) data, buf->data);
814 while (MoreArgs (s));
819 static int parse_lists (BUFFER * buf, BUFFER * s,
820 unsigned long data __attribute__ ((unused)),
824 mutt_extract_token (buf, s, 0);
825 remove_from_rx_list (&UnMailLists, buf->data);
827 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
830 while (MoreArgs (s));
835 /* always wise to do what someone else did before */
836 static void _attachments_clean (void) {
838 if (Context && Context->msgcount) {
839 for (i = 0; i < Context->msgcount; i++)
840 Context->hdrs[i]->attach_valid = 0;
844 static int parse_attach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
845 BUFFER *err __attribute__ ((unused))) {
847 string_list_t *listp, *lastp;
852 /* Find the last item in the list that data points to. */
854 for (listp = *ldata; listp; listp = listp->next) {
855 a = (ATTACH_MATCH *)listp->data;
860 mutt_extract_token (buf, s, 0);
862 if (!buf->data || *buf->data == '\0')
865 a = p_new(ATTACH_MATCH, 1);
867 /* some cheap hacks that I expect to remove */
868 if (!m_strcasecmp(buf->data, "any"))
869 a->major = m_strdup("*/.*");
870 else if (!m_strcasecmp(buf->data, "none"))
871 a->major = m_strdup("cheap_hack/this_should_never_match");
873 a->major = m_strdup(buf->data);
875 if ((p = strchr(a->major, '/'))) {
880 a->minor = "unknown";
883 len = m_strlen(a->minor);
884 tmpminor = p_new(char, len + 3);
885 m_strcpy(&tmpminor[1], len + 3, a->minor);
887 tmpminor[len+1] = '$';
888 tmpminor[len+2] = '\0';
890 a->major_int = mutt_check_mime_type(a->major);
891 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
895 listp = p_new(string_list_t, 1);
896 listp->data = (char *)a;
905 while (MoreArgs (s));
907 _attachments_clean();
911 static int parse_unattach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
912 BUFFER *err __attribute__ ((unused))) {
914 string_list_t *lp, *lastp, *newlp;
920 mutt_extract_token (buf, s, 0);
922 if (!m_strcasecmp(buf->data, "any"))
923 tmp = m_strdup("*/.*");
924 else if (!m_strcasecmp(buf->data, "none"))
925 tmp = m_strdup("cheap_hack/this_should_never_match");
927 tmp = m_strdup(buf->data);
929 if ((minor = strchr(tmp, '/'))) {
933 minor = m_strdup("unknown");
935 major = mutt_check_mime_type(tmp);
937 /* We must do our own walk here because remove_from_list() will only
938 * remove the string_list_t->data, not anything pointed to by the string_list_t->data. */
940 for(lp = *ldata; lp; ) {
941 a = (ATTACH_MATCH *)lp->data;
942 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
943 regfree(&a->minor_rx);
946 /* Relink backward */
948 lastp->next = lp->next;
953 p_delete(&lp->data); /* same as a */
963 while (MoreArgs (s));
966 _attachments_clean();
970 static int print_attach_list (string_list_t *lp, char op, const char *name) {
972 printf("attachments %c%s %s/%s\n", op, name,
973 ((ATTACH_MATCH *)lp->data)->major,
974 ((ATTACH_MATCH *)lp->data)->minor);
981 static int parse_attachments (BUFFER *buf, BUFFER *s,
982 unsigned long data __attribute__ ((unused)),
985 string_list_t **listp;
987 mutt_extract_token(buf, s, 0);
988 if (!buf->data || *buf->data == '\0') {
989 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
993 category = buf->data;
999 printf("\nCurrent attachments settings:\n\n");
1000 print_attach_list(AttachAllow, '+', "A");
1001 print_attach_list(AttachExclude, '-', "A");
1002 print_attach_list(InlineAllow, '+', "I");
1003 print_attach_list(InlineExclude, '-', "I");
1004 set_option (OPTFORCEREDRAWINDEX);
1005 set_option (OPTFORCEREDRAWPAGER);
1006 mutt_any_key_to_continue (NULL);
1010 if (op != '+' && op != '-') {
1014 if (!m_strncasecmp(category, "attachment", strlen(category))) {
1016 listp = &AttachAllow;
1018 listp = &AttachExclude;
1020 else if (!m_strncasecmp(category, "inline", strlen(category))) {
1022 listp = &InlineAllow;
1024 listp = &InlineExclude;
1026 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
1030 return parse_attach_list(buf, s, listp, err);
1033 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
1035 string_list_t **listp;
1037 mutt_extract_token(buf, s, 0);
1038 if (!buf->data || *buf->data == '\0') {
1039 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
1045 if (op != '+' && op != '-') {
1049 if (!m_strncasecmp(p, "attachment", strlen(p))) {
1051 listp = &AttachAllow;
1053 listp = &AttachExclude;
1055 else if (!m_strncasecmp(p, "inline", strlen(p))) {
1057 listp = &InlineAllow;
1059 listp = &InlineExclude;
1062 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
1066 return parse_unattach_list(buf, s, listp, err);
1069 static int parse_unlists (BUFFER * buf, BUFFER * s,
1070 unsigned long data __attribute__ ((unused)),
1071 BUFFER * err __attribute__ ((unused)))
1074 mutt_extract_token (buf, s, 0);
1075 remove_from_rx_list (&SubscribedLists, buf->data);
1076 remove_from_rx_list (&MailLists, buf->data);
1078 if (m_strcmp(buf->data, "*") &&
1079 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1082 while (MoreArgs (s));
1087 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1091 mutt_extract_token (buf, s, 0);
1092 remove_from_rx_list (&UnMailLists, buf->data);
1093 remove_from_rx_list (&UnSubscribedLists, buf->data);
1095 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1097 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1100 while (MoreArgs (s));
1105 static int parse_unsubscribe (BUFFER * buf, BUFFER * s,
1106 unsigned long data __attribute__ ((unused)),
1107 BUFFER * err __attribute__ ((unused)))
1110 mutt_extract_token (buf, s, 0);
1111 remove_from_rx_list (&SubscribedLists, buf->data);
1113 if (m_strcmp(buf->data, "*") &&
1114 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1117 while (MoreArgs (s));
1122 static int parse_unalias (BUFFER * buf, BUFFER * s,
1123 unsigned long data __attribute__ ((unused)),
1124 BUFFER * err __attribute__ ((unused)))
1126 alias_t *tmp, **last;
1129 mutt_extract_token (buf, s, 0);
1131 if (!m_strcmp("*", buf->data) == 0) {
1132 if (CurrentMenu == MENU_ALIAS) {
1133 for (tmp = Aliases; tmp; tmp = tmp->next)
1135 set_option(OPTFORCEREDRAWINDEX);
1137 alias_list_wipe(&Aliases);
1143 for (last = &Aliases; *last; last = &(*last)->next) {
1144 if (!m_strcasecmp(buf->data, (*last)->name)) {
1145 if (CurrentMenu == MENU_ALIAS) {
1147 set_option (OPTFORCEREDRAWINDEX);
1149 tmp = alias_list_pop(last);
1155 } while (MoreArgs(s));
1160 static int parse_alias (BUFFER * buf, BUFFER * s,
1161 unsigned long data __attribute__ ((unused)),
1167 if (!MoreArgs (s)) {
1168 m_strcpy(err->data, err->dsize, _("alias: no address"));
1172 mutt_extract_token (buf, s, 0);
1174 /* check to see if an alias with this name already exists */
1175 for (last = &Aliases; *last; last = &(*last)->next) {
1176 if (!m_strcasecmp((*last)->name, buf->data))
1181 /* create a new alias */
1182 *last = alias_new();
1183 (*last)->name = m_strdup(buf->data);
1184 /* give the main addressbook code a chance */
1185 if (CurrentMenu == MENU_ALIAS)
1186 set_option (OPTMENUCALLER);
1188 /* override the previous value */
1189 address_list_wipe(&(*last)->addr);
1190 if (CurrentMenu == MENU_ALIAS)
1191 set_option (OPTFORCEREDRAWINDEX);
1194 mutt_extract_token(buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE);
1195 (*last)->addr = mutt_parse_adrlist((*last)->addr, buf->data);
1196 if (mutt_addrlist_to_idna((*last)->addr, &estr)) {
1197 snprintf (err->data, err->dsize,
1198 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, (*last)->name);
1207 parse_unmy_hdr(BUFFER * buf, BUFFER * s,
1208 unsigned long data __attribute__ ((unused)),
1209 BUFFER * err __attribute__ ((unused)))
1212 mutt_extract_token (buf, s, 0);
1214 if (!m_strcmp("*", buf->data)) {
1215 string_list_wipe(&UserHeader);
1217 string_list_t **last = &UserHeader;
1218 ssize_t l = m_strlen(buf->data);
1220 if (buf->data[l - 1] == ':')
1224 if (!ascii_strncasecmp(buf->data, (*last)->data, l)
1225 && (*last)->data[l] == ':')
1227 string_list_t *tmp = string_list_pop(last);
1228 string_item_delete(&tmp);
1230 last = &(*last)->next;
1234 } while (MoreArgs(s));
1239 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1246 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1247 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1248 m_strcpy(err->data, err->dsize, _("invalid header field"));
1251 keylen = p - buf->data + 1;
1254 for (tmp = UserHeader;; tmp = tmp->next) {
1255 /* see if there is already a field by this name */
1256 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1257 /* replace the old value */
1258 p_delete(&tmp->data);
1259 tmp->data = buf->data;
1266 tmp->next = string_item_new();
1270 tmp = string_item_new();
1273 tmp->data = buf->data;
1279 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1280 char* errbuf, ssize_t errlen) {
1283 if (m_strncmp("reverse-", s, 8) == 0) {
1285 flags = SORT_REVERSE;
1288 if (m_strncmp("last-", s, 5) == 0) {
1293 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1295 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1299 *((short*) dst->data) = i | flags;
1303 /* if additional data more == 1, we want to resolve synonyms */
1304 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
1306 char buf[LONG_STRING];
1307 struct option_t *ptr = p;
1309 if (DTYPE(ptr->type) == DT_SYN) {
1312 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1314 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_fromstr)
1317 mutt_option_value(ptr->option, buf, sizeof(buf));
1318 if (m_strlen(ptr->init) == 0 && buf && *buf)
1319 ptr->init = m_strdup(buf);
1322 static int init_expand (char** dst, struct option_t* src) {
1328 if (DTYPE(src->type) == DT_STR ||
1329 DTYPE(src->type) == DT_PATH) {
1330 /* only expand for string as it's the only place where
1331 * we want to expand vars right now */
1332 if (src->init && *src->init) {
1335 len = m_strlen(src->init) + 2;
1336 in.data = p_new(char, len + 1);
1337 snprintf (in.data, len, "\"%s\"", src->init);
1340 mutt_extract_token (&token, &in, 0);
1341 if (token.data && *token.data)
1342 *dst = m_strdup(token.data);
1344 *dst = m_strdup("");
1346 p_delete(&token.data);
1348 *dst = m_strdup("");
1350 /* for non-string: take value as is */
1351 *dst = m_strdup(src->init);
1355 /* if additional data more == 1, we want to resolve synonyms */
1356 static void mutt_restore_default (const char* name __attribute__ ((unused)),
1357 void* p, unsigned long more) {
1358 char errbuf[STRING];
1359 struct option_t* ptr = (struct option_t*) p;
1362 if (DTYPE (ptr->type) == DT_SYN) {
1365 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1369 if (FuncTable[DTYPE (ptr->type)].opt_fromstr) {
1370 init_expand (&init, ptr);
1371 if (!FuncTable[DTYPE (ptr->type)].opt_fromstr (ptr, init, errbuf,
1373 if (!option (OPTNOCURSES))
1375 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1376 "Please report this error: \"%s\"\n"),
1377 ptr->option, NONULL (init), errbuf);
1383 if (ptr->flags & R_INDEX)
1384 set_option (OPTFORCEREDRAWINDEX);
1385 if (ptr->flags & R_PAGER)
1386 set_option (OPTFORCEREDRAWPAGER);
1387 if (ptr->flags & R_RESORT_SUB)
1388 set_option (OPTSORTSUBTHREADS);
1389 if (ptr->flags & R_RESORT)
1390 set_option (OPTNEEDRESORT);
1391 if (ptr->flags & R_RESORT_INIT)
1392 set_option (OPTRESORTINIT);
1393 if (ptr->flags & R_TREE)
1394 set_option (OPTREDRAWTREE);
1397 /* check whether value for $dsn_return would be valid */
1398 static int check_dsn_return (const char* option __attribute__ ((unused)), unsigned long p,
1399 char* errbuf, ssize_t errlen) {
1400 char* val = (char*) p;
1401 if (val && *val && m_strncmp(val, "hdrs", 4) != 0 &&
1402 m_strncmp(val, "full", 4) != 0) {
1404 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1410 /* check whether value for $dsn_notify would be valid */
1412 check_dsn_notify (const char* option __attribute__ ((unused)),
1413 unsigned long val, char* errbuf, ssize_t errlen)
1415 const char *p = (const char*)val;
1418 const char *q = m_strchrnul(p, ',');
1421 if (!m_strncmp(p, "never", len) && !m_strncmp(p, "delay", len)
1422 && !m_strncmp(p, "failure", len) && !m_strncmp(p, "success", len))
1425 snprintf(errbuf, errlen, _("'%.*s' is invalid for $%s"),
1426 len, p, "dsn_notify");
1436 static int check_num (const char* option, unsigned long p,
1437 char* errbuf, ssize_t errlen) {
1440 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1446 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1447 char* errbuf, ssize_t errlen) {
1448 if (!check_num ("history", p, errbuf, errlen))
1450 mutt_init_history ();
1454 static int check_special (const char* name, unsigned long val,
1455 char* errbuf, ssize_t errlen) {
1458 for (i = 0; SpecialVars[i].name; i++) {
1459 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1460 return (SpecialVars[i].check (SpecialVars[i].name,
1461 val, errbuf, errlen));
1467 static const struct mapping_t* get_sortmap (struct option_t* option) {
1468 const struct mapping_t* map = NULL;
1470 switch (option->type & DT_SUBTYPE_MASK) {
1472 map = SortAliasMethods;
1474 case DT_SORT_BROWSER:
1475 map = SortBrowserMethods;
1478 map = SortKeyMethods;
1481 map = SortAuxMethods;
1490 #define CHECK_PAGER \
1491 if ((CurrentMenu == MENU_PAGER) && \
1492 (!option || (option->flags & R_RESORT))) \
1494 snprintf (err->data, err->dsize, \
1495 _("Not available in this menu.")); \
1499 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1502 int query, unset, inv, reset, r = 0;
1503 struct option_t* option = NULL;
1505 while (MoreArgs (s)) {
1506 /* reset state variables */
1508 unset = data & M_SET_UNSET;
1509 inv = data & M_SET_INV;
1510 reset = data & M_SET_RESET;
1512 if (*s->dptr == '?') {
1516 else if (m_strncmp("no", s->dptr, 2) == 0) {
1520 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1524 else if (*s->dptr == '&') {
1529 /* get the variable name */
1530 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1532 /* resolve synonyms */
1533 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1534 DTYPE (option->type == DT_SYN))
1536 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1537 syn_t* syn = syn_new();
1538 syn->f = m_strdup(CurRCFile);
1542 syn_list_push(&Synonyms, syn);
1546 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1547 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1550 s->dptr = vskipspaces(s->dptr);
1553 if (query || unset || inv) {
1554 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1558 if (s && *s->dptr == '=') {
1559 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1563 if (!m_strcmp("all", tmp->data)) {
1564 if (CurrentMenu == MENU_PAGER) {
1565 snprintf (err->data, err->dsize, _("Not available in this menu."));
1568 hash_map (ConfigOptions, mutt_restore_default, 1);
1569 set_option (OPTFORCEREDRAWINDEX);
1570 set_option (OPTFORCEREDRAWPAGER);
1571 set_option (OPTSORTSUBTHREADS);
1572 set_option (OPTNEEDRESORT);
1573 set_option (OPTRESORTINIT);
1574 set_option (OPTREDRAWTREE);
1578 mutt_restore_default (NULL, option, 1);
1581 else if (DTYPE (option->type) == DT_BOOL) {
1582 /* XXX this currently ignores the function table
1583 * as we don't get invert and stuff into it */
1584 if (s && *s->dptr == '=') {
1585 if (unset || inv || query) {
1586 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1591 mutt_extract_token (tmp, s, 0);
1592 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1594 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1597 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1603 bool_to_string (err->data, err->dsize, option);
1609 unset_option (option->data);
1611 toggle_option (option->data);
1613 set_option (option->data);
1615 else if (DTYPE (option->type) == DT_STR ||
1616 DTYPE (option->type) == DT_PATH ||
1617 DTYPE (option->type) == DT_ADDR ||
1618 DTYPE (option->type) == DT_MAGIC ||
1619 DTYPE (option->type) == DT_NUM ||
1620 DTYPE (option->type) == DT_SORT ||
1621 DTYPE (option->type) == DT_RX)
1623 /* XXX maybe we need to get unset into handlers? */
1624 if (DTYPE (option->type) == DT_STR ||
1625 DTYPE (option->type) == DT_PATH ||
1626 DTYPE (option->type) == DT_ADDR)
1630 if (DTYPE (option->type) == DT_ADDR)
1631 address_list_wipe((address_t **) option->data);
1633 p_delete((void **)(void *)&option->data);
1638 if (query || *s->dptr != '=') {
1639 FuncTable[DTYPE (option->type)].opt_tostr
1640 (err->data, err->dsize, option);
1646 mutt_extract_token (tmp, s, 0);
1647 if (!FuncTable[DTYPE (option->type)].opt_fromstr
1648 (option, tmp->data, err->data, err->dsize))
1651 else if (DTYPE (option->type) == DT_QUAD) {
1654 quad_to_string (err->data, err->dsize, option);
1658 if (*s->dptr == '=') {
1661 mutt_extract_token (tmp, s, 0);
1662 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1663 set_quadoption (option->data, M_YES);
1664 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1665 set_quadoption (option->data, M_NO);
1666 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1667 set_quadoption (option->data, M_ASKYES);
1668 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1669 set_quadoption (option->data, M_ASKNO);
1671 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1672 tmp->data, option->option);
1679 toggle_quadoption (option->data);
1681 set_quadoption (option->data, M_NO);
1683 set_quadoption (option->data, M_YES);
1687 snprintf (err->data, err->dsize, _("%s: unknown type"),
1693 if (option->flags & R_INDEX)
1694 set_option (OPTFORCEREDRAWINDEX);
1695 if (option->flags & R_PAGER)
1696 set_option (OPTFORCEREDRAWPAGER);
1697 if (option->flags & R_RESORT_SUB)
1698 set_option (OPTSORTSUBTHREADS);
1699 if (option->flags & R_RESORT)
1700 set_option (OPTNEEDRESORT);
1701 if (option->flags & R_RESORT_INIT)
1702 set_option (OPTRESORTINIT);
1703 if (option->flags & R_TREE)
1704 set_option (OPTREDRAWTREE);
1711 /* reads the specified initialization file. returns -1 if errors were found
1712 so that we can pause to let the user know... */
1713 static int source_rc (const char *rcfile, BUFFER * err)
1716 int line = 0, rc = 0, conv = 0;
1718 char *linebuf = NULL;
1719 char *currentline = NULL;
1723 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1724 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1729 while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
1730 conv = ConfigCharset && (*ConfigCharset) && Charset;
1732 currentline = m_strdup(linebuf);
1735 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1738 currentline = linebuf;
1743 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1744 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1745 if (--rc < -MAXERRS) {
1747 p_delete(¤tline);
1756 p_delete(¤tline);
1758 p_delete(&token.data);
1762 mutt_wait_filter (pid);
1764 /* the muttrc source keyword */
1765 snprintf (err->data, err->dsize,
1766 rc >= -MAXERRS ? _("source: errors in %s")
1767 : _("source: reading aborted due too many errors in %s"),
1776 static int parse_source (BUFFER * tmp, BUFFER * s,
1777 unsigned long data __attribute__ ((unused)),
1780 char path[_POSIX_PATH_MAX];
1784 if (mutt_extract_token (tmp, s, 0) != 0) {
1785 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1789 m_strcpy(path, sizeof(path), tmp->data);
1790 mutt_expand_path (path, sizeof(path));
1792 rc += source_rc (path, err);
1794 while (MoreArgs (s));
1796 return ((rc < 0) ? -1 : 0);
1799 /* line command to execute
1801 token scratch buffer to be used by parser. caller should free
1802 token->data when finished. the reason for this variable is
1803 to avoid having to allocate and deallocate a lot of memory
1804 if we are parsing many lines. the caller can pass in the
1805 memory to use, which avoids having to create new space for
1806 every call to this function.
1808 err where to write error messages */
1809 int mutt_parse_rc_line (const char *line, BUFFER * token, BUFFER * err)
1815 expn.data = expn.dptr = line;
1816 expn.dsize = m_strlen(line);
1820 expn.dptr = vskipspaces(expn.dptr);
1821 while (*expn.dptr) {
1822 if (*expn.dptr == '#')
1823 break; /* rest of line is a comment */
1824 if (*expn.dptr == ';') {
1828 mutt_extract_token (token, &expn, 0);
1829 for (i = 0; Commands[i].name; i++) {
1830 if (!m_strcmp(token->data, Commands[i].name)) {
1831 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1836 if (!Commands[i].name) {
1837 snprintf (err->data, err->dsize, _("%s: unknown command"),
1838 NONULL (token->data));
1845 p_delete(&expn.data);
1850 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
1851 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
1852 /* initial string that starts completion. No telling how much crap
1853 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1854 char User_typed[LONG_STRING] = { 0 };
1856 int Num_matched = 0; /* Number of matches for completion */
1857 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1858 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1860 /* helper function for completion. Changes the dest buffer if
1861 necessary/possible to aid completion.
1862 dest == completion result gets here.
1863 src == candidate for completion.
1864 try == user entered data for completion.
1865 len == length of dest buffer.
1867 static void candidate (char *dest, char *try, const char *src, int len)
1871 if (strstr (src, try) == src) {
1872 Matches[Num_matched++] = src;
1874 m_strcpy(dest, len, src);
1876 for (l = 0; src[l] && src[l] == dest[l]; l++);
1882 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
1886 int spaces; /* keep track of the number of leading spaces on the line */
1888 buffer = vskipspaces(buffer);
1889 spaces = buffer - pt;
1891 pt = buffer + pos - spaces;
1892 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1895 if (pt == buffer) { /* complete cmd */
1896 /* first TAB. Collect all the matches */
1899 m_strcpy(User_typed, sizeof(User_typed), pt);
1900 p_clear(Matches, countof(Matches));
1901 p_clear(Completed, countof(Completed));
1902 for (num = 0; Commands[num].name; num++)
1903 candidate (Completed, User_typed, Commands[num].name,
1905 Matches[Num_matched++] = User_typed;
1907 /* All matches are stored. Longest non-ambiguous string is ""
1908 * i.e. dont change 'buffer'. Fake successful return this time */
1909 if (User_typed[0] == 0)
1913 if (Completed[0] == 0 && User_typed[0])
1916 /* Num_matched will _always_ be atleast 1 since the initial
1917 * user-typed string is always stored */
1918 if (numtabs == 1 && Num_matched == 2)
1919 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1920 else if (numtabs > 1 && Num_matched > 2)
1921 /* cycle thru all the matches */
1922 snprintf (Completed, sizeof(Completed), "%s",
1923 Matches[(numtabs - 2) % Num_matched]);
1925 /* return the completed command */
1926 m_strcpy(buffer, len - spaces, Completed);
1928 else if (!m_strncmp(buffer, "set", 3)
1929 || !m_strncmp(buffer, "unset", 5)
1930 || !m_strncmp(buffer, "reset", 5)
1931 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
1932 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
1935 /* loop through all the possible prefixes (no, inv, ...) */
1936 if (!m_strncmp(buffer, "set", 3)) {
1937 for (num = 0; prefixes[num]; num++) {
1938 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
1939 pt += m_strlen(prefixes[num]);
1945 /* first TAB. Collect all the matches */
1948 m_strcpy(User_typed, sizeof(User_typed), pt);
1949 p_clear(Matches, countof(Matches));
1950 p_clear(Completed, countof(Completed));
1951 for (num = 0; MuttVars[num].option; num++)
1952 candidate(Completed, User_typed, MuttVars[num].option,
1954 Matches[Num_matched++] = User_typed;
1956 /* All matches are stored. Longest non-ambiguous string is ""
1957 * i.e. dont change 'buffer'. Fake successful return this time */
1958 if (User_typed[0] == 0)
1962 if (Completed[0] == 0 && User_typed[0])
1965 /* Num_matched will _always_ be atleast 1 since the initial
1966 * user-typed string is always stored */
1967 if (numtabs == 1 && Num_matched == 2)
1968 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1969 else if (numtabs > 1 && Num_matched > 2)
1970 /* cycle thru all the matches */
1971 snprintf (Completed, sizeof(Completed), "%s",
1972 Matches[(numtabs - 2) % Num_matched]);
1974 m_strcpy(pt, buffer + len - pt - spaces, Completed);
1976 else if (!m_strncmp(buffer, "exec", 4)) {
1977 struct binding_t *menu = km_get_table (CurrentMenu);
1979 if (!menu && CurrentMenu != MENU_PAGER)
1983 /* first TAB. Collect all the matches */
1986 m_strcpy(User_typed, sizeof(User_typed), pt);
1987 p_clear(Matches, countof(Matches));
1988 p_clear(Completed, countof(Completed));
1989 for (num = 0; menu[num].name; num++)
1990 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
1991 /* try the generic menu */
1992 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1994 for (num = 0; menu[num].name; num++)
1995 candidate (Completed, User_typed, menu[num].name,
1998 Matches[Num_matched++] = User_typed;
2000 /* All matches are stored. Longest non-ambiguous string is ""
2001 * i.e. dont change 'buffer'. Fake successful return this time */
2002 if (User_typed[0] == 0)
2006 if (Completed[0] == 0 && User_typed[0])
2009 /* Num_matched will _always_ be atleast 1 since the initial
2010 * user-typed string is always stored */
2011 if (numtabs == 1 && Num_matched == 2)
2012 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2013 else if (numtabs > 1 && Num_matched > 2)
2014 /* cycle thru all the matches */
2015 snprintf (Completed, sizeof(Completed), "%s",
2016 Matches[(numtabs - 2) % Num_matched]);
2018 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2026 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
2028 char var[STRING], *pt = buffer;
2030 struct option_t* option = NULL;
2035 buffer = vskipspaces(buffer);
2036 spaces = buffer - pt;
2038 pt = buffer + pos - spaces;
2039 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2041 pt++; /* move past the space */
2042 if (*pt == '=') /* abort if no var before the '=' */
2045 if (m_strncmp(buffer, "set", 3) == 0) {
2046 m_strcpy(var, sizeof(var), pt);
2047 /* ignore the trailing '=' when comparing */
2048 var[m_strlen(var) - 1] = 0;
2049 if (!(option = hash_find (ConfigOptions, var)))
2050 return 0; /* no such variable. */
2052 char tmp[LONG_STRING], tmp2[LONG_STRING];
2054 ssize_t dlen = buffer + len - pt - spaces;
2055 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2059 if ((DTYPE (option->type) == DT_STR) ||
2060 (DTYPE (option->type) == DT_PATH) ||
2061 (DTYPE (option->type) == DT_RX)) {
2062 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
2063 if (DTYPE (option->type) == DT_PATH)
2064 mutt_pretty_mailbox (tmp);
2066 else if (DTYPE (option->type) == DT_ADDR) {
2067 rfc822_addrcat(tmp, sizeof(tmp), *((address_t **) option->data), 0);
2069 else if (DTYPE (option->type) == DT_QUAD)
2070 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
2071 else if (DTYPE (option->type) == DT_NUM)
2072 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
2073 else if (DTYPE (option->type) == DT_SORT) {
2074 const struct mapping_t *map;
2077 switch (option->type & DT_SUBTYPE_MASK) {
2079 map = SortAliasMethods;
2081 case DT_SORT_BROWSER:
2082 map = SortBrowserMethods;
2085 map = SortKeyMethods;
2091 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2092 snprintf(tmp, sizeof(tmp), "%s%s%s",
2093 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2094 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2096 else if (DTYPE (option->type) == DT_MAGIC) {
2098 switch (DefaultMagic) {
2114 m_strcpy(tmp, sizeof(tmp), p);
2116 else if (DTYPE (option->type) == DT_BOOL)
2117 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
2121 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
2122 if (*s == '\\' || *s == '"')
2128 m_strcpy(tmp, sizeof(tmp), pt);
2129 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2137 /* Implement the -Q command line flag */
2138 int mutt_query_variables (string_list_t * queries)
2142 char errbuff[STRING];
2143 char command[STRING];
2151 err.dsize = sizeof(errbuff);
2153 for (p = queries; p; p = p->next) {
2154 snprintf (command, sizeof(command), "set ?%s\n", p->data);
2155 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2156 fprintf (stderr, "%s\n", err.data);
2157 p_delete(&token.data);
2160 printf ("%s\n", err.data);
2163 p_delete(&token.data);
2167 static int mutt_execute_commands (string_list_t * p)
2170 char errstr[STRING];
2174 err.dsize = sizeof(errstr);
2176 for (; p; p = p->next) {
2177 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2178 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2179 p_delete(&token.data);
2183 p_delete(&token.data);
2187 void mutt_init (int skip_sys_rc, string_list_t * commands)
2190 struct utsname utsname;
2192 char buffer[STRING], error[STRING];
2193 int default_rc = 0, need_pause = 0;
2199 err.dsize = sizeof(error);
2201 ConfigOptions = hash_new (sizeof(MuttVars) * 2, 0);
2202 for (i = 0; MuttVars[i].option; i++) {
2203 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i]);
2207 * XXX - use something even more difficult to predict?
2209 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2210 "\033]9;%ld\a", (long) time (NULL));
2212 /* Get some information about the user */
2213 if ((pw = getpwuid (getuid ()))) {
2215 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, GecosMask.rx);
2216 Realname = m_strdup(rnbuf);
2220 /* And about the host... */
2222 /* some systems report the FQDN instead of just the hostname */
2223 if ((p = strchr (utsname.nodename, '.'))) {
2224 Hostname = p_dupstr(utsname.nodename, p - utsname.nodename);
2226 m_strcpy(buffer, sizeof(buffer), p); /* save the domain for below */
2229 Hostname = m_strdup(utsname.nodename);
2231 if (!p && getdnsdomainname(buffer, sizeof(buffer)) == -1)
2232 Fqdn = m_strdup("@");
2234 if (*buffer != '@') {
2235 Fqdn = p_new(char, m_strlen(buffer) + m_strlen(Hostname) + 2);
2236 sprintf (Fqdn, "%s.%s", NONULL(Hostname), buffer);
2239 Fqdn = m_strdup(NONULL (Hostname));
2246 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2248 fgets (buffer, sizeof(buffer), f);
2249 p = vskipspaces(buffer);
2251 while (*q && !isspace(*q))
2254 NewsServer = m_strdup(p);
2258 if ((p = getenv ("NNTPSERVER")))
2259 NewsServer = m_strdup(p);
2262 if ((p = getenv ("MAIL")))
2263 Spoolfile = m_strdup(p);
2264 else if ((p = getenv ("MAILDIR")))
2265 Spoolfile = m_strdup(p);
2268 mutt_concat_path(buffer, sizeof(buffer), NONULL(MCore.homedir), MAILPATH);
2270 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(MCore.username));
2272 Spoolfile = m_strdup(buffer);
2275 if ((p = getenv ("MAILCAPS")))
2276 MailcapPath = m_strdup(p);
2278 /* Default search path from RFC1524 */
2280 m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2281 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2284 if ((p = getenv ("REPLYTO")) != NULL) {
2287 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2290 buf.data = buf.dptr = buffer;
2291 buf.dsize = m_strlen(buffer);
2294 parse_my_hdr (&token, &buf, 0, &err);
2295 p_delete(&token.data);
2298 if ((p = getenv ("EMAIL")) != NULL)
2299 From = rfc822_parse_adrlist (NULL, p);
2301 charset_initialize();
2304 /* Set standard defaults */
2305 hash_map (ConfigOptions, mutt_set_default, 0);
2306 hash_map (ConfigOptions, mutt_restore_default, 0);
2308 CurrentMenu = MENU_MAIN;
2311 /* Unset suspend by default if we're the session leader */
2312 if (getsid (0) == getpid ())
2313 unset_option (OPTSUSPEND);
2316 mutt_init_history ();
2319 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL(MCore.homedir));
2320 if (access (buffer, F_OK) == -1)
2321 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
2322 NONULL(MCore.homedir));
2325 Muttrc = m_strdup(buffer);
2328 m_strcpy(buffer, sizeof(buffer), Muttrc);
2330 mutt_expand_path (buffer, sizeof(buffer));
2331 Muttrc = m_strdup(buffer);
2333 p_delete(&AliasFile);
2334 AliasFile = m_strdup(NONULL (Muttrc));
2336 /* Process the global rc file if it exists and the user hasn't explicity
2337 requested not to via "-n". */
2339 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
2341 if (access (buffer, F_OK) == -1)
2342 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
2343 if (access (buffer, F_OK) == -1)
2344 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
2346 if (access (buffer, F_OK) == -1)
2347 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
2348 if (access (buffer, F_OK) != -1) {
2349 if (source_rc (buffer, &err) != 0) {
2350 fputs (err.data, stderr);
2351 fputc ('\n', stderr);
2357 /* Read the user's initialization file. */
2358 if (access (Muttrc, F_OK) != -1) {
2359 if (!option (OPTNOCURSES))
2361 if (source_rc (Muttrc, &err) != 0) {
2362 fputs (err.data, stderr);
2363 fputc ('\n', stderr);
2367 else if (!default_rc) {
2368 /* file specified by -F does not exist */
2369 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2370 mutt_endwin (buffer);
2375 snprintf(buffer, sizeof(buffer), "%s/.madmutt.lua", NONULL(MCore.homedir));
2376 if (access(buffer, F_OK) < 0)
2377 snprintf(buffer, sizeof(buffer), "%s/.madmutt/cfg.lua", NONULL(MCore.homedir));
2378 if (!access(buffer, F_OK)) {
2379 need_pause = luaM_wrap(mutt_error, luaM_dofile(buffer));
2383 if (mutt_execute_commands (commands) != 0)
2386 /* warn about synonym variables */
2390 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2392 for (syn = Synonyms; syn; syn = syn->next) {
2393 fprintf(stderr, "$%s ($%s should be used) (%s:%d)\n",
2394 syn->o ? NONULL(syn->o->option) : "",
2395 syn->n ? NONULL(syn->n->option) : "",
2396 NONULL(syn->f), syn->l);
2398 fprintf (stderr, _("Warning: synonym variables are scheduled"
2399 " for removal.\n"));
2400 syn_list_wipe(&Synonyms);
2404 if (need_pause && !option (OPTNOCURSES)) {
2405 if (mutt_any_key_to_continue (NULL) == -1)
2410 int mutt_get_hook_type (const char *name)
2412 struct command_t *c;
2414 for (c = Commands; c->name; c++)
2415 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2420 /* dump out the value of all the variables we have */
2421 int mutt_dump_variables (int full) {
2424 /* get all non-synonyms into list... */
2425 for (i = 0; MuttVars[i].option; i++) {
2426 struct option_t *option = MuttVars + i;
2427 char buf[LONG_STRING];
2429 if (DTYPE(option->type) == DT_SYN)
2433 mutt_option_value(option->option, buf, sizeof(buf));
2434 if (!m_strcmp(buf, option->init))
2439 FuncTable[DTYPE(option->type)].opt_tostr(buf, sizeof(buf), option);
2440 printf ("%s\n", buf);
2443 printf ("\n# vi""m:set ft=muttrc:\n");