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_quadoption (int opt, const char *prompt)
499 int v = quadoption (opt);
507 v = mutt_yesorno (prompt, (v == M_ASKYES));
508 CLEARLINE (LINES - 1);
515 static void add_to_list(string_list_t **list, const char *str)
517 /* don't add a NULL or empty string to the list */
518 if (m_strisempty(str))
521 /* check to make sure the item is not already on this list */
523 if (!ascii_strcasecmp(str, (*list)->data))
525 list = &(*list)->next;
528 *list = p_new(string_list_t, 1);
529 (*list)->data = m_strdup(str);
533 add_to_rx_list(rx_t **list, const char *s, int flags, BUFFER *err)
540 if (rx_lookup(list, s))
543 rx = rx_compile(s, flags);
545 snprintf(err->data, err->dsize, "Bad regexp: %s\n", s);
549 rx_list_append(list, rx);
553 static int add_to_spam_list(rx_t **list, const char *pat,
554 const char *templ, BUFFER * err)
558 if (m_strisempty(pat) || !templ)
561 if (!(rx = rx_compile (pat, REG_ICASE))) {
562 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
566 /* check to make sure the item is not already on this list */
568 if (!ascii_strcasecmp(rx->pattern, (*list)->pattern)) {
569 rx_t *tmp = rx_list_pop(list);
572 list = &(*list)->next;
577 rx_set_template(rx, templ);
581 static int remove_from_spam_list (rx_t ** list, const char *pat)
586 if (!m_strcmp((*list)->pattern, pat)) {
587 rx_t *spam = rx_list_pop(list);
591 list = &(*list)->next;
599 static void remove_from_list(string_list_t **l, const char *str)
601 if (!m_strcmp("*", str)) {
602 string_list_wipe(l); /* ``unCMD *'' means delete all current entries */
607 if (!ascii_strcasecmp(str, (*l)->data)) {
608 string_list_t *it = string_list_pop(l);
609 string_item_delete(&it);
616 static int remove_from_rx_list(rx_t **l, const char *str)
618 if (m_strcmp("*", str) == 0) {
623 l = rx_lookup(l, str);
625 rx_t *r = rx_list_pop(l);
633 static int parse_unignore (BUFFER * buf, BUFFER * s,
634 unsigned long data __attribute__ ((unused)),
635 BUFFER * err __attribute__ ((unused)))
638 mutt_extract_token (buf, s, 0);
640 /* don't add "*" to the unignore list */
641 if (m_strcmp (buf->data, "*"))
642 add_to_list (&UnIgnore, buf->data);
644 remove_from_list (&Ignore, buf->data);
645 } while (MoreArgs (s));
650 static int parse_ignore (BUFFER * buf, BUFFER * s,
651 unsigned long data __attribute__ ((unused)),
652 BUFFER * err __attribute__ ((unused)))
655 mutt_extract_token (buf, s, 0);
656 remove_from_list (&UnIgnore, buf->data);
657 add_to_list (&Ignore, buf->data);
658 } while (MoreArgs(s));
662 static int parse_list(BUFFER * buf, BUFFER * s, unsigned long data,
663 BUFFER * err __attribute__ ((unused)))
666 mutt_extract_token (buf, s, 0);
667 add_to_list ((string_list_t **) data, buf->data);
668 } while (MoreArgs(s));
672 static void _alternates_clean (void)
676 if (Context && Context->msgcount) {
677 for (i = 0; i < Context->msgcount; i++)
678 Context->hdrs[i]->recip_valid = 0;
682 static int parse_alternates (BUFFER * buf, BUFFER * s,
683 unsigned long data __attribute__ ((unused)),
684 BUFFER * err __attribute__ ((unused)))
686 _alternates_clean ();
688 mutt_extract_token (buf, s, 0);
689 remove_from_rx_list (&UnAlternates, buf->data);
691 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
694 while (MoreArgs (s));
699 static int parse_unalternates (BUFFER * buf, BUFFER * s,
700 unsigned long data __attribute__ ((unused)),
701 BUFFER * err __attribute__ ((unused)))
703 _alternates_clean ();
705 mutt_extract_token (buf, s, 0);
706 remove_from_rx_list (&Alternates, buf->data);
708 if (m_strcmp(buf->data, "*") &&
709 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
713 while (MoreArgs (s));
718 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
725 /* Insist on at least one parameter */
728 m_strcpy(err->data, err->dsize, _("spam: no matching pattern"));
730 m_strcpy(err->data, err->dsize, _("nospam: no matching pattern"));
734 /* Extract the first token, a regexp */
735 mutt_extract_token (buf, s, 0);
737 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
738 if (data == M_SPAM) {
739 /* If there's a second parameter, it's a template for the spam tag. */
741 mutt_extract_token (&templ, s, 0);
743 /* Add to the spam list. */
744 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
745 p_delete(&templ.data);
748 p_delete(&templ.data);
751 /* If not, try to remove from the nospam list. */
753 remove_from_rx_list (&NoSpamList, buf->data);
759 /* M_NOSPAM is for nospam commands. */
760 else if (data == M_NOSPAM) {
761 /* nospam only ever has one parameter. */
763 /* "*" is a special case. */
764 if (!m_strcmp(buf->data, "*")) {
765 rx_list_wipe(&SpamList);
766 rx_list_wipe(&NoSpamList);
770 /* If it's on the spam list, just remove it. */
771 if (remove_from_spam_list (&SpamList, buf->data) != 0)
774 /* Otherwise, add it to the nospam list. */
775 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
781 /* This should not happen. */
782 m_strcpy(err->data, err->dsize, "This is no good at all.");
786 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
787 BUFFER * err __attribute__ ((unused)))
790 mutt_extract_token (buf, s, 0);
792 * Check for deletion of entire list
794 if (m_strcmp(buf->data, "*") == 0) {
795 string_list_wipe((string_list_t **) data);
798 remove_from_list ((string_list_t **) data, buf->data);
800 while (MoreArgs (s));
805 static int parse_lists (BUFFER * buf, BUFFER * s,
806 unsigned long data __attribute__ ((unused)),
810 mutt_extract_token (buf, s, 0);
811 remove_from_rx_list (&UnMailLists, buf->data);
813 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
816 while (MoreArgs (s));
821 /* always wise to do what someone else did before */
822 static void _attachments_clean (void) {
824 if (Context && Context->msgcount) {
825 for (i = 0; i < Context->msgcount; i++)
826 Context->hdrs[i]->attach_valid = 0;
830 static int parse_attach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
831 BUFFER *err __attribute__ ((unused))) {
833 string_list_t *listp, *lastp;
838 /* Find the last item in the list that data points to. */
840 for (listp = *ldata; listp; listp = listp->next) {
841 a = (ATTACH_MATCH *)listp->data;
846 mutt_extract_token (buf, s, 0);
848 if (!buf->data || *buf->data == '\0')
851 a = p_new(ATTACH_MATCH, 1);
853 /* some cheap hacks that I expect to remove */
854 if (!m_strcasecmp(buf->data, "any"))
855 a->major = m_strdup("*/.*");
856 else if (!m_strcasecmp(buf->data, "none"))
857 a->major = m_strdup("cheap_hack/this_should_never_match");
859 a->major = m_strdup(buf->data);
861 if ((p = strchr(a->major, '/'))) {
866 a->minor = "unknown";
869 len = m_strlen(a->minor);
870 tmpminor = p_new(char, len + 3);
871 m_strcpy(&tmpminor[1], len + 3, a->minor);
873 tmpminor[len+1] = '$';
874 tmpminor[len+2] = '\0';
876 a->major_int = mutt_check_mime_type(a->major);
877 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
881 listp = p_new(string_list_t, 1);
882 listp->data = (char *)a;
891 while (MoreArgs (s));
893 _attachments_clean();
897 static int parse_unattach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
898 BUFFER *err __attribute__ ((unused))) {
900 string_list_t *lp, *lastp, *newlp;
906 mutt_extract_token (buf, s, 0);
908 if (!m_strcasecmp(buf->data, "any"))
909 tmp = m_strdup("*/.*");
910 else if (!m_strcasecmp(buf->data, "none"))
911 tmp = m_strdup("cheap_hack/this_should_never_match");
913 tmp = m_strdup(buf->data);
915 if ((minor = strchr(tmp, '/'))) {
919 minor = m_strdup("unknown");
921 major = mutt_check_mime_type(tmp);
923 /* We must do our own walk here because remove_from_list() will only
924 * remove the string_list_t->data, not anything pointed to by the string_list_t->data. */
926 for(lp = *ldata; lp; ) {
927 a = (ATTACH_MATCH *)lp->data;
928 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
929 regfree(&a->minor_rx);
932 /* Relink backward */
934 lastp->next = lp->next;
939 p_delete(&lp->data); /* same as a */
949 while (MoreArgs (s));
952 _attachments_clean();
956 static int print_attach_list (string_list_t *lp, char op, const char *name) {
958 printf("attachments %c%s %s/%s\n", op, name,
959 ((ATTACH_MATCH *)lp->data)->major,
960 ((ATTACH_MATCH *)lp->data)->minor);
967 static int parse_attachments (BUFFER *buf, BUFFER *s,
968 unsigned long data __attribute__ ((unused)),
971 string_list_t **listp;
973 mutt_extract_token(buf, s, 0);
974 if (!buf->data || *buf->data == '\0') {
975 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
979 category = buf->data;
985 printf("\nCurrent attachments settings:\n\n");
986 print_attach_list(AttachAllow, '+', "A");
987 print_attach_list(AttachExclude, '-', "A");
988 print_attach_list(InlineAllow, '+', "I");
989 print_attach_list(InlineExclude, '-', "I");
990 set_option (OPTFORCEREDRAWINDEX);
991 set_option (OPTFORCEREDRAWPAGER);
992 mutt_any_key_to_continue (NULL);
996 if (op != '+' && op != '-') {
1000 if (!m_strncasecmp(category, "attachment", strlen(category))) {
1002 listp = &AttachAllow;
1004 listp = &AttachExclude;
1006 else if (!m_strncasecmp(category, "inline", strlen(category))) {
1008 listp = &InlineAllow;
1010 listp = &InlineExclude;
1012 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
1016 return parse_attach_list(buf, s, listp, err);
1019 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
1021 string_list_t **listp;
1023 mutt_extract_token(buf, s, 0);
1024 if (!buf->data || *buf->data == '\0') {
1025 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
1031 if (op != '+' && op != '-') {
1035 if (!m_strncasecmp(p, "attachment", strlen(p))) {
1037 listp = &AttachAllow;
1039 listp = &AttachExclude;
1041 else if (!m_strncasecmp(p, "inline", strlen(p))) {
1043 listp = &InlineAllow;
1045 listp = &InlineExclude;
1048 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
1052 return parse_unattach_list(buf, s, listp, err);
1055 static int parse_unlists (BUFFER * buf, BUFFER * s,
1056 unsigned long data __attribute__ ((unused)),
1057 BUFFER * err __attribute__ ((unused)))
1060 mutt_extract_token (buf, s, 0);
1061 remove_from_rx_list (&SubscribedLists, buf->data);
1062 remove_from_rx_list (&MailLists, buf->data);
1064 if (m_strcmp(buf->data, "*") &&
1065 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1068 while (MoreArgs (s));
1073 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1077 mutt_extract_token (buf, s, 0);
1078 remove_from_rx_list (&UnMailLists, buf->data);
1079 remove_from_rx_list (&UnSubscribedLists, buf->data);
1081 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1083 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1086 while (MoreArgs (s));
1091 static int parse_unsubscribe (BUFFER * buf, BUFFER * s,
1092 unsigned long data __attribute__ ((unused)),
1093 BUFFER * err __attribute__ ((unused)))
1096 mutt_extract_token (buf, s, 0);
1097 remove_from_rx_list (&SubscribedLists, buf->data);
1099 if (m_strcmp(buf->data, "*") &&
1100 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1103 while (MoreArgs (s));
1108 static int parse_unalias (BUFFER * buf, BUFFER * s,
1109 unsigned long data __attribute__ ((unused)),
1110 BUFFER * err __attribute__ ((unused)))
1112 alias_t *tmp, **last;
1115 mutt_extract_token (buf, s, 0);
1117 if (!m_strcmp("*", buf->data) == 0) {
1118 if (CurrentMenu == MENU_ALIAS) {
1119 for (tmp = Aliases; tmp; tmp = tmp->next)
1121 set_option(OPTFORCEREDRAWINDEX);
1123 alias_list_wipe(&Aliases);
1129 for (last = &Aliases; *last; last = &(*last)->next) {
1130 if (!m_strcasecmp(buf->data, (*last)->name)) {
1131 if (CurrentMenu == MENU_ALIAS) {
1133 set_option (OPTFORCEREDRAWINDEX);
1135 tmp = alias_list_pop(last);
1141 } while (MoreArgs(s));
1146 static int parse_alias (BUFFER * buf, BUFFER * s,
1147 unsigned long data __attribute__ ((unused)),
1153 if (!MoreArgs (s)) {
1154 m_strcpy(err->data, err->dsize, _("alias: no address"));
1158 mutt_extract_token (buf, s, 0);
1160 /* check to see if an alias with this name already exists */
1161 for (last = &Aliases; *last; last = &(*last)->next) {
1162 if (!m_strcasecmp((*last)->name, buf->data))
1167 /* create a new alias */
1168 *last = alias_new();
1169 (*last)->name = m_strdup(buf->data);
1170 /* give the main addressbook code a chance */
1171 if (CurrentMenu == MENU_ALIAS)
1172 set_option (OPTMENUCALLER);
1174 /* override the previous value */
1175 address_list_wipe(&(*last)->addr);
1176 if (CurrentMenu == MENU_ALIAS)
1177 set_option (OPTFORCEREDRAWINDEX);
1180 mutt_extract_token(buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE);
1181 (*last)->addr = mutt_parse_adrlist((*last)->addr, buf->data);
1182 if (mutt_addrlist_to_idna((*last)->addr, &estr)) {
1183 snprintf (err->data, err->dsize,
1184 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, (*last)->name);
1193 parse_unmy_hdr(BUFFER * buf, BUFFER * s,
1194 unsigned long data __attribute__ ((unused)),
1195 BUFFER * err __attribute__ ((unused)))
1198 mutt_extract_token (buf, s, 0);
1200 if (!m_strcmp("*", buf->data)) {
1201 string_list_wipe(&UserHeader);
1203 string_list_t **last = &UserHeader;
1204 ssize_t l = m_strlen(buf->data);
1206 if (buf->data[l - 1] == ':')
1210 if (!ascii_strncasecmp(buf->data, (*last)->data, l)
1211 && (*last)->data[l] == ':')
1213 string_list_t *tmp = string_list_pop(last);
1214 string_item_delete(&tmp);
1216 last = &(*last)->next;
1220 } while (MoreArgs(s));
1225 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1232 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1233 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1234 m_strcpy(err->data, err->dsize, _("invalid header field"));
1237 keylen = p - buf->data + 1;
1240 for (tmp = UserHeader;; tmp = tmp->next) {
1241 /* see if there is already a field by this name */
1242 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1243 /* replace the old value */
1244 p_delete(&tmp->data);
1245 tmp->data = buf->data;
1252 tmp->next = string_item_new();
1256 tmp = string_item_new();
1259 tmp->data = buf->data;
1265 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1266 char* errbuf, ssize_t errlen) {
1269 if (m_strncmp("reverse-", s, 8) == 0) {
1271 flags = SORT_REVERSE;
1274 if (m_strncmp("last-", s, 5) == 0) {
1279 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1281 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1285 *((short*) dst->data) = i | flags;
1289 /* if additional data more == 1, we want to resolve synonyms */
1290 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
1292 char buf[LONG_STRING];
1293 struct option_t *ptr = p;
1295 if (DTYPE(ptr->type) == DT_SYN) {
1298 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1300 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_fromstr)
1303 mutt_option_value(ptr->option, buf, sizeof(buf));
1304 if (m_strlen(ptr->init) == 0 && buf && *buf)
1305 ptr->init = m_strdup(buf);
1308 static struct option_t* add_option (const char* name, const char* init,
1309 short type, short dodup) {
1310 struct option_t* option = p_new(struct option_t, 1);
1312 option->option = m_strdup(name);
1313 option->type = type;
1315 option->init = dodup ? m_strdup(init) : (char*) init;
1319 static int init_expand (char** dst, struct option_t* src) {
1325 if (DTYPE(src->type) == DT_STR ||
1326 DTYPE(src->type) == DT_PATH) {
1327 /* only expand for string as it's the only place where
1328 * we want to expand vars right now */
1329 if (src->init && *src->init) {
1332 len = m_strlen(src->init) + 2;
1333 in.data = p_new(char, len + 1);
1334 snprintf (in.data, len, "\"%s\"", src->init);
1337 mutt_extract_token (&token, &in, 0);
1338 if (token.data && *token.data)
1339 *dst = m_strdup(token.data);
1341 *dst = m_strdup("");
1343 p_delete(&token.data);
1345 *dst = m_strdup("");
1347 /* for non-string: take value as is */
1348 *dst = m_strdup(src->init);
1352 /* if additional data more == 1, we want to resolve synonyms */
1353 static void mutt_restore_default (const char* name __attribute__ ((unused)),
1354 void* p, unsigned long more) {
1355 char errbuf[STRING];
1356 struct option_t* ptr = (struct option_t*) p;
1359 if (DTYPE (ptr->type) == DT_SYN) {
1362 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1366 if (FuncTable[DTYPE (ptr->type)].opt_fromstr) {
1367 init_expand (&init, ptr);
1368 if (!FuncTable[DTYPE (ptr->type)].opt_fromstr (ptr, init, errbuf,
1370 if (!option (OPTNOCURSES))
1372 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1373 "Please report this error: \"%s\"\n"),
1374 ptr->option, NONULL (init), errbuf);
1380 if (ptr->flags & R_INDEX)
1381 set_option (OPTFORCEREDRAWINDEX);
1382 if (ptr->flags & R_PAGER)
1383 set_option (OPTFORCEREDRAWPAGER);
1384 if (ptr->flags & R_RESORT_SUB)
1385 set_option (OPTSORTSUBTHREADS);
1386 if (ptr->flags & R_RESORT)
1387 set_option (OPTNEEDRESORT);
1388 if (ptr->flags & R_RESORT_INIT)
1389 set_option (OPTRESORTINIT);
1390 if (ptr->flags & R_TREE)
1391 set_option (OPTREDRAWTREE);
1394 /* check whether value for $dsn_return would be valid */
1395 static int check_dsn_return (const char* option __attribute__ ((unused)), unsigned long p,
1396 char* errbuf, ssize_t errlen) {
1397 char* val = (char*) p;
1398 if (val && *val && m_strncmp(val, "hdrs", 4) != 0 &&
1399 m_strncmp(val, "full", 4) != 0) {
1401 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1407 /* check whether value for $dsn_notify would be valid */
1409 check_dsn_notify (const char* option __attribute__ ((unused)),
1410 unsigned long val, char* errbuf, ssize_t errlen)
1412 const char *p = (const char*)val;
1415 const char *q = m_strchrnul(p, ',');
1418 if (!m_strncmp(p, "never", len) && !m_strncmp(p, "delay", len)
1419 && !m_strncmp(p, "failure", len) && !m_strncmp(p, "success", len))
1422 snprintf(errbuf, errlen, _("'%.*s' is invalid for $%s"),
1423 len, p, "dsn_notify");
1433 static int check_num (const char* option, unsigned long p,
1434 char* errbuf, ssize_t errlen) {
1437 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1443 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1444 char* errbuf, ssize_t errlen) {
1445 if (!check_num ("history", p, errbuf, errlen))
1447 mutt_init_history ();
1451 static int check_special (const char* name, unsigned long val,
1452 char* errbuf, ssize_t errlen) {
1455 for (i = 0; SpecialVars[i].name; i++) {
1456 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1457 return (SpecialVars[i].check (SpecialVars[i].name,
1458 val, errbuf, errlen));
1464 static const struct mapping_t* get_sortmap (struct option_t* option) {
1465 const struct mapping_t* map = NULL;
1467 switch (option->type & DT_SUBTYPE_MASK) {
1469 map = SortAliasMethods;
1471 case DT_SORT_BROWSER:
1472 map = SortBrowserMethods;
1475 map = SortKeyMethods;
1478 map = SortAuxMethods;
1487 #define CHECK_PAGER \
1488 if ((CurrentMenu == MENU_PAGER) && \
1489 (!option || (option->flags & R_RESORT))) \
1491 snprintf (err->data, err->dsize, \
1492 _("Not available in this menu.")); \
1496 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1499 int query, unset, inv, reset, r = 0;
1500 struct option_t* option = NULL;
1502 while (MoreArgs (s)) {
1503 /* reset state variables */
1505 unset = data & M_SET_UNSET;
1506 inv = data & M_SET_INV;
1507 reset = data & M_SET_RESET;
1509 if (*s->dptr == '?') {
1513 else if (m_strncmp("no", s->dptr, 2) == 0) {
1517 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1521 else if (*s->dptr == '&') {
1526 /* get the variable name */
1527 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1529 /* resolve synonyms */
1530 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1531 DTYPE (option->type == DT_SYN))
1533 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1534 syn_t* syn = syn_new();
1535 syn->f = m_strdup(CurRCFile);
1539 syn_list_push(&Synonyms, syn);
1543 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1544 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1547 s->dptr = vskipspaces(s->dptr);
1550 if (query || unset || inv) {
1551 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1555 if (s && *s->dptr == '=') {
1556 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1560 if (!m_strcmp("all", tmp->data)) {
1561 if (CurrentMenu == MENU_PAGER) {
1562 snprintf (err->data, err->dsize, _("Not available in this menu."));
1565 hash_map (ConfigOptions, mutt_restore_default, 1);
1566 set_option (OPTFORCEREDRAWINDEX);
1567 set_option (OPTFORCEREDRAWPAGER);
1568 set_option (OPTSORTSUBTHREADS);
1569 set_option (OPTNEEDRESORT);
1570 set_option (OPTRESORTINIT);
1571 set_option (OPTREDRAWTREE);
1574 else if (!FuncTable[DTYPE (option->type)].opt_fromstr) {
1575 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
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 (!FuncTable[DTYPE (option->type)].opt_fromstr) {
1633 snprintf (err->data, err->dsize, _("$%s is read-only"),
1637 } else if (DTYPE (option->type) == DT_ADDR)
1638 address_list_wipe((address_t **) option->data);
1640 p_delete((void **)(void *)&option->data);
1645 if (query || *s->dptr != '=') {
1646 FuncTable[DTYPE (option->type)].opt_tostr
1647 (err->data, err->dsize, option);
1651 /* the $madmutt_ variables are read-only */
1652 if (!FuncTable[DTYPE (option->type)].opt_fromstr) {
1653 snprintf (err->data, err->dsize, _("$%s is read-only"),
1660 mutt_extract_token (tmp, s, 0);
1661 if (!FuncTable[DTYPE (option->type)].opt_fromstr
1662 (option, tmp->data, err->data, err->dsize))
1666 else if (DTYPE (option->type) == DT_QUAD) {
1669 quad_to_string (err->data, err->dsize, option);
1673 if (*s->dptr == '=') {
1676 mutt_extract_token (tmp, s, 0);
1677 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1678 set_quadoption (option->data, M_YES);
1679 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1680 set_quadoption (option->data, M_NO);
1681 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1682 set_quadoption (option->data, M_ASKYES);
1683 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1684 set_quadoption (option->data, M_ASKNO);
1686 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1687 tmp->data, option->option);
1694 toggle_quadoption (option->data);
1696 set_quadoption (option->data, M_NO);
1698 set_quadoption (option->data, M_YES);
1702 snprintf (err->data, err->dsize, _("%s: unknown type"),
1708 if (option->flags & R_INDEX)
1709 set_option (OPTFORCEREDRAWINDEX);
1710 if (option->flags & R_PAGER)
1711 set_option (OPTFORCEREDRAWPAGER);
1712 if (option->flags & R_RESORT_SUB)
1713 set_option (OPTSORTSUBTHREADS);
1714 if (option->flags & R_RESORT)
1715 set_option (OPTNEEDRESORT);
1716 if (option->flags & R_RESORT_INIT)
1717 set_option (OPTRESORTINIT);
1718 if (option->flags & R_TREE)
1719 set_option (OPTREDRAWTREE);
1726 /* reads the specified initialization file. returns -1 if errors were found
1727 so that we can pause to let the user know... */
1728 static int source_rc (const char *rcfile, BUFFER * err)
1731 int line = 0, rc = 0, conv = 0;
1733 char *linebuf = NULL;
1734 char *currentline = NULL;
1738 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1739 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1744 while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
1745 conv = ConfigCharset && (*ConfigCharset) && Charset;
1747 currentline = m_strdup(linebuf);
1750 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1753 currentline = linebuf;
1758 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1759 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1760 if (--rc < -MAXERRS) {
1762 p_delete(¤tline);
1771 p_delete(¤tline);
1773 p_delete(&token.data);
1777 mutt_wait_filter (pid);
1779 /* the muttrc source keyword */
1780 snprintf (err->data, err->dsize,
1781 rc >= -MAXERRS ? _("source: errors in %s")
1782 : _("source: reading aborted due too many errors in %s"),
1791 static int parse_source (BUFFER * tmp, BUFFER * s,
1792 unsigned long data __attribute__ ((unused)),
1795 char path[_POSIX_PATH_MAX];
1799 if (mutt_extract_token (tmp, s, 0) != 0) {
1800 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1804 m_strcpy(path, sizeof(path), tmp->data);
1805 mutt_expand_path (path, sizeof(path));
1807 rc += source_rc (path, err);
1809 while (MoreArgs (s));
1811 return ((rc < 0) ? -1 : 0);
1814 /* line command to execute
1816 token scratch buffer to be used by parser. caller should free
1817 token->data when finished. the reason for this variable is
1818 to avoid having to allocate and deallocate a lot of memory
1819 if we are parsing many lines. the caller can pass in the
1820 memory to use, which avoids having to create new space for
1821 every call to this function.
1823 err where to write error messages */
1824 int mutt_parse_rc_line (const char *line, BUFFER * token, BUFFER * err)
1830 expn.data = expn.dptr = line;
1831 expn.dsize = m_strlen(line);
1835 expn.dptr = vskipspaces(expn.dptr);
1836 while (*expn.dptr) {
1837 if (*expn.dptr == '#')
1838 break; /* rest of line is a comment */
1839 if (*expn.dptr == ';') {
1843 mutt_extract_token (token, &expn, 0);
1844 for (i = 0; Commands[i].name; i++) {
1845 if (!m_strcmp(token->data, Commands[i].name)) {
1846 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1851 if (!Commands[i].name) {
1852 snprintf (err->data, err->dsize, _("%s: unknown command"),
1853 NONULL (token->data));
1860 p_delete(&expn.data);
1865 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
1866 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
1867 /* initial string that starts completion. No telling how much crap
1868 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1869 char User_typed[LONG_STRING] = { 0 };
1871 int Num_matched = 0; /* Number of matches for completion */
1872 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1873 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1875 /* helper function for completion. Changes the dest buffer if
1876 necessary/possible to aid completion.
1877 dest == completion result gets here.
1878 src == candidate for completion.
1879 try == user entered data for completion.
1880 len == length of dest buffer.
1882 static void candidate (char *dest, char *try, const char *src, int len)
1886 if (strstr (src, try) == src) {
1887 Matches[Num_matched++] = src;
1889 m_strcpy(dest, len, src);
1891 for (l = 0; src[l] && src[l] == dest[l]; l++);
1897 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
1901 int spaces; /* keep track of the number of leading spaces on the line */
1903 buffer = vskipspaces(buffer);
1904 spaces = buffer - pt;
1906 pt = buffer + pos - spaces;
1907 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1910 if (pt == buffer) { /* complete cmd */
1911 /* first TAB. Collect all the matches */
1914 m_strcpy(User_typed, sizeof(User_typed), pt);
1915 p_clear(Matches, countof(Matches));
1916 p_clear(Completed, countof(Completed));
1917 for (num = 0; Commands[num].name; num++)
1918 candidate (Completed, User_typed, Commands[num].name,
1920 Matches[Num_matched++] = User_typed;
1922 /* All matches are stored. Longest non-ambiguous string is ""
1923 * i.e. dont change 'buffer'. Fake successful return this time */
1924 if (User_typed[0] == 0)
1928 if (Completed[0] == 0 && User_typed[0])
1931 /* Num_matched will _always_ be atleast 1 since the initial
1932 * user-typed string is always stored */
1933 if (numtabs == 1 && Num_matched == 2)
1934 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1935 else if (numtabs > 1 && Num_matched > 2)
1936 /* cycle thru all the matches */
1937 snprintf (Completed, sizeof(Completed), "%s",
1938 Matches[(numtabs - 2) % Num_matched]);
1940 /* return the completed command */
1941 m_strcpy(buffer, len - spaces, Completed);
1943 else if (!m_strncmp(buffer, "set", 3)
1944 || !m_strncmp(buffer, "unset", 5)
1945 || !m_strncmp(buffer, "reset", 5)
1946 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
1947 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
1950 /* loop through all the possible prefixes (no, inv, ...) */
1951 if (!m_strncmp(buffer, "set", 3)) {
1952 for (num = 0; prefixes[num]; num++) {
1953 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
1954 pt += m_strlen(prefixes[num]);
1960 /* first TAB. Collect all the matches */
1963 m_strcpy(User_typed, sizeof(User_typed), pt);
1964 p_clear(Matches, countof(Matches));
1965 p_clear(Completed, countof(Completed));
1966 for (num = 0; MuttVars[num].option; num++)
1967 candidate(Completed, User_typed, MuttVars[num].option,
1969 Matches[Num_matched++] = User_typed;
1971 /* All matches are stored. Longest non-ambiguous string is ""
1972 * i.e. dont change 'buffer'. Fake successful return this time */
1973 if (User_typed[0] == 0)
1977 if (Completed[0] == 0 && User_typed[0])
1980 /* Num_matched will _always_ be atleast 1 since the initial
1981 * user-typed string is always stored */
1982 if (numtabs == 1 && Num_matched == 2)
1983 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1984 else if (numtabs > 1 && Num_matched > 2)
1985 /* cycle thru all the matches */
1986 snprintf (Completed, sizeof(Completed), "%s",
1987 Matches[(numtabs - 2) % Num_matched]);
1989 m_strcpy(pt, buffer + len - pt - spaces, Completed);
1991 else if (!m_strncmp(buffer, "exec", 4)) {
1992 struct binding_t *menu = km_get_table (CurrentMenu);
1994 if (!menu && CurrentMenu != MENU_PAGER)
1998 /* first TAB. Collect all the matches */
2001 m_strcpy(User_typed, sizeof(User_typed), pt);
2002 p_clear(Matches, countof(Matches));
2003 p_clear(Completed, countof(Completed));
2004 for (num = 0; menu[num].name; num++)
2005 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
2006 /* try the generic menu */
2007 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2009 for (num = 0; menu[num].name; num++)
2010 candidate (Completed, User_typed, menu[num].name,
2013 Matches[Num_matched++] = User_typed;
2015 /* All matches are stored. Longest non-ambiguous string is ""
2016 * i.e. dont change 'buffer'. Fake successful return this time */
2017 if (User_typed[0] == 0)
2021 if (Completed[0] == 0 && User_typed[0])
2024 /* Num_matched will _always_ be atleast 1 since the initial
2025 * user-typed string is always stored */
2026 if (numtabs == 1 && Num_matched == 2)
2027 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2028 else if (numtabs > 1 && Num_matched > 2)
2029 /* cycle thru all the matches */
2030 snprintf (Completed, sizeof(Completed), "%s",
2031 Matches[(numtabs - 2) % Num_matched]);
2033 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2041 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
2043 char var[STRING], *pt = buffer;
2045 struct option_t* option = NULL;
2050 buffer = vskipspaces(buffer);
2051 spaces = buffer - pt;
2053 pt = buffer + pos - spaces;
2054 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2056 pt++; /* move past the space */
2057 if (*pt == '=') /* abort if no var before the '=' */
2060 if (m_strncmp(buffer, "set", 3) == 0) {
2061 m_strcpy(var, sizeof(var), pt);
2062 /* ignore the trailing '=' when comparing */
2063 var[m_strlen(var) - 1] = 0;
2064 if (!(option = hash_find (ConfigOptions, var)))
2065 return 0; /* no such variable. */
2067 char tmp[LONG_STRING], tmp2[LONG_STRING];
2069 ssize_t dlen = buffer + len - pt - spaces;
2070 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2074 if ((DTYPE (option->type) == DT_STR) ||
2075 (DTYPE (option->type) == DT_PATH) ||
2076 (DTYPE (option->type) == DT_RX)) {
2077 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
2078 if (DTYPE (option->type) == DT_PATH)
2079 mutt_pretty_mailbox (tmp);
2081 else if (DTYPE (option->type) == DT_ADDR) {
2082 rfc822_addrcat(tmp, sizeof(tmp), *((address_t **) option->data), 0);
2084 else if (DTYPE (option->type) == DT_QUAD)
2085 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
2086 else if (DTYPE (option->type) == DT_NUM)
2087 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
2088 else if (DTYPE (option->type) == DT_SORT) {
2089 const struct mapping_t *map;
2092 switch (option->type & DT_SUBTYPE_MASK) {
2094 map = SortAliasMethods;
2096 case DT_SORT_BROWSER:
2097 map = SortBrowserMethods;
2100 map = SortKeyMethods;
2106 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2107 snprintf(tmp, sizeof(tmp), "%s%s%s",
2108 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2109 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2111 else if (DTYPE (option->type) == DT_MAGIC) {
2113 switch (DefaultMagic) {
2129 m_strcpy(tmp, sizeof(tmp), p);
2131 else if (DTYPE (option->type) == DT_BOOL)
2132 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
2136 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
2137 if (*s == '\\' || *s == '"')
2143 m_strcpy(tmp, sizeof(tmp), pt);
2144 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2152 /* Implement the -Q command line flag */
2153 int mutt_query_variables (string_list_t * queries)
2157 char errbuff[STRING];
2158 char command[STRING];
2166 err.dsize = sizeof(errbuff);
2168 for (p = queries; p; p = p->next) {
2169 snprintf (command, sizeof(command), "set ?%s\n", p->data);
2170 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2171 fprintf (stderr, "%s\n", err.data);
2172 p_delete(&token.data);
2175 printf ("%s\n", err.data);
2178 p_delete(&token.data);
2182 static int mutt_execute_commands (string_list_t * p)
2185 char errstr[STRING];
2189 err.dsize = sizeof(errstr);
2191 for (; p; p = p->next) {
2192 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2193 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2194 p_delete(&token.data);
2198 p_delete(&token.data);
2202 void mutt_init (int skip_sys_rc, string_list_t * commands)
2205 struct utsname utsname;
2207 char buffer[STRING], error[STRING];
2208 int default_rc = 0, need_pause = 0;
2214 err.dsize = sizeof(error);
2216 ConfigOptions = hash_create (sizeof(MuttVars) * 2);
2217 for (i = 0; MuttVars[i].option; i++) {
2218 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2222 * XXX - use something even more difficult to predict?
2224 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2225 "\033]9;%ld\a", (long) time (NULL));
2227 /* on one of the systems I use, getcwd() does not return the same prefix
2228 as is listed in the passwd file */
2229 if ((p = getenv ("HOME")))
2230 Homedir = m_strdup(p);
2232 /* Get some information about the user */
2233 if ((pw = getpwuid (getuid ()))) {
2236 Username = m_strdup(pw->pw_name);
2238 Homedir = m_strdup(pw->pw_dir);
2240 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, GecosMask.rx);
2241 Realname = m_strdup(rnbuf);
2242 Shell = m_strdup(pw->pw_shell);
2248 fputs (_("unable to determine home directory"), stderr);
2251 if ((p = getenv ("USER")))
2252 Username = m_strdup(p);
2255 fputs (_("unable to determine username"), stderr);
2258 Shell = m_strdup((p = getenv ("SHELL")) ? p : "/bin/sh");
2261 /* And about the host... */
2263 /* some systems report the FQDN instead of just the hostname */
2264 if ((p = strchr (utsname.nodename, '.'))) {
2265 Hostname = p_dupstr(utsname.nodename, p - utsname.nodename);
2267 m_strcpy(buffer, sizeof(buffer), p); /* save the domain for below */
2270 Hostname = m_strdup(utsname.nodename);
2272 if (!p && getdnsdomainname(buffer, sizeof(buffer)) == -1)
2273 Fqdn = m_strdup("@");
2275 if (*buffer != '@') {
2276 Fqdn = p_new(char, m_strlen(buffer) + m_strlen(Hostname) + 2);
2277 sprintf (Fqdn, "%s.%s", NONULL(Hostname), buffer);
2280 Fqdn = m_strdup(NONULL (Hostname));
2287 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2289 fgets (buffer, sizeof(buffer), f);
2290 p = vskipspaces(buffer);
2292 while (*q && !isspace(*q))
2295 NewsServer = m_strdup(p);
2299 if ((p = getenv ("NNTPSERVER")))
2300 NewsServer = m_strdup(p);
2303 if ((p = getenv ("MAIL")))
2304 Spoolfile = m_strdup(p);
2305 else if ((p = getenv ("MAILDIR")))
2306 Spoolfile = m_strdup(p);
2309 mutt_concat_path(buffer, sizeof(buffer), NONULL(Homedir), MAILPATH);
2311 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(Username));
2313 Spoolfile = m_strdup(buffer);
2316 if ((p = getenv ("MAILCAPS")))
2317 MailcapPath = m_strdup(p);
2319 /* Default search path from RFC1524 */
2321 m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2322 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2325 Tempdir = m_strdup((p = getenv ("TMPDIR")) ? p : "/tmp");
2327 p = getenv ("VISUAL");
2329 p = getenv ("EDITOR");
2333 Editor = m_strdup(p);
2335 if ((p = getenv ("REPLYTO")) != NULL) {
2338 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2341 buf.data = buf.dptr = buffer;
2342 buf.dsize = m_strlen(buffer);
2345 parse_my_hdr (&token, &buf, 0, &err);
2346 p_delete(&token.data);
2349 if ((p = getenv ("EMAIL")) != NULL)
2350 From = rfc822_parse_adrlist (NULL, p);
2352 charset_initialize();
2355 /* Set standard defaults */
2356 hash_map (ConfigOptions, mutt_set_default, 0);
2357 hash_map (ConfigOptions, mutt_restore_default, 0);
2359 CurrentMenu = MENU_MAIN;
2362 /* Unset suspend by default if we're the session leader */
2363 if (getsid (0) == getpid ())
2364 unset_option (OPTSUSPEND);
2367 mutt_init_history ();
2370 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL (Homedir));
2371 if (access (buffer, F_OK) == -1)
2372 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
2376 Muttrc = m_strdup(buffer);
2379 m_strcpy(buffer, sizeof(buffer), Muttrc);
2381 mutt_expand_path (buffer, sizeof(buffer));
2382 Muttrc = m_strdup(buffer);
2384 p_delete(&AliasFile);
2385 AliasFile = m_strdup(NONULL (Muttrc));
2387 /* Process the global rc file if it exists and the user hasn't explicity
2388 requested not to via "-n". */
2390 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
2392 if (access (buffer, F_OK) == -1)
2393 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
2394 if (access (buffer, F_OK) == -1)
2395 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
2397 if (access (buffer, F_OK) == -1)
2398 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
2399 if (access (buffer, F_OK) != -1) {
2400 if (source_rc (buffer, &err) != 0) {
2401 fputs (err.data, stderr);
2402 fputc ('\n', stderr);
2408 /* Read the user's initialization file. */
2409 if (access (Muttrc, F_OK) != -1) {
2410 if (!option (OPTNOCURSES))
2412 if (source_rc (Muttrc, &err) != 0) {
2413 fputs (err.data, stderr);
2414 fputc ('\n', stderr);
2418 else if (!default_rc) {
2419 /* file specified by -F does not exist */
2420 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2421 mutt_endwin (buffer);
2426 snprintf(buffer, sizeof(buffer), "%s/.madmutt.lua", NONULL(Homedir));
2427 if (access(buffer, F_OK) < 0)
2428 snprintf(buffer, sizeof(buffer), "%s/.madmutt/cfg.lua", NONULL(Homedir));
2429 if (!access(buffer, F_OK)) {
2430 need_pause = mlua_wrap(mutt_error, mlua_dofile(buffer));
2434 if (mutt_execute_commands (commands) != 0)
2437 /* warn about synonym variables */
2441 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2443 for (syn = Synonyms; syn; syn = syn->next) {
2444 fprintf(stderr, "$%s ($%s should be used) (%s:%d)\n",
2445 syn->o ? NONULL(syn->o->option) : "",
2446 syn->n ? NONULL(syn->n->option) : "",
2447 NONULL(syn->f), syn->l);
2449 fprintf (stderr, _("Warning: synonym variables are scheduled"
2450 " for removal.\n"));
2451 syn_list_wipe(&Synonyms);
2455 if (need_pause && !option (OPTNOCURSES)) {
2456 if (mutt_any_key_to_continue (NULL) == -1)
2461 int mutt_get_hook_type (const char *name)
2463 struct command_t *c;
2465 for (c = Commands; c->name; c++)
2466 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2471 /* dump out the value of all the variables we have */
2472 int mutt_dump_variables (int full) {
2475 /* get all non-synonyms into list... */
2476 for (i = 0; MuttVars[i].option; i++) {
2477 struct option_t *option = MuttVars + i;
2478 char buf[LONG_STRING];
2480 if (DTYPE(option->type) == DT_SYN)
2484 mutt_option_value(option->option, buf, sizeof(buf));
2485 if (!m_strcmp(buf, option->init))
2490 FuncTable[DTYPE(option->type)].opt_tostr(buf, sizeof(buf), option);
2491 printf ("%s\n", buf);
2494 printf ("\n# vi""m:set ft=muttrc:\n");