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.
21 #include "mutt_curses.h"
27 #include "mutt_crypt.h"
28 #include "mutt_idna.h"
30 #if defined(USE_SSL) || defined(USE_GNUTLS)
34 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
35 #include "mutt_libesmtp.h"
46 #include "lib/debug.h"
52 #include <sys/utsname.h>
59 static const struct mapping_t* get_sortmap (struct option_t* option);
60 static int parse_sort (struct option_t* dst, const char *s,
61 const struct mapping_t *map,
62 char* errbuf, size_t errlen);
64 static HASH* ConfigOptions = NULL;
66 /* for synonym warning reports: synonym found during parsing */
70 struct option_t* n; /* new */
71 struct option_t* o; /* old */
74 /* for synonym warning reports: list of synonyms found */
75 static list2_t* Synonyms;
76 /* for synonym warning reports: current rc file */
77 static const char* CurRCFile = NULL;
78 /* for synonym warning reports: current rc line */
79 static int CurRCLine = 0;
81 /* prototypes for checking for special vars */
82 static int check_dsn_return (const char* option, unsigned long val,
83 char* errbuf, size_t errlen);
84 static int check_dsn_notify (const char* option, unsigned long val,
85 char* errbuf, size_t errlen);
86 static int check_history (const char* option, unsigned long val,
87 char* errbuf, size_t errlen);
88 /* this checks that numbers are >= 0 */
89 static int check_num (const char* option, unsigned long val,
90 char* errbuf, size_t errlen);
92 static int check_debug (const char* option, unsigned long val,
93 char* errbuf, size_t errlen);
96 /* use this to check only */
97 static int check_special (const char* option, unsigned long val,
98 char* errbuf, size_t errlen);
100 /* variable <-> sanity check function mappings
101 * when changing these, make sure the proper _from_string handler
102 * does this checking!
106 int (*check) (const char* option, unsigned long val,
107 char* errbuf, size_t errlen);
109 { "dsn_notify", check_dsn_notify },
110 { "dsn_return", check_dsn_return },
111 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
112 { "smtp_use_tls", mutt_libesmtp_check_usetls },
114 { "history", check_history },
115 { "pager_index_lines", check_num },
117 { "debug_level", check_debug },
123 /* protos for config type handles: convert value to string */
124 static void bool_to_string (char* dst, size_t dstlen, struct option_t* option);
125 static void num_to_string (char* dst, size_t dstlen, struct option_t* option);
126 static void str_to_string (char* dst, size_t dstlen, struct option_t* option);
127 static void quad_to_string (char* dst, size_t dstlen, struct option_t* option);
128 static void sort_to_string (char* dst, size_t dstlen, struct option_t* option);
129 static void rx_to_string (char* dst, size_t dstlen, struct option_t* option);
130 static void magic_to_string (char* dst, size_t dstlen, struct option_t* option);
131 static void addr_to_string (char* dst, size_t dstlen, struct option_t* option);
132 static void user_to_string (char* dst, size_t dstlen, struct option_t* option);
133 static void sys_to_string (char* dst, size_t dstlen, struct option_t* option);
135 /* protos for config type handles: convert to value from string */
136 static int bool_from_string (struct option_t* dst, const char* val,
137 char* errbuf, size_t errlen);
138 static int num_from_string (struct option_t* dst, const char* val,
139 char* errbuf, size_t errlen);
140 static int str_from_string (struct option_t* dst, const char* val,
141 char* errbuf, size_t errlen);
142 static int path_from_string (struct option_t* dst, const char* val,
143 char* errbuf, size_t errlen);
144 static int quad_from_string (struct option_t* dst, const char* val,
145 char* errbuf, size_t errlen);
146 static int sort_from_string (struct option_t* dst, const char* val,
147 char* errbuf, size_t errlen);
148 static int rx_from_string (struct option_t* dst, const char* val,
149 char* errbuf, size_t errlen);
150 static int magic_from_string (struct option_t* dst, const char* val,
151 char* errbuf, size_t errlen);
152 static int addr_from_string (struct option_t* dst, const char* val,
153 char* errbuf, size_t errlen);
154 static int user_from_string (struct option_t* dst, const char* val,
155 char* errbuf, size_t errlen);
159 void (*opt_to_string) (char* dst, size_t dstlen, struct option_t* option);
160 int (*opt_from_string) (struct option_t* dst, const char* val,
161 char* errbuf, size_t errlen);
163 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
164 { DT_BOOL, bool_to_string, bool_from_string },
165 { DT_NUM, num_to_string, num_from_string },
166 { DT_STR, str_to_string, str_from_string },
167 { DT_PATH, str_to_string, path_from_string },
168 { DT_QUAD, quad_to_string, quad_from_string },
169 { DT_SORT, sort_to_string, sort_from_string },
170 { DT_RX, rx_to_string, rx_from_string },
171 { DT_MAGIC, magic_to_string, magic_from_string },
172 /* synonyms should be resolved already so we don't need this
173 * but must define it as DT_ is used for indexing */
174 { DT_SYN, NULL, NULL },
175 { DT_ADDR, addr_to_string, addr_from_string },
176 { DT_USER, user_to_string, user_from_string },
177 { DT_SYS, sys_to_string, NULL },
180 static void bool_to_string (char* dst, size_t dstlen,
181 struct option_t* option) {
182 snprintf (dst, dstlen, "%s=%s", option->option,
183 option (option->data) ? "yes" : "no");
186 static int bool_from_string (struct option_t* dst, const char* val,
187 char* errbuf, size_t errlen) {
192 if (ascii_strncasecmp (val, "yes", 3) == 0)
194 else if (ascii_strncasecmp (val, "no", 2) == 0)
200 set_option (dst->data);
202 unset_option (dst->data);
206 static void num_to_string (char* dst, size_t dstlen,
207 struct option_t* option) {
209 const char* fmt = (str_cmp (option->option, "umask") == 0) ?
211 snprintf (dst, dstlen, fmt, option->option,
212 *((short*) option->data));
215 static int num_from_string (struct option_t* dst, const char* val,
216 char* errbuf, size_t errlen) {
217 int num = 0, old = 0;
223 num = strtol (val, &t, 0);
225 if (!*val || *t || (short) num != num) {
227 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
233 /* just temporarily accept new val so that check_special for
234 * $history already has it when doing history's init() */
235 old = *((short*) dst->data);
236 *((short*) dst->data) = (short) num;
238 if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
239 *((short*) dst->data) = old;
246 static void str_to_string (char* dst, size_t dstlen,
247 struct option_t* option) {
248 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
249 NONULL (*((char**) option->data)));
252 static void user_to_string (char* dst, size_t dstlen,
253 struct option_t* option) {
254 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
255 NONULL (((char*) option->data)));
258 static void sys_to_string (char* dst, size_t dstlen,
259 struct option_t* option) {
260 snprintf (dst, dstlen, "%s=\"%s\"", option->option, option->init);
263 static int path_from_string (struct option_t* dst, const char* val,
264 char* errbuf, size_t errlen) {
265 char path[_POSIX_PATH_MAX];
271 mem_free ((char**) dst->data);
276 strfcpy (path, val, sizeof (path));
277 mutt_expand_path (path, sizeof (path));
278 str_replace ((char **) dst->data, path);
282 static int str_from_string (struct option_t* dst, const char* val,
283 char* errbuf, size_t errlen) {
287 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
290 str_replace ((char**) dst->data, val);
294 static int user_from_string (struct option_t* dst, const char* val,
295 char* errbuf, size_t errlen) {
296 /* if dst == NULL, we may get here in case the user did unset it,
297 * see parse_set() where item is free()'d before coming here; so
298 * just silently ignore it */
301 if (str_len ((char*) dst->data) == 0)
302 dst->data = (unsigned long) str_dup (val);
304 char* s = (char*) dst->data;
305 str_replace (&s, val);
307 if (str_len (dst->init) == 0)
308 dst->init = str_dup ((char*) dst->data);
312 static void quad_to_string (char* dst, size_t dstlen,
313 struct option_t* option) {
314 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
315 snprintf (dst, dstlen, "%s=%s", option->option,
316 vals[quadoption (option->data)]);
319 static int quad_from_string (struct option_t* dst, const char* val,
320 char* errbuf, size_t errlen) {
325 if (ascii_strncasecmp (val, "yes", 3) == 0)
327 else if (ascii_strncasecmp (val, "no", 2) == 0)
329 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
331 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
337 set_quadoption (dst->data, flag);
341 static void sort_to_string (char* dst, size_t dstlen,
342 struct option_t* option) {
343 const struct mapping_t *map = get_sortmap (option);
347 snprintf (dst, sizeof (dst), "%s=unknown", option->option);
351 p = mutt_getnamebyvalue (*((short *) option->data) & SORT_MASK,
354 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
355 (*((short *) option->data) & SORT_REVERSE) ?
357 (*((short *) option->data) & SORT_LAST) ? "last-" :
361 static int sort_from_string (struct option_t* dst, const char* val,
362 char* errbuf, size_t errlen) {
363 const struct mapping_t *map = NULL;
364 if (!(map = get_sortmap (dst))) {
366 snprintf (errbuf, errlen, _("%s: Unknown type."),
370 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
375 static void rx_to_string (char* dst, size_t dstlen,
376 struct option_t* option) {
377 rx_t* p = (rx_t*) option->data;
378 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
379 NONULL (p->pattern));
382 static int rx_from_string (struct option_t* dst, const char* val,
383 char* errbuf, size_t errlen) {
386 int flags = 0, e = 0, not = 0;
392 if (option (OPTATTACHMSG) && !str_cmp (dst->option, "reply_regexp")) {
394 snprintf (errbuf, errlen,
395 "Operation not permitted when in attach-message mode.");
399 if (!((rx_t*) dst->data))
400 *((rx_t**) dst->data) = mem_calloc (1, sizeof (rx_t));
402 p = (rx_t*) dst->data;
404 /* something to do? */
405 if (!val || !*val || (p->pattern && str_cmp (p->pattern, val) == 0))
408 if (str_cmp (dst->option, "mask") != 0)
409 flags |= mutt_which_case (val);
412 if (str_cmp (dst->option, "mask") == 0 && *s == '!') {
417 rx = mem_malloc (sizeof (regex_t));
419 if ((e = REGCOMP (rx, s, flags)) != 0) {
420 regerror (e, rx, errbuf, errlen);
430 str_replace (&p->pattern, val);
434 if (str_cmp (dst->option, "reply_regexp") == 0)
435 mutt_adjust_all_subjects ();
440 static void magic_to_string (char* dst, size_t dstlen,
441 struct option_t* option) {
442 const char* s = NULL;
443 switch (option->data) {
444 case M_MBOX: s = "mbox"; break;
445 case M_MMDF: s = "MMDF"; break;
446 case M_MH: s = "MH"; break;
447 case M_MAILDIR: s = "Maildir"; break;
448 default: s = "unknown"; break;
450 snprintf (dst, dstlen, "%s=%s", option->option, s);
453 static int magic_from_string (struct option_t* dst, const char* val,
454 char* errbuf, size_t errlen) {
457 if (!dst || !val || !*val)
459 if (ascii_strncasecmp (val, "mbox", 4) == 0)
461 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
463 else if (ascii_strncasecmp (val, "mh", 2) == 0)
465 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
471 *((short*) dst->data) = flag;
476 static void addr_to_string (char* dst, size_t dstlen,
477 struct option_t* option) {
480 rfc822_write_address (s, sizeof (s), *((ADDRESS**) option->data), 0);
481 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
484 static int addr_from_string (struct option_t* dst, const char* val,
485 char* errbuf, size_t errlen) {
486 if (!dst || !val || !*val)
488 rfc822_free_address ((ADDRESS**) dst->data);
489 *((ADDRESS**) dst->data) = rfc822_parse_adrlist (NULL, val);
493 int mutt_option_value (const char* val, char* dst, size_t dstlen) {
494 struct option_t* option = NULL;
495 char* tmp = NULL, *t = NULL;
498 if (!(option = hash_find (ConfigOptions, val))) {
499 debug_print (1, ("var '%s' not found\n", val));
503 tmp = mem_malloc (dstlen+1);
504 FuncTable[DTYPE (option->type)].opt_to_string (tmp, dstlen, option);
506 /* as we get things of type $var=value and don't want to bloat the
507 * above "just" for expansion, we do the stripping here */
508 debug_print (1, ("orig == '%s'\n", tmp));
509 t = strchr (tmp, '=');
513 if (t[l-1] == '"' && *t == '"') {
518 memcpy (dst, t, l+1);
520 debug_print (1, ("stripped == '%s'\n", dst));
525 /* for synonym warning reports: adds synonym to end of list */
526 static void syn_add (struct option_t* n, struct option_t* o) {
527 syn_t* tmp = mem_malloc (sizeof (syn_t));
528 tmp->f = str_dup (CurRCFile);
532 list_push_back (&Synonyms, tmp);
535 /* for synonym warning reports: free single item (for list_del()) */
536 static void syn_del (void** p) {
537 mem_free(&(*(syn_t**) p)->f);
541 void toggle_quadoption (int opt)
544 int b = (opt % 4) * 2;
546 QuadOptions[n] ^= (1 << b);
549 void set_quadoption (int opt, int flag)
552 int b = (opt % 4) * 2;
554 QuadOptions[n] &= ~(0x3 << b);
555 QuadOptions[n] |= (flag & 0x3) << b;
558 int quadoption (int opt)
561 int b = (opt % 4) * 2;
563 return (QuadOptions[n] >> b) & 0x3;
566 int query_quadoption (int opt, const char *prompt)
568 int v = quadoption (opt);
576 v = mutt_yesorno (prompt, (v == M_ASKYES));
577 CLEARLINE (LINES - 1);
584 static void add_to_list (LIST ** list, const char *str)
586 LIST *t, *last = NULL;
588 /* don't add a NULL or empty string to the list */
589 if (!str || *str == '\0')
592 /* check to make sure the item is not already on this list */
593 for (last = *list; last; last = last->next) {
594 if (ascii_strcasecmp (str, last->data) == 0) {
595 /* already on the list, so just ignore it */
603 if (!*list || last) {
604 t = (LIST *) mem_calloc (1, sizeof (LIST));
605 t->data = str_dup (str);
615 static int add_to_rx_list (list2_t** list, const char *s, int flags,
624 if (!(rx = rx_compile (s, flags))) {
625 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
629 i = rx_lookup ((*list), rx->pattern);
633 list_push_back (list, rx);
637 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
638 const char *templ, BUFFER * err)
640 SPAM_LIST *t = NULL, *last = NULL;
645 if (!pat || !*pat || !templ)
648 if (!(rx = rx_compile (pat, REG_ICASE))) {
649 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
653 /* check to make sure the item is not already on this list */
654 for (last = *list; last; last = last->next) {
655 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
656 /* Already on the list. Formerly we just skipped this case, but
657 * now we're supporting removals, which means we're supporting
658 * re-adds conceptually. So we probably want this to imply a
659 * removal, then do an add. We can achieve the removal by freeing
660 * the template, and leaving t pointed at the current item.
663 mem_free(t->template);
670 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
671 * update. Otherwise we want to make a new one to link at the list's end.
674 t = mutt_new_spam_list ();
682 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
683 t->template = str_dup (templ);
685 /* Find highest match number in template string */
687 for (p = templ; *p;) {
692 while (*p && isdigit ((int) *p))
698 t->nmatch++; /* match 0 is always the whole expr */
703 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
705 SPAM_LIST *spam, *prev;
708 /* Being first is a special case. */
712 if (spam->rx && !str_cmp (spam->rx->pattern, pat)) {
715 mem_free(&spam->template);
721 for (spam = prev->next; spam;) {
722 if (!str_cmp (spam->rx->pattern, pat)) {
723 prev->next = spam->next;
725 mem_free(spam->template);
738 static void remove_from_list (LIST ** l, const char *str)
740 LIST *p, *last = NULL;
742 if (str_cmp ("*", str) == 0)
743 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
748 if (ascii_strcasecmp (str, p->data) == 0) {
751 last->next = p->next;
764 static int remove_from_rx_list (list2_t** l, const char *str)
768 if (str_cmp ("*", str) == 0) {
769 list_del (l, (list_del_t*) rx_free);
773 i = rx_lookup ((*l), str);
775 rx_t* r = list_pop_idx ((*l), i);
783 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
788 struct option_t* option = NULL;
790 memset (&token, 0, sizeof (token));
791 mutt_extract_token (tmp, s, 0);
793 /* is the item defined as a variable or a function? */
794 if ((option = hash_find (ConfigOptions, tmp->data)))
797 for (i = 0; !res && i < MENU_MAX; i++) {
798 struct binding_t *b = km_get_table (Menus[i].value);
803 for (j = 0; b[j].name; j++)
804 if (!ascii_strncasecmp (tmp->data, b[j].name, str_len (tmp->data))
805 && (str_len (b[j].name) == str_len (tmp->data))) {
811 /* check for feature_* */
816 j = str_len (tmp->data);
817 /* need at least input of 'feature_X' */
821 while (Features[i].name) {
822 if (str_len (Features[i].name) == j &&
823 ascii_strncasecmp (Features[i].name, p, j)) {
834 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
836 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
839 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
841 if ((data && res) || (!data && !res)) {
842 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
843 mutt_error ("Error: %s", err->data);
844 mem_free (&token.data);
847 mem_free (&token.data);
852 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
856 mutt_extract_token (buf, s, 0);
858 /* don't add "*" to the unignore list */
859 if (strcmp (buf->data, "*"))
860 add_to_list (&UnIgnore, buf->data);
862 remove_from_list (&Ignore, buf->data);
864 while (MoreArgs (s));
869 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
873 mutt_extract_token (buf, s, 0);
874 remove_from_list (&UnIgnore, buf->data);
875 add_to_list (&Ignore, buf->data);
877 while (MoreArgs (s));
882 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
886 mutt_extract_token (buf, s, 0);
887 add_to_list ((LIST **) data, buf->data);
889 while (MoreArgs (s));
894 static void _alternates_clean (void)
898 if (Context && Context->msgcount) {
899 for (i = 0; i < Context->msgcount; i++)
900 Context->hdrs[i]->recip_valid = 0;
904 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
907 _alternates_clean ();
909 mutt_extract_token (buf, s, 0);
910 remove_from_rx_list (&UnAlternates, buf->data);
912 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
915 while (MoreArgs (s));
920 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
923 _alternates_clean ();
925 mutt_extract_token (buf, s, 0);
926 remove_from_rx_list (&Alternates, buf->data);
928 if (str_cmp (buf->data, "*") &&
929 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
933 while (MoreArgs (s));
938 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
943 memset (&templ, 0, sizeof (templ));
945 /* Insist on at least one parameter */
948 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
950 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
954 /* Extract the first token, a regexp */
955 mutt_extract_token (buf, s, 0);
957 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
958 if (data == M_SPAM) {
959 /* If there's a second parameter, it's a template for the spam tag. */
961 mutt_extract_token (&templ, s, 0);
963 /* Add to the spam list. */
964 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
965 mem_free (&templ.data);
968 mem_free (&templ.data);
971 /* If not, try to remove from the nospam list. */
973 remove_from_rx_list (&NoSpamList, buf->data);
979 /* M_NOSPAM is for nospam commands. */
980 else if (data == M_NOSPAM) {
981 /* nospam only ever has one parameter. */
983 /* "*" is a special case. */
984 if (!str_cmp (buf->data, "*")) {
985 mutt_free_spam_list (&SpamList);
986 list_del (&NoSpamList, (list_del_t*) rx_free);
990 /* If it's on the spam list, just remove it. */
991 if (remove_from_spam_list (&SpamList, buf->data) != 0)
994 /* Otherwise, add it to the nospam list. */
995 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
1001 /* This should not happen. */
1002 strfcpy (err->data, "This is no good at all.", err->dsize);
1006 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
1010 mutt_extract_token (buf, s, 0);
1012 * Check for deletion of entire list
1014 if (str_cmp (buf->data, "*") == 0) {
1015 mutt_free_list ((LIST **) data);
1018 remove_from_list ((LIST **) data, buf->data);
1020 while (MoreArgs (s));
1025 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
1029 mutt_extract_token (buf, s, 0);
1030 remove_from_rx_list (&UnMailLists, buf->data);
1032 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1035 while (MoreArgs (s));
1040 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
1044 mutt_extract_token (buf, s, 0);
1045 remove_from_rx_list (&SubscribedLists, buf->data);
1046 remove_from_rx_list (&MailLists, buf->data);
1048 if (str_cmp (buf->data, "*") &&
1049 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1052 while (MoreArgs (s));
1057 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1061 mutt_extract_token (buf, s, 0);
1062 remove_from_rx_list (&UnMailLists, buf->data);
1063 remove_from_rx_list (&UnSubscribedLists, buf->data);
1065 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1067 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1070 while (MoreArgs (s));
1075 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1079 mutt_extract_token (buf, s, 0);
1080 remove_from_rx_list (&SubscribedLists, buf->data);
1082 if (str_cmp (buf->data, "*") &&
1083 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1086 while (MoreArgs (s));
1091 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
1094 ALIAS *tmp, *last = NULL;
1097 mutt_extract_token (buf, s, 0);
1099 if (str_cmp ("*", buf->data) == 0) {
1100 if (CurrentMenu == MENU_ALIAS) {
1101 for (tmp = Aliases; tmp; tmp = tmp->next)
1103 set_option (OPTFORCEREDRAWINDEX);
1106 mutt_free_alias (&Aliases);
1110 for (tmp = Aliases; tmp; tmp = tmp->next) {
1111 if (str_casecmp (buf->data, tmp->name) == 0) {
1112 if (CurrentMenu == MENU_ALIAS) {
1114 set_option (OPTFORCEREDRAWINDEX);
1119 last->next = tmp->next;
1121 Aliases = tmp->next;
1123 mutt_free_alias (&tmp);
1129 while (MoreArgs (s));
1133 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
1136 ALIAS *tmp = Aliases;
1140 if (!MoreArgs (s)) {
1141 strfcpy (err->data, _("alias: no address"), err->dsize);
1145 mutt_extract_token (buf, s, 0);
1147 debug_print (2, ("first token is '%s'.\n", buf->data));
1149 /* check to see if an alias with this name already exists */
1150 for (; tmp; tmp = tmp->next) {
1151 if (!str_casecmp (tmp->name, buf->data))
1157 /* create a new alias */
1158 tmp = (ALIAS *) mem_calloc (1, sizeof (ALIAS));
1160 tmp->name = str_dup (buf->data);
1161 /* give the main addressbook code a chance */
1162 if (CurrentMenu == MENU_ALIAS)
1163 set_option (OPTMENUCALLER);
1166 /* override the previous value */
1167 rfc822_free_address (&tmp->addr);
1168 if (CurrentMenu == MENU_ALIAS)
1169 set_option (OPTFORCEREDRAWINDEX);
1172 mutt_extract_token (buf, s,
1173 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1174 debug_print (2, ("second token is '%s'.\n", buf->data));
1175 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1180 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1181 snprintf (err->data, err->dsize,
1182 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1186 if (DebugLevel >= 2) {
1189 for (a = tmp->addr; a; a = a->next) {
1191 debug_print (2, ("%s\n", a->mailbox));
1193 debug_print (2, ("group %s\n", a->mailbox));
1201 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
1204 LIST *tmp = UserHeader;
1209 mutt_extract_token (buf, s, 0);
1210 if (str_cmp ("*", buf->data) == 0)
1211 mutt_free_list (&UserHeader);
1216 l = str_len (buf->data);
1217 if (buf->data[l - 1] == ':')
1221 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1222 && tmp->data[l] == ':') {
1225 last->next = tmp->next;
1227 UserHeader = tmp->next;
1230 mutt_free_list (&ptr);
1239 while (MoreArgs (s));
1243 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
1250 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1251 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1252 strfcpy (err->data, _("invalid header field"), err->dsize);
1255 keylen = p - buf->data + 1;
1258 for (tmp = UserHeader;; tmp = tmp->next) {
1259 /* see if there is already a field by this name */
1260 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1261 /* replace the old value */
1262 mem_free (&tmp->data);
1263 tmp->data = buf->data;
1264 memset (buf, 0, sizeof (BUFFER));
1270 tmp->next = mutt_new_list ();
1274 tmp = mutt_new_list ();
1277 tmp->data = buf->data;
1278 memset (buf, 0, sizeof (BUFFER));
1283 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1284 char* errbuf, size_t errlen) {
1287 if (str_ncmp ("reverse-", s, 8) == 0) {
1289 flags = SORT_REVERSE;
1292 if (str_ncmp ("last-", s, 5) == 0) {
1297 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1299 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1303 *((short*) dst->data) = i | flags;
1307 /* if additional data more == 1, we want to resolve synonyms */
1308 static void mutt_set_default (const char* name, void* p, unsigned long more) {
1309 char buf[LONG_STRING];
1310 struct option_t* ptr = (struct option_t*) p;
1312 if (DTYPE (ptr->type) == DT_SYN) {
1315 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1317 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_from_string)
1319 mutt_option_value (ptr->option, buf, sizeof (buf));
1320 if (str_len (ptr->init) == 0 && buf && *buf)
1321 ptr->init = str_dup (buf);
1324 static struct option_t* add_option (const char* name, const char* init,
1325 short type, short dup) {
1326 struct option_t* option = mem_calloc (1, sizeof (struct option_t));
1328 debug_print (1, ("adding $%s\n", name));
1330 option->option = str_dup (name);
1331 option->type = type;
1333 option->init = dup ? str_dup (init) : (char*) init;
1337 /* creates new option_t* of type DT_USER for $user_ var */
1338 static struct option_t* add_user_option (const char* name) {
1339 return (add_option (name, NULL, DT_USER, 1));
1342 /* free()'s option_t* */
1343 static void del_option (void* p) {
1344 struct option_t* ptr = (struct option_t*) p;
1345 char* s = (char*) ptr->data;
1346 debug_print (1, ("removing option '%s' from table\n", NONULL (ptr->option)));
1347 mem_free (&ptr->option);
1349 mem_free (&ptr->init);
1353 /* if additional data more == 1, we want to resolve synonyms */
1354 static void mutt_restore_default (const char* name, void* p,
1355 unsigned long more) {
1356 char errbuf[STRING];
1357 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_from_string &&
1367 FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, ptr->init, errbuf,
1368 sizeof (errbuf)) < 0) {
1370 fprintf (stderr, _("Invalid default setting found. Please report this "
1371 "error:\n\"%s\"\n"), errbuf);
1375 if (ptr->flags & R_INDEX)
1376 set_option (OPTFORCEREDRAWINDEX);
1377 if (ptr->flags & R_PAGER)
1378 set_option (OPTFORCEREDRAWPAGER);
1379 if (ptr->flags & R_RESORT_SUB)
1380 set_option (OPTSORTSUBTHREADS);
1381 if (ptr->flags & R_RESORT)
1382 set_option (OPTNEEDRESORT);
1383 if (ptr->flags & R_RESORT_INIT)
1384 set_option (OPTRESORTINIT);
1385 if (ptr->flags & R_TREE)
1386 set_option (OPTREDRAWTREE);
1389 /* check whether value for $dsn_return would be valid */
1390 static int check_dsn_return (const char* option, unsigned long p,
1391 char* errbuf, size_t errlen) {
1392 char* val = (char*) p;
1393 if (val && *val && str_ncmp (val, "hdrs", 4) != 0 &&
1394 str_ncmp (val, "full", 4) != 0) {
1396 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1402 /* check whether value for $dsn_notify would be valid */
1403 static int check_dsn_notify (const char* option, unsigned long p,
1404 char* errbuf, size_t errlen) {
1405 list2_t* list = NULL;
1407 char* val = (char*) p;
1411 list = list_from_str (val, ",");
1412 if (list_empty (list))
1415 for (i = 0; i < list->length; i++)
1416 if (str_ncmp (list->data[i], "never", 5) != 0 &&
1417 str_ncmp (list->data[i], "failure", 7) != 0 &&
1418 str_ncmp (list->data[i], "delay", 5) != 0 &&
1419 str_ncmp (list->data[i], "success", 7) != 0) {
1421 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1422 (char*) list->data[i], "dsn_notify");
1426 list_del (&list, (list_del_t*) _mem_free);
1430 static int check_num (const char* option, unsigned long p,
1431 char* errbuf, size_t errlen) {
1434 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1441 static int check_debug (const char* option, unsigned long p,
1442 char* errbuf, size_t errlen) {
1443 if ((int) p <= DEBUG_MAX_LEVEL &&
1444 (int) p >= DEBUG_MIN_LEVEL)
1448 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1453 static int check_history (const char* option, unsigned long p,
1454 char* errbuf, size_t errlen) {
1455 if (!check_num ("history", p, errbuf, errlen))
1457 mutt_init_history ();
1461 static int check_special (const char* name, unsigned long val,
1462 char* errbuf, size_t errlen) {
1465 for (i = 0; SpecialVars[i].name; i++) {
1466 if (str_cmp (SpecialVars[i].name, name) == 0) {
1467 return (SpecialVars[i].check (SpecialVars[i].name,
1468 val, errbuf, errlen));
1474 static const struct mapping_t* get_sortmap (struct option_t* option) {
1475 const struct mapping_t* map = NULL;
1477 switch (option->type & DT_SUBTYPE_MASK) {
1479 map = SortAliasMethods;
1481 case DT_SORT_BROWSER:
1482 map = SortBrowserMethods;
1485 if ((WithCrypto & APPLICATION_PGP))
1486 map = SortKeyMethods;
1489 map = SortAuxMethods;
1498 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1501 int query, unset, inv, reset, r = 0;
1502 struct option_t* option = NULL;
1504 while (MoreArgs (s)) {
1505 /* reset state variables */
1507 unset = data & M_SET_UNSET;
1508 inv = data & M_SET_INV;
1509 reset = data & M_SET_RESET;
1511 if (*s->dptr == '?') {
1515 else if (str_ncmp ("no", s->dptr, 2) == 0) {
1519 else if (str_ncmp ("inv", s->dptr, 3) == 0) {
1523 else if (*s->dptr == '&') {
1528 /* get the variable name */
1529 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1531 /* resolve synonyms */
1532 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1533 DTYPE (option->type == DT_SYN)) {
1534 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1535 syn_add (newopt, option);
1539 /* see if we need to add $user_ var */
1540 if (!option && ascii_strncmp ("user_", tmp->data, 5) == 0) {
1541 /* there's no option named like this yet so only add one
1542 * if the action isn't any of: reset, unset, query */
1543 if (!(reset || unset || query || *s->dptr != '=')) {
1544 debug_print (1, ("adding user option '%s'\n", tmp->data));
1545 option = add_user_option (tmp->data);
1546 hash_insert (ConfigOptions, option->option, option, 0);
1550 if (!option && !(reset && str_cmp ("all", tmp->data) == 0)) {
1551 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1557 if (query || unset || inv) {
1558 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1562 if (s && *s->dptr == '=') {
1563 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1567 if (!str_cmp ("all", tmp->data)) {
1568 hash_map (ConfigOptions, mutt_restore_default, 1);
1571 else if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1572 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1576 mutt_restore_default (NULL, option, 1);
1578 else if (DTYPE (option->type) == DT_BOOL) {
1579 /* XXX this currently ignores the function table
1580 * as we don't get invert and stuff into it */
1581 if (s && *s->dptr == '=') {
1582 if (unset || inv || query) {
1583 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1588 mutt_extract_token (tmp, s, 0);
1589 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1591 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1594 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1600 bool_to_string (err->data, err->dsize, option);
1605 unset_option (option->data);
1607 toggle_option (option->data);
1609 set_option (option->data);
1611 else if (DTYPE (option->type) == DT_STR ||
1612 DTYPE (option->type) == DT_PATH ||
1613 DTYPE (option->type) == DT_ADDR ||
1614 DTYPE (option->type) == DT_MAGIC ||
1615 DTYPE (option->type) == DT_NUM ||
1616 DTYPE (option->type) == DT_SORT ||
1617 DTYPE (option->type) == DT_RX ||
1618 DTYPE (option->type) == DT_USER ||
1619 DTYPE (option->type) == DT_SYS) {
1621 /* XXX maybe we need to get unset into handlers? */
1622 if (DTYPE (option->type) == DT_STR ||
1623 DTYPE (option->type) == DT_PATH ||
1624 DTYPE (option->type) == DT_ADDR ||
1625 DTYPE (option->type) == DT_USER ||
1626 DTYPE (option->type) == DT_SYS) {
1628 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1629 snprintf (err->data, err->dsize, _("$%s is read-only"),
1633 } else if (DTYPE (option->type) == DT_ADDR)
1634 rfc822_free_address ((ADDRESS **) option->data);
1635 else if (DTYPE (option->type) == DT_USER)
1636 /* to unset $user_ means remove */
1637 hash_delete (ConfigOptions, option->option,
1638 option, del_option);
1640 mem_free ((void *) option->data);
1645 if (query || *s->dptr != '=') {
1646 FuncTable[DTYPE (option->type)].opt_to_string
1647 (err->data, err->dsize, option);
1651 /* the $muttng_ variables are read-only */
1652 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1653 snprintf (err->data, err->dsize, _("$%s is read-only"),
1659 mutt_extract_token (tmp, s, 0);
1660 if (!FuncTable[DTYPE (option->type)].opt_from_string
1661 (option, tmp->data, err->data, err->dsize))
1665 else if (DTYPE (option->type) == DT_QUAD) {
1668 quad_to_string (err->data, err->dsize, option);
1672 if (*s->dptr == '=') {
1674 mutt_extract_token (tmp, s, 0);
1675 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1676 set_quadoption (option->data, M_YES);
1677 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1678 set_quadoption (option->data, M_NO);
1679 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1680 set_quadoption (option->data, M_ASKYES);
1681 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1682 set_quadoption (option->data, M_ASKNO);
1684 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1685 tmp->data, option->option);
1692 toggle_quadoption (option->data);
1694 set_quadoption (option->data, M_NO);
1696 set_quadoption (option->data, M_YES);
1700 snprintf (err->data, err->dsize, _("%s: unknown type"),
1706 if (option->flags & R_INDEX)
1707 set_option (OPTFORCEREDRAWINDEX);
1708 if (option->flags & R_PAGER)
1709 set_option (OPTFORCEREDRAWPAGER);
1710 if (option->flags & R_RESORT_SUB)
1711 set_option (OPTSORTSUBTHREADS);
1712 if (option->flags & R_RESORT)
1713 set_option (OPTNEEDRESORT);
1714 if (option->flags & R_RESORT_INIT)
1715 set_option (OPTRESORTINIT);
1716 if (option->flags & R_TREE)
1717 set_option (OPTREDRAWTREE);
1724 /* reads the specified initialization file. returns -1 if errors were found
1725 so that we can pause to let the user know... */
1726 static int source_rc (const char *rcfile, BUFFER * err)
1729 int line = 0, rc = 0, conv = 0;
1731 char *linebuf = NULL;
1732 char *currentline = NULL;
1736 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
1738 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1739 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1743 memset (&token, 0, sizeof (token));
1744 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
1745 conv = ConfigCharset && (*ConfigCharset) && Charset;
1747 currentline = str_dup (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 mem_free (¤tline);
1771 mem_free (¤tline);
1773 mem_free (&token.data);
1774 mem_free (&linebuf);
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, unsigned long data,
1794 char path[_POSIX_PATH_MAX];
1798 if (mutt_extract_token (tmp, s, 0) != 0) {
1799 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1803 strfcpy (path, tmp->data, sizeof (path));
1804 mutt_expand_path (path, sizeof (path));
1806 rc += source_rc (path, err);
1808 while (MoreArgs (s));
1810 return ((rc < 0) ? -1 : 0);
1813 /* line command to execute
1815 token scratch buffer to be used by parser. caller should free
1816 token->data when finished. the reason for this variable is
1817 to avoid having to allocate and deallocate a lot of memory
1818 if we are parsing many lines. the caller can pass in the
1819 memory to use, which avoids having to create new space for
1820 every call to this function.
1822 err where to write error messages */
1823 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1828 memset (&expn, 0, sizeof (expn));
1829 expn.data = expn.dptr = line;
1830 expn.dsize = str_len (line);
1835 while (*expn.dptr) {
1836 if (*expn.dptr == '#')
1837 break; /* rest of line is a comment */
1838 if (*expn.dptr == ';') {
1842 mutt_extract_token (token, &expn, 0);
1843 for (i = 0; Commands[i].name; i++) {
1844 if (!str_cmp (token->data, Commands[i].name)) {
1845 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1850 if (!Commands[i].name) {
1851 snprintf (err->data, err->dsize, _("%s: unknown command"),
1852 NONULL (token->data));
1859 mem_free (&expn.data);
1864 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1865 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1866 /* initial string that starts completion. No telling how much crap
1867 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1868 char User_typed[LONG_STRING] = { 0 };
1870 int Num_matched = 0; /* Number of matches for completion */
1871 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1872 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1874 /* helper function for completion. Changes the dest buffer if
1875 necessary/possible to aid completion.
1876 dest == completion result gets here.
1877 src == candidate for completion.
1878 try == user entered data for completion.
1879 len == length of dest buffer.
1881 static void candidate (char *dest, char *try, char *src, int len)
1885 if (strstr (src, try) == src) {
1886 Matches[Num_matched++] = src;
1888 strfcpy (dest, src, len);
1890 for (l = 0; src[l] && src[l] == dest[l]; l++);
1896 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1900 int spaces; /* keep track of the number of leading spaces on the line */
1903 spaces = buffer - pt;
1905 pt = buffer + pos - spaces;
1906 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1909 if (pt == buffer) { /* complete cmd */
1910 /* first TAB. Collect all the matches */
1913 strfcpy (User_typed, pt, sizeof (User_typed));
1914 memset (Matches, 0, sizeof (Matches));
1915 memset (Completed, 0, sizeof (Completed));
1916 for (num = 0; Commands[num].name; num++)
1917 candidate (Completed, User_typed, Commands[num].name,
1918 sizeof (Completed));
1919 Matches[Num_matched++] = User_typed;
1921 /* All matches are stored. Longest non-ambiguous string is ""
1922 * i.e. dont change 'buffer'. Fake successful return this time */
1923 if (User_typed[0] == 0)
1927 if (Completed[0] == 0 && User_typed[0])
1930 /* Num_matched will _always_ be atleast 1 since the initial
1931 * user-typed string is always stored */
1932 if (numtabs == 1 && Num_matched == 2)
1933 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1934 else if (numtabs > 1 && Num_matched > 2)
1935 /* cycle thru all the matches */
1936 snprintf (Completed, sizeof (Completed), "%s",
1937 Matches[(numtabs - 2) % Num_matched]);
1939 /* return the completed command */
1940 strncpy (buffer, Completed, len - spaces);
1942 else if (!str_ncmp (buffer, "set", 3)
1943 || !str_ncmp (buffer, "unset", 5)
1944 || !str_ncmp (buffer, "reset", 5)
1945 || !str_ncmp (buffer, "toggle", 6)) { /* complete variables */
1946 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1949 /* loop through all the possible prefixes (no, inv, ...) */
1950 if (!str_ncmp (buffer, "set", 3)) {
1951 for (num = 0; prefixes[num]; num++) {
1952 if (!str_ncmp (pt, prefixes[num], str_len (prefixes[num]))) {
1953 pt += str_len (prefixes[num]);
1959 /* first TAB. Collect all the matches */
1962 strfcpy (User_typed, pt, sizeof (User_typed));
1963 memset (Matches, 0, sizeof (Matches));
1964 memset (Completed, 0, sizeof (Completed));
1965 for (num = 0; MuttVars[num].option; num++)
1966 candidate (Completed, User_typed, MuttVars[num].option,
1967 sizeof (Completed));
1968 Matches[Num_matched++] = User_typed;
1970 /* All matches are stored. Longest non-ambiguous string is ""
1971 * i.e. dont change 'buffer'. Fake successful return this time */
1972 if (User_typed[0] == 0)
1976 if (Completed[0] == 0 && User_typed[0])
1979 /* Num_matched will _always_ be atleast 1 since the initial
1980 * user-typed string is always stored */
1981 if (numtabs == 1 && Num_matched == 2)
1982 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1983 else if (numtabs > 1 && Num_matched > 2)
1984 /* cycle thru all the matches */
1985 snprintf (Completed, sizeof (Completed), "%s",
1986 Matches[(numtabs - 2) % Num_matched]);
1988 strncpy (pt, Completed, buffer + len - pt - spaces);
1990 else if (!str_ncmp (buffer, "exec", 4)) {
1991 struct binding_t *menu = km_get_table (CurrentMenu);
1993 if (!menu && CurrentMenu != MENU_PAGER)
1997 /* first TAB. Collect all the matches */
2000 strfcpy (User_typed, pt, sizeof (User_typed));
2001 memset (Matches, 0, sizeof (Matches));
2002 memset (Completed, 0, sizeof (Completed));
2003 for (num = 0; menu[num].name; num++)
2004 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
2005 /* try the generic menu */
2006 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2008 for (num = 0; menu[num].name; num++)
2009 candidate (Completed, User_typed, menu[num].name,
2010 sizeof (Completed));
2012 Matches[Num_matched++] = User_typed;
2014 /* All matches are stored. Longest non-ambiguous string is ""
2015 * i.e. dont change 'buffer'. Fake successful return this time */
2016 if (User_typed[0] == 0)
2020 if (Completed[0] == 0 && User_typed[0])
2023 /* Num_matched will _always_ be atleast 1 since the initial
2024 * user-typed string is always stored */
2025 if (numtabs == 1 && Num_matched == 2)
2026 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
2027 else if (numtabs > 1 && Num_matched > 2)
2028 /* cycle thru all the matches */
2029 snprintf (Completed, sizeof (Completed), "%s",
2030 Matches[(numtabs - 2) % Num_matched]);
2032 strncpy (pt, Completed, buffer + len - pt - spaces);
2040 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2042 char var[STRING], *pt = buffer;
2044 struct option_t* option = NULL;
2050 spaces = buffer - pt;
2052 pt = buffer + pos - spaces;
2053 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2055 pt++; /* move past the space */
2056 if (*pt == '=') /* abort if no var before the '=' */
2059 if (str_ncmp (buffer, "set", 3) == 0) {
2060 strfcpy (var, pt, sizeof (var));
2061 /* ignore the trailing '=' when comparing */
2062 var[str_len (var) - 1] = 0;
2063 if (!(option = hash_find (ConfigOptions, var)))
2064 return 0; /* no such variable. */
2066 char tmp[LONG_STRING], tmp2[LONG_STRING];
2068 size_t dlen = buffer + len - pt - spaces;
2069 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2073 if ((DTYPE (option->type) == DT_STR) ||
2074 (DTYPE (option->type) == DT_PATH) ||
2075 (DTYPE (option->type) == DT_RX)) {
2076 strfcpy (tmp, NONULL (*((char **) option->data)), sizeof (tmp));
2077 if (DTYPE (option->type) == DT_PATH)
2078 mutt_pretty_mailbox (tmp);
2080 else if (DTYPE (option->type) == DT_ADDR) {
2081 rfc822_write_address (tmp, sizeof (tmp),
2082 *((ADDRESS **) option->data), 0);
2084 else if (DTYPE (option->type) == DT_QUAD)
2085 strfcpy (tmp, vals[quadoption (option->data)], sizeof (tmp));
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 if ((WithCrypto & APPLICATION_PGP))
2101 map = SortKeyMethods;
2110 mutt_getnamebyvalue (*((short *) option->data) & SORT_MASK,
2112 snprintf (tmp, sizeof (tmp), "%s%s%s",
2113 (*((short *) option->data) & SORT_REVERSE) ?
2115 (*((short *) option->data) & SORT_LAST) ? "last-" :
2118 else if (DTYPE (option->type) == DT_MAGIC) {
2120 switch (DefaultMagic) {
2136 strfcpy (tmp, p, sizeof (tmp));
2138 else if (DTYPE (option->type) == DT_BOOL)
2139 strfcpy (tmp, option (option->data) ? "yes" : "no",
2144 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
2145 if (*s == '\\' || *s == '"')
2151 strfcpy (tmp, pt, sizeof (tmp));
2152 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2160 /* Implement the -Q command line flag */
2161 int mutt_query_variables (LIST * queries)
2165 char errbuff[STRING];
2166 char command[STRING];
2170 memset (&err, 0, sizeof (err));
2171 memset (&token, 0, sizeof (token));
2174 err.dsize = sizeof (errbuff);
2176 for (p = queries; p; p = p->next) {
2177 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2178 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2179 fprintf (stderr, "%s\n", err.data);
2180 mem_free (&token.data);
2183 printf ("%s\n", err.data);
2186 mem_free (&token.data);
2190 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2194 for (i = 0; map[i].name; i++)
2195 if (map[i].value == val)
2196 return (map[i].name);
2200 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2204 for (i = 0; map[i].name; i++)
2205 if (ascii_strcasecmp (map[i].name, name) == 0)
2206 return (map[i].value);
2210 static int mutt_execute_commands (LIST * p)
2213 char errstr[SHORT_STRING];
2215 memset (&err, 0, sizeof (err));
2217 err.dsize = sizeof (errstr);
2218 memset (&token, 0, sizeof (token));
2219 for (; p; p = p->next) {
2220 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2221 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2222 mem_free (&token.data);
2226 mem_free (&token.data);
2230 void mutt_init (int skip_sys_rc, LIST * commands)
2233 struct utsname utsname;
2234 char *p, buffer[STRING], error[STRING];
2235 int i, default_rc = 0, need_pause = 0;
2238 memset (&err, 0, sizeof (err));
2240 err.dsize = sizeof (error);
2242 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2243 * to have some room for $user_ vars */
2244 ConfigOptions = hash_create (sizeof (MuttVars) * 3);
2245 for (i = 0; MuttVars[i].option; i++) {
2246 if (DTYPE (MuttVars[i].type) != DT_SYS)
2247 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2249 hash_insert (ConfigOptions, MuttVars[i].option,
2250 add_option (MuttVars[i].option, MuttVars[i].init,
2255 * XXX - use something even more difficult to predict?
2257 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2258 "\033]9;%ld\a", (long) time (NULL));
2260 /* on one of the systems I use, getcwd() does not return the same prefix
2261 as is listed in the passwd file */
2262 if ((p = getenv ("HOME")))
2263 Homedir = str_dup (p);
2265 /* Get some information about the user */
2266 if ((pw = getpwuid (getuid ()))) {
2269 Username = str_dup (pw->pw_name);
2271 Homedir = str_dup (pw->pw_dir);
2273 Realname = str_dup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2274 Shell = str_dup (pw->pw_shell);
2279 fputs (_("unable to determine home directory"), stderr);
2282 if ((p = getenv ("USER")))
2283 Username = str_dup (p);
2286 fputs (_("unable to determine username"), stderr);
2289 Shell = str_dup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2292 debug_start(Homedir);
2294 /* And about the host... */
2296 /* some systems report the FQDN instead of just the hostname */
2297 if ((p = strchr (utsname.nodename, '.'))) {
2298 Hostname = str_substrdup (utsname.nodename, p);
2300 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2303 Hostname = str_dup (utsname.nodename);
2306 #define DOMAIN buffer
2307 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2308 Fqdn = str_dup ("@");
2311 if (*DOMAIN != '@') {
2312 Fqdn = mem_malloc (str_len (DOMAIN) + str_len (Hostname) + 2);
2313 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2316 Fqdn = str_dup (NONULL (Hostname));
2323 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2325 fgets (buffer, sizeof (buffer), f);
2326 p = (char*) &buffer;
2329 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r')
2333 NewsServer = str_dup (p);
2337 if ((p = getenv ("NNTPSERVER")))
2338 NewsServer = str_dup (p);
2341 if ((p = getenv ("MAIL")))
2342 Spoolfile = str_dup (p);
2343 else if ((p = getenv ("MAILDIR")))
2344 Spoolfile = str_dup (p);
2347 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2349 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
2351 Spoolfile = str_dup (buffer);
2354 if ((p = getenv ("MAILCAPS")))
2355 MailcapPath = str_dup (p);
2357 /* Default search path from RFC1524 */
2359 str_dup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2360 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2363 Tempdir = str_dup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2365 p = getenv ("VISUAL");
2367 p = getenv ("EDITOR");
2371 Editor = str_dup (p);
2372 Visual = str_dup (p);
2374 if ((p = getenv ("REPLYTO")) != NULL) {
2377 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2379 memset (&buf, 0, sizeof (buf));
2380 buf.data = buf.dptr = buffer;
2381 buf.dsize = str_len (buffer);
2383 memset (&token, 0, sizeof (token));
2384 parse_my_hdr (&token, &buf, 0, &err);
2385 mem_free (&token.data);
2388 if ((p = getenv ("EMAIL")) != NULL)
2389 From = rfc822_parse_adrlist (NULL, p);
2391 mutt_set_langinfo_charset ();
2392 mutt_set_charset (Charset);
2395 /* Set standard defaults */
2396 hash_map (ConfigOptions, mutt_set_default, 0);
2397 hash_map (ConfigOptions, mutt_restore_default, 0);
2399 CurrentMenu = MENU_MAIN;
2402 #ifndef LOCALES_HACK
2403 /* Do we have a locale definition? */
2404 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2405 ((p = getenv ("LANG")) != NULL && p[0]) ||
2406 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2407 set_option (OPTLOCALES);
2411 /* Unset suspend by default if we're the session leader */
2412 if (getsid (0) == getpid ())
2413 unset_option (OPTSUSPEND);
2416 mutt_init_history ();
2425 * When changing the code which looks for a configuration file,
2426 * please also change the corresponding code in muttbug.sh.in.
2436 snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2438 if (access (buffer, F_OK) == -1)
2440 snprintf (buffer, sizeof (buffer), "%s/.muttngrc", NONULL (Homedir));
2441 if (access (buffer, F_OK) == -1)
2443 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s",
2444 NONULL (Homedir), MUTT_VERSION);
2445 if (access (buffer, F_OK) == -1)
2447 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc",
2451 Muttrc = str_dup (buffer);
2454 strfcpy (buffer, Muttrc, sizeof (buffer));
2456 mutt_expand_path (buffer, sizeof (buffer));
2457 Muttrc = str_dup (buffer);
2459 mem_free (&AliasFile);
2460 AliasFile = str_dup (NONULL (Muttrc));
2462 /* Process the global rc file if it exists and the user hasn't explicity
2463 requested not to via "-n". */
2465 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2467 if (access (buffer, F_OK) == -1)
2468 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", SYSCONFDIR);
2469 if (access (buffer, F_OK) == -1)
2470 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR,
2472 if (access (buffer, F_OK) == -1)
2473 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2474 if (access (buffer, F_OK) != -1) {
2475 if (source_rc (buffer, &err) != 0) {
2476 fputs (err.data, stderr);
2477 fputc ('\n', stderr);
2483 /* Read the user's initialization file. */
2484 if (access (Muttrc, F_OK) != -1) {
2485 if (!option (OPTNOCURSES))
2487 if (source_rc (Muttrc, &err) != 0) {
2488 fputs (err.data, stderr);
2489 fputc ('\n', stderr);
2493 else if (!default_rc) {
2494 /* file specified by -F does not exist */
2495 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2496 mutt_endwin (buffer);
2500 if (mutt_execute_commands (commands) != 0)
2503 /* warn about synonym variables */
2504 if (!list_empty(Synonyms)) {
2506 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2507 for (i = 0; i < Synonyms->length; i++) {
2508 struct option_t* newopt = NULL, *oldopt = NULL;
2509 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2510 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2511 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2512 oldopt ? NONULL (oldopt->option) : "",
2513 newopt ? NONULL (newopt->option) : "",
2514 NONULL(((syn_t*) Synonyms->data[i])->f),
2515 ((syn_t*) Synonyms->data[i])->l);
2517 fprintf (stderr, _("Warning: synonym variables are scheduled"
2518 " for removal.\n"));
2519 list_del (&Synonyms, syn_del);
2523 if (need_pause && !option (OPTNOCURSES)) {
2524 if (mutt_any_key_to_continue (NULL) == -1)
2529 set_option (OPTWEED); /* turn weeding on by default */
2533 int mutt_get_hook_type (const char *name)
2535 struct command_t *c;
2537 for (c = Commands; c->name; c++)
2538 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2543 /* compare two option_t*'s for sorting -t/-T output */
2544 static int opt_cmp (const void* a, const void* b) {
2545 return (str_cmp ((*(struct option_t**) a)->option,
2546 (*(struct option_t**) b)->option));
2549 /* callback for hash_map() to put all non-synonym vars into list */
2550 static void opt_sel_full (const char* key, void* data,
2551 unsigned long more) {
2552 list2_t** l = (list2_t**) more;
2553 struct option_t* option = (struct option_t*) data;
2555 if (DTYPE (option->type) == DT_SYN)
2557 list_push_back (l, option);
2560 /* callback for hash_map() to put all changed non-synonym vars into list */
2561 static void opt_sel_diff (const char* key, void* data,
2562 unsigned long more) {
2563 list2_t** l = (list2_t**) more;
2564 struct option_t* option = (struct option_t*) data;
2565 char buf[LONG_STRING];
2567 if (DTYPE (option->type) == DT_SYN)
2570 mutt_option_value (option->option, buf, sizeof (buf));
2571 if (str_cmp (buf, option->init) != 0)
2572 list_push_back (l, option);
2575 /* dump out the value of all the variables we have */
2576 int mutt_dump_variables (int full) {
2578 char outbuf[STRING];
2579 list2_t* tmp = NULL;
2580 struct option_t* option = NULL;
2582 /* get all non-synonyms into list... */
2583 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2584 (unsigned long) &tmp);
2586 if (!list_empty(tmp)) {
2587 /* ...and dump list sorted */
2588 qsort (tmp->data, tmp->length, sizeof (void*), opt_cmp);
2589 for (i = 0; i < tmp->length; i++) {
2590 option = (struct option_t*) tmp->data[i];
2591 FuncTable[DTYPE (option->type)].opt_to_string
2592 (outbuf, sizeof (outbuf), option);
2593 printf ("%s\n", outbuf);
2596 list_del (&tmp, NULL);