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 char* val = NULL, *t = NULL;
263 /* get some $muttng_ values dynamically */
264 if (ascii_strcmp ("muttng_pwd", option->option) == 0) {
265 val = mem_malloc (_POSIX_PATH_MAX);
266 val = getcwd (val, _POSIX_PATH_MAX-1);
268 } else if (ascii_strcmp ("muttng_folder_path", option->option) == 0 &&
269 CurrentFolder && *CurrentFolder) {
271 } else if (ascii_strcmp ("muttng_folder_name", option->option) == 0 &&
272 CurrentFolder && *CurrentFolder) {
273 if ((t = strrchr (CurrentFolder, '/')) != NULL)
280 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (val));
285 static int path_from_string (struct option_t* dst, const char* val,
286 char* errbuf, size_t errlen) {
287 char path[_POSIX_PATH_MAX];
293 mem_free ((char**) dst->data);
298 strfcpy (path, val, sizeof (path));
299 mutt_expand_path (path, sizeof (path));
300 str_replace ((char **) dst->data, path);
304 static int str_from_string (struct option_t* dst, const char* val,
305 char* errbuf, size_t errlen) {
309 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
312 str_replace ((char**) dst->data, val);
316 static int user_from_string (struct option_t* dst, const char* val,
317 char* errbuf, size_t errlen) {
318 /* if dst == NULL, we may get here in case the user did unset it,
319 * see parse_set() where item is free()'d before coming here; so
320 * just silently ignore it */
323 if (str_len ((char*) dst->data) == 0)
324 dst->data = (unsigned long) str_dup (val);
326 char* s = (char*) dst->data;
327 str_replace (&s, val);
329 if (str_len (dst->init) == 0)
330 dst->init = str_dup ((char*) dst->data);
334 static void quad_to_string (char* dst, size_t dstlen,
335 struct option_t* option) {
336 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
337 snprintf (dst, dstlen, "%s=%s", option->option,
338 vals[quadoption (option->data)]);
341 static int quad_from_string (struct option_t* dst, const char* val,
342 char* errbuf, size_t errlen) {
347 if (ascii_strncasecmp (val, "yes", 3) == 0)
349 else if (ascii_strncasecmp (val, "no", 2) == 0)
351 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
353 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
359 set_quadoption (dst->data, flag);
363 static void sort_to_string (char* dst, size_t dstlen,
364 struct option_t* option) {
365 const struct mapping_t *map = get_sortmap (option);
369 snprintf (dst, sizeof (dst), "%s=unknown", option->option);
373 p = mutt_getnamebyvalue (*((short *) option->data) & SORT_MASK,
376 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
377 (*((short *) option->data) & SORT_REVERSE) ?
379 (*((short *) option->data) & SORT_LAST) ? "last-" :
383 static int sort_from_string (struct option_t* dst, const char* val,
384 char* errbuf, size_t errlen) {
385 const struct mapping_t *map = NULL;
386 if (!(map = get_sortmap (dst))) {
388 snprintf (errbuf, errlen, _("%s: Unknown type."),
392 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
397 static void rx_to_string (char* dst, size_t dstlen,
398 struct option_t* option) {
399 rx_t* p = (rx_t*) option->data;
400 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
401 NONULL (p->pattern));
404 static int rx_from_string (struct option_t* dst, const char* val,
405 char* errbuf, size_t errlen) {
408 int flags = 0, e = 0, not = 0;
414 if (option (OPTATTACHMSG) && !str_cmp (dst->option, "reply_regexp")) {
416 snprintf (errbuf, errlen,
417 "Operation not permitted when in attach-message mode.");
421 if (!((rx_t*) dst->data))
422 *((rx_t**) dst->data) = mem_calloc (1, sizeof (rx_t));
424 p = (rx_t*) dst->data;
426 /* something to do? */
427 if (!val || !*val || (p->pattern && str_cmp (p->pattern, val) == 0))
430 if (str_cmp (dst->option, "mask") != 0)
431 flags |= mutt_which_case (val);
434 if (str_cmp (dst->option, "mask") == 0 && *s == '!') {
439 rx = mem_malloc (sizeof (regex_t));
441 if ((e = REGCOMP (rx, s, flags)) != 0) {
442 regerror (e, rx, errbuf, errlen);
453 str_replace (&p->pattern, val);
457 if (str_cmp (dst->option, "reply_regexp") == 0)
458 mutt_adjust_all_subjects ();
463 static void magic_to_string (char* dst, size_t dstlen,
464 struct option_t* option) {
465 const char* s = NULL;
466 switch (option->data) {
467 case M_MBOX: s = "mbox"; break;
468 case M_MMDF: s = "MMDF"; break;
469 case M_MH: s = "MH"; break;
470 case M_MAILDIR: s = "Maildir"; break;
471 default: s = "unknown"; break;
473 snprintf (dst, dstlen, "%s=%s", option->option, s);
476 static int magic_from_string (struct option_t* dst, const char* val,
477 char* errbuf, size_t errlen) {
480 if (!dst || !val || !*val)
482 if (ascii_strncasecmp (val, "mbox", 4) == 0)
484 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
486 else if (ascii_strncasecmp (val, "mh", 2) == 0)
488 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
494 *((short*) dst->data) = flag;
499 static void addr_to_string (char* dst, size_t dstlen,
500 struct option_t* option) {
503 rfc822_write_address (s, sizeof (s), *((ADDRESS**) option->data), 0);
504 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
507 static int addr_from_string (struct option_t* dst, const char* val,
508 char* errbuf, size_t errlen) {
511 rfc822_free_address ((ADDRESS**) dst->data);
513 *((ADDRESS**) dst->data) = rfc822_parse_adrlist (NULL, val);
517 int mutt_option_value (const char* val, char* dst, size_t dstlen) {
518 struct option_t* option = NULL;
519 char* tmp = NULL, *t = NULL;
522 if (!(option = hash_find (ConfigOptions, val))) {
523 debug_print (1, ("var '%s' not found\n", val));
527 tmp = mem_malloc (dstlen+1);
528 FuncTable[DTYPE (option->type)].opt_to_string (tmp, dstlen, option);
530 /* as we get things of type $var=value and don't want to bloat the
531 * above "just" for expansion, we do the stripping here */
532 debug_print (1, ("orig == '%s'\n", tmp));
533 t = strchr (tmp, '=');
537 if (t[l-1] == '"' && *t == '"') {
542 memcpy (dst, t, l+1);
544 debug_print (1, ("stripped == '%s'\n", dst));
549 /* for synonym warning reports: adds synonym to end of list */
550 static void syn_add (struct option_t* n, struct option_t* o) {
551 syn_t* tmp = mem_malloc (sizeof (syn_t));
552 tmp->f = str_dup (CurRCFile);
556 list_push_back (&Synonyms, tmp);
559 /* for synonym warning reports: free single item (for list_del()) */
560 static void syn_del (void** p) {
561 mem_free(&(*(syn_t**) p)->f);
565 void toggle_quadoption (int opt)
568 int b = (opt % 4) * 2;
570 QuadOptions[n] ^= (1 << b);
573 void set_quadoption (int opt, int flag)
576 int b = (opt % 4) * 2;
578 QuadOptions[n] &= ~(0x3 << b);
579 QuadOptions[n] |= (flag & 0x3) << b;
582 int quadoption (int opt)
585 int b = (opt % 4) * 2;
587 return (QuadOptions[n] >> b) & 0x3;
590 int query_quadoption (int opt, const char *prompt)
592 int v = quadoption (opt);
600 v = mutt_yesorno (prompt, (v == M_ASKYES));
601 CLEARLINE (LINES - 1);
608 static void add_to_list (LIST ** list, const char *str)
610 LIST *t, *last = NULL;
612 /* don't add a NULL or empty string to the list */
613 if (!str || *str == '\0')
616 /* check to make sure the item is not already on this list */
617 for (last = *list; last; last = last->next) {
618 if (ascii_strcasecmp (str, last->data) == 0) {
619 /* already on the list, so just ignore it */
627 if (!*list || last) {
628 t = (LIST *) mem_calloc (1, sizeof (LIST));
629 t->data = str_dup (str);
639 static int add_to_rx_list (list2_t** list, const char *s, int flags,
648 if (!(rx = rx_compile (s, flags))) {
649 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
653 i = rx_lookup ((*list), rx->pattern);
657 list_push_back (list, rx);
661 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
662 const char *templ, BUFFER * err)
664 SPAM_LIST *t = NULL, *last = NULL;
669 if (!pat || !*pat || !templ)
672 if (!(rx = rx_compile (pat, REG_ICASE))) {
673 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
677 /* check to make sure the item is not already on this list */
678 for (last = *list; last; last = last->next) {
679 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
680 /* Already on the list. Formerly we just skipped this case, but
681 * now we're supporting removals, which means we're supporting
682 * re-adds conceptually. So we probably want this to imply a
683 * removal, then do an add. We can achieve the removal by freeing
684 * the template, and leaving t pointed at the current item.
687 mem_free(t->template);
694 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
695 * update. Otherwise we want to make a new one to link at the list's end.
698 t = mutt_new_spam_list ();
706 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
707 t->template = str_dup (templ);
709 /* Find highest match number in template string */
711 for (p = templ; *p;) {
716 while (*p && isdigit ((int) *p))
722 t->nmatch++; /* match 0 is always the whole expr */
727 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
729 SPAM_LIST *spam, *prev;
732 /* Being first is a special case. */
736 if (spam->rx && !str_cmp (spam->rx->pattern, pat)) {
739 mem_free(&spam->template);
745 for (spam = prev->next; spam;) {
746 if (!str_cmp (spam->rx->pattern, pat)) {
747 prev->next = spam->next;
749 mem_free(spam->template);
762 static void remove_from_list (LIST ** l, const char *str)
764 LIST *p, *last = NULL;
766 if (str_cmp ("*", str) == 0)
767 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
772 if (ascii_strcasecmp (str, p->data) == 0) {
775 last->next = p->next;
788 static int remove_from_rx_list (list2_t** l, const char *str)
792 if (str_cmp ("*", str) == 0) {
793 list_del (l, (list_del_t*) rx_free);
797 i = rx_lookup ((*l), str);
799 rx_t* r = list_pop_idx ((*l), i);
807 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
812 struct option_t* option = NULL;
814 memset (&token, 0, sizeof (token));
815 mutt_extract_token (tmp, s, 0);
817 /* is the item defined as a variable or a function? */
818 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL)
821 for (i = 0; !res && i < MENU_MAX; i++) {
822 struct binding_t *b = km_get_table (Menus[i].value);
827 for (j = 0; b[j].name; j++)
828 if (!ascii_strncasecmp (tmp->data, b[j].name, str_len (tmp->data))
829 && (str_len (b[j].name) == str_len (tmp->data))) {
835 /* check for feature_* */
836 if (!res && ascii_strncasecmp (tmp->data, "feature_", 8) == 0 &&
837 (j = str_len (tmp->data)) > 8) {
839 while (Features[i]) {
840 if (str_len (Features[i]) == j-8 &&
841 ascii_strncasecmp (Features[i], tmp->data+8, j-8) == 0) {
851 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
853 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
857 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
860 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
861 mutt_error ("Error: %s", err->data);
862 mem_free (&token.data);
865 mem_free (&token.data);
870 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
874 mutt_extract_token (buf, s, 0);
876 /* don't add "*" to the unignore list */
877 if (strcmp (buf->data, "*"))
878 add_to_list (&UnIgnore, buf->data);
880 remove_from_list (&Ignore, buf->data);
882 while (MoreArgs (s));
887 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
891 mutt_extract_token (buf, s, 0);
892 remove_from_list (&UnIgnore, buf->data);
893 add_to_list (&Ignore, buf->data);
895 while (MoreArgs (s));
900 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
904 mutt_extract_token (buf, s, 0);
905 add_to_list ((LIST **) data, buf->data);
907 while (MoreArgs (s));
912 static void _alternates_clean (void)
916 if (Context && Context->msgcount) {
917 for (i = 0; i < Context->msgcount; i++)
918 Context->hdrs[i]->recip_valid = 0;
922 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
925 _alternates_clean ();
927 mutt_extract_token (buf, s, 0);
928 remove_from_rx_list (&UnAlternates, buf->data);
930 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
933 while (MoreArgs (s));
938 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
941 _alternates_clean ();
943 mutt_extract_token (buf, s, 0);
944 remove_from_rx_list (&Alternates, buf->data);
946 if (str_cmp (buf->data, "*") &&
947 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
951 while (MoreArgs (s));
956 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
961 memset (&templ, 0, sizeof (templ));
963 /* Insist on at least one parameter */
966 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
968 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
972 /* Extract the first token, a regexp */
973 mutt_extract_token (buf, s, 0);
975 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
976 if (data == M_SPAM) {
977 /* If there's a second parameter, it's a template for the spam tag. */
979 mutt_extract_token (&templ, s, 0);
981 /* Add to the spam list. */
982 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
983 mem_free (&templ.data);
986 mem_free (&templ.data);
989 /* If not, try to remove from the nospam list. */
991 remove_from_rx_list (&NoSpamList, buf->data);
997 /* M_NOSPAM is for nospam commands. */
998 else if (data == M_NOSPAM) {
999 /* nospam only ever has one parameter. */
1001 /* "*" is a special case. */
1002 if (!str_cmp (buf->data, "*")) {
1003 mutt_free_spam_list (&SpamList);
1004 list_del (&NoSpamList, (list_del_t*) rx_free);
1008 /* If it's on the spam list, just remove it. */
1009 if (remove_from_spam_list (&SpamList, buf->data) != 0)
1012 /* Otherwise, add it to the nospam list. */
1013 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
1019 /* This should not happen. */
1020 strfcpy (err->data, "This is no good at all.", err->dsize);
1024 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
1028 mutt_extract_token (buf, s, 0);
1030 * Check for deletion of entire list
1032 if (str_cmp (buf->data, "*") == 0) {
1033 mutt_free_list ((LIST **) data);
1036 remove_from_list ((LIST **) data, buf->data);
1038 while (MoreArgs (s));
1043 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
1047 mutt_extract_token (buf, s, 0);
1048 remove_from_rx_list (&UnMailLists, buf->data);
1050 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1053 while (MoreArgs (s));
1058 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
1062 mutt_extract_token (buf, s, 0);
1063 remove_from_rx_list (&SubscribedLists, buf->data);
1064 remove_from_rx_list (&MailLists, buf->data);
1066 if (str_cmp (buf->data, "*") &&
1067 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1070 while (MoreArgs (s));
1075 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1079 mutt_extract_token (buf, s, 0);
1080 remove_from_rx_list (&UnMailLists, buf->data);
1081 remove_from_rx_list (&UnSubscribedLists, buf->data);
1083 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1085 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1088 while (MoreArgs (s));
1093 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1097 mutt_extract_token (buf, s, 0);
1098 remove_from_rx_list (&SubscribedLists, buf->data);
1100 if (str_cmp (buf->data, "*") &&
1101 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1104 while (MoreArgs (s));
1109 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
1112 ALIAS *tmp, *last = NULL;
1115 mutt_extract_token (buf, s, 0);
1117 if (str_cmp ("*", buf->data) == 0) {
1118 if (CurrentMenu == MENU_ALIAS) {
1119 for (tmp = Aliases; tmp; tmp = tmp->next)
1121 set_option (OPTFORCEREDRAWINDEX);
1124 mutt_free_alias (&Aliases);
1128 for (tmp = Aliases; tmp; tmp = tmp->next) {
1129 if (str_casecmp (buf->data, tmp->name) == 0) {
1130 if (CurrentMenu == MENU_ALIAS) {
1132 set_option (OPTFORCEREDRAWINDEX);
1137 last->next = tmp->next;
1139 Aliases = tmp->next;
1141 mutt_free_alias (&tmp);
1147 while (MoreArgs (s));
1151 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
1154 ALIAS *tmp = Aliases;
1158 if (!MoreArgs (s)) {
1159 strfcpy (err->data, _("alias: no address"), err->dsize);
1163 mutt_extract_token (buf, s, 0);
1165 debug_print (2, ("first token is '%s'.\n", buf->data));
1167 /* check to see if an alias with this name already exists */
1168 for (; tmp; tmp = tmp->next) {
1169 if (!str_casecmp (tmp->name, buf->data))
1175 /* create a new alias */
1176 tmp = (ALIAS *) mem_calloc (1, sizeof (ALIAS));
1178 tmp->name = str_dup (buf->data);
1179 /* give the main addressbook code a chance */
1180 if (CurrentMenu == MENU_ALIAS)
1181 set_option (OPTMENUCALLER);
1184 /* override the previous value */
1185 rfc822_free_address (&tmp->addr);
1186 if (CurrentMenu == MENU_ALIAS)
1187 set_option (OPTFORCEREDRAWINDEX);
1190 mutt_extract_token (buf, s,
1191 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1192 debug_print (2, ("second token is '%s'.\n", buf->data));
1193 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1198 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1199 snprintf (err->data, err->dsize,
1200 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1204 if (DebugLevel >= 2) {
1207 for (a = tmp->addr; a; a = a->next) {
1209 debug_print (2, ("%s\n", a->mailbox));
1211 debug_print (2, ("group %s\n", a->mailbox));
1219 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
1222 LIST *tmp = UserHeader;
1227 mutt_extract_token (buf, s, 0);
1228 if (str_cmp ("*", buf->data) == 0)
1229 mutt_free_list (&UserHeader);
1234 l = str_len (buf->data);
1235 if (buf->data[l - 1] == ':')
1239 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1240 && tmp->data[l] == ':') {
1243 last->next = tmp->next;
1245 UserHeader = tmp->next;
1248 mutt_free_list (&ptr);
1257 while (MoreArgs (s));
1261 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
1268 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1269 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1270 strfcpy (err->data, _("invalid header field"), err->dsize);
1273 keylen = p - buf->data + 1;
1276 for (tmp = UserHeader;; tmp = tmp->next) {
1277 /* see if there is already a field by this name */
1278 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1279 /* replace the old value */
1280 mem_free (&tmp->data);
1281 tmp->data = buf->data;
1282 memset (buf, 0, sizeof (BUFFER));
1288 tmp->next = mutt_new_list ();
1292 tmp = mutt_new_list ();
1295 tmp->data = buf->data;
1296 memset (buf, 0, sizeof (BUFFER));
1301 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1302 char* errbuf, size_t errlen) {
1305 if (str_ncmp ("reverse-", s, 8) == 0) {
1307 flags = SORT_REVERSE;
1310 if (str_ncmp ("last-", s, 5) == 0) {
1315 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1317 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1321 *((short*) dst->data) = i | flags;
1325 /* if additional data more == 1, we want to resolve synonyms */
1326 static void mutt_set_default (const char* name, void* p, unsigned long more) {
1327 char buf[LONG_STRING];
1328 struct option_t* ptr = (struct option_t*) p;
1330 if (DTYPE (ptr->type) == DT_SYN) {
1333 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1335 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_from_string)
1337 mutt_option_value (ptr->option, buf, sizeof (buf));
1338 if (str_len (ptr->init) == 0 && buf && *buf)
1339 ptr->init = str_dup (buf);
1342 static struct option_t* add_option (const char* name, const char* init,
1343 short type, short dup) {
1344 struct option_t* option = mem_calloc (1, sizeof (struct option_t));
1346 debug_print (1, ("adding $%s\n", name));
1348 option->option = str_dup (name);
1349 option->type = type;
1351 option->init = dup ? str_dup (init) : (char*) init;
1355 /* creates new option_t* of type DT_USER for $user_ var */
1356 static struct option_t* add_user_option (const char* name) {
1357 return (add_option (name, NULL, DT_USER, 1));
1360 /* free()'s option_t* */
1361 static void del_option (void* p) {
1362 struct option_t* ptr = (struct option_t*) p;
1363 char* s = (char*) ptr->data;
1364 debug_print (1, ("removing option '%s' from table\n", NONULL (ptr->option)));
1365 mem_free (&ptr->option);
1367 mem_free (&ptr->init);
1371 static int init_expand (char** dst, struct option_t* src) {
1377 if (DTYPE(src->type) == DT_STR ||
1378 DTYPE(src->type) == DT_PATH) {
1379 /* only expand for string as it's the only place where
1380 * we want to expand vars right now */
1381 if (src->init && *src->init) {
1382 memset (&token, 0, sizeof (BUFFER));
1383 memset (&in, 0, sizeof (BUFFER));
1384 len = str_len (src->init) + 2;
1385 in.data = mem_malloc (len+1);
1386 snprintf (in.data, len, "\"%s\"", src->init);
1389 mutt_extract_token (&token, &in, 0);
1390 if (token.data && *token.data)
1391 *dst = str_dup (token.data);
1393 *dst = str_dup ("");
1394 mem_free (&in.data);
1395 mem_free (&token.data);
1397 *dst = str_dup ("");
1399 /* for non-string: take value as is */
1400 *dst = str_dup (src->init);
1404 /* if additional data more == 1, we want to resolve synonyms */
1405 static void mutt_restore_default (const char* name, void* p,
1406 unsigned long more) {
1407 char errbuf[STRING];
1408 struct option_t* ptr = (struct option_t*) p;
1411 if (DTYPE (ptr->type) == DT_SYN) {
1414 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1418 if (FuncTable[DTYPE (ptr->type)].opt_from_string) {
1419 init_expand (&init, ptr);
1420 if (!FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, init, errbuf,
1422 if (!option (OPTNOCURSES))
1424 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1425 "Please report this error: \"%s\"\n"),
1426 ptr->option, NONULL (init), errbuf);
1432 if (ptr->flags & R_INDEX)
1433 set_option (OPTFORCEREDRAWINDEX);
1434 if (ptr->flags & R_PAGER)
1435 set_option (OPTFORCEREDRAWPAGER);
1436 if (ptr->flags & R_RESORT_SUB)
1437 set_option (OPTSORTSUBTHREADS);
1438 if (ptr->flags & R_RESORT)
1439 set_option (OPTNEEDRESORT);
1440 if (ptr->flags & R_RESORT_INIT)
1441 set_option (OPTRESORTINIT);
1442 if (ptr->flags & R_TREE)
1443 set_option (OPTREDRAWTREE);
1446 /* check whether value for $dsn_return would be valid */
1447 static int check_dsn_return (const char* option, unsigned long p,
1448 char* errbuf, size_t errlen) {
1449 char* val = (char*) p;
1450 if (val && *val && str_ncmp (val, "hdrs", 4) != 0 &&
1451 str_ncmp (val, "full", 4) != 0) {
1453 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1459 /* check whether value for $dsn_notify would be valid */
1460 static int check_dsn_notify (const char* option, unsigned long p,
1461 char* errbuf, size_t errlen) {
1462 list2_t* list = NULL;
1464 char* val = (char*) p;
1468 list = list_from_str (val, ",");
1469 if (list_empty (list))
1472 for (i = 0; i < list->length; i++)
1473 if (str_ncmp (list->data[i], "never", 5) != 0 &&
1474 str_ncmp (list->data[i], "failure", 7) != 0 &&
1475 str_ncmp (list->data[i], "delay", 5) != 0 &&
1476 str_ncmp (list->data[i], "success", 7) != 0) {
1478 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1479 (char*) list->data[i], "dsn_notify");
1483 list_del (&list, (list_del_t*) _mem_free);
1487 static int check_num (const char* option, unsigned long p,
1488 char* errbuf, size_t errlen) {
1491 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1498 static int check_debug (const char* option, unsigned long p,
1499 char* errbuf, size_t errlen) {
1500 if ((int) p <= DEBUG_MAX_LEVEL &&
1501 (int) p >= DEBUG_MIN_LEVEL)
1505 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1510 static int check_history (const char* option, unsigned long p,
1511 char* errbuf, size_t errlen) {
1512 if (!check_num ("history", p, errbuf, errlen))
1514 mutt_init_history ();
1518 static int check_special (const char* name, unsigned long val,
1519 char* errbuf, size_t errlen) {
1522 for (i = 0; SpecialVars[i].name; i++) {
1523 if (str_cmp (SpecialVars[i].name, name) == 0) {
1524 return (SpecialVars[i].check (SpecialVars[i].name,
1525 val, errbuf, errlen));
1531 static const struct mapping_t* get_sortmap (struct option_t* option) {
1532 const struct mapping_t* map = NULL;
1534 switch (option->type & DT_SUBTYPE_MASK) {
1536 map = SortAliasMethods;
1538 case DT_SORT_BROWSER:
1539 map = SortBrowserMethods;
1542 if ((WithCrypto & APPLICATION_PGP))
1543 map = SortKeyMethods;
1546 map = SortAuxMethods;
1555 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1558 int query, unset, inv, reset, r = 0;
1559 struct option_t* option = NULL;
1561 while (MoreArgs (s)) {
1562 /* reset state variables */
1564 unset = data & M_SET_UNSET;
1565 inv = data & M_SET_INV;
1566 reset = data & M_SET_RESET;
1568 if (*s->dptr == '?') {
1572 else if (str_ncmp ("no", s->dptr, 2) == 0) {
1576 else if (str_ncmp ("inv", s->dptr, 3) == 0) {
1580 else if (*s->dptr == '&') {
1585 /* get the variable name */
1586 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1588 /* resolve synonyms */
1589 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1590 DTYPE (option->type == DT_SYN)) {
1591 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1592 syn_add (newopt, option);
1596 /* see if we need to add $user_ var */
1597 if (!option && ascii_strncmp ("user_", tmp->data, 5) == 0) {
1598 /* there's no option named like this yet so only add one
1599 * if the action isn't any of: reset, unset, query */
1600 if (!(reset || unset || query || *s->dptr != '=')) {
1601 debug_print (1, ("adding user option '%s'\n", tmp->data));
1602 option = add_user_option (tmp->data);
1603 hash_insert (ConfigOptions, option->option, option, 0);
1607 if (!option && !(reset && str_cmp ("all", tmp->data) == 0)) {
1608 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1614 if (query || unset || inv) {
1615 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1619 if (s && *s->dptr == '=') {
1620 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1624 if (!str_cmp ("all", tmp->data)) {
1625 hash_map (ConfigOptions, mutt_restore_default, 1);
1628 else if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1629 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1633 mutt_restore_default (NULL, option, 1);
1635 else if (DTYPE (option->type) == DT_BOOL) {
1636 /* XXX this currently ignores the function table
1637 * as we don't get invert and stuff into it */
1638 if (s && *s->dptr == '=') {
1639 if (unset || inv || query) {
1640 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1645 mutt_extract_token (tmp, s, 0);
1646 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1648 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1651 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1657 bool_to_string (err->data, err->dsize, option);
1662 unset_option (option->data);
1664 toggle_option (option->data);
1666 set_option (option->data);
1668 else if (DTYPE (option->type) == DT_STR ||
1669 DTYPE (option->type) == DT_PATH ||
1670 DTYPE (option->type) == DT_ADDR ||
1671 DTYPE (option->type) == DT_MAGIC ||
1672 DTYPE (option->type) == DT_NUM ||
1673 DTYPE (option->type) == DT_SORT ||
1674 DTYPE (option->type) == DT_RX ||
1675 DTYPE (option->type) == DT_USER ||
1676 DTYPE (option->type) == DT_SYS) {
1678 /* XXX maybe we need to get unset into handlers? */
1679 if (DTYPE (option->type) == DT_STR ||
1680 DTYPE (option->type) == DT_PATH ||
1681 DTYPE (option->type) == DT_ADDR ||
1682 DTYPE (option->type) == DT_USER ||
1683 DTYPE (option->type) == DT_SYS) {
1685 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1686 snprintf (err->data, err->dsize, _("$%s is read-only"),
1690 } else if (DTYPE (option->type) == DT_ADDR)
1691 rfc822_free_address ((ADDRESS **) option->data);
1692 else if (DTYPE (option->type) == DT_USER)
1693 /* to unset $user_ means remove */
1694 hash_delete (ConfigOptions, option->option,
1695 option, del_option);
1697 mem_free ((void *) option->data);
1702 if (query || *s->dptr != '=') {
1703 FuncTable[DTYPE (option->type)].opt_to_string
1704 (err->data, err->dsize, option);
1708 /* the $muttng_ variables are read-only */
1709 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1710 snprintf (err->data, err->dsize, _("$%s is read-only"),
1716 mutt_extract_token (tmp, s, 0);
1717 if (!FuncTable[DTYPE (option->type)].opt_from_string
1718 (option, tmp->data, err->data, err->dsize))
1722 else if (DTYPE (option->type) == DT_QUAD) {
1725 quad_to_string (err->data, err->dsize, option);
1729 if (*s->dptr == '=') {
1731 mutt_extract_token (tmp, s, 0);
1732 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1733 set_quadoption (option->data, M_YES);
1734 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1735 set_quadoption (option->data, M_NO);
1736 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1737 set_quadoption (option->data, M_ASKYES);
1738 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1739 set_quadoption (option->data, M_ASKNO);
1741 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1742 tmp->data, option->option);
1749 toggle_quadoption (option->data);
1751 set_quadoption (option->data, M_NO);
1753 set_quadoption (option->data, M_YES);
1757 snprintf (err->data, err->dsize, _("%s: unknown type"),
1763 if (option->flags & R_INDEX)
1764 set_option (OPTFORCEREDRAWINDEX);
1765 if (option->flags & R_PAGER)
1766 set_option (OPTFORCEREDRAWPAGER);
1767 if (option->flags & R_RESORT_SUB)
1768 set_option (OPTSORTSUBTHREADS);
1769 if (option->flags & R_RESORT)
1770 set_option (OPTNEEDRESORT);
1771 if (option->flags & R_RESORT_INIT)
1772 set_option (OPTRESORTINIT);
1773 if (option->flags & R_TREE)
1774 set_option (OPTREDRAWTREE);
1781 /* reads the specified initialization file. returns -1 if errors were found
1782 so that we can pause to let the user know... */
1783 static int source_rc (const char *rcfile, BUFFER * err)
1786 int line = 0, rc = 0, conv = 0;
1788 char *linebuf = NULL;
1789 char *currentline = NULL;
1793 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
1795 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1796 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1800 memset (&token, 0, sizeof (token));
1801 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
1802 conv = ConfigCharset && (*ConfigCharset) && Charset;
1804 currentline = str_dup (linebuf);
1807 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1810 currentline = linebuf;
1815 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1816 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1817 if (--rc < -MAXERRS) {
1819 mem_free (¤tline);
1828 mem_free (¤tline);
1830 mem_free (&token.data);
1831 mem_free (&linebuf);
1834 mutt_wait_filter (pid);
1836 /* the muttrc source keyword */
1837 snprintf (err->data, err->dsize,
1838 rc >= -MAXERRS ? _("source: errors in %s")
1839 : _("source: reading aborted due too many errors in %s"),
1848 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
1851 char path[_POSIX_PATH_MAX];
1855 if (mutt_extract_token (tmp, s, 0) != 0) {
1856 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1860 strfcpy (path, tmp->data, sizeof (path));
1861 mutt_expand_path (path, sizeof (path));
1863 rc += source_rc (path, err);
1865 while (MoreArgs (s));
1867 return ((rc < 0) ? -1 : 0);
1870 /* line command to execute
1872 token scratch buffer to be used by parser. caller should free
1873 token->data when finished. the reason for this variable is
1874 to avoid having to allocate and deallocate a lot of memory
1875 if we are parsing many lines. the caller can pass in the
1876 memory to use, which avoids having to create new space for
1877 every call to this function.
1879 err where to write error messages */
1880 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1885 memset (&expn, 0, sizeof (expn));
1886 expn.data = expn.dptr = line;
1887 expn.dsize = str_len (line);
1891 debug_print (1, ("expand '%s'\n", line));
1894 while (*expn.dptr) {
1895 if (*expn.dptr == '#')
1896 break; /* rest of line is a comment */
1897 if (*expn.dptr == ';') {
1901 mutt_extract_token (token, &expn, 0);
1902 for (i = 0; Commands[i].name; i++) {
1903 if (!str_cmp (token->data, Commands[i].name)) {
1904 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1909 if (!Commands[i].name) {
1910 snprintf (err->data, err->dsize, _("%s: unknown command"),
1911 NONULL (token->data));
1918 mem_free (&expn.data);
1923 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1924 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1925 /* initial string that starts completion. No telling how much crap
1926 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1927 char User_typed[LONG_STRING] = { 0 };
1929 int Num_matched = 0; /* Number of matches for completion */
1930 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1931 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1933 /* helper function for completion. Changes the dest buffer if
1934 necessary/possible to aid completion.
1935 dest == completion result gets here.
1936 src == candidate for completion.
1937 try == user entered data for completion.
1938 len == length of dest buffer.
1940 static void candidate (char *dest, char *try, char *src, int len)
1944 if (strstr (src, try) == src) {
1945 Matches[Num_matched++] = src;
1947 strfcpy (dest, src, len);
1949 for (l = 0; src[l] && src[l] == dest[l]; l++);
1955 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1959 int spaces; /* keep track of the number of leading spaces on the line */
1962 spaces = buffer - pt;
1964 pt = buffer + pos - spaces;
1965 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1968 if (pt == buffer) { /* complete cmd */
1969 /* first TAB. Collect all the matches */
1972 strfcpy (User_typed, pt, sizeof (User_typed));
1973 memset (Matches, 0, sizeof (Matches));
1974 memset (Completed, 0, sizeof (Completed));
1975 for (num = 0; Commands[num].name; num++)
1976 candidate (Completed, User_typed, Commands[num].name,
1977 sizeof (Completed));
1978 Matches[Num_matched++] = User_typed;
1980 /* All matches are stored. Longest non-ambiguous string is ""
1981 * i.e. dont change 'buffer'. Fake successful return this time */
1982 if (User_typed[0] == 0)
1986 if (Completed[0] == 0 && User_typed[0])
1989 /* Num_matched will _always_ be atleast 1 since the initial
1990 * user-typed string is always stored */
1991 if (numtabs == 1 && Num_matched == 2)
1992 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1993 else if (numtabs > 1 && Num_matched > 2)
1994 /* cycle thru all the matches */
1995 snprintf (Completed, sizeof (Completed), "%s",
1996 Matches[(numtabs - 2) % Num_matched]);
1998 /* return the completed command */
1999 strncpy (buffer, Completed, len - spaces);
2001 else if (!str_ncmp (buffer, "set", 3)
2002 || !str_ncmp (buffer, "unset", 5)
2003 || !str_ncmp (buffer, "reset", 5)
2004 || !str_ncmp (buffer, "toggle", 6)) { /* complete variables */
2005 char *prefixes[] = { "no", "inv", "?", "&", 0 };
2008 /* loop through all the possible prefixes (no, inv, ...) */
2009 if (!str_ncmp (buffer, "set", 3)) {
2010 for (num = 0; prefixes[num]; num++) {
2011 if (!str_ncmp (pt, prefixes[num], str_len (prefixes[num]))) {
2012 pt += str_len (prefixes[num]);
2018 /* first TAB. Collect all the matches */
2021 strfcpy (User_typed, pt, sizeof (User_typed));
2022 memset (Matches, 0, sizeof (Matches));
2023 memset (Completed, 0, sizeof (Completed));
2024 for (num = 0; MuttVars[num].option; num++)
2025 candidate (Completed, User_typed, MuttVars[num].option,
2026 sizeof (Completed));
2027 Matches[Num_matched++] = User_typed;
2029 /* All matches are stored. Longest non-ambiguous string is ""
2030 * i.e. dont change 'buffer'. Fake successful return this time */
2031 if (User_typed[0] == 0)
2035 if (Completed[0] == 0 && User_typed[0])
2038 /* Num_matched will _always_ be atleast 1 since the initial
2039 * user-typed string is always stored */
2040 if (numtabs == 1 && Num_matched == 2)
2041 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
2042 else if (numtabs > 1 && Num_matched > 2)
2043 /* cycle thru all the matches */
2044 snprintf (Completed, sizeof (Completed), "%s",
2045 Matches[(numtabs - 2) % Num_matched]);
2047 strncpy (pt, Completed, buffer + len - pt - spaces);
2049 else if (!str_ncmp (buffer, "exec", 4)) {
2050 struct binding_t *menu = km_get_table (CurrentMenu);
2052 if (!menu && CurrentMenu != MENU_PAGER)
2056 /* first TAB. Collect all the matches */
2059 strfcpy (User_typed, pt, sizeof (User_typed));
2060 memset (Matches, 0, sizeof (Matches));
2061 memset (Completed, 0, sizeof (Completed));
2062 for (num = 0; menu[num].name; num++)
2063 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
2064 /* try the generic menu */
2065 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2067 for (num = 0; menu[num].name; num++)
2068 candidate (Completed, User_typed, menu[num].name,
2069 sizeof (Completed));
2071 Matches[Num_matched++] = User_typed;
2073 /* All matches are stored. Longest non-ambiguous string is ""
2074 * i.e. dont change 'buffer'. Fake successful return this time */
2075 if (User_typed[0] == 0)
2079 if (Completed[0] == 0 && User_typed[0])
2082 /* Num_matched will _always_ be atleast 1 since the initial
2083 * user-typed string is always stored */
2084 if (numtabs == 1 && Num_matched == 2)
2085 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
2086 else if (numtabs > 1 && Num_matched > 2)
2087 /* cycle thru all the matches */
2088 snprintf (Completed, sizeof (Completed), "%s",
2089 Matches[(numtabs - 2) % Num_matched]);
2091 strncpy (pt, Completed, buffer + len - pt - spaces);
2099 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2101 char var[STRING], *pt = buffer;
2103 struct option_t* option = NULL;
2109 spaces = buffer - pt;
2111 pt = buffer + pos - spaces;
2112 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2114 pt++; /* move past the space */
2115 if (*pt == '=') /* abort if no var before the '=' */
2118 if (str_ncmp (buffer, "set", 3) == 0) {
2119 strfcpy (var, pt, sizeof (var));
2120 /* ignore the trailing '=' when comparing */
2121 var[str_len (var) - 1] = 0;
2122 if (!(option = hash_find (ConfigOptions, var)))
2123 return 0; /* no such variable. */
2125 char tmp[LONG_STRING], tmp2[LONG_STRING];
2127 size_t dlen = buffer + len - pt - spaces;
2128 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2132 if ((DTYPE (option->type) == DT_STR) ||
2133 (DTYPE (option->type) == DT_PATH) ||
2134 (DTYPE (option->type) == DT_RX)) {
2135 strfcpy (tmp, NONULL (*((char **) option->data)), sizeof (tmp));
2136 if (DTYPE (option->type) == DT_PATH)
2137 mutt_pretty_mailbox (tmp);
2139 else if (DTYPE (option->type) == DT_ADDR) {
2140 rfc822_write_address (tmp, sizeof (tmp),
2141 *((ADDRESS **) option->data), 0);
2143 else if (DTYPE (option->type) == DT_QUAD)
2144 strfcpy (tmp, vals[quadoption (option->data)], sizeof (tmp));
2145 else if (DTYPE (option->type) == DT_NUM)
2146 snprintf (tmp, sizeof (tmp), "%d", (*((short *) option->data)));
2147 else if (DTYPE (option->type) == DT_SORT) {
2148 const struct mapping_t *map;
2151 switch (option->type & DT_SUBTYPE_MASK) {
2153 map = SortAliasMethods;
2155 case DT_SORT_BROWSER:
2156 map = SortBrowserMethods;
2159 if ((WithCrypto & APPLICATION_PGP))
2160 map = SortKeyMethods;
2169 mutt_getnamebyvalue (*((short *) option->data) & SORT_MASK,
2171 snprintf (tmp, sizeof (tmp), "%s%s%s",
2172 (*((short *) option->data) & SORT_REVERSE) ?
2174 (*((short *) option->data) & SORT_LAST) ? "last-" :
2177 else if (DTYPE (option->type) == DT_MAGIC) {
2179 switch (DefaultMagic) {
2195 strfcpy (tmp, p, sizeof (tmp));
2197 else if (DTYPE (option->type) == DT_BOOL)
2198 strfcpy (tmp, option (option->data) ? "yes" : "no",
2203 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
2204 if (*s == '\\' || *s == '"')
2210 strfcpy (tmp, pt, sizeof (tmp));
2211 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2219 /* Implement the -Q command line flag */
2220 int mutt_query_variables (LIST * queries)
2224 char errbuff[STRING];
2225 char command[STRING];
2229 memset (&err, 0, sizeof (err));
2230 memset (&token, 0, sizeof (token));
2233 err.dsize = sizeof (errbuff);
2235 for (p = queries; p; p = p->next) {
2236 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2237 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2238 fprintf (stderr, "%s\n", err.data);
2239 mem_free (&token.data);
2242 printf ("%s\n", err.data);
2245 mem_free (&token.data);
2249 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2253 for (i = 0; map[i].name; i++)
2254 if (map[i].value == val)
2255 return (map[i].name);
2259 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2263 for (i = 0; map[i].name; i++)
2264 if (ascii_strcasecmp (map[i].name, name) == 0)
2265 return (map[i].value);
2269 static int mutt_execute_commands (LIST * p)
2272 char errstr[SHORT_STRING];
2274 memset (&err, 0, sizeof (err));
2276 err.dsize = sizeof (errstr);
2277 memset (&token, 0, sizeof (token));
2278 for (; p; p = p->next) {
2279 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2280 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2281 mem_free (&token.data);
2285 mem_free (&token.data);
2289 void mutt_init (int skip_sys_rc, LIST * commands)
2292 struct utsname utsname;
2293 char *p, buffer[STRING], error[STRING];
2294 int i, default_rc = 0, need_pause = 0;
2297 memset (&err, 0, sizeof (err));
2299 err.dsize = sizeof (error);
2301 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2302 * to have some room for $user_ vars */
2303 ConfigOptions = hash_create (sizeof (MuttVars) * 3);
2304 for (i = 0; MuttVars[i].option; i++) {
2305 if (DTYPE (MuttVars[i].type) != DT_SYS)
2306 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2308 hash_insert (ConfigOptions, MuttVars[i].option,
2309 add_option (MuttVars[i].option, MuttVars[i].init,
2314 * XXX - use something even more difficult to predict?
2316 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2317 "\033]9;%ld\a", (long) time (NULL));
2319 /* on one of the systems I use, getcwd() does not return the same prefix
2320 as is listed in the passwd file */
2321 if ((p = getenv ("HOME")))
2322 Homedir = str_dup (p);
2324 /* Get some information about the user */
2325 if ((pw = getpwuid (getuid ()))) {
2328 Username = str_dup (pw->pw_name);
2330 Homedir = str_dup (pw->pw_dir);
2332 Realname = str_dup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2333 Shell = str_dup (pw->pw_shell);
2339 fputs (_("unable to determine home directory"), stderr);
2342 if ((p = getenv ("USER")))
2343 Username = str_dup (p);
2346 fputs (_("unable to determine username"), stderr);
2349 Shell = str_dup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2352 debug_start(Homedir);
2354 /* And about the host... */
2356 /* some systems report the FQDN instead of just the hostname */
2357 if ((p = strchr (utsname.nodename, '.'))) {
2358 Hostname = str_substrdup (utsname.nodename, p);
2360 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2363 Hostname = str_dup (utsname.nodename);
2366 #define DOMAIN buffer
2367 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2368 Fqdn = str_dup ("@");
2371 if (*DOMAIN != '@') {
2372 Fqdn = mem_malloc (str_len (DOMAIN) + str_len (Hostname) + 2);
2373 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2376 Fqdn = str_dup (NONULL (Hostname));
2383 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2385 fgets (buffer, sizeof (buffer), f);
2386 p = (char*) &buffer;
2389 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r')
2393 NewsServer = str_dup (p);
2397 if ((p = getenv ("NNTPSERVER")))
2398 NewsServer = str_dup (p);
2401 if ((p = getenv ("MAIL")))
2402 Spoolfile = str_dup (p);
2403 else if ((p = getenv ("MAILDIR")))
2404 Spoolfile = str_dup (p);
2407 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2409 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
2411 Spoolfile = str_dup (buffer);
2414 if ((p = getenv ("MAILCAPS")))
2415 MailcapPath = str_dup (p);
2417 /* Default search path from RFC1524 */
2419 str_dup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2420 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2423 Tempdir = str_dup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2425 p = getenv ("VISUAL");
2427 p = getenv ("EDITOR");
2431 Editor = str_dup (p);
2432 Visual = str_dup (p);
2434 if ((p = getenv ("REPLYTO")) != NULL) {
2437 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2439 memset (&buf, 0, sizeof (buf));
2440 buf.data = buf.dptr = buffer;
2441 buf.dsize = str_len (buffer);
2443 memset (&token, 0, sizeof (token));
2444 parse_my_hdr (&token, &buf, 0, &err);
2445 mem_free (&token.data);
2448 if ((p = getenv ("EMAIL")) != NULL)
2449 From = rfc822_parse_adrlist (NULL, p);
2451 mutt_set_langinfo_charset ();
2452 mutt_set_charset (Charset);
2455 /* Set standard defaults */
2456 hash_map (ConfigOptions, mutt_set_default, 0);
2457 hash_map (ConfigOptions, mutt_restore_default, 0);
2459 CurrentMenu = MENU_MAIN;
2462 #ifndef LOCALES_HACK
2463 /* Do we have a locale definition? */
2464 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2465 ((p = getenv ("LANG")) != NULL && p[0]) ||
2466 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2467 set_option (OPTLOCALES);
2471 /* Unset suspend by default if we're the session leader */
2472 if (getsid (0) == getpid ())
2473 unset_option (OPTSUSPEND);
2476 mutt_init_history ();
2485 * When changing the code which looks for a configuration file,
2486 * please also change the corresponding code in muttbug.sh.in.
2496 snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2498 if (access (buffer, F_OK) == -1)
2500 snprintf (buffer, sizeof (buffer), "%s/.muttngrc", NONULL (Homedir));
2501 if (access (buffer, F_OK) == -1)
2503 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s",
2504 NONULL (Homedir), MUTT_VERSION);
2505 if (access (buffer, F_OK) == -1)
2507 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc",
2511 Muttrc = str_dup (buffer);
2514 strfcpy (buffer, Muttrc, sizeof (buffer));
2516 mutt_expand_path (buffer, sizeof (buffer));
2517 Muttrc = str_dup (buffer);
2519 mem_free (&AliasFile);
2520 AliasFile = str_dup (NONULL (Muttrc));
2522 /* Process the global rc file if it exists and the user hasn't explicity
2523 requested not to via "-n". */
2525 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2527 if (access (buffer, F_OK) == -1)
2528 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", SYSCONFDIR);
2529 if (access (buffer, F_OK) == -1)
2530 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR,
2532 if (access (buffer, F_OK) == -1)
2533 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2534 if (access (buffer, F_OK) != -1) {
2535 if (source_rc (buffer, &err) != 0) {
2536 fputs (err.data, stderr);
2537 fputc ('\n', stderr);
2543 /* Read the user's initialization file. */
2544 if (access (Muttrc, F_OK) != -1) {
2545 if (!option (OPTNOCURSES))
2547 if (source_rc (Muttrc, &err) != 0) {
2548 fputs (err.data, stderr);
2549 fputc ('\n', stderr);
2553 else if (!default_rc) {
2554 /* file specified by -F does not exist */
2555 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2556 mutt_endwin (buffer);
2560 if (mutt_execute_commands (commands) != 0)
2563 /* warn about synonym variables */
2564 if (!list_empty(Synonyms)) {
2566 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2567 for (i = 0; i < Synonyms->length; i++) {
2568 struct option_t* newopt = NULL, *oldopt = NULL;
2569 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2570 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2571 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2572 oldopt ? NONULL (oldopt->option) : "",
2573 newopt ? NONULL (newopt->option) : "",
2574 NONULL(((syn_t*) Synonyms->data[i])->f),
2575 ((syn_t*) Synonyms->data[i])->l);
2577 fprintf (stderr, _("Warning: synonym variables are scheduled"
2578 " for removal.\n"));
2579 list_del (&Synonyms, syn_del);
2583 if (need_pause && !option (OPTNOCURSES)) {
2584 if (mutt_any_key_to_continue (NULL) == -1)
2589 set_option (OPTWEED); /* turn weeding on by default */
2593 int mutt_get_hook_type (const char *name)
2595 struct command_t *c;
2597 for (c = Commands; c->name; c++)
2598 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2603 /* compare two option_t*'s for sorting -t/-T output */
2604 static int opt_cmp (const void* a, const void* b) {
2605 return (str_cmp ((*(struct option_t**) a)->option,
2606 (*(struct option_t**) b)->option));
2609 /* callback for hash_map() to put all non-synonym vars into list */
2610 static void opt_sel_full (const char* key, void* data,
2611 unsigned long more) {
2612 list2_t** l = (list2_t**) more;
2613 struct option_t* option = (struct option_t*) data;
2615 if (DTYPE (option->type) == DT_SYN)
2617 list_push_back (l, option);
2620 /* callback for hash_map() to put all changed non-synonym vars into list */
2621 static void opt_sel_diff (const char* key, void* data,
2622 unsigned long more) {
2623 list2_t** l = (list2_t**) more;
2624 struct option_t* option = (struct option_t*) data;
2625 char buf[LONG_STRING];
2627 if (DTYPE (option->type) == DT_SYN)
2630 mutt_option_value (option->option, buf, sizeof (buf));
2631 if (str_cmp (buf, option->init) != 0)
2632 list_push_back (l, option);
2635 /* dump out the value of all the variables we have */
2636 int mutt_dump_variables (int full) {
2638 char outbuf[STRING];
2639 list2_t* tmp = NULL;
2640 struct option_t* option = NULL;
2642 /* get all non-synonyms into list... */
2643 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2644 (unsigned long) &tmp);
2646 if (!list_empty(tmp)) {
2647 /* ...and dump list sorted */
2648 qsort (tmp->data, tmp->length, sizeof (void*), opt_cmp);
2649 for (i = 0; i < tmp->length; i++) {
2650 option = (struct option_t*) tmp->data[i];
2651 FuncTable[DTYPE (option->type)].opt_to_string
2652 (outbuf, sizeof (outbuf), option);
2653 printf ("%s\n", outbuf);
2656 list_del (&tmp, NULL);