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 *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 opt, const char *prompt)
499 int v = mlua_reggeti(opt);
507 v = mutt_yesorno(prompt, (v == M_ASKYES));
508 CLEARLINE (LINES - 1);
513 int query_quadoption (int opt, const char *prompt)
515 int v = quadoption (opt);
523 v = mutt_yesorno (prompt, (v == M_ASKYES));
524 CLEARLINE (LINES - 1);
531 static void add_to_list(string_list_t **list, const char *str)
533 /* don't add a NULL or empty string to the list */
534 if (m_strisempty(str))
537 /* check to make sure the item is not already on this list */
539 if (!ascii_strcasecmp(str, (*list)->data))
541 list = &(*list)->next;
544 *list = p_new(string_list_t, 1);
545 (*list)->data = m_strdup(str);
549 add_to_rx_list(rx_t **list, const char *s, int flags, BUFFER *err)
556 if (rx_lookup(list, s))
559 rx = rx_compile(s, flags);
561 snprintf(err->data, err->dsize, "Bad regexp: %s\n", s);
565 rx_list_append(list, rx);
569 static int add_to_spam_list(rx_t **list, const char *pat,
570 const char *templ, BUFFER * err)
574 if (m_strisempty(pat) || !templ)
577 if (!(rx = rx_compile (pat, REG_ICASE))) {
578 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
582 /* check to make sure the item is not already on this list */
584 if (!ascii_strcasecmp(rx->pattern, (*list)->pattern)) {
585 rx_t *tmp = rx_list_pop(list);
588 list = &(*list)->next;
593 rx_set_template(rx, templ);
597 static int remove_from_spam_list (rx_t ** list, const char *pat)
602 if (!m_strcmp((*list)->pattern, pat)) {
603 rx_t *spam = rx_list_pop(list);
607 list = &(*list)->next;
615 static void remove_from_list(string_list_t **l, const char *str)
617 if (!m_strcmp("*", str)) {
618 string_list_wipe(l); /* ``unCMD *'' means delete all current entries */
623 if (!ascii_strcasecmp(str, (*l)->data)) {
624 string_list_t *it = string_list_pop(l);
625 string_item_delete(&it);
632 static int remove_from_rx_list(rx_t **l, const char *str)
634 if (m_strcmp("*", str) == 0) {
639 l = rx_lookup(l, str);
641 rx_t *r = rx_list_pop(l);
649 static int parse_unignore (BUFFER * buf, BUFFER * s,
650 unsigned long data __attribute__ ((unused)),
651 BUFFER * err __attribute__ ((unused)))
654 mutt_extract_token (buf, s, 0);
656 /* don't add "*" to the unignore list */
657 if (m_strcmp (buf->data, "*"))
658 add_to_list (&UnIgnore, buf->data);
660 remove_from_list (&Ignore, buf->data);
661 } while (MoreArgs (s));
666 static int parse_ignore (BUFFER * buf, BUFFER * s,
667 unsigned long data __attribute__ ((unused)),
668 BUFFER * err __attribute__ ((unused)))
671 mutt_extract_token (buf, s, 0);
672 remove_from_list (&UnIgnore, buf->data);
673 add_to_list (&Ignore, buf->data);
674 } while (MoreArgs(s));
678 static int parse_list(BUFFER * buf, BUFFER * s, unsigned long data,
679 BUFFER * err __attribute__ ((unused)))
682 mutt_extract_token (buf, s, 0);
683 add_to_list ((string_list_t **) data, buf->data);
684 } while (MoreArgs(s));
688 static void _alternates_clean (void)
692 if (Context && Context->msgcount) {
693 for (i = 0; i < Context->msgcount; i++)
694 Context->hdrs[i]->recip_valid = 0;
698 static int parse_alternates (BUFFER * buf, BUFFER * s,
699 unsigned long data __attribute__ ((unused)),
700 BUFFER * err __attribute__ ((unused)))
702 _alternates_clean ();
704 mutt_extract_token (buf, s, 0);
705 remove_from_rx_list (&UnAlternates, buf->data);
707 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
710 while (MoreArgs (s));
715 static int parse_unalternates (BUFFER * buf, BUFFER * s,
716 unsigned long data __attribute__ ((unused)),
717 BUFFER * err __attribute__ ((unused)))
719 _alternates_clean ();
721 mutt_extract_token (buf, s, 0);
722 remove_from_rx_list (&Alternates, buf->data);
724 if (m_strcmp(buf->data, "*") &&
725 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
729 while (MoreArgs (s));
734 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
741 /* Insist on at least one parameter */
744 m_strcpy(err->data, err->dsize, _("spam: no matching pattern"));
746 m_strcpy(err->data, err->dsize, _("nospam: no matching pattern"));
750 /* Extract the first token, a regexp */
751 mutt_extract_token (buf, s, 0);
753 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
754 if (data == M_SPAM) {
755 /* If there's a second parameter, it's a template for the spam tag. */
757 mutt_extract_token (&templ, s, 0);
759 /* Add to the spam list. */
760 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
761 p_delete(&templ.data);
764 p_delete(&templ.data);
767 /* If not, try to remove from the nospam list. */
769 remove_from_rx_list (&NoSpamList, buf->data);
775 /* M_NOSPAM is for nospam commands. */
776 else if (data == M_NOSPAM) {
777 /* nospam only ever has one parameter. */
779 /* "*" is a special case. */
780 if (!m_strcmp(buf->data, "*")) {
781 rx_list_wipe(&SpamList);
782 rx_list_wipe(&NoSpamList);
786 /* If it's on the spam list, just remove it. */
787 if (remove_from_spam_list (&SpamList, buf->data) != 0)
790 /* Otherwise, add it to the nospam list. */
791 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
797 /* This should not happen. */
798 m_strcpy(err->data, err->dsize, "This is no good at all.");
802 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
803 BUFFER * err __attribute__ ((unused)))
806 mutt_extract_token (buf, s, 0);
808 * Check for deletion of entire list
810 if (m_strcmp(buf->data, "*") == 0) {
811 string_list_wipe((string_list_t **) data);
814 remove_from_list ((string_list_t **) data, buf->data);
816 while (MoreArgs (s));
821 static int parse_lists (BUFFER * buf, BUFFER * s,
822 unsigned long data __attribute__ ((unused)),
826 mutt_extract_token (buf, s, 0);
827 remove_from_rx_list (&UnMailLists, buf->data);
829 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
832 while (MoreArgs (s));
837 /* always wise to do what someone else did before */
838 static void _attachments_clean (void) {
840 if (Context && Context->msgcount) {
841 for (i = 0; i < Context->msgcount; i++)
842 Context->hdrs[i]->attach_valid = 0;
846 static int parse_attach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
847 BUFFER *err __attribute__ ((unused))) {
849 string_list_t *listp, *lastp;
854 /* Find the last item in the list that data points to. */
856 for (listp = *ldata; listp; listp = listp->next) {
857 a = (ATTACH_MATCH *)listp->data;
862 mutt_extract_token (buf, s, 0);
864 if (!buf->data || *buf->data == '\0')
867 a = p_new(ATTACH_MATCH, 1);
869 /* some cheap hacks that I expect to remove */
870 if (!m_strcasecmp(buf->data, "any"))
871 a->major = m_strdup("*/.*");
872 else if (!m_strcasecmp(buf->data, "none"))
873 a->major = m_strdup("cheap_hack/this_should_never_match");
875 a->major = m_strdup(buf->data);
877 if ((p = strchr(a->major, '/'))) {
882 a->minor = "unknown";
885 len = m_strlen(a->minor);
886 tmpminor = p_new(char, len + 3);
887 m_strcpy(&tmpminor[1], len + 3, a->minor);
889 tmpminor[len+1] = '$';
890 tmpminor[len+2] = '\0';
892 a->major_int = mutt_check_mime_type(a->major);
893 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
897 listp = p_new(string_list_t, 1);
898 listp->data = (char *)a;
907 while (MoreArgs (s));
909 _attachments_clean();
913 static int parse_unattach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
914 BUFFER *err __attribute__ ((unused))) {
916 string_list_t *lp, *lastp, *newlp;
922 mutt_extract_token (buf, s, 0);
924 if (!m_strcasecmp(buf->data, "any"))
925 tmp = m_strdup("*/.*");
926 else if (!m_strcasecmp(buf->data, "none"))
927 tmp = m_strdup("cheap_hack/this_should_never_match");
929 tmp = m_strdup(buf->data);
931 if ((minor = strchr(tmp, '/'))) {
935 minor = m_strdup("unknown");
937 major = mutt_check_mime_type(tmp);
939 /* We must do our own walk here because remove_from_list() will only
940 * remove the string_list_t->data, not anything pointed to by the string_list_t->data. */
942 for(lp = *ldata; lp; ) {
943 a = (ATTACH_MATCH *)lp->data;
944 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
945 regfree(&a->minor_rx);
948 /* Relink backward */
950 lastp->next = lp->next;
955 p_delete(&lp->data); /* same as a */
965 while (MoreArgs (s));
968 _attachments_clean();
972 static int print_attach_list (string_list_t *lp, char op, const char *name) {
974 printf("attachments %c%s %s/%s\n", op, name,
975 ((ATTACH_MATCH *)lp->data)->major,
976 ((ATTACH_MATCH *)lp->data)->minor);
983 static int parse_attachments (BUFFER *buf, BUFFER *s,
984 unsigned long data __attribute__ ((unused)),
987 string_list_t **listp;
989 mutt_extract_token(buf, s, 0);
990 if (!buf->data || *buf->data == '\0') {
991 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
995 category = buf->data;
1001 printf("\nCurrent attachments settings:\n\n");
1002 print_attach_list(AttachAllow, '+', "A");
1003 print_attach_list(AttachExclude, '-', "A");
1004 print_attach_list(InlineAllow, '+', "I");
1005 print_attach_list(InlineExclude, '-', "I");
1006 set_option (OPTFORCEREDRAWINDEX);
1007 set_option (OPTFORCEREDRAWPAGER);
1008 mutt_any_key_to_continue (NULL);
1012 if (op != '+' && op != '-') {
1016 if (!m_strncasecmp(category, "attachment", strlen(category))) {
1018 listp = &AttachAllow;
1020 listp = &AttachExclude;
1022 else if (!m_strncasecmp(category, "inline", strlen(category))) {
1024 listp = &InlineAllow;
1026 listp = &InlineExclude;
1028 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
1032 return parse_attach_list(buf, s, listp, err);
1035 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
1037 string_list_t **listp;
1039 mutt_extract_token(buf, s, 0);
1040 if (!buf->data || *buf->data == '\0') {
1041 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
1047 if (op != '+' && op != '-') {
1051 if (!m_strncasecmp(p, "attachment", strlen(p))) {
1053 listp = &AttachAllow;
1055 listp = &AttachExclude;
1057 else if (!m_strncasecmp(p, "inline", strlen(p))) {
1059 listp = &InlineAllow;
1061 listp = &InlineExclude;
1064 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
1068 return parse_unattach_list(buf, s, listp, err);
1071 static int parse_unlists (BUFFER * buf, BUFFER * s,
1072 unsigned long data __attribute__ ((unused)),
1073 BUFFER * err __attribute__ ((unused)))
1076 mutt_extract_token (buf, s, 0);
1077 remove_from_rx_list (&SubscribedLists, buf->data);
1078 remove_from_rx_list (&MailLists, buf->data);
1080 if (m_strcmp(buf->data, "*") &&
1081 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1084 while (MoreArgs (s));
1089 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1093 mutt_extract_token (buf, s, 0);
1094 remove_from_rx_list (&UnMailLists, buf->data);
1095 remove_from_rx_list (&UnSubscribedLists, buf->data);
1097 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1099 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1102 while (MoreArgs (s));
1107 static int parse_unsubscribe (BUFFER * buf, BUFFER * s,
1108 unsigned long data __attribute__ ((unused)),
1109 BUFFER * err __attribute__ ((unused)))
1112 mutt_extract_token (buf, s, 0);
1113 remove_from_rx_list (&SubscribedLists, buf->data);
1115 if (m_strcmp(buf->data, "*") &&
1116 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1119 while (MoreArgs (s));
1124 static int parse_unalias (BUFFER * buf, BUFFER * s,
1125 unsigned long data __attribute__ ((unused)),
1126 BUFFER * err __attribute__ ((unused)))
1128 alias_t *tmp, **last;
1131 mutt_extract_token (buf, s, 0);
1133 if (!m_strcmp("*", buf->data) == 0) {
1134 if (CurrentMenu == MENU_ALIAS) {
1135 for (tmp = Aliases; tmp; tmp = tmp->next)
1137 set_option(OPTFORCEREDRAWINDEX);
1139 alias_list_wipe(&Aliases);
1145 for (last = &Aliases; *last; last = &(*last)->next) {
1146 if (!m_strcasecmp(buf->data, (*last)->name)) {
1147 if (CurrentMenu == MENU_ALIAS) {
1149 set_option (OPTFORCEREDRAWINDEX);
1151 tmp = alias_list_pop(last);
1157 } while (MoreArgs(s));
1162 static int parse_alias (BUFFER * buf, BUFFER * s,
1163 unsigned long data __attribute__ ((unused)),
1169 if (!MoreArgs (s)) {
1170 m_strcpy(err->data, err->dsize, _("alias: no address"));
1174 mutt_extract_token (buf, s, 0);
1176 /* check to see if an alias with this name already exists */
1177 for (last = &Aliases; *last; last = &(*last)->next) {
1178 if (!m_strcasecmp((*last)->name, buf->data))
1183 /* create a new alias */
1184 *last = alias_new();
1185 (*last)->name = m_strdup(buf->data);
1186 /* give the main addressbook code a chance */
1187 if (CurrentMenu == MENU_ALIAS)
1188 set_option (OPTMENUCALLER);
1190 /* override the previous value */
1191 address_list_wipe(&(*last)->addr);
1192 if (CurrentMenu == MENU_ALIAS)
1193 set_option (OPTFORCEREDRAWINDEX);
1196 mutt_extract_token(buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE);
1197 (*last)->addr = mutt_parse_adrlist((*last)->addr, buf->data);
1198 if (mutt_addrlist_to_idna((*last)->addr, &estr)) {
1199 snprintf (err->data, err->dsize,
1200 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, (*last)->name);
1209 parse_unmy_hdr(BUFFER * buf, BUFFER * s,
1210 unsigned long data __attribute__ ((unused)),
1211 BUFFER * err __attribute__ ((unused)))
1214 mutt_extract_token (buf, s, 0);
1216 if (!m_strcmp("*", buf->data)) {
1217 string_list_wipe(&UserHeader);
1219 string_list_t **last = &UserHeader;
1220 ssize_t l = m_strlen(buf->data);
1222 if (buf->data[l - 1] == ':')
1226 if (!ascii_strncasecmp(buf->data, (*last)->data, l)
1227 && (*last)->data[l] == ':')
1229 string_list_t *tmp = string_list_pop(last);
1230 string_item_delete(&tmp);
1232 last = &(*last)->next;
1236 } while (MoreArgs(s));
1241 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1248 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1249 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1250 m_strcpy(err->data, err->dsize, _("invalid header field"));
1253 keylen = p - buf->data + 1;
1256 for (tmp = UserHeader;; tmp = tmp->next) {
1257 /* see if there is already a field by this name */
1258 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1259 /* replace the old value */
1260 p_delete(&tmp->data);
1261 tmp->data = buf->data;
1268 tmp->next = string_item_new();
1272 tmp = string_item_new();
1275 tmp->data = buf->data;
1281 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1282 char* errbuf, ssize_t errlen) {
1285 if (m_strncmp("reverse-", s, 8) == 0) {
1287 flags = SORT_REVERSE;
1290 if (m_strncmp("last-", s, 5) == 0) {
1295 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1297 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1301 *((short*) dst->data) = i | flags;
1305 /* if additional data more == 1, we want to resolve synonyms */
1306 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
1308 char buf[LONG_STRING];
1309 struct option_t *ptr = p;
1311 if (DTYPE(ptr->type) == DT_SYN) {
1314 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1316 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_fromstr)
1319 mutt_option_value(ptr->option, buf, sizeof(buf));
1320 if (m_strlen(ptr->init) == 0 && buf && *buf)
1321 ptr->init = m_strdup(buf);
1324 static int init_expand (char** dst, struct option_t* src) {
1330 if (DTYPE(src->type) == DT_STR ||
1331 DTYPE(src->type) == DT_PATH) {
1332 /* only expand for string as it's the only place where
1333 * we want to expand vars right now */
1334 if (src->init && *src->init) {
1337 len = m_strlen(src->init) + 2;
1338 in.data = p_new(char, len + 1);
1339 snprintf (in.data, len, "\"%s\"", src->init);
1342 mutt_extract_token (&token, &in, 0);
1343 if (token.data && *token.data)
1344 *dst = m_strdup(token.data);
1346 *dst = m_strdup("");
1348 p_delete(&token.data);
1350 *dst = m_strdup("");
1352 /* for non-string: take value as is */
1353 *dst = m_strdup(src->init);
1357 /* if additional data more == 1, we want to resolve synonyms */
1358 static void mutt_restore_default (const char* name __attribute__ ((unused)),
1359 void* p, unsigned long more) {
1360 char errbuf[STRING];
1361 struct option_t* ptr = (struct option_t*) p;
1364 if (DTYPE (ptr->type) == DT_SYN) {
1367 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1371 if (FuncTable[DTYPE (ptr->type)].opt_fromstr) {
1372 init_expand (&init, ptr);
1373 if (!FuncTable[DTYPE (ptr->type)].opt_fromstr (ptr, init, errbuf,
1375 if (!option (OPTNOCURSES))
1377 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1378 "Please report this error: \"%s\"\n"),
1379 ptr->option, NONULL (init), errbuf);
1385 if (ptr->flags & R_INDEX)
1386 set_option (OPTFORCEREDRAWINDEX);
1387 if (ptr->flags & R_PAGER)
1388 set_option (OPTFORCEREDRAWPAGER);
1389 if (ptr->flags & R_RESORT_SUB)
1390 set_option (OPTSORTSUBTHREADS);
1391 if (ptr->flags & R_RESORT)
1392 set_option (OPTNEEDRESORT);
1393 if (ptr->flags & R_RESORT_INIT)
1394 set_option (OPTRESORTINIT);
1395 if (ptr->flags & R_TREE)
1396 set_option (OPTREDRAWTREE);
1399 /* check whether value for $dsn_return would be valid */
1400 static int check_dsn_return (const char* option __attribute__ ((unused)), unsigned long p,
1401 char* errbuf, ssize_t errlen) {
1402 char* val = (char*) p;
1403 if (val && *val && m_strncmp(val, "hdrs", 4) != 0 &&
1404 m_strncmp(val, "full", 4) != 0) {
1406 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1412 /* check whether value for $dsn_notify would be valid */
1414 check_dsn_notify (const char* option __attribute__ ((unused)),
1415 unsigned long val, char* errbuf, ssize_t errlen)
1417 const char *p = (const char*)val;
1420 const char *q = m_strchrnul(p, ',');
1423 if (!m_strncmp(p, "never", len) && !m_strncmp(p, "delay", len)
1424 && !m_strncmp(p, "failure", len) && !m_strncmp(p, "success", len))
1427 snprintf(errbuf, errlen, _("'%.*s' is invalid for $%s"),
1428 len, p, "dsn_notify");
1438 static int check_num (const char* option, unsigned long p,
1439 char* errbuf, ssize_t errlen) {
1442 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1448 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1449 char* errbuf, ssize_t errlen) {
1450 if (!check_num ("history", p, errbuf, errlen))
1452 mutt_init_history ();
1456 static int check_special (const char* name, unsigned long val,
1457 char* errbuf, ssize_t errlen) {
1460 for (i = 0; SpecialVars[i].name; i++) {
1461 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1462 return (SpecialVars[i].check (SpecialVars[i].name,
1463 val, errbuf, errlen));
1469 static const struct mapping_t* get_sortmap (struct option_t* option) {
1470 const struct mapping_t* map = NULL;
1472 switch (option->type & DT_SUBTYPE_MASK) {
1474 map = SortAliasMethods;
1476 case DT_SORT_BROWSER:
1477 map = SortBrowserMethods;
1480 map = SortKeyMethods;
1483 map = SortAuxMethods;
1492 #define CHECK_PAGER \
1493 if ((CurrentMenu == MENU_PAGER) && \
1494 (!option || (option->flags & R_RESORT))) \
1496 snprintf (err->data, err->dsize, \
1497 _("Not available in this menu.")); \
1501 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1504 int query, unset, inv, reset, r = 0;
1505 struct option_t* option = NULL;
1507 while (MoreArgs (s)) {
1508 /* reset state variables */
1510 unset = data & M_SET_UNSET;
1511 inv = data & M_SET_INV;
1512 reset = data & M_SET_RESET;
1514 if (*s->dptr == '?') {
1518 else if (m_strncmp("no", s->dptr, 2) == 0) {
1522 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1526 else if (*s->dptr == '&') {
1531 /* get the variable name */
1532 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1534 /* resolve synonyms */
1535 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1536 DTYPE (option->type == DT_SYN))
1538 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1539 syn_t* syn = syn_new();
1540 syn->f = m_strdup(CurRCFile);
1544 syn_list_push(&Synonyms, syn);
1548 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1549 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1552 s->dptr = vskipspaces(s->dptr);
1555 if (query || unset || inv) {
1556 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1560 if (s && *s->dptr == '=') {
1561 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1565 if (!m_strcmp("all", tmp->data)) {
1566 if (CurrentMenu == MENU_PAGER) {
1567 snprintf (err->data, err->dsize, _("Not available in this menu."));
1570 hash_map (ConfigOptions, mutt_restore_default, 1);
1571 set_option (OPTFORCEREDRAWINDEX);
1572 set_option (OPTFORCEREDRAWPAGER);
1573 set_option (OPTSORTSUBTHREADS);
1574 set_option (OPTNEEDRESORT);
1575 set_option (OPTRESORTINIT);
1576 set_option (OPTREDRAWTREE);
1580 mutt_restore_default (NULL, option, 1);
1583 else if (DTYPE (option->type) == DT_BOOL) {
1584 /* XXX this currently ignores the function table
1585 * as we don't get invert and stuff into it */
1586 if (s && *s->dptr == '=') {
1587 if (unset || inv || query) {
1588 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1593 mutt_extract_token (tmp, s, 0);
1594 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1596 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1599 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1605 bool_to_string (err->data, err->dsize, option);
1611 unset_option (option->data);
1613 toggle_option (option->data);
1615 set_option (option->data);
1617 else if (DTYPE (option->type) == DT_STR ||
1618 DTYPE (option->type) == DT_PATH ||
1619 DTYPE (option->type) == DT_ADDR ||
1620 DTYPE (option->type) == DT_MAGIC ||
1621 DTYPE (option->type) == DT_NUM ||
1622 DTYPE (option->type) == DT_SORT ||
1623 DTYPE (option->type) == DT_RX)
1625 /* XXX maybe we need to get unset into handlers? */
1626 if (DTYPE (option->type) == DT_STR ||
1627 DTYPE (option->type) == DT_PATH ||
1628 DTYPE (option->type) == DT_ADDR)
1632 if (DTYPE (option->type) == DT_ADDR)
1633 address_list_wipe((address_t **) option->data);
1635 p_delete((void **)(void *)&option->data);
1640 if (query || *s->dptr != '=') {
1641 FuncTable[DTYPE (option->type)].opt_tostr
1642 (err->data, err->dsize, option);
1648 mutt_extract_token (tmp, s, 0);
1649 if (!FuncTable[DTYPE (option->type)].opt_fromstr
1650 (option, tmp->data, err->data, err->dsize))
1653 else if (DTYPE (option->type) == DT_QUAD) {
1656 quad_to_string (err->data, err->dsize, option);
1660 if (*s->dptr == '=') {
1663 mutt_extract_token (tmp, s, 0);
1664 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1665 set_quadoption (option->data, M_YES);
1666 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1667 set_quadoption (option->data, M_NO);
1668 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1669 set_quadoption (option->data, M_ASKYES);
1670 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1671 set_quadoption (option->data, M_ASKNO);
1673 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1674 tmp->data, option->option);
1681 toggle_quadoption (option->data);
1683 set_quadoption (option->data, M_NO);
1685 set_quadoption (option->data, M_YES);
1689 snprintf (err->data, err->dsize, _("%s: unknown type"),
1695 if (option->flags & R_INDEX)
1696 set_option (OPTFORCEREDRAWINDEX);
1697 if (option->flags & R_PAGER)
1698 set_option (OPTFORCEREDRAWPAGER);
1699 if (option->flags & R_RESORT_SUB)
1700 set_option (OPTSORTSUBTHREADS);
1701 if (option->flags & R_RESORT)
1702 set_option (OPTNEEDRESORT);
1703 if (option->flags & R_RESORT_INIT)
1704 set_option (OPTRESORTINIT);
1705 if (option->flags & R_TREE)
1706 set_option (OPTREDRAWTREE);
1713 /* reads the specified initialization file. returns -1 if errors were found
1714 so that we can pause to let the user know... */
1715 static int source_rc (const char *rcfile, BUFFER * err)
1718 int line = 0, rc = 0, conv = 0;
1720 char *linebuf = NULL;
1721 char *currentline = NULL;
1725 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1726 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1731 while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
1732 conv = ConfigCharset && (*ConfigCharset) && Charset;
1734 currentline = m_strdup(linebuf);
1737 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1740 currentline = linebuf;
1745 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1746 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1747 if (--rc < -MAXERRS) {
1749 p_delete(¤tline);
1758 p_delete(¤tline);
1760 p_delete(&token.data);
1764 mutt_wait_filter (pid);
1766 /* the muttrc source keyword */
1767 snprintf (err->data, err->dsize,
1768 rc >= -MAXERRS ? _("source: errors in %s")
1769 : _("source: reading aborted due too many errors in %s"),
1778 static int parse_source (BUFFER * tmp, BUFFER * s,
1779 unsigned long data __attribute__ ((unused)),
1782 char path[_POSIX_PATH_MAX];
1786 if (mutt_extract_token (tmp, s, 0) != 0) {
1787 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1791 m_strcpy(path, sizeof(path), tmp->data);
1792 mutt_expand_path (path, sizeof(path));
1794 rc += source_rc (path, err);
1796 while (MoreArgs (s));
1798 return ((rc < 0) ? -1 : 0);
1801 /* line command to execute
1803 token scratch buffer to be used by parser. caller should free
1804 token->data when finished. the reason for this variable is
1805 to avoid having to allocate and deallocate a lot of memory
1806 if we are parsing many lines. the caller can pass in the
1807 memory to use, which avoids having to create new space for
1808 every call to this function.
1810 err where to write error messages */
1811 int mutt_parse_rc_line (const char *line, BUFFER * token, BUFFER * err)
1817 expn.data = expn.dptr = line;
1818 expn.dsize = m_strlen(line);
1822 expn.dptr = vskipspaces(expn.dptr);
1823 while (*expn.dptr) {
1824 if (*expn.dptr == '#')
1825 break; /* rest of line is a comment */
1826 if (*expn.dptr == ';') {
1830 mutt_extract_token (token, &expn, 0);
1831 for (i = 0; Commands[i].name; i++) {
1832 if (!m_strcmp(token->data, Commands[i].name)) {
1833 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1838 if (!Commands[i].name) {
1839 snprintf (err->data, err->dsize, _("%s: unknown command"),
1840 NONULL (token->data));
1847 p_delete(&expn.data);
1852 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
1853 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
1854 /* initial string that starts completion. No telling how much crap
1855 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1856 char User_typed[LONG_STRING] = { 0 };
1858 int Num_matched = 0; /* Number of matches for completion */
1859 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1860 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1862 /* helper function for completion. Changes the dest buffer if
1863 necessary/possible to aid completion.
1864 dest == completion result gets here.
1865 src == candidate for completion.
1866 try == user entered data for completion.
1867 len == length of dest buffer.
1869 static void candidate (char *dest, char *try, const char *src, int len)
1873 if (strstr (src, try) == src) {
1874 Matches[Num_matched++] = src;
1876 m_strcpy(dest, len, src);
1878 for (l = 0; src[l] && src[l] == dest[l]; l++);
1884 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
1888 int spaces; /* keep track of the number of leading spaces on the line */
1890 buffer = vskipspaces(buffer);
1891 spaces = buffer - pt;
1893 pt = buffer + pos - spaces;
1894 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1897 if (pt == buffer) { /* complete cmd */
1898 /* first TAB. Collect all the matches */
1901 m_strcpy(User_typed, sizeof(User_typed), pt);
1902 p_clear(Matches, countof(Matches));
1903 p_clear(Completed, countof(Completed));
1904 for (num = 0; Commands[num].name; num++)
1905 candidate (Completed, User_typed, Commands[num].name,
1907 Matches[Num_matched++] = User_typed;
1909 /* All matches are stored. Longest non-ambiguous string is ""
1910 * i.e. dont change 'buffer'. Fake successful return this time */
1911 if (User_typed[0] == 0)
1915 if (Completed[0] == 0 && User_typed[0])
1918 /* Num_matched will _always_ be atleast 1 since the initial
1919 * user-typed string is always stored */
1920 if (numtabs == 1 && Num_matched == 2)
1921 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1922 else if (numtabs > 1 && Num_matched > 2)
1923 /* cycle thru all the matches */
1924 snprintf (Completed, sizeof(Completed), "%s",
1925 Matches[(numtabs - 2) % Num_matched]);
1927 /* return the completed command */
1928 m_strcpy(buffer, len - spaces, Completed);
1930 else if (!m_strncmp(buffer, "set", 3)
1931 || !m_strncmp(buffer, "unset", 5)
1932 || !m_strncmp(buffer, "reset", 5)
1933 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
1934 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
1937 /* loop through all the possible prefixes (no, inv, ...) */
1938 if (!m_strncmp(buffer, "set", 3)) {
1939 for (num = 0; prefixes[num]; num++) {
1940 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
1941 pt += m_strlen(prefixes[num]);
1947 /* first TAB. Collect all the matches */
1950 m_strcpy(User_typed, sizeof(User_typed), pt);
1951 p_clear(Matches, countof(Matches));
1952 p_clear(Completed, countof(Completed));
1953 for (num = 0; MuttVars[num].option; num++)
1954 candidate(Completed, User_typed, MuttVars[num].option,
1956 Matches[Num_matched++] = User_typed;
1958 /* All matches are stored. Longest non-ambiguous string is ""
1959 * i.e. dont change 'buffer'. Fake successful return this time */
1960 if (User_typed[0] == 0)
1964 if (Completed[0] == 0 && User_typed[0])
1967 /* Num_matched will _always_ be atleast 1 since the initial
1968 * user-typed string is always stored */
1969 if (numtabs == 1 && Num_matched == 2)
1970 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1971 else if (numtabs > 1 && Num_matched > 2)
1972 /* cycle thru all the matches */
1973 snprintf (Completed, sizeof(Completed), "%s",
1974 Matches[(numtabs - 2) % Num_matched]);
1976 m_strcpy(pt, buffer + len - pt - spaces, Completed);
1978 else if (!m_strncmp(buffer, "exec", 4)) {
1979 struct binding_t *menu = km_get_table (CurrentMenu);
1981 if (!menu && CurrentMenu != MENU_PAGER)
1985 /* first TAB. Collect all the matches */
1988 m_strcpy(User_typed, sizeof(User_typed), pt);
1989 p_clear(Matches, countof(Matches));
1990 p_clear(Completed, countof(Completed));
1991 for (num = 0; menu[num].name; num++)
1992 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
1993 /* try the generic menu */
1994 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1996 for (num = 0; menu[num].name; num++)
1997 candidate (Completed, User_typed, menu[num].name,
2000 Matches[Num_matched++] = User_typed;
2002 /* All matches are stored. Longest non-ambiguous string is ""
2003 * i.e. dont change 'buffer'. Fake successful return this time */
2004 if (User_typed[0] == 0)
2008 if (Completed[0] == 0 && User_typed[0])
2011 /* Num_matched will _always_ be atleast 1 since the initial
2012 * user-typed string is always stored */
2013 if (numtabs == 1 && Num_matched == 2)
2014 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2015 else if (numtabs > 1 && Num_matched > 2)
2016 /* cycle thru all the matches */
2017 snprintf (Completed, sizeof(Completed), "%s",
2018 Matches[(numtabs - 2) % Num_matched]);
2020 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2028 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
2030 char var[STRING], *pt = buffer;
2032 struct option_t* option = NULL;
2037 buffer = vskipspaces(buffer);
2038 spaces = buffer - pt;
2040 pt = buffer + pos - spaces;
2041 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2043 pt++; /* move past the space */
2044 if (*pt == '=') /* abort if no var before the '=' */
2047 if (m_strncmp(buffer, "set", 3) == 0) {
2048 m_strcpy(var, sizeof(var), pt);
2049 /* ignore the trailing '=' when comparing */
2050 var[m_strlen(var) - 1] = 0;
2051 if (!(option = hash_find (ConfigOptions, var)))
2052 return 0; /* no such variable. */
2054 char tmp[LONG_STRING], tmp2[LONG_STRING];
2056 ssize_t dlen = buffer + len - pt - spaces;
2057 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2061 if ((DTYPE (option->type) == DT_STR) ||
2062 (DTYPE (option->type) == DT_PATH) ||
2063 (DTYPE (option->type) == DT_RX)) {
2064 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
2065 if (DTYPE (option->type) == DT_PATH)
2066 mutt_pretty_mailbox (tmp);
2068 else if (DTYPE (option->type) == DT_ADDR) {
2069 rfc822_addrcat(tmp, sizeof(tmp), *((address_t **) option->data), 0);
2071 else if (DTYPE (option->type) == DT_QUAD)
2072 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
2073 else if (DTYPE (option->type) == DT_NUM)
2074 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
2075 else if (DTYPE (option->type) == DT_SORT) {
2076 const struct mapping_t *map;
2079 switch (option->type & DT_SUBTYPE_MASK) {
2081 map = SortAliasMethods;
2083 case DT_SORT_BROWSER:
2084 map = SortBrowserMethods;
2087 map = SortKeyMethods;
2093 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2094 snprintf(tmp, sizeof(tmp), "%s%s%s",
2095 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2096 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2098 else if (DTYPE (option->type) == DT_MAGIC) {
2100 switch (DefaultMagic) {
2116 m_strcpy(tmp, sizeof(tmp), p);
2118 else if (DTYPE (option->type) == DT_BOOL)
2119 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
2123 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
2124 if (*s == '\\' || *s == '"')
2130 m_strcpy(tmp, sizeof(tmp), pt);
2131 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2139 /* Implement the -Q command line flag */
2140 int mutt_query_variables (string_list_t * queries)
2144 char errbuff[STRING];
2145 char command[STRING];
2153 err.dsize = sizeof(errbuff);
2155 for (p = queries; p; p = p->next) {
2156 snprintf (command, sizeof(command), "set ?%s\n", p->data);
2157 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2158 fprintf (stderr, "%s\n", err.data);
2159 p_delete(&token.data);
2162 printf ("%s\n", err.data);
2165 p_delete(&token.data);
2169 static int mutt_execute_commands (string_list_t * p)
2172 char errstr[STRING];
2176 err.dsize = sizeof(errstr);
2178 for (; p; p = p->next) {
2179 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2180 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2181 p_delete(&token.data);
2185 p_delete(&token.data);
2189 void mutt_init (int skip_sys_rc, string_list_t * commands)
2192 struct utsname utsname;
2194 char buffer[STRING], error[STRING];
2195 int default_rc = 0, need_pause = 0;
2201 err.dsize = sizeof(error);
2203 ConfigOptions = hash_create (sizeof(MuttVars) * 2, 0);
2204 for (i = 0; MuttVars[i].option; i++) {
2205 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i]);
2209 * XXX - use something even more difficult to predict?
2211 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2212 "\033]9;%ld\a", (long) time (NULL));
2214 /* on one of the systems I use, getcwd() does not return the same prefix
2215 as is listed in the passwd file */
2216 if ((p = getenv ("HOME")))
2217 Homedir = m_strdup(p);
2219 /* Get some information about the user */
2220 if ((pw = getpwuid (getuid ()))) {
2223 Username = m_strdup(pw->pw_name);
2225 Homedir = m_strdup(pw->pw_dir);
2227 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, GecosMask.rx);
2228 Realname = m_strdup(rnbuf);
2234 fputs (_("unable to determine home directory"), stderr);
2237 if ((p = getenv ("USER")))
2238 Username = m_strdup(p);
2241 fputs (_("unable to determine username"), stderr);
2246 /* And about the host... */
2248 /* some systems report the FQDN instead of just the hostname */
2249 if ((p = strchr (utsname.nodename, '.'))) {
2250 Hostname = p_dupstr(utsname.nodename, p - utsname.nodename);
2252 m_strcpy(buffer, sizeof(buffer), p); /* save the domain for below */
2255 Hostname = m_strdup(utsname.nodename);
2257 if (!p && getdnsdomainname(buffer, sizeof(buffer)) == -1)
2258 Fqdn = m_strdup("@");
2260 if (*buffer != '@') {
2261 Fqdn = p_new(char, m_strlen(buffer) + m_strlen(Hostname) + 2);
2262 sprintf (Fqdn, "%s.%s", NONULL(Hostname), buffer);
2265 Fqdn = m_strdup(NONULL (Hostname));
2272 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2274 fgets (buffer, sizeof(buffer), f);
2275 p = vskipspaces(buffer);
2277 while (*q && !isspace(*q))
2280 NewsServer = m_strdup(p);
2284 if ((p = getenv ("NNTPSERVER")))
2285 NewsServer = m_strdup(p);
2288 if ((p = getenv ("MAIL")))
2289 Spoolfile = m_strdup(p);
2290 else if ((p = getenv ("MAILDIR")))
2291 Spoolfile = m_strdup(p);
2294 mutt_concat_path(buffer, sizeof(buffer), NONULL(Homedir), MAILPATH);
2296 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(Username));
2298 Spoolfile = m_strdup(buffer);
2301 if ((p = getenv ("MAILCAPS")))
2302 MailcapPath = m_strdup(p);
2304 /* Default search path from RFC1524 */
2306 m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2307 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2310 Tempdir = m_strdup((p = getenv ("TMPDIR")) ? p : "/tmp");
2312 if ((p = getenv ("REPLYTO")) != NULL) {
2315 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2318 buf.data = buf.dptr = buffer;
2319 buf.dsize = m_strlen(buffer);
2322 parse_my_hdr (&token, &buf, 0, &err);
2323 p_delete(&token.data);
2326 if ((p = getenv ("EMAIL")) != NULL)
2327 From = rfc822_parse_adrlist (NULL, p);
2329 charset_initialize();
2332 /* Set standard defaults */
2333 hash_map (ConfigOptions, mutt_set_default, 0);
2334 hash_map (ConfigOptions, mutt_restore_default, 0);
2336 CurrentMenu = MENU_MAIN;
2339 /* Unset suspend by default if we're the session leader */
2340 if (getsid (0) == getpid ())
2341 unset_option (OPTSUSPEND);
2344 mutt_init_history ();
2347 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL (Homedir));
2348 if (access (buffer, F_OK) == -1)
2349 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
2353 Muttrc = m_strdup(buffer);
2356 m_strcpy(buffer, sizeof(buffer), Muttrc);
2358 mutt_expand_path (buffer, sizeof(buffer));
2359 Muttrc = m_strdup(buffer);
2361 p_delete(&AliasFile);
2362 AliasFile = m_strdup(NONULL (Muttrc));
2364 /* Process the global rc file if it exists and the user hasn't explicity
2365 requested not to via "-n". */
2367 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
2369 if (access (buffer, F_OK) == -1)
2370 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
2371 if (access (buffer, F_OK) == -1)
2372 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
2374 if (access (buffer, F_OK) == -1)
2375 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
2376 if (access (buffer, F_OK) != -1) {
2377 if (source_rc (buffer, &err) != 0) {
2378 fputs (err.data, stderr);
2379 fputc ('\n', stderr);
2385 /* Read the user's initialization file. */
2386 if (access (Muttrc, F_OK) != -1) {
2387 if (!option (OPTNOCURSES))
2389 if (source_rc (Muttrc, &err) != 0) {
2390 fputs (err.data, stderr);
2391 fputc ('\n', stderr);
2395 else if (!default_rc) {
2396 /* file specified by -F does not exist */
2397 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2398 mutt_endwin (buffer);
2403 snprintf(buffer, sizeof(buffer), "%s/.madmutt.lua", NONULL(Homedir));
2404 if (access(buffer, F_OK) < 0)
2405 snprintf(buffer, sizeof(buffer), "%s/.madmutt/cfg.lua", NONULL(Homedir));
2406 if (!access(buffer, F_OK)) {
2407 need_pause = mlua_wrap(mutt_error, mlua_dofile(buffer));
2411 if (mutt_execute_commands (commands) != 0)
2414 /* warn about synonym variables */
2418 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2420 for (syn = Synonyms; syn; syn = syn->next) {
2421 fprintf(stderr, "$%s ($%s should be used) (%s:%d)\n",
2422 syn->o ? NONULL(syn->o->option) : "",
2423 syn->n ? NONULL(syn->n->option) : "",
2424 NONULL(syn->f), syn->l);
2426 fprintf (stderr, _("Warning: synonym variables are scheduled"
2427 " for removal.\n"));
2428 syn_list_wipe(&Synonyms);
2432 if (need_pause && !option (OPTNOCURSES)) {
2433 if (mutt_any_key_to_continue (NULL) == -1)
2438 int mutt_get_hook_type (const char *name)
2440 struct command_t *c;
2442 for (c = Commands; c->name; c++)
2443 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2448 /* dump out the value of all the variables we have */
2449 int mutt_dump_variables (int full) {
2452 /* get all non-synonyms into list... */
2453 for (i = 0; MuttVars[i].option; i++) {
2454 struct option_t *option = MuttVars + i;
2455 char buf[LONG_STRING];
2457 if (DTYPE(option->type) == DT_SYN)
2461 mutt_option_value(option->option, buf, sizeof(buf));
2462 if (!m_strcmp(buf, option->init))
2467 FuncTable[DTYPE(option->type)].opt_tostr(buf, sizeof(buf), option);
2468 printf ("%s\n", buf);
2471 printf ("\n# vi""m:set ft=muttrc:\n");