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);
447 if (p->pattern && p->rx) {
452 str_replace (&p->pattern, val);
456 if (str_cmp (dst->option, "reply_regexp") == 0)
457 mutt_adjust_all_subjects ();
462 static void magic_to_string (char* dst, size_t dstlen,
463 struct option_t* option) {
464 const char* s = NULL;
465 switch (option->data) {
466 case M_MBOX: s = "mbox"; break;
467 case M_MMDF: s = "MMDF"; break;
468 case M_MH: s = "MH"; break;
469 case M_MAILDIR: s = "Maildir"; break;
470 default: s = "unknown"; break;
472 snprintf (dst, dstlen, "%s=%s", option->option, s);
475 static int magic_from_string (struct option_t* dst, const char* val,
476 char* errbuf, size_t errlen) {
479 if (!dst || !val || !*val)
481 if (ascii_strncasecmp (val, "mbox", 4) == 0)
483 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
485 else if (ascii_strncasecmp (val, "mh", 2) == 0)
487 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
493 *((short*) dst->data) = flag;
498 static void addr_to_string (char* dst, size_t dstlen,
499 struct option_t* option) {
502 rfc822_write_address (s, sizeof (s), *((ADDRESS**) option->data), 0);
503 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
506 static int addr_from_string (struct option_t* dst, const char* val,
507 char* errbuf, size_t errlen) {
508 if (!dst || !val || !*val)
510 rfc822_free_address ((ADDRESS**) dst->data);
511 *((ADDRESS**) dst->data) = rfc822_parse_adrlist (NULL, val);
515 int mutt_option_value (const char* val, char* dst, size_t dstlen) {
516 struct option_t* option = NULL;
517 char* tmp = NULL, *t = NULL;
520 if (!(option = hash_find (ConfigOptions, val))) {
521 debug_print (1, ("var '%s' not found\n", val));
525 tmp = mem_malloc (dstlen+1);
526 FuncTable[DTYPE (option->type)].opt_to_string (tmp, dstlen, option);
528 /* as we get things of type $var=value and don't want to bloat the
529 * above "just" for expansion, we do the stripping here */
530 debug_print (1, ("orig == '%s'\n", tmp));
531 t = strchr (tmp, '=');
535 if (t[l-1] == '"' && *t == '"') {
540 memcpy (dst, t, l+1);
542 debug_print (1, ("stripped == '%s'\n", dst));
547 /* for synonym warning reports: adds synonym to end of list */
548 static void syn_add (struct option_t* n, struct option_t* o) {
549 syn_t* tmp = mem_malloc (sizeof (syn_t));
550 tmp->f = str_dup (CurRCFile);
554 list_push_back (&Synonyms, tmp);
557 /* for synonym warning reports: free single item (for list_del()) */
558 static void syn_del (void** p) {
559 mem_free(&(*(syn_t**) p)->f);
563 void toggle_quadoption (int opt)
566 int b = (opt % 4) * 2;
568 QuadOptions[n] ^= (1 << b);
571 void set_quadoption (int opt, int flag)
574 int b = (opt % 4) * 2;
576 QuadOptions[n] &= ~(0x3 << b);
577 QuadOptions[n] |= (flag & 0x3) << b;
580 int quadoption (int opt)
583 int b = (opt % 4) * 2;
585 return (QuadOptions[n] >> b) & 0x3;
588 int query_quadoption (int opt, const char *prompt)
590 int v = quadoption (opt);
598 v = mutt_yesorno (prompt, (v == M_ASKYES));
599 CLEARLINE (LINES - 1);
606 static void add_to_list (LIST ** list, const char *str)
608 LIST *t, *last = NULL;
610 /* don't add a NULL or empty string to the list */
611 if (!str || *str == '\0')
614 /* check to make sure the item is not already on this list */
615 for (last = *list; last; last = last->next) {
616 if (ascii_strcasecmp (str, last->data) == 0) {
617 /* already on the list, so just ignore it */
625 if (!*list || last) {
626 t = (LIST *) mem_calloc (1, sizeof (LIST));
627 t->data = str_dup (str);
637 static int add_to_rx_list (list2_t** list, const char *s, int flags,
646 if (!(rx = rx_compile (s, flags))) {
647 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
651 i = rx_lookup ((*list), rx->pattern);
655 list_push_back (list, rx);
659 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
660 const char *templ, BUFFER * err)
662 SPAM_LIST *t = NULL, *last = NULL;
667 if (!pat || !*pat || !templ)
670 if (!(rx = rx_compile (pat, REG_ICASE))) {
671 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
675 /* check to make sure the item is not already on this list */
676 for (last = *list; last; last = last->next) {
677 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
678 /* Already on the list. Formerly we just skipped this case, but
679 * now we're supporting removals, which means we're supporting
680 * re-adds conceptually. So we probably want this to imply a
681 * removal, then do an add. We can achieve the removal by freeing
682 * the template, and leaving t pointed at the current item.
685 mem_free(t->template);
692 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
693 * update. Otherwise we want to make a new one to link at the list's end.
696 t = mutt_new_spam_list ();
704 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
705 t->template = str_dup (templ);
707 /* Find highest match number in template string */
709 for (p = templ; *p;) {
714 while (*p && isdigit ((int) *p))
720 t->nmatch++; /* match 0 is always the whole expr */
725 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
727 SPAM_LIST *spam, *prev;
730 /* Being first is a special case. */
734 if (spam->rx && !str_cmp (spam->rx->pattern, pat)) {
737 mem_free(&spam->template);
743 for (spam = prev->next; spam;) {
744 if (!str_cmp (spam->rx->pattern, pat)) {
745 prev->next = spam->next;
747 mem_free(spam->template);
760 static void remove_from_list (LIST ** l, const char *str)
762 LIST *p, *last = NULL;
764 if (str_cmp ("*", str) == 0)
765 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
770 if (ascii_strcasecmp (str, p->data) == 0) {
773 last->next = p->next;
786 static int remove_from_rx_list (list2_t** l, const char *str)
790 if (str_cmp ("*", str) == 0) {
791 list_del (l, (list_del_t*) rx_free);
795 i = rx_lookup ((*l), str);
797 rx_t* r = list_pop_idx ((*l), i);
805 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
810 struct option_t* option = NULL;
812 memset (&token, 0, sizeof (token));
813 mutt_extract_token (tmp, s, 0);
815 /* is the item defined as a variable or a function? */
816 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL)
819 for (i = 0; !res && i < MENU_MAX; i++) {
820 struct binding_t *b = km_get_table (Menus[i].value);
825 for (j = 0; b[j].name; j++)
826 if (!ascii_strncasecmp (tmp->data, b[j].name, str_len (tmp->data))
827 && (str_len (b[j].name) == str_len (tmp->data))) {
833 /* check for feature_* */
834 if (!res && ascii_strncasecmp (tmp->data, "feature_", 8) == 0 &&
835 (j = str_len (tmp->data)) > 8) {
837 while (Features[i]) {
838 if (str_len (Features[i]) == j-8 &&
839 ascii_strncasecmp (Features[i], tmp->data+8, j-8) == 0) {
849 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
851 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
855 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
858 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
859 mutt_error ("Error: %s", err->data);
860 mem_free (&token.data);
863 mem_free (&token.data);
868 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
872 mutt_extract_token (buf, s, 0);
874 /* don't add "*" to the unignore list */
875 if (strcmp (buf->data, "*"))
876 add_to_list (&UnIgnore, buf->data);
878 remove_from_list (&Ignore, buf->data);
880 while (MoreArgs (s));
885 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
889 mutt_extract_token (buf, s, 0);
890 remove_from_list (&UnIgnore, buf->data);
891 add_to_list (&Ignore, buf->data);
893 while (MoreArgs (s));
898 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
902 mutt_extract_token (buf, s, 0);
903 add_to_list ((LIST **) data, buf->data);
905 while (MoreArgs (s));
910 static void _alternates_clean (void)
914 if (Context && Context->msgcount) {
915 for (i = 0; i < Context->msgcount; i++)
916 Context->hdrs[i]->recip_valid = 0;
920 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
923 _alternates_clean ();
925 mutt_extract_token (buf, s, 0);
926 remove_from_rx_list (&UnAlternates, buf->data);
928 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
931 while (MoreArgs (s));
936 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
939 _alternates_clean ();
941 mutt_extract_token (buf, s, 0);
942 remove_from_rx_list (&Alternates, buf->data);
944 if (str_cmp (buf->data, "*") &&
945 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
949 while (MoreArgs (s));
954 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
959 memset (&templ, 0, sizeof (templ));
961 /* Insist on at least one parameter */
964 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
966 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
970 /* Extract the first token, a regexp */
971 mutt_extract_token (buf, s, 0);
973 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
974 if (data == M_SPAM) {
975 /* If there's a second parameter, it's a template for the spam tag. */
977 mutt_extract_token (&templ, s, 0);
979 /* Add to the spam list. */
980 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
981 mem_free (&templ.data);
984 mem_free (&templ.data);
987 /* If not, try to remove from the nospam list. */
989 remove_from_rx_list (&NoSpamList, buf->data);
995 /* M_NOSPAM is for nospam commands. */
996 else if (data == M_NOSPAM) {
997 /* nospam only ever has one parameter. */
999 /* "*" is a special case. */
1000 if (!str_cmp (buf->data, "*")) {
1001 mutt_free_spam_list (&SpamList);
1002 list_del (&NoSpamList, (list_del_t*) rx_free);
1006 /* If it's on the spam list, just remove it. */
1007 if (remove_from_spam_list (&SpamList, buf->data) != 0)
1010 /* Otherwise, add it to the nospam list. */
1011 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
1017 /* This should not happen. */
1018 strfcpy (err->data, "This is no good at all.", err->dsize);
1022 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
1026 mutt_extract_token (buf, s, 0);
1028 * Check for deletion of entire list
1030 if (str_cmp (buf->data, "*") == 0) {
1031 mutt_free_list ((LIST **) data);
1034 remove_from_list ((LIST **) data, buf->data);
1036 while (MoreArgs (s));
1041 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
1045 mutt_extract_token (buf, s, 0);
1046 remove_from_rx_list (&UnMailLists, buf->data);
1048 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1051 while (MoreArgs (s));
1056 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
1060 mutt_extract_token (buf, s, 0);
1061 remove_from_rx_list (&SubscribedLists, buf->data);
1062 remove_from_rx_list (&MailLists, buf->data);
1064 if (str_cmp (buf->data, "*") &&
1065 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1068 while (MoreArgs (s));
1073 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1077 mutt_extract_token (buf, s, 0);
1078 remove_from_rx_list (&UnMailLists, buf->data);
1079 remove_from_rx_list (&UnSubscribedLists, buf->data);
1081 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1083 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1086 while (MoreArgs (s));
1091 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1095 mutt_extract_token (buf, s, 0);
1096 remove_from_rx_list (&SubscribedLists, buf->data);
1098 if (str_cmp (buf->data, "*") &&
1099 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1102 while (MoreArgs (s));
1107 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
1110 ALIAS *tmp, *last = NULL;
1113 mutt_extract_token (buf, s, 0);
1115 if (str_cmp ("*", buf->data) == 0) {
1116 if (CurrentMenu == MENU_ALIAS) {
1117 for (tmp = Aliases; tmp; tmp = tmp->next)
1119 set_option (OPTFORCEREDRAWINDEX);
1122 mutt_free_alias (&Aliases);
1126 for (tmp = Aliases; tmp; tmp = tmp->next) {
1127 if (str_casecmp (buf->data, tmp->name) == 0) {
1128 if (CurrentMenu == MENU_ALIAS) {
1130 set_option (OPTFORCEREDRAWINDEX);
1135 last->next = tmp->next;
1137 Aliases = tmp->next;
1139 mutt_free_alias (&tmp);
1145 while (MoreArgs (s));
1149 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
1152 ALIAS *tmp = Aliases;
1156 if (!MoreArgs (s)) {
1157 strfcpy (err->data, _("alias: no address"), err->dsize);
1161 mutt_extract_token (buf, s, 0);
1163 debug_print (2, ("first token is '%s'.\n", buf->data));
1165 /* check to see if an alias with this name already exists */
1166 for (; tmp; tmp = tmp->next) {
1167 if (!str_casecmp (tmp->name, buf->data))
1173 /* create a new alias */
1174 tmp = (ALIAS *) mem_calloc (1, sizeof (ALIAS));
1176 tmp->name = str_dup (buf->data);
1177 /* give the main addressbook code a chance */
1178 if (CurrentMenu == MENU_ALIAS)
1179 set_option (OPTMENUCALLER);
1182 /* override the previous value */
1183 rfc822_free_address (&tmp->addr);
1184 if (CurrentMenu == MENU_ALIAS)
1185 set_option (OPTFORCEREDRAWINDEX);
1188 mutt_extract_token (buf, s,
1189 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1190 debug_print (2, ("second token is '%s'.\n", buf->data));
1191 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1196 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1197 snprintf (err->data, err->dsize,
1198 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1202 if (DebugLevel >= 2) {
1205 for (a = tmp->addr; a; a = a->next) {
1207 debug_print (2, ("%s\n", a->mailbox));
1209 debug_print (2, ("group %s\n", a->mailbox));
1217 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
1220 LIST *tmp = UserHeader;
1225 mutt_extract_token (buf, s, 0);
1226 if (str_cmp ("*", buf->data) == 0)
1227 mutt_free_list (&UserHeader);
1232 l = str_len (buf->data);
1233 if (buf->data[l - 1] == ':')
1237 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1238 && tmp->data[l] == ':') {
1241 last->next = tmp->next;
1243 UserHeader = tmp->next;
1246 mutt_free_list (&ptr);
1255 while (MoreArgs (s));
1259 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
1266 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1267 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1268 strfcpy (err->data, _("invalid header field"), err->dsize);
1271 keylen = p - buf->data + 1;
1274 for (tmp = UserHeader;; tmp = tmp->next) {
1275 /* see if there is already a field by this name */
1276 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1277 /* replace the old value */
1278 mem_free (&tmp->data);
1279 tmp->data = buf->data;
1280 memset (buf, 0, sizeof (BUFFER));
1286 tmp->next = mutt_new_list ();
1290 tmp = mutt_new_list ();
1293 tmp->data = buf->data;
1294 memset (buf, 0, sizeof (BUFFER));
1299 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1300 char* errbuf, size_t errlen) {
1303 if (str_ncmp ("reverse-", s, 8) == 0) {
1305 flags = SORT_REVERSE;
1308 if (str_ncmp ("last-", s, 5) == 0) {
1313 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1315 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1319 *((short*) dst->data) = i | flags;
1323 /* if additional data more == 1, we want to resolve synonyms */
1324 static void mutt_set_default (const char* name, void* p, unsigned long more) {
1325 char buf[LONG_STRING];
1326 struct option_t* ptr = (struct option_t*) p;
1328 if (DTYPE (ptr->type) == DT_SYN) {
1331 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1333 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_from_string)
1335 mutt_option_value (ptr->option, buf, sizeof (buf));
1336 if (str_len (ptr->init) == 0 && buf && *buf)
1337 ptr->init = str_dup (buf);
1340 static struct option_t* add_option (const char* name, const char* init,
1341 short type, short dup) {
1342 struct option_t* option = mem_calloc (1, sizeof (struct option_t));
1344 debug_print (1, ("adding $%s\n", name));
1346 option->option = str_dup (name);
1347 option->type = type;
1349 option->init = dup ? str_dup (init) : (char*) init;
1353 /* creates new option_t* of type DT_USER for $user_ var */
1354 static struct option_t* add_user_option (const char* name) {
1355 return (add_option (name, NULL, DT_USER, 1));
1358 /* free()'s option_t* */
1359 static void del_option (void* p) {
1360 struct option_t* ptr = (struct option_t*) p;
1361 char* s = (char*) ptr->data;
1362 debug_print (1, ("removing option '%s' from table\n", NONULL (ptr->option)));
1363 mem_free (&ptr->option);
1365 mem_free (&ptr->init);
1369 static int init_expand (char** dst, const char* src) {
1374 memset (&token, 0, sizeof (BUFFER));
1375 memset (&in, 0, sizeof (BUFFER));
1376 in.data = (char*) src;
1378 in.dsize = str_len (src);
1379 mutt_extract_token (&token, &in, M_TOKEN_SPACE);
1381 if (token.data && *token.data)
1382 *dst = str_dup (token.data);
1384 *dst = str_dup ("");
1385 mem_free (&token.data);
1387 *dst = str_dup ("");
1391 /* if additional data more == 1, we want to resolve synonyms */
1392 static void mutt_restore_default (const char* name, void* p,
1393 unsigned long more) {
1394 char errbuf[STRING];
1395 struct option_t* ptr = (struct option_t*) p;
1398 if (DTYPE (ptr->type) == DT_SYN) {
1401 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1405 if (FuncTable[DTYPE (ptr->type)].opt_from_string) {
1406 init_expand (&init, ptr->init);
1407 if (FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, init, errbuf,
1408 sizeof (errbuf)) < 0) {
1410 fprintf (stderr, _("Invalid default setting found. Please report this "
1411 "error:\n\"%s\"\n"), errbuf);
1417 if (ptr->flags & R_INDEX)
1418 set_option (OPTFORCEREDRAWINDEX);
1419 if (ptr->flags & R_PAGER)
1420 set_option (OPTFORCEREDRAWPAGER);
1421 if (ptr->flags & R_RESORT_SUB)
1422 set_option (OPTSORTSUBTHREADS);
1423 if (ptr->flags & R_RESORT)
1424 set_option (OPTNEEDRESORT);
1425 if (ptr->flags & R_RESORT_INIT)
1426 set_option (OPTRESORTINIT);
1427 if (ptr->flags & R_TREE)
1428 set_option (OPTREDRAWTREE);
1431 /* check whether value for $dsn_return would be valid */
1432 static int check_dsn_return (const char* option, unsigned long p,
1433 char* errbuf, size_t errlen) {
1434 char* val = (char*) p;
1435 if (val && *val && str_ncmp (val, "hdrs", 4) != 0 &&
1436 str_ncmp (val, "full", 4) != 0) {
1438 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1444 /* check whether value for $dsn_notify would be valid */
1445 static int check_dsn_notify (const char* option, unsigned long p,
1446 char* errbuf, size_t errlen) {
1447 list2_t* list = NULL;
1449 char* val = (char*) p;
1453 list = list_from_str (val, ",");
1454 if (list_empty (list))
1457 for (i = 0; i < list->length; i++)
1458 if (str_ncmp (list->data[i], "never", 5) != 0 &&
1459 str_ncmp (list->data[i], "failure", 7) != 0 &&
1460 str_ncmp (list->data[i], "delay", 5) != 0 &&
1461 str_ncmp (list->data[i], "success", 7) != 0) {
1463 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1464 (char*) list->data[i], "dsn_notify");
1468 list_del (&list, (list_del_t*) _mem_free);
1472 static int check_num (const char* option, unsigned long p,
1473 char* errbuf, size_t errlen) {
1476 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1483 static int check_debug (const char* option, unsigned long p,
1484 char* errbuf, size_t errlen) {
1485 if ((int) p <= DEBUG_MAX_LEVEL &&
1486 (int) p >= DEBUG_MIN_LEVEL)
1490 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1495 static int check_history (const char* option, unsigned long p,
1496 char* errbuf, size_t errlen) {
1497 if (!check_num ("history", p, errbuf, errlen))
1499 mutt_init_history ();
1503 static int check_special (const char* name, unsigned long val,
1504 char* errbuf, size_t errlen) {
1507 for (i = 0; SpecialVars[i].name; i++) {
1508 if (str_cmp (SpecialVars[i].name, name) == 0) {
1509 return (SpecialVars[i].check (SpecialVars[i].name,
1510 val, errbuf, errlen));
1516 static const struct mapping_t* get_sortmap (struct option_t* option) {
1517 const struct mapping_t* map = NULL;
1519 switch (option->type & DT_SUBTYPE_MASK) {
1521 map = SortAliasMethods;
1523 case DT_SORT_BROWSER:
1524 map = SortBrowserMethods;
1527 if ((WithCrypto & APPLICATION_PGP))
1528 map = SortKeyMethods;
1531 map = SortAuxMethods;
1540 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1543 int query, unset, inv, reset, r = 0;
1544 struct option_t* option = NULL;
1546 while (MoreArgs (s)) {
1547 /* reset state variables */
1549 unset = data & M_SET_UNSET;
1550 inv = data & M_SET_INV;
1551 reset = data & M_SET_RESET;
1553 if (*s->dptr == '?') {
1557 else if (str_ncmp ("no", s->dptr, 2) == 0) {
1561 else if (str_ncmp ("inv", s->dptr, 3) == 0) {
1565 else if (*s->dptr == '&') {
1570 /* get the variable name */
1571 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1573 /* resolve synonyms */
1574 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1575 DTYPE (option->type == DT_SYN)) {
1576 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1577 syn_add (newopt, option);
1581 /* see if we need to add $user_ var */
1582 if (!option && ascii_strncmp ("user_", tmp->data, 5) == 0) {
1583 /* there's no option named like this yet so only add one
1584 * if the action isn't any of: reset, unset, query */
1585 if (!(reset || unset || query || *s->dptr != '=')) {
1586 debug_print (1, ("adding user option '%s'\n", tmp->data));
1587 option = add_user_option (tmp->data);
1588 hash_insert (ConfigOptions, option->option, option, 0);
1592 if (!option && !(reset && str_cmp ("all", tmp->data) == 0)) {
1593 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1599 if (query || unset || inv) {
1600 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1604 if (s && *s->dptr == '=') {
1605 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1609 if (!str_cmp ("all", tmp->data)) {
1610 hash_map (ConfigOptions, mutt_restore_default, 1);
1613 else if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1614 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1618 mutt_restore_default (NULL, option, 1);
1620 else if (DTYPE (option->type) == DT_BOOL) {
1621 /* XXX this currently ignores the function table
1622 * as we don't get invert and stuff into it */
1623 if (s && *s->dptr == '=') {
1624 if (unset || inv || query) {
1625 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1630 mutt_extract_token (tmp, s, 0);
1631 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1633 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1636 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1642 bool_to_string (err->data, err->dsize, option);
1647 unset_option (option->data);
1649 toggle_option (option->data);
1651 set_option (option->data);
1653 else if (DTYPE (option->type) == DT_STR ||
1654 DTYPE (option->type) == DT_PATH ||
1655 DTYPE (option->type) == DT_ADDR ||
1656 DTYPE (option->type) == DT_MAGIC ||
1657 DTYPE (option->type) == DT_NUM ||
1658 DTYPE (option->type) == DT_SORT ||
1659 DTYPE (option->type) == DT_RX ||
1660 DTYPE (option->type) == DT_USER ||
1661 DTYPE (option->type) == DT_SYS) {
1663 /* XXX maybe we need to get unset into handlers? */
1664 if (DTYPE (option->type) == DT_STR ||
1665 DTYPE (option->type) == DT_PATH ||
1666 DTYPE (option->type) == DT_ADDR ||
1667 DTYPE (option->type) == DT_USER ||
1668 DTYPE (option->type) == DT_SYS) {
1670 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1671 snprintf (err->data, err->dsize, _("$%s is read-only"),
1675 } else if (DTYPE (option->type) == DT_ADDR)
1676 rfc822_free_address ((ADDRESS **) option->data);
1677 else if (DTYPE (option->type) == DT_USER)
1678 /* to unset $user_ means remove */
1679 hash_delete (ConfigOptions, option->option,
1680 option, del_option);
1682 mem_free ((void *) option->data);
1687 if (query || *s->dptr != '=') {
1688 FuncTable[DTYPE (option->type)].opt_to_string
1689 (err->data, err->dsize, option);
1693 /* the $muttng_ variables are read-only */
1694 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1695 snprintf (err->data, err->dsize, _("$%s is read-only"),
1701 mutt_extract_token (tmp, s, 0);
1702 if (!FuncTable[DTYPE (option->type)].opt_from_string
1703 (option, tmp->data, err->data, err->dsize))
1707 else if (DTYPE (option->type) == DT_QUAD) {
1710 quad_to_string (err->data, err->dsize, option);
1714 if (*s->dptr == '=') {
1716 mutt_extract_token (tmp, s, 0);
1717 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1718 set_quadoption (option->data, M_YES);
1719 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1720 set_quadoption (option->data, M_NO);
1721 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1722 set_quadoption (option->data, M_ASKYES);
1723 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1724 set_quadoption (option->data, M_ASKNO);
1726 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1727 tmp->data, option->option);
1734 toggle_quadoption (option->data);
1736 set_quadoption (option->data, M_NO);
1738 set_quadoption (option->data, M_YES);
1742 snprintf (err->data, err->dsize, _("%s: unknown type"),
1748 if (option->flags & R_INDEX)
1749 set_option (OPTFORCEREDRAWINDEX);
1750 if (option->flags & R_PAGER)
1751 set_option (OPTFORCEREDRAWPAGER);
1752 if (option->flags & R_RESORT_SUB)
1753 set_option (OPTSORTSUBTHREADS);
1754 if (option->flags & R_RESORT)
1755 set_option (OPTNEEDRESORT);
1756 if (option->flags & R_RESORT_INIT)
1757 set_option (OPTRESORTINIT);
1758 if (option->flags & R_TREE)
1759 set_option (OPTREDRAWTREE);
1766 /* reads the specified initialization file. returns -1 if errors were found
1767 so that we can pause to let the user know... */
1768 static int source_rc (const char *rcfile, BUFFER * err)
1771 int line = 0, rc = 0, conv = 0;
1773 char *linebuf = NULL;
1774 char *currentline = NULL;
1778 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
1780 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1781 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1785 memset (&token, 0, sizeof (token));
1786 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
1787 conv = ConfigCharset && (*ConfigCharset) && Charset;
1789 currentline = str_dup (linebuf);
1792 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1795 currentline = linebuf;
1800 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1801 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1802 if (--rc < -MAXERRS) {
1804 mem_free (¤tline);
1813 mem_free (¤tline);
1815 mem_free (&token.data);
1816 mem_free (&linebuf);
1819 mutt_wait_filter (pid);
1821 /* the muttrc source keyword */
1822 snprintf (err->data, err->dsize,
1823 rc >= -MAXERRS ? _("source: errors in %s")
1824 : _("source: reading aborted due too many errors in %s"),
1833 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
1836 char path[_POSIX_PATH_MAX];
1840 if (mutt_extract_token (tmp, s, 0) != 0) {
1841 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1845 strfcpy (path, tmp->data, sizeof (path));
1846 mutt_expand_path (path, sizeof (path));
1848 rc += source_rc (path, err);
1850 while (MoreArgs (s));
1852 return ((rc < 0) ? -1 : 0);
1855 /* line command to execute
1857 token scratch buffer to be used by parser. caller should free
1858 token->data when finished. the reason for this variable is
1859 to avoid having to allocate and deallocate a lot of memory
1860 if we are parsing many lines. the caller can pass in the
1861 memory to use, which avoids having to create new space for
1862 every call to this function.
1864 err where to write error messages */
1865 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1870 memset (&expn, 0, sizeof (expn));
1871 expn.data = expn.dptr = line;
1872 expn.dsize = str_len (line);
1876 debug_print (1, ("expand '%s'\n", line));
1879 while (*expn.dptr) {
1880 if (*expn.dptr == '#')
1881 break; /* rest of line is a comment */
1882 if (*expn.dptr == ';') {
1886 mutt_extract_token (token, &expn, 0);
1887 for (i = 0; Commands[i].name; i++) {
1888 if (!str_cmp (token->data, Commands[i].name)) {
1889 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1894 if (!Commands[i].name) {
1895 snprintf (err->data, err->dsize, _("%s: unknown command"),
1896 NONULL (token->data));
1903 mem_free (&expn.data);
1908 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1909 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1910 /* initial string that starts completion. No telling how much crap
1911 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1912 char User_typed[LONG_STRING] = { 0 };
1914 int Num_matched = 0; /* Number of matches for completion */
1915 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1916 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1918 /* helper function for completion. Changes the dest buffer if
1919 necessary/possible to aid completion.
1920 dest == completion result gets here.
1921 src == candidate for completion.
1922 try == user entered data for completion.
1923 len == length of dest buffer.
1925 static void candidate (char *dest, char *try, char *src, int len)
1929 if (strstr (src, try) == src) {
1930 Matches[Num_matched++] = src;
1932 strfcpy (dest, src, len);
1934 for (l = 0; src[l] && src[l] == dest[l]; l++);
1940 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1944 int spaces; /* keep track of the number of leading spaces on the line */
1947 spaces = buffer - pt;
1949 pt = buffer + pos - spaces;
1950 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1953 if (pt == buffer) { /* complete cmd */
1954 /* first TAB. Collect all the matches */
1957 strfcpy (User_typed, pt, sizeof (User_typed));
1958 memset (Matches, 0, sizeof (Matches));
1959 memset (Completed, 0, sizeof (Completed));
1960 for (num = 0; Commands[num].name; num++)
1961 candidate (Completed, User_typed, Commands[num].name,
1962 sizeof (Completed));
1963 Matches[Num_matched++] = User_typed;
1965 /* All matches are stored. Longest non-ambiguous string is ""
1966 * i.e. dont change 'buffer'. Fake successful return this time */
1967 if (User_typed[0] == 0)
1971 if (Completed[0] == 0 && User_typed[0])
1974 /* Num_matched will _always_ be atleast 1 since the initial
1975 * user-typed string is always stored */
1976 if (numtabs == 1 && Num_matched == 2)
1977 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1978 else if (numtabs > 1 && Num_matched > 2)
1979 /* cycle thru all the matches */
1980 snprintf (Completed, sizeof (Completed), "%s",
1981 Matches[(numtabs - 2) % Num_matched]);
1983 /* return the completed command */
1984 strncpy (buffer, Completed, len - spaces);
1986 else if (!str_ncmp (buffer, "set", 3)
1987 || !str_ncmp (buffer, "unset", 5)
1988 || !str_ncmp (buffer, "reset", 5)
1989 || !str_ncmp (buffer, "toggle", 6)) { /* complete variables */
1990 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1993 /* loop through all the possible prefixes (no, inv, ...) */
1994 if (!str_ncmp (buffer, "set", 3)) {
1995 for (num = 0; prefixes[num]; num++) {
1996 if (!str_ncmp (pt, prefixes[num], str_len (prefixes[num]))) {
1997 pt += str_len (prefixes[num]);
2003 /* first TAB. Collect all the matches */
2006 strfcpy (User_typed, pt, sizeof (User_typed));
2007 memset (Matches, 0, sizeof (Matches));
2008 memset (Completed, 0, sizeof (Completed));
2009 for (num = 0; MuttVars[num].option; num++)
2010 candidate (Completed, User_typed, MuttVars[num].option,
2011 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);
2034 else if (!str_ncmp (buffer, "exec", 4)) {
2035 struct binding_t *menu = km_get_table (CurrentMenu);
2037 if (!menu && CurrentMenu != MENU_PAGER)
2041 /* first TAB. Collect all the matches */
2044 strfcpy (User_typed, pt, sizeof (User_typed));
2045 memset (Matches, 0, sizeof (Matches));
2046 memset (Completed, 0, sizeof (Completed));
2047 for (num = 0; menu[num].name; num++)
2048 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
2049 /* try the generic menu */
2050 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2052 for (num = 0; menu[num].name; num++)
2053 candidate (Completed, User_typed, menu[num].name,
2054 sizeof (Completed));
2056 Matches[Num_matched++] = User_typed;
2058 /* All matches are stored. Longest non-ambiguous string is ""
2059 * i.e. dont change 'buffer'. Fake successful return this time */
2060 if (User_typed[0] == 0)
2064 if (Completed[0] == 0 && User_typed[0])
2067 /* Num_matched will _always_ be atleast 1 since the initial
2068 * user-typed string is always stored */
2069 if (numtabs == 1 && Num_matched == 2)
2070 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
2071 else if (numtabs > 1 && Num_matched > 2)
2072 /* cycle thru all the matches */
2073 snprintf (Completed, sizeof (Completed), "%s",
2074 Matches[(numtabs - 2) % Num_matched]);
2076 strncpy (pt, Completed, buffer + len - pt - spaces);
2084 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2086 char var[STRING], *pt = buffer;
2088 struct option_t* option = NULL;
2094 spaces = buffer - pt;
2096 pt = buffer + pos - spaces;
2097 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2099 pt++; /* move past the space */
2100 if (*pt == '=') /* abort if no var before the '=' */
2103 if (str_ncmp (buffer, "set", 3) == 0) {
2104 strfcpy (var, pt, sizeof (var));
2105 /* ignore the trailing '=' when comparing */
2106 var[str_len (var) - 1] = 0;
2107 if (!(option = hash_find (ConfigOptions, var)))
2108 return 0; /* no such variable. */
2110 char tmp[LONG_STRING], tmp2[LONG_STRING];
2112 size_t dlen = buffer + len - pt - spaces;
2113 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2117 if ((DTYPE (option->type) == DT_STR) ||
2118 (DTYPE (option->type) == DT_PATH) ||
2119 (DTYPE (option->type) == DT_RX)) {
2120 strfcpy (tmp, NONULL (*((char **) option->data)), sizeof (tmp));
2121 if (DTYPE (option->type) == DT_PATH)
2122 mutt_pretty_mailbox (tmp);
2124 else if (DTYPE (option->type) == DT_ADDR) {
2125 rfc822_write_address (tmp, sizeof (tmp),
2126 *((ADDRESS **) option->data), 0);
2128 else if (DTYPE (option->type) == DT_QUAD)
2129 strfcpy (tmp, vals[quadoption (option->data)], sizeof (tmp));
2130 else if (DTYPE (option->type) == DT_NUM)
2131 snprintf (tmp, sizeof (tmp), "%d", (*((short *) option->data)));
2132 else if (DTYPE (option->type) == DT_SORT) {
2133 const struct mapping_t *map;
2136 switch (option->type & DT_SUBTYPE_MASK) {
2138 map = SortAliasMethods;
2140 case DT_SORT_BROWSER:
2141 map = SortBrowserMethods;
2144 if ((WithCrypto & APPLICATION_PGP))
2145 map = SortKeyMethods;
2154 mutt_getnamebyvalue (*((short *) option->data) & SORT_MASK,
2156 snprintf (tmp, sizeof (tmp), "%s%s%s",
2157 (*((short *) option->data) & SORT_REVERSE) ?
2159 (*((short *) option->data) & SORT_LAST) ? "last-" :
2162 else if (DTYPE (option->type) == DT_MAGIC) {
2164 switch (DefaultMagic) {
2180 strfcpy (tmp, p, sizeof (tmp));
2182 else if (DTYPE (option->type) == DT_BOOL)
2183 strfcpy (tmp, option (option->data) ? "yes" : "no",
2188 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
2189 if (*s == '\\' || *s == '"')
2195 strfcpy (tmp, pt, sizeof (tmp));
2196 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2204 /* Implement the -Q command line flag */
2205 int mutt_query_variables (LIST * queries)
2209 char errbuff[STRING];
2210 char command[STRING];
2214 memset (&err, 0, sizeof (err));
2215 memset (&token, 0, sizeof (token));
2218 err.dsize = sizeof (errbuff);
2220 for (p = queries; p; p = p->next) {
2221 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2222 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2223 fprintf (stderr, "%s\n", err.data);
2224 mem_free (&token.data);
2227 printf ("%s\n", err.data);
2230 mem_free (&token.data);
2234 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2238 for (i = 0; map[i].name; i++)
2239 if (map[i].value == val)
2240 return (map[i].name);
2244 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2248 for (i = 0; map[i].name; i++)
2249 if (ascii_strcasecmp (map[i].name, name) == 0)
2250 return (map[i].value);
2254 static int mutt_execute_commands (LIST * p)
2257 char errstr[SHORT_STRING];
2259 memset (&err, 0, sizeof (err));
2261 err.dsize = sizeof (errstr);
2262 memset (&token, 0, sizeof (token));
2263 for (; p; p = p->next) {
2264 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2265 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2266 mem_free (&token.data);
2270 mem_free (&token.data);
2274 void mutt_init (int skip_sys_rc, LIST * commands)
2277 struct utsname utsname;
2278 char *p, buffer[STRING], error[STRING];
2279 int i, default_rc = 0, need_pause = 0;
2282 memset (&err, 0, sizeof (err));
2284 err.dsize = sizeof (error);
2286 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2287 * to have some room for $user_ vars */
2288 ConfigOptions = hash_create (sizeof (MuttVars) * 3);
2289 for (i = 0; MuttVars[i].option; i++) {
2290 if (DTYPE (MuttVars[i].type) != DT_SYS)
2291 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2293 hash_insert (ConfigOptions, MuttVars[i].option,
2294 add_option (MuttVars[i].option, MuttVars[i].init,
2299 * XXX - use something even more difficult to predict?
2301 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2302 "\033]9;%ld\a", (long) time (NULL));
2304 /* on one of the systems I use, getcwd() does not return the same prefix
2305 as is listed in the passwd file */
2306 if ((p = getenv ("HOME")))
2307 Homedir = str_dup (p);
2309 /* Get some information about the user */
2310 if ((pw = getpwuid (getuid ()))) {
2313 Username = str_dup (pw->pw_name);
2315 Homedir = str_dup (pw->pw_dir);
2317 Realname = str_dup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2318 Shell = str_dup (pw->pw_shell);
2324 fputs (_("unable to determine home directory"), stderr);
2327 if ((p = getenv ("USER")))
2328 Username = str_dup (p);
2331 fputs (_("unable to determine username"), stderr);
2334 Shell = str_dup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2337 debug_start(Homedir);
2339 /* And about the host... */
2341 /* some systems report the FQDN instead of just the hostname */
2342 if ((p = strchr (utsname.nodename, '.'))) {
2343 Hostname = str_substrdup (utsname.nodename, p);
2345 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2348 Hostname = str_dup (utsname.nodename);
2351 #define DOMAIN buffer
2352 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2353 Fqdn = str_dup ("@");
2356 if (*DOMAIN != '@') {
2357 Fqdn = mem_malloc (str_len (DOMAIN) + str_len (Hostname) + 2);
2358 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2361 Fqdn = str_dup (NONULL (Hostname));
2368 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2370 fgets (buffer, sizeof (buffer), f);
2371 p = (char*) &buffer;
2374 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r')
2378 NewsServer = str_dup (p);
2382 if ((p = getenv ("NNTPSERVER")))
2383 NewsServer = str_dup (p);
2386 if ((p = getenv ("MAIL")))
2387 Spoolfile = str_dup (p);
2388 else if ((p = getenv ("MAILDIR")))
2389 Spoolfile = str_dup (p);
2392 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2394 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
2396 Spoolfile = str_dup (buffer);
2399 if ((p = getenv ("MAILCAPS")))
2400 MailcapPath = str_dup (p);
2402 /* Default search path from RFC1524 */
2404 str_dup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2405 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2408 Tempdir = str_dup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2410 p = getenv ("VISUAL");
2412 p = getenv ("EDITOR");
2416 Editor = str_dup (p);
2417 Visual = str_dup (p);
2419 if ((p = getenv ("REPLYTO")) != NULL) {
2422 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2424 memset (&buf, 0, sizeof (buf));
2425 buf.data = buf.dptr = buffer;
2426 buf.dsize = str_len (buffer);
2428 memset (&token, 0, sizeof (token));
2429 parse_my_hdr (&token, &buf, 0, &err);
2430 mem_free (&token.data);
2433 if ((p = getenv ("EMAIL")) != NULL)
2434 From = rfc822_parse_adrlist (NULL, p);
2436 mutt_set_langinfo_charset ();
2437 mutt_set_charset (Charset);
2440 /* Set standard defaults */
2441 hash_map (ConfigOptions, mutt_set_default, 0);
2442 hash_map (ConfigOptions, mutt_restore_default, 0);
2444 CurrentMenu = MENU_MAIN;
2447 #ifndef LOCALES_HACK
2448 /* Do we have a locale definition? */
2449 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2450 ((p = getenv ("LANG")) != NULL && p[0]) ||
2451 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2452 set_option (OPTLOCALES);
2456 /* Unset suspend by default if we're the session leader */
2457 if (getsid (0) == getpid ())
2458 unset_option (OPTSUSPEND);
2461 mutt_init_history ();
2470 * When changing the code which looks for a configuration file,
2471 * please also change the corresponding code in muttbug.sh.in.
2481 snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2483 if (access (buffer, F_OK) == -1)
2485 snprintf (buffer, sizeof (buffer), "%s/.muttngrc", NONULL (Homedir));
2486 if (access (buffer, F_OK) == -1)
2488 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s",
2489 NONULL (Homedir), MUTT_VERSION);
2490 if (access (buffer, F_OK) == -1)
2492 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc",
2496 Muttrc = str_dup (buffer);
2499 strfcpy (buffer, Muttrc, sizeof (buffer));
2501 mutt_expand_path (buffer, sizeof (buffer));
2502 Muttrc = str_dup (buffer);
2504 mem_free (&AliasFile);
2505 AliasFile = str_dup (NONULL (Muttrc));
2507 /* Process the global rc file if it exists and the user hasn't explicity
2508 requested not to via "-n". */
2510 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2512 if (access (buffer, F_OK) == -1)
2513 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", SYSCONFDIR);
2514 if (access (buffer, F_OK) == -1)
2515 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR,
2517 if (access (buffer, F_OK) == -1)
2518 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2519 if (access (buffer, F_OK) != -1) {
2520 if (source_rc (buffer, &err) != 0) {
2521 fputs (err.data, stderr);
2522 fputc ('\n', stderr);
2528 /* Read the user's initialization file. */
2529 if (access (Muttrc, F_OK) != -1) {
2530 if (!option (OPTNOCURSES))
2532 if (source_rc (Muttrc, &err) != 0) {
2533 fputs (err.data, stderr);
2534 fputc ('\n', stderr);
2538 else if (!default_rc) {
2539 /* file specified by -F does not exist */
2540 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2541 mutt_endwin (buffer);
2545 if (mutt_execute_commands (commands) != 0)
2548 /* warn about synonym variables */
2549 if (!list_empty(Synonyms)) {
2551 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2552 for (i = 0; i < Synonyms->length; i++) {
2553 struct option_t* newopt = NULL, *oldopt = NULL;
2554 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2555 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2556 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2557 oldopt ? NONULL (oldopt->option) : "",
2558 newopt ? NONULL (newopt->option) : "",
2559 NONULL(((syn_t*) Synonyms->data[i])->f),
2560 ((syn_t*) Synonyms->data[i])->l);
2562 fprintf (stderr, _("Warning: synonym variables are scheduled"
2563 " for removal.\n"));
2564 list_del (&Synonyms, syn_del);
2568 if (need_pause && !option (OPTNOCURSES)) {
2569 if (mutt_any_key_to_continue (NULL) == -1)
2574 set_option (OPTWEED); /* turn weeding on by default */
2578 int mutt_get_hook_type (const char *name)
2580 struct command_t *c;
2582 for (c = Commands; c->name; c++)
2583 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2588 /* compare two option_t*'s for sorting -t/-T output */
2589 static int opt_cmp (const void* a, const void* b) {
2590 return (str_cmp ((*(struct option_t**) a)->option,
2591 (*(struct option_t**) b)->option));
2594 /* callback for hash_map() to put all non-synonym vars into list */
2595 static void opt_sel_full (const char* key, void* data,
2596 unsigned long more) {
2597 list2_t** l = (list2_t**) more;
2598 struct option_t* option = (struct option_t*) data;
2600 if (DTYPE (option->type) == DT_SYN)
2602 list_push_back (l, option);
2605 /* callback for hash_map() to put all changed non-synonym vars into list */
2606 static void opt_sel_diff (const char* key, void* data,
2607 unsigned long more) {
2608 list2_t** l = (list2_t**) more;
2609 struct option_t* option = (struct option_t*) data;
2610 char buf[LONG_STRING];
2612 if (DTYPE (option->type) == DT_SYN)
2615 mutt_option_value (option->option, buf, sizeof (buf));
2616 if (str_cmp (buf, option->init) != 0)
2617 list_push_back (l, option);
2620 /* dump out the value of all the variables we have */
2621 int mutt_dump_variables (int full) {
2623 char outbuf[STRING];
2624 list2_t* tmp = NULL;
2625 struct option_t* option = NULL;
2627 /* get all non-synonyms into list... */
2628 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2629 (unsigned long) &tmp);
2631 if (!list_empty(tmp)) {
2632 /* ...and dump list sorted */
2633 qsort (tmp->data, tmp->length, sizeof (void*), opt_cmp);
2634 for (i = 0; i < tmp->length; i++) {
2635 option = (struct option_t*) tmp->data[i];
2636 FuncTable[DTYPE (option->type)].opt_to_string
2637 (outbuf, sizeof (outbuf), option);
2638 printf ("%s\n", outbuf);
2641 list_del (&tmp, NULL);