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);
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 /* if additional data more == 1, we want to resolve synonyms */
1370 static void mutt_restore_default (const char* name, void* p,
1371 unsigned long more) {
1372 char errbuf[STRING];
1373 struct option_t* ptr = (struct option_t*) p;
1375 if (DTYPE (ptr->type) == DT_SYN) {
1378 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1382 if (FuncTable[DTYPE (ptr->type)].opt_from_string &&
1383 FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, ptr->init, errbuf,
1384 sizeof (errbuf)) < 0) {
1386 fprintf (stderr, _("Invalid default setting found. Please report this "
1387 "error:\n\"%s\"\n"), errbuf);
1391 if (ptr->flags & R_INDEX)
1392 set_option (OPTFORCEREDRAWINDEX);
1393 if (ptr->flags & R_PAGER)
1394 set_option (OPTFORCEREDRAWPAGER);
1395 if (ptr->flags & R_RESORT_SUB)
1396 set_option (OPTSORTSUBTHREADS);
1397 if (ptr->flags & R_RESORT)
1398 set_option (OPTNEEDRESORT);
1399 if (ptr->flags & R_RESORT_INIT)
1400 set_option (OPTRESORTINIT);
1401 if (ptr->flags & R_TREE)
1402 set_option (OPTREDRAWTREE);
1405 /* check whether value for $dsn_return would be valid */
1406 static int check_dsn_return (const char* option, unsigned long p,
1407 char* errbuf, size_t errlen) {
1408 char* val = (char*) p;
1409 if (val && *val && str_ncmp (val, "hdrs", 4) != 0 &&
1410 str_ncmp (val, "full", 4) != 0) {
1412 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1418 /* check whether value for $dsn_notify would be valid */
1419 static int check_dsn_notify (const char* option, unsigned long p,
1420 char* errbuf, size_t errlen) {
1421 list2_t* list = NULL;
1423 char* val = (char*) p;
1427 list = list_from_str (val, ",");
1428 if (list_empty (list))
1431 for (i = 0; i < list->length; i++)
1432 if (str_ncmp (list->data[i], "never", 5) != 0 &&
1433 str_ncmp (list->data[i], "failure", 7) != 0 &&
1434 str_ncmp (list->data[i], "delay", 5) != 0 &&
1435 str_ncmp (list->data[i], "success", 7) != 0) {
1437 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1438 (char*) list->data[i], "dsn_notify");
1442 list_del (&list, (list_del_t*) _mem_free);
1446 static int check_num (const char* option, unsigned long p,
1447 char* errbuf, size_t errlen) {
1450 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1457 static int check_debug (const char* option, unsigned long p,
1458 char* errbuf, size_t errlen) {
1459 if ((int) p <= DEBUG_MAX_LEVEL &&
1460 (int) p >= DEBUG_MIN_LEVEL)
1464 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1469 static int check_history (const char* option, unsigned long p,
1470 char* errbuf, size_t errlen) {
1471 if (!check_num ("history", p, errbuf, errlen))
1473 mutt_init_history ();
1477 static int check_special (const char* name, unsigned long val,
1478 char* errbuf, size_t errlen) {
1481 for (i = 0; SpecialVars[i].name; i++) {
1482 if (str_cmp (SpecialVars[i].name, name) == 0) {
1483 return (SpecialVars[i].check (SpecialVars[i].name,
1484 val, errbuf, errlen));
1490 static const struct mapping_t* get_sortmap (struct option_t* option) {
1491 const struct mapping_t* map = NULL;
1493 switch (option->type & DT_SUBTYPE_MASK) {
1495 map = SortAliasMethods;
1497 case DT_SORT_BROWSER:
1498 map = SortBrowserMethods;
1501 if ((WithCrypto & APPLICATION_PGP))
1502 map = SortKeyMethods;
1505 map = SortAuxMethods;
1514 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1517 int query, unset, inv, reset, r = 0;
1518 struct option_t* option = NULL;
1520 while (MoreArgs (s)) {
1521 /* reset state variables */
1523 unset = data & M_SET_UNSET;
1524 inv = data & M_SET_INV;
1525 reset = data & M_SET_RESET;
1527 if (*s->dptr == '?') {
1531 else if (str_ncmp ("no", s->dptr, 2) == 0) {
1535 else if (str_ncmp ("inv", s->dptr, 3) == 0) {
1539 else if (*s->dptr == '&') {
1544 /* get the variable name */
1545 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1547 /* resolve synonyms */
1548 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1549 DTYPE (option->type == DT_SYN)) {
1550 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1551 syn_add (newopt, option);
1555 /* see if we need to add $user_ var */
1556 if (!option && ascii_strncmp ("user_", tmp->data, 5) == 0) {
1557 /* there's no option named like this yet so only add one
1558 * if the action isn't any of: reset, unset, query */
1559 if (!(reset || unset || query || *s->dptr != '=')) {
1560 debug_print (1, ("adding user option '%s'\n", tmp->data));
1561 option = add_user_option (tmp->data);
1562 hash_insert (ConfigOptions, option->option, option, 0);
1566 if (!option && !(reset && str_cmp ("all", tmp->data) == 0)) {
1567 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1573 if (query || unset || inv) {
1574 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1578 if (s && *s->dptr == '=') {
1579 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1583 if (!str_cmp ("all", tmp->data)) {
1584 hash_map (ConfigOptions, mutt_restore_default, 1);
1587 else if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1588 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1592 mutt_restore_default (NULL, option, 1);
1594 else if (DTYPE (option->type) == DT_BOOL) {
1595 /* XXX this currently ignores the function table
1596 * as we don't get invert and stuff into it */
1597 if (s && *s->dptr == '=') {
1598 if (unset || inv || query) {
1599 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1604 mutt_extract_token (tmp, s, 0);
1605 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1607 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1610 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1616 bool_to_string (err->data, err->dsize, option);
1621 unset_option (option->data);
1623 toggle_option (option->data);
1625 set_option (option->data);
1627 else if (DTYPE (option->type) == DT_STR ||
1628 DTYPE (option->type) == DT_PATH ||
1629 DTYPE (option->type) == DT_ADDR ||
1630 DTYPE (option->type) == DT_MAGIC ||
1631 DTYPE (option->type) == DT_NUM ||
1632 DTYPE (option->type) == DT_SORT ||
1633 DTYPE (option->type) == DT_RX ||
1634 DTYPE (option->type) == DT_USER ||
1635 DTYPE (option->type) == DT_SYS) {
1637 /* XXX maybe we need to get unset into handlers? */
1638 if (DTYPE (option->type) == DT_STR ||
1639 DTYPE (option->type) == DT_PATH ||
1640 DTYPE (option->type) == DT_ADDR ||
1641 DTYPE (option->type) == DT_USER ||
1642 DTYPE (option->type) == DT_SYS) {
1644 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1645 snprintf (err->data, err->dsize, _("$%s is read-only"),
1649 } else if (DTYPE (option->type) == DT_ADDR)
1650 rfc822_free_address ((ADDRESS **) option->data);
1651 else if (DTYPE (option->type) == DT_USER)
1652 /* to unset $user_ means remove */
1653 hash_delete (ConfigOptions, option->option,
1654 option, del_option);
1656 mem_free ((void *) option->data);
1661 if (query || *s->dptr != '=') {
1662 FuncTable[DTYPE (option->type)].opt_to_string
1663 (err->data, err->dsize, option);
1667 /* the $muttng_ variables are read-only */
1668 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1669 snprintf (err->data, err->dsize, _("$%s is read-only"),
1675 mutt_extract_token (tmp, s, 0);
1676 if (!FuncTable[DTYPE (option->type)].opt_from_string
1677 (option, tmp->data, err->data, err->dsize))
1681 else if (DTYPE (option->type) == DT_QUAD) {
1684 quad_to_string (err->data, err->dsize, option);
1688 if (*s->dptr == '=') {
1690 mutt_extract_token (tmp, s, 0);
1691 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1692 set_quadoption (option->data, M_YES);
1693 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1694 set_quadoption (option->data, M_NO);
1695 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1696 set_quadoption (option->data, M_ASKYES);
1697 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1698 set_quadoption (option->data, M_ASKNO);
1700 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1701 tmp->data, option->option);
1708 toggle_quadoption (option->data);
1710 set_quadoption (option->data, M_NO);
1712 set_quadoption (option->data, M_YES);
1716 snprintf (err->data, err->dsize, _("%s: unknown type"),
1722 if (option->flags & R_INDEX)
1723 set_option (OPTFORCEREDRAWINDEX);
1724 if (option->flags & R_PAGER)
1725 set_option (OPTFORCEREDRAWPAGER);
1726 if (option->flags & R_RESORT_SUB)
1727 set_option (OPTSORTSUBTHREADS);
1728 if (option->flags & R_RESORT)
1729 set_option (OPTNEEDRESORT);
1730 if (option->flags & R_RESORT_INIT)
1731 set_option (OPTRESORTINIT);
1732 if (option->flags & R_TREE)
1733 set_option (OPTREDRAWTREE);
1740 /* reads the specified initialization file. returns -1 if errors were found
1741 so that we can pause to let the user know... */
1742 static int source_rc (const char *rcfile, BUFFER * err)
1745 int line = 0, rc = 0, conv = 0;
1747 char *linebuf = NULL;
1748 char *currentline = NULL;
1752 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
1754 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1755 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1759 memset (&token, 0, sizeof (token));
1760 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
1761 conv = ConfigCharset && (*ConfigCharset) && Charset;
1763 currentline = str_dup (linebuf);
1766 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1769 currentline = linebuf;
1774 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1775 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1776 if (--rc < -MAXERRS) {
1778 mem_free (¤tline);
1787 mem_free (¤tline);
1789 mem_free (&token.data);
1790 mem_free (&linebuf);
1793 mutt_wait_filter (pid);
1795 /* the muttrc source keyword */
1796 snprintf (err->data, err->dsize,
1797 rc >= -MAXERRS ? _("source: errors in %s")
1798 : _("source: reading aborted due too many errors in %s"),
1807 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
1810 char path[_POSIX_PATH_MAX];
1814 if (mutt_extract_token (tmp, s, 0) != 0) {
1815 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1819 strfcpy (path, tmp->data, sizeof (path));
1820 mutt_expand_path (path, sizeof (path));
1822 rc += source_rc (path, err);
1824 while (MoreArgs (s));
1826 return ((rc < 0) ? -1 : 0);
1829 /* line command to execute
1831 token scratch buffer to be used by parser. caller should free
1832 token->data when finished. the reason for this variable is
1833 to avoid having to allocate and deallocate a lot of memory
1834 if we are parsing many lines. the caller can pass in the
1835 memory to use, which avoids having to create new space for
1836 every call to this function.
1838 err where to write error messages */
1839 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1844 memset (&expn, 0, sizeof (expn));
1845 expn.data = expn.dptr = line;
1846 expn.dsize = str_len (line);
1850 debug_print (1, ("expand '%s'\n", line));
1853 while (*expn.dptr) {
1854 if (*expn.dptr == '#')
1855 break; /* rest of line is a comment */
1856 if (*expn.dptr == ';') {
1860 mutt_extract_token (token, &expn, 0);
1861 for (i = 0; Commands[i].name; i++) {
1862 if (!str_cmp (token->data, Commands[i].name)) {
1863 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1868 if (!Commands[i].name) {
1869 snprintf (err->data, err->dsize, _("%s: unknown command"),
1870 NONULL (token->data));
1877 mem_free (&expn.data);
1882 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1883 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1884 /* initial string that starts completion. No telling how much crap
1885 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1886 char User_typed[LONG_STRING] = { 0 };
1888 int Num_matched = 0; /* Number of matches for completion */
1889 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1890 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1892 /* helper function for completion. Changes the dest buffer if
1893 necessary/possible to aid completion.
1894 dest == completion result gets here.
1895 src == candidate for completion.
1896 try == user entered data for completion.
1897 len == length of dest buffer.
1899 static void candidate (char *dest, char *try, char *src, int len)
1903 if (strstr (src, try) == src) {
1904 Matches[Num_matched++] = src;
1906 strfcpy (dest, src, len);
1908 for (l = 0; src[l] && src[l] == dest[l]; l++);
1914 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1918 int spaces; /* keep track of the number of leading spaces on the line */
1921 spaces = buffer - pt;
1923 pt = buffer + pos - spaces;
1924 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1927 if (pt == buffer) { /* complete cmd */
1928 /* first TAB. Collect all the matches */
1931 strfcpy (User_typed, pt, sizeof (User_typed));
1932 memset (Matches, 0, sizeof (Matches));
1933 memset (Completed, 0, sizeof (Completed));
1934 for (num = 0; Commands[num].name; num++)
1935 candidate (Completed, User_typed, Commands[num].name,
1936 sizeof (Completed));
1937 Matches[Num_matched++] = User_typed;
1939 /* All matches are stored. Longest non-ambiguous string is ""
1940 * i.e. dont change 'buffer'. Fake successful return this time */
1941 if (User_typed[0] == 0)
1945 if (Completed[0] == 0 && User_typed[0])
1948 /* Num_matched will _always_ be atleast 1 since the initial
1949 * user-typed string is always stored */
1950 if (numtabs == 1 && Num_matched == 2)
1951 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1952 else if (numtabs > 1 && Num_matched > 2)
1953 /* cycle thru all the matches */
1954 snprintf (Completed, sizeof (Completed), "%s",
1955 Matches[(numtabs - 2) % Num_matched]);
1957 /* return the completed command */
1958 strncpy (buffer, Completed, len - spaces);
1960 else if (!str_ncmp (buffer, "set", 3)
1961 || !str_ncmp (buffer, "unset", 5)
1962 || !str_ncmp (buffer, "reset", 5)
1963 || !str_ncmp (buffer, "toggle", 6)) { /* complete variables */
1964 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1967 /* loop through all the possible prefixes (no, inv, ...) */
1968 if (!str_ncmp (buffer, "set", 3)) {
1969 for (num = 0; prefixes[num]; num++) {
1970 if (!str_ncmp (pt, prefixes[num], str_len (prefixes[num]))) {
1971 pt += str_len (prefixes[num]);
1977 /* first TAB. Collect all the matches */
1980 strfcpy (User_typed, pt, sizeof (User_typed));
1981 memset (Matches, 0, sizeof (Matches));
1982 memset (Completed, 0, sizeof (Completed));
1983 for (num = 0; MuttVars[num].option; num++)
1984 candidate (Completed, User_typed, MuttVars[num].option,
1985 sizeof (Completed));
1986 Matches[Num_matched++] = User_typed;
1988 /* All matches are stored. Longest non-ambiguous string is ""
1989 * i.e. dont change 'buffer'. Fake successful return this time */
1990 if (User_typed[0] == 0)
1994 if (Completed[0] == 0 && User_typed[0])
1997 /* Num_matched will _always_ be atleast 1 since the initial
1998 * user-typed string is always stored */
1999 if (numtabs == 1 && Num_matched == 2)
2000 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
2001 else if (numtabs > 1 && Num_matched > 2)
2002 /* cycle thru all the matches */
2003 snprintf (Completed, sizeof (Completed), "%s",
2004 Matches[(numtabs - 2) % Num_matched]);
2006 strncpy (pt, Completed, buffer + len - pt - spaces);
2008 else if (!str_ncmp (buffer, "exec", 4)) {
2009 struct binding_t *menu = km_get_table (CurrentMenu);
2011 if (!menu && CurrentMenu != MENU_PAGER)
2015 /* first TAB. Collect all the matches */
2018 strfcpy (User_typed, pt, sizeof (User_typed));
2019 memset (Matches, 0, sizeof (Matches));
2020 memset (Completed, 0, sizeof (Completed));
2021 for (num = 0; menu[num].name; num++)
2022 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
2023 /* try the generic menu */
2024 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2026 for (num = 0; menu[num].name; num++)
2027 candidate (Completed, User_typed, menu[num].name,
2028 sizeof (Completed));
2030 Matches[Num_matched++] = User_typed;
2032 /* All matches are stored. Longest non-ambiguous string is ""
2033 * i.e. dont change 'buffer'. Fake successful return this time */
2034 if (User_typed[0] == 0)
2038 if (Completed[0] == 0 && User_typed[0])
2041 /* Num_matched will _always_ be atleast 1 since the initial
2042 * user-typed string is always stored */
2043 if (numtabs == 1 && Num_matched == 2)
2044 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
2045 else if (numtabs > 1 && Num_matched > 2)
2046 /* cycle thru all the matches */
2047 snprintf (Completed, sizeof (Completed), "%s",
2048 Matches[(numtabs - 2) % Num_matched]);
2050 strncpy (pt, Completed, buffer + len - pt - spaces);
2058 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2060 char var[STRING], *pt = buffer;
2062 struct option_t* option = NULL;
2068 spaces = buffer - pt;
2070 pt = buffer + pos - spaces;
2071 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2073 pt++; /* move past the space */
2074 if (*pt == '=') /* abort if no var before the '=' */
2077 if (str_ncmp (buffer, "set", 3) == 0) {
2078 strfcpy (var, pt, sizeof (var));
2079 /* ignore the trailing '=' when comparing */
2080 var[str_len (var) - 1] = 0;
2081 if (!(option = hash_find (ConfigOptions, var)))
2082 return 0; /* no such variable. */
2084 char tmp[LONG_STRING], tmp2[LONG_STRING];
2086 size_t dlen = buffer + len - pt - spaces;
2087 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2091 if ((DTYPE (option->type) == DT_STR) ||
2092 (DTYPE (option->type) == DT_PATH) ||
2093 (DTYPE (option->type) == DT_RX)) {
2094 strfcpy (tmp, NONULL (*((char **) option->data)), sizeof (tmp));
2095 if (DTYPE (option->type) == DT_PATH)
2096 mutt_pretty_mailbox (tmp);
2098 else if (DTYPE (option->type) == DT_ADDR) {
2099 rfc822_write_address (tmp, sizeof (tmp),
2100 *((ADDRESS **) option->data), 0);
2102 else if (DTYPE (option->type) == DT_QUAD)
2103 strfcpy (tmp, vals[quadoption (option->data)], sizeof (tmp));
2104 else if (DTYPE (option->type) == DT_NUM)
2105 snprintf (tmp, sizeof (tmp), "%d", (*((short *) option->data)));
2106 else if (DTYPE (option->type) == DT_SORT) {
2107 const struct mapping_t *map;
2110 switch (option->type & DT_SUBTYPE_MASK) {
2112 map = SortAliasMethods;
2114 case DT_SORT_BROWSER:
2115 map = SortBrowserMethods;
2118 if ((WithCrypto & APPLICATION_PGP))
2119 map = SortKeyMethods;
2128 mutt_getnamebyvalue (*((short *) option->data) & SORT_MASK,
2130 snprintf (tmp, sizeof (tmp), "%s%s%s",
2131 (*((short *) option->data) & SORT_REVERSE) ?
2133 (*((short *) option->data) & SORT_LAST) ? "last-" :
2136 else if (DTYPE (option->type) == DT_MAGIC) {
2138 switch (DefaultMagic) {
2154 strfcpy (tmp, p, sizeof (tmp));
2156 else if (DTYPE (option->type) == DT_BOOL)
2157 strfcpy (tmp, option (option->data) ? "yes" : "no",
2162 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
2163 if (*s == '\\' || *s == '"')
2169 strfcpy (tmp, pt, sizeof (tmp));
2170 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2178 /* Implement the -Q command line flag */
2179 int mutt_query_variables (LIST * queries)
2183 char errbuff[STRING];
2184 char command[STRING];
2188 memset (&err, 0, sizeof (err));
2189 memset (&token, 0, sizeof (token));
2192 err.dsize = sizeof (errbuff);
2194 for (p = queries; p; p = p->next) {
2195 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2196 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2197 fprintf (stderr, "%s\n", err.data);
2198 mem_free (&token.data);
2201 printf ("%s\n", err.data);
2204 mem_free (&token.data);
2208 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2212 for (i = 0; map[i].name; i++)
2213 if (map[i].value == val)
2214 return (map[i].name);
2218 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2222 for (i = 0; map[i].name; i++)
2223 if (ascii_strcasecmp (map[i].name, name) == 0)
2224 return (map[i].value);
2228 static int mutt_execute_commands (LIST * p)
2231 char errstr[SHORT_STRING];
2233 memset (&err, 0, sizeof (err));
2235 err.dsize = sizeof (errstr);
2236 memset (&token, 0, sizeof (token));
2237 for (; p; p = p->next) {
2238 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2239 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2240 mem_free (&token.data);
2244 mem_free (&token.data);
2248 void mutt_init (int skip_sys_rc, LIST * commands)
2251 struct utsname utsname;
2252 char *p, buffer[STRING], error[STRING];
2253 int i, default_rc = 0, need_pause = 0;
2256 memset (&err, 0, sizeof (err));
2258 err.dsize = sizeof (error);
2260 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2261 * to have some room for $user_ vars */
2262 ConfigOptions = hash_create (sizeof (MuttVars) * 3);
2263 for (i = 0; MuttVars[i].option; i++) {
2264 if (DTYPE (MuttVars[i].type) != DT_SYS)
2265 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2267 hash_insert (ConfigOptions, MuttVars[i].option,
2268 add_option (MuttVars[i].option, MuttVars[i].init,
2273 * XXX - use something even more difficult to predict?
2275 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2276 "\033]9;%ld\a", (long) time (NULL));
2278 /* on one of the systems I use, getcwd() does not return the same prefix
2279 as is listed in the passwd file */
2280 if ((p = getenv ("HOME")))
2281 Homedir = str_dup (p);
2283 /* Get some information about the user */
2284 if ((pw = getpwuid (getuid ()))) {
2287 Username = str_dup (pw->pw_name);
2289 Homedir = str_dup (pw->pw_dir);
2291 Realname = str_dup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2292 Shell = str_dup (pw->pw_shell);
2297 fputs (_("unable to determine home directory"), stderr);
2300 if ((p = getenv ("USER")))
2301 Username = str_dup (p);
2304 fputs (_("unable to determine username"), stderr);
2307 Shell = str_dup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2310 debug_start(Homedir);
2312 /* And about the host... */
2314 /* some systems report the FQDN instead of just the hostname */
2315 if ((p = strchr (utsname.nodename, '.'))) {
2316 Hostname = str_substrdup (utsname.nodename, p);
2318 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2321 Hostname = str_dup (utsname.nodename);
2324 #define DOMAIN buffer
2325 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2326 Fqdn = str_dup ("@");
2329 if (*DOMAIN != '@') {
2330 Fqdn = mem_malloc (str_len (DOMAIN) + str_len (Hostname) + 2);
2331 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2334 Fqdn = str_dup (NONULL (Hostname));
2341 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2343 fgets (buffer, sizeof (buffer), f);
2344 p = (char*) &buffer;
2347 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r')
2351 NewsServer = str_dup (p);
2355 if ((p = getenv ("NNTPSERVER")))
2356 NewsServer = str_dup (p);
2359 if ((p = getenv ("MAIL")))
2360 Spoolfile = str_dup (p);
2361 else if ((p = getenv ("MAILDIR")))
2362 Spoolfile = str_dup (p);
2365 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2367 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
2369 Spoolfile = str_dup (buffer);
2372 if ((p = getenv ("MAILCAPS")))
2373 MailcapPath = str_dup (p);
2375 /* Default search path from RFC1524 */
2377 str_dup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2378 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2381 Tempdir = str_dup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2383 p = getenv ("VISUAL");
2385 p = getenv ("EDITOR");
2389 Editor = str_dup (p);
2390 Visual = str_dup (p);
2392 if ((p = getenv ("REPLYTO")) != NULL) {
2395 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2397 memset (&buf, 0, sizeof (buf));
2398 buf.data = buf.dptr = buffer;
2399 buf.dsize = str_len (buffer);
2401 memset (&token, 0, sizeof (token));
2402 parse_my_hdr (&token, &buf, 0, &err);
2403 mem_free (&token.data);
2406 if ((p = getenv ("EMAIL")) != NULL)
2407 From = rfc822_parse_adrlist (NULL, p);
2409 mutt_set_langinfo_charset ();
2410 mutt_set_charset (Charset);
2413 /* Set standard defaults */
2414 hash_map (ConfigOptions, mutt_set_default, 0);
2415 hash_map (ConfigOptions, mutt_restore_default, 0);
2417 CurrentMenu = MENU_MAIN;
2420 #ifndef LOCALES_HACK
2421 /* Do we have a locale definition? */
2422 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2423 ((p = getenv ("LANG")) != NULL && p[0]) ||
2424 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2425 set_option (OPTLOCALES);
2429 /* Unset suspend by default if we're the session leader */
2430 if (getsid (0) == getpid ())
2431 unset_option (OPTSUSPEND);
2434 mutt_init_history ();
2443 * When changing the code which looks for a configuration file,
2444 * please also change the corresponding code in muttbug.sh.in.
2454 snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2456 if (access (buffer, F_OK) == -1)
2458 snprintf (buffer, sizeof (buffer), "%s/.muttngrc", NONULL (Homedir));
2459 if (access (buffer, F_OK) == -1)
2461 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s",
2462 NONULL (Homedir), MUTT_VERSION);
2463 if (access (buffer, F_OK) == -1)
2465 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc",
2469 Muttrc = str_dup (buffer);
2472 strfcpy (buffer, Muttrc, sizeof (buffer));
2474 mutt_expand_path (buffer, sizeof (buffer));
2475 Muttrc = str_dup (buffer);
2477 mem_free (&AliasFile);
2478 AliasFile = str_dup (NONULL (Muttrc));
2480 /* Process the global rc file if it exists and the user hasn't explicity
2481 requested not to via "-n". */
2483 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2485 if (access (buffer, F_OK) == -1)
2486 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", SYSCONFDIR);
2487 if (access (buffer, F_OK) == -1)
2488 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR,
2490 if (access (buffer, F_OK) == -1)
2491 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2492 if (access (buffer, F_OK) != -1) {
2493 if (source_rc (buffer, &err) != 0) {
2494 fputs (err.data, stderr);
2495 fputc ('\n', stderr);
2501 /* Read the user's initialization file. */
2502 if (access (Muttrc, F_OK) != -1) {
2503 if (!option (OPTNOCURSES))
2505 if (source_rc (Muttrc, &err) != 0) {
2506 fputs (err.data, stderr);
2507 fputc ('\n', stderr);
2511 else if (!default_rc) {
2512 /* file specified by -F does not exist */
2513 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2514 mutt_endwin (buffer);
2518 if (mutt_execute_commands (commands) != 0)
2521 /* warn about synonym variables */
2522 if (!list_empty(Synonyms)) {
2524 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2525 for (i = 0; i < Synonyms->length; i++) {
2526 struct option_t* newopt = NULL, *oldopt = NULL;
2527 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2528 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2529 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2530 oldopt ? NONULL (oldopt->option) : "",
2531 newopt ? NONULL (newopt->option) : "",
2532 NONULL(((syn_t*) Synonyms->data[i])->f),
2533 ((syn_t*) Synonyms->data[i])->l);
2535 fprintf (stderr, _("Warning: synonym variables are scheduled"
2536 " for removal.\n"));
2537 list_del (&Synonyms, syn_del);
2541 if (need_pause && !option (OPTNOCURSES)) {
2542 if (mutt_any_key_to_continue (NULL) == -1)
2547 set_option (OPTWEED); /* turn weeding on by default */
2551 int mutt_get_hook_type (const char *name)
2553 struct command_t *c;
2555 for (c = Commands; c->name; c++)
2556 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2561 /* compare two option_t*'s for sorting -t/-T output */
2562 static int opt_cmp (const void* a, const void* b) {
2563 return (str_cmp ((*(struct option_t**) a)->option,
2564 (*(struct option_t**) b)->option));
2567 /* callback for hash_map() to put all non-synonym vars into list */
2568 static void opt_sel_full (const char* key, void* data,
2569 unsigned long more) {
2570 list2_t** l = (list2_t**) more;
2571 struct option_t* option = (struct option_t*) data;
2573 if (DTYPE (option->type) == DT_SYN)
2575 list_push_back (l, option);
2578 /* callback for hash_map() to put all changed non-synonym vars into list */
2579 static void opt_sel_diff (const char* key, void* data,
2580 unsigned long more) {
2581 list2_t** l = (list2_t**) more;
2582 struct option_t* option = (struct option_t*) data;
2583 char buf[LONG_STRING];
2585 if (DTYPE (option->type) == DT_SYN)
2588 mutt_option_value (option->option, buf, sizeof (buf));
2589 if (str_cmp (buf, option->init) != 0)
2590 list_push_back (l, option);
2593 /* dump out the value of all the variables we have */
2594 int mutt_dump_variables (int full) {
2596 char outbuf[STRING];
2597 list2_t* tmp = NULL;
2598 struct option_t* option = NULL;
2600 /* get all non-synonyms into list... */
2601 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2602 (unsigned long) &tmp);
2604 if (!list_empty(tmp)) {
2605 /* ...and dump list sorted */
2606 qsort (tmp->data, tmp->length, sizeof (void*), opt_cmp);
2607 for (i = 0; i < tmp->length; i++) {
2608 option = (struct option_t*) tmp->data[i];
2609 FuncTable[DTYPE (option->type)].opt_to_string
2610 (outbuf, sizeof (outbuf), option);
2611 printf ("%s\n", outbuf);
2614 list_del (&tmp, NULL);