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 /* use this to check only */
93 static int check_special (const char* option, unsigned long val,
94 char* errbuf, size_t errlen);
96 /* variable <-> sanity check function mappings
97 * when changing these, make sure the proper _from_string handler
102 int (*check) (const char* option, unsigned long val,
103 char* errbuf, size_t errlen);
105 { "dsn_notify", check_dsn_notify },
106 { "dsn_return", check_dsn_return },
107 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
108 { "smtp_use_tls", mutt_libesmtp_check_usetls },
110 { "history", check_history },
111 { "pager_index_lines", check_num },
116 /* protos for config type handles: convert value to string */
117 static void bool_to_string (char* dst, size_t dstlen, struct option_t* option);
118 static void num_to_string (char* dst, size_t dstlen, struct option_t* option);
119 static void str_to_string (char* dst, size_t dstlen, struct option_t* option);
120 static void quad_to_string (char* dst, size_t dstlen, struct option_t* option);
121 static void sort_to_string (char* dst, size_t dstlen, struct option_t* option);
122 static void rx_to_string (char* dst, size_t dstlen, struct option_t* option);
123 static void magic_to_string (char* dst, size_t dstlen, struct option_t* option);
124 static void addr_to_string (char* dst, size_t dstlen, struct option_t* option);
125 static void user_to_string (char* dst, size_t dstlen, struct option_t* option);
127 /* protos for config type handles: convert to value from string */
128 static int bool_from_string (struct option_t* dst, const char* val,
129 char* errbuf, size_t errlen);
130 static int num_from_string (struct option_t* dst, const char* val,
131 char* errbuf, size_t errlen);
132 static int str_from_string (struct option_t* dst, const char* val,
133 char* errbuf, size_t errlen);
134 static int path_from_string (struct option_t* dst, const char* val,
135 char* errbuf, size_t errlen);
136 static int quad_from_string (struct option_t* dst, const char* val,
137 char* errbuf, size_t errlen);
138 static int sort_from_string (struct option_t* dst, const char* val,
139 char* errbuf, size_t errlen);
140 static int rx_from_string (struct option_t* dst, const char* val,
141 char* errbuf, size_t errlen);
142 static int magic_from_string (struct option_t* dst, const char* val,
143 char* errbuf, size_t errlen);
144 static int addr_from_string (struct option_t* dst, const char* val,
145 char* errbuf, size_t errlen);
146 static int user_from_string (struct option_t* dst, const char* val,
147 char* errbuf, size_t errlen);
151 void (*opt_to_string) (char* dst, size_t dstlen, struct option_t* option);
152 int (*opt_from_string) (struct option_t* dst, const char* val,
153 char* errbuf, size_t errlen);
155 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
156 { DT_BOOL, bool_to_string, bool_from_string },
157 { DT_NUM, num_to_string, num_from_string },
158 { DT_STR, str_to_string, str_from_string },
159 { DT_PATH, str_to_string, path_from_string },
160 { DT_QUAD, quad_to_string, quad_from_string },
161 { DT_SORT, sort_to_string, sort_from_string },
162 { DT_RX, rx_to_string, rx_from_string },
163 { DT_MAGIC, magic_to_string, magic_from_string },
164 /* synonyms should be resolved already so we don't need this
165 * but must define it as DT_ is used for indexing */
166 { DT_SYN, NULL, NULL },
167 { DT_ADDR, addr_to_string, addr_from_string },
168 { DT_USER, user_to_string, user_from_string },
171 static void bool_to_string (char* dst, size_t dstlen,
172 struct option_t* option) {
173 snprintf (dst, dstlen, "%s=%s", option->option,
174 option (option->data) ? "yes" : "no");
177 static int bool_from_string (struct option_t* dst, const char* val,
178 char* errbuf, size_t errlen) {
183 if (ascii_strncasecmp (val, "yes", 3) == 0)
185 else if (ascii_strncasecmp (val, "no", 2) == 0)
191 set_option (dst->data);
193 unset_option (dst->data);
197 static void num_to_string (char* dst, size_t dstlen,
198 struct option_t* option) {
200 const char* fmt = (str_cmp (option->option, "umask") == 0) ?
202 snprintf (dst, dstlen, fmt, option->option,
203 *((short*) option->data));
206 static int num_from_string (struct option_t* dst, const char* val,
207 char* errbuf, size_t errlen) {
208 int num = 0, old = 0;
214 num = strtol (val, &t, 0);
216 if (!*val || *t || (short) num != num) {
218 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
224 /* just temporarily accept new val so that check_special for
225 * $history already has it when doing history's init() */
226 old = *((short*) dst->data);
227 *((short*) dst->data) = (short) num;
229 if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
230 *((short*) dst->data) = old;
237 static void str_to_string (char* dst, size_t dstlen,
238 struct option_t* option) {
239 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
240 NONULL (*((char**) option->data)));
243 static void user_to_string (char* dst, size_t dstlen,
244 struct option_t* option) {
245 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
246 NONULL (((char*) option->data)));
249 static int path_from_string (struct option_t* dst, const char* val,
250 char* errbuf, size_t errlen) {
251 char path[_POSIX_PATH_MAX];
257 mem_free ((char**) dst->data);
262 strfcpy (path, val, sizeof (path));
263 mutt_expand_path (path, sizeof (path));
264 str_replace ((char **) dst->data, path);
268 static int str_from_string (struct option_t* dst, const char* val,
269 char* errbuf, size_t errlen) {
273 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
276 str_replace ((char**) dst->data, val);
280 static int user_from_string (struct option_t* dst, const char* val,
281 char* errbuf, size_t errlen) {
284 if (str_len ((char*) dst->data) == 0)
285 dst->data = (unsigned long) str_dup (val);
287 char* s = (char*) dst->data;
288 str_replace (&s, val);
293 static void quad_to_string (char* dst, size_t dstlen,
294 struct option_t* option) {
295 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
296 snprintf (dst, dstlen, "%s=%s", option->option,
297 vals[quadoption (option->data)]);
300 static int quad_from_string (struct option_t* dst, const char* val,
301 char* errbuf, size_t errlen) {
306 if (ascii_strncasecmp (val, "yes", 3) == 0)
308 else if (ascii_strncasecmp (val, "no", 2) == 0)
310 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
312 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
318 set_quadoption (dst->data, flag);
322 static void sort_to_string (char* dst, size_t dstlen,
323 struct option_t* option) {
324 const struct mapping_t *map = get_sortmap (option);
328 snprintf (dst, sizeof (dst), "%s=unknown", option->option);
332 p = mutt_getnamebyvalue (*((short *) option->data) & SORT_MASK,
335 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
336 (*((short *) option->data) & SORT_REVERSE) ?
338 (*((short *) option->data) & SORT_LAST) ? "last-" :
342 static int sort_from_string (struct option_t* dst, const char* val,
343 char* errbuf, size_t errlen) {
344 const struct mapping_t *map = NULL;
345 if (!(map = get_sortmap (dst))) {
347 snprintf (errbuf, errlen, _("%s: Unknown type."),
351 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
356 static void rx_to_string (char* dst, size_t dstlen,
357 struct option_t* option) {
358 rx_t* p = (rx_t*) option->data;
359 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
360 NONULL (p->pattern));
363 static int rx_from_string (struct option_t* dst, const char* val,
364 char* errbuf, size_t errlen) {
367 int flags = 0, e = 0, not = 0;
373 if (option (OPTATTACHMSG) && !str_cmp (dst->option, "reply_regexp")) {
375 snprintf (errbuf, errlen,
376 "Operation not permitted when in attach-message mode.");
380 if (!((rx_t*) dst->data))
381 *((rx_t**) dst->data) = mem_calloc (1, sizeof (rx_t));
383 p = (rx_t*) dst->data;
385 /* something to do? */
386 if (!val || !*val || (p->pattern && str_cmp (p->pattern, val) == 0))
389 if (str_cmp (dst->option, "mask") != 0)
390 flags |= mutt_which_case (val);
393 if (str_cmp (dst->option, "mask") == 0 && *s == '!') {
398 rx = mem_malloc (sizeof (regex_t));
400 if ((e = REGCOMP (rx, s, flags)) != 0) {
401 regerror (e, rx, errbuf, errlen);
411 str_replace (&p->pattern, val);
415 if (str_cmp (dst->option, "reply_regexp") == 0)
416 mutt_adjust_all_subjects ();
421 static void magic_to_string (char* dst, size_t dstlen,
422 struct option_t* option) {
423 const char* s = NULL;
424 switch (option->data) {
425 case M_MBOX: s = "mbox"; break;
426 case M_MMDF: s = "MMDF"; break;
427 case M_MH: s = "MH"; break;
428 case M_MAILDIR: s = "Maildir"; break;
429 default: s = "unknown"; break;
431 snprintf (dst, dstlen, "%s=%s", option->option, s);
434 static int magic_from_string (struct option_t* dst, const char* val,
435 char* errbuf, size_t errlen) {
438 if (!dst || !val || !*val)
440 if (ascii_strncasecmp (val, "mbox", 4) == 0)
442 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
444 else if (ascii_strncasecmp (val, "mh", 2) == 0)
446 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
452 *((short*) dst->data) = flag;
457 static void addr_to_string (char* dst, size_t dstlen,
458 struct option_t* option) {
461 rfc822_write_address (s, sizeof (s), *((ADDRESS**) option->data), 0);
462 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
465 static int addr_from_string (struct option_t* dst, const char* val,
466 char* errbuf, size_t errlen) {
467 if (!dst || !val || !*val)
469 rfc822_free_address ((ADDRESS**) dst->data);
470 *((ADDRESS**) dst->data) = rfc822_parse_adrlist (NULL, val);
474 int mutt_option_value (const char* val, char* dst, size_t dstlen) {
475 struct option_t* option = NULL;
476 char* tmp = NULL, *t = NULL;
479 if (!(option = hash_find (ConfigOptions, val))) {
480 debug_print (1, ("var '%s' not found\n", val));
484 tmp = mem_malloc (dstlen+1);
485 FuncTable[DTYPE (option->type)].opt_to_string (tmp, dstlen, option);
487 /* as we get things of type $var=value and don't want to bloat the
488 * above "just" for expansion, we do the stripping here */
489 debug_print (1, ("orig == '%s'\n", tmp));
490 t = strchr (tmp, '=');
494 if (t[l-1] == '"' && *t == '"') {
499 memcpy (dst, t, l+1);
501 debug_print (1, ("stripped == '%s'\n", dst));
506 /* for synonym warning reports: adds synonym to end of list */
507 static void syn_add (struct option_t* n, struct option_t* o) {
508 syn_t* tmp = mem_malloc (sizeof (syn_t));
509 tmp->f = str_dup (CurRCFile);
513 list_push_back (&Synonyms, tmp);
516 /* for synonym warning reports: free single item (for list_del()) */
517 static void syn_del (void** p) {
518 mem_free(&(*(syn_t**) p)->f);
522 void toggle_quadoption (int opt)
525 int b = (opt % 4) * 2;
527 QuadOptions[n] ^= (1 << b);
530 void set_quadoption (int opt, int flag)
533 int b = (opt % 4) * 2;
535 QuadOptions[n] &= ~(0x3 << b);
536 QuadOptions[n] |= (flag & 0x3) << b;
539 int quadoption (int opt)
542 int b = (opt % 4) * 2;
544 return (QuadOptions[n] >> b) & 0x3;
547 int query_quadoption (int opt, const char *prompt)
549 int v = quadoption (opt);
557 v = mutt_yesorno (prompt, (v == M_ASKYES));
558 CLEARLINE (LINES - 1);
565 static void add_to_list (LIST ** list, const char *str)
567 LIST *t, *last = NULL;
569 /* don't add a NULL or empty string to the list */
570 if (!str || *str == '\0')
573 /* check to make sure the item is not already on this list */
574 for (last = *list; last; last = last->next) {
575 if (ascii_strcasecmp (str, last->data) == 0) {
576 /* already on the list, so just ignore it */
584 if (!*list || last) {
585 t = (LIST *) mem_calloc (1, sizeof (LIST));
586 t->data = str_dup (str);
596 static int add_to_rx_list (list2_t** list, const char *s, int flags,
605 if (!(rx = rx_compile (s, flags))) {
606 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
610 i = rx_lookup ((*list), rx->pattern);
614 list_push_back (list, rx);
618 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
619 const char *templ, BUFFER * err)
621 SPAM_LIST *t = NULL, *last = NULL;
626 if (!pat || !*pat || !templ)
629 if (!(rx = rx_compile (pat, REG_ICASE))) {
630 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
634 /* check to make sure the item is not already on this list */
635 for (last = *list; last; last = last->next) {
636 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
637 /* Already on the list. Formerly we just skipped this case, but
638 * now we're supporting removals, which means we're supporting
639 * re-adds conceptually. So we probably want this to imply a
640 * removal, then do an add. We can achieve the removal by freeing
641 * the template, and leaving t pointed at the current item.
644 mem_free(t->template);
651 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
652 * update. Otherwise we want to make a new one to link at the list's end.
655 t = mutt_new_spam_list ();
663 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
664 t->template = str_dup (templ);
666 /* Find highest match number in template string */
668 for (p = templ; *p;) {
673 while (*p && isdigit ((int) *p))
679 t->nmatch++; /* match 0 is always the whole expr */
684 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
686 SPAM_LIST *spam, *prev;
689 /* Being first is a special case. */
693 if (spam->rx && !str_cmp (spam->rx->pattern, pat)) {
696 mem_free(&spam->template);
702 for (spam = prev->next; spam;) {
703 if (!str_cmp (spam->rx->pattern, pat)) {
704 prev->next = spam->next;
706 mem_free(spam->template);
719 static void remove_from_list (LIST ** l, const char *str)
721 LIST *p, *last = NULL;
723 if (str_cmp ("*", str) == 0)
724 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
729 if (ascii_strcasecmp (str, p->data) == 0) {
732 last->next = p->next;
745 static int remove_from_rx_list (list2_t** l, const char *str)
749 if (str_cmp ("*", str) == 0) {
750 list_del (l, (list_del_t*) rx_free);
754 i = rx_lookup ((*l), str);
756 rx_t* r = list_pop_idx ((*l), i);
764 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
769 struct option_t* option = NULL;
771 memset (&token, 0, sizeof (token));
772 mutt_extract_token (tmp, s, 0);
774 /* is the item defined as a variable or a function? */
775 if ((option = hash_find (ConfigOptions, tmp->data)))
778 for (i = 0; !res && i < MENU_MAX; i++) {
779 struct binding_t *b = km_get_table (Menus[i].value);
784 for (j = 0; b[j].name; j++)
785 if (!ascii_strncasecmp (tmp->data, b[j].name, str_len (tmp->data))
786 && (str_len (b[j].name) == str_len (tmp->data))) {
792 /* check for feature_* */
797 j = str_len (tmp->data);
798 /* need at least input of 'feature_X' */
802 while (Features[i].name) {
803 if (str_len (Features[i].name) == j &&
804 ascii_strncasecmp (Features[i].name, p, j)) {
815 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
817 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
820 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
822 if ((data && res) || (!data && !res)) {
823 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
824 mutt_error ("Error: %s", err->data);
825 mem_free (&token.data);
828 mem_free (&token.data);
833 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
837 mutt_extract_token (buf, s, 0);
839 /* don't add "*" to the unignore list */
840 if (strcmp (buf->data, "*"))
841 add_to_list (&UnIgnore, buf->data);
843 remove_from_list (&Ignore, buf->data);
845 while (MoreArgs (s));
850 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
854 mutt_extract_token (buf, s, 0);
855 remove_from_list (&UnIgnore, buf->data);
856 add_to_list (&Ignore, buf->data);
858 while (MoreArgs (s));
863 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
867 mutt_extract_token (buf, s, 0);
868 add_to_list ((LIST **) data, buf->data);
870 while (MoreArgs (s));
875 static void _alternates_clean (void)
879 if (Context && Context->msgcount) {
880 for (i = 0; i < Context->msgcount; i++)
881 Context->hdrs[i]->recip_valid = 0;
885 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
888 _alternates_clean ();
890 mutt_extract_token (buf, s, 0);
891 remove_from_rx_list (&UnAlternates, buf->data);
893 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
896 while (MoreArgs (s));
901 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
904 _alternates_clean ();
906 mutt_extract_token (buf, s, 0);
907 remove_from_rx_list (&Alternates, buf->data);
909 if (str_cmp (buf->data, "*") &&
910 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
914 while (MoreArgs (s));
919 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
924 memset (&templ, 0, sizeof (templ));
926 /* Insist on at least one parameter */
929 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
931 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
935 /* Extract the first token, a regexp */
936 mutt_extract_token (buf, s, 0);
938 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
939 if (data == M_SPAM) {
940 /* If there's a second parameter, it's a template for the spam tag. */
942 mutt_extract_token (&templ, s, 0);
944 /* Add to the spam list. */
945 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
946 mem_free (&templ.data);
949 mem_free (&templ.data);
952 /* If not, try to remove from the nospam list. */
954 remove_from_rx_list (&NoSpamList, buf->data);
960 /* M_NOSPAM is for nospam commands. */
961 else if (data == M_NOSPAM) {
962 /* nospam only ever has one parameter. */
964 /* "*" is a special case. */
965 if (!str_cmp (buf->data, "*")) {
966 mutt_free_spam_list (&SpamList);
967 list_del (&NoSpamList, (list_del_t*) rx_free);
971 /* If it's on the spam list, just remove it. */
972 if (remove_from_spam_list (&SpamList, buf->data) != 0)
975 /* Otherwise, add it to the nospam list. */
976 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
982 /* This should not happen. */
983 strfcpy (err->data, "This is no good at all.", err->dsize);
987 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
991 mutt_extract_token (buf, s, 0);
993 * Check for deletion of entire list
995 if (str_cmp (buf->data, "*") == 0) {
996 mutt_free_list ((LIST **) data);
999 remove_from_list ((LIST **) data, buf->data);
1001 while (MoreArgs (s));
1006 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
1010 mutt_extract_token (buf, s, 0);
1011 remove_from_rx_list (&UnMailLists, buf->data);
1013 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1016 while (MoreArgs (s));
1021 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
1025 mutt_extract_token (buf, s, 0);
1026 remove_from_rx_list (&SubscribedLists, buf->data);
1027 remove_from_rx_list (&MailLists, buf->data);
1029 if (str_cmp (buf->data, "*") &&
1030 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1033 while (MoreArgs (s));
1038 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1042 mutt_extract_token (buf, s, 0);
1043 remove_from_rx_list (&UnMailLists, buf->data);
1044 remove_from_rx_list (&UnSubscribedLists, buf->data);
1046 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1048 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1051 while (MoreArgs (s));
1056 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1060 mutt_extract_token (buf, s, 0);
1061 remove_from_rx_list (&SubscribedLists, buf->data);
1063 if (str_cmp (buf->data, "*") &&
1064 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1067 while (MoreArgs (s));
1072 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
1075 ALIAS *tmp, *last = NULL;
1078 mutt_extract_token (buf, s, 0);
1080 if (str_cmp ("*", buf->data) == 0) {
1081 if (CurrentMenu == MENU_ALIAS) {
1082 for (tmp = Aliases; tmp; tmp = tmp->next)
1084 set_option (OPTFORCEREDRAWINDEX);
1087 mutt_free_alias (&Aliases);
1091 for (tmp = Aliases; tmp; tmp = tmp->next) {
1092 if (str_casecmp (buf->data, tmp->name) == 0) {
1093 if (CurrentMenu == MENU_ALIAS) {
1095 set_option (OPTFORCEREDRAWINDEX);
1100 last->next = tmp->next;
1102 Aliases = tmp->next;
1104 mutt_free_alias (&tmp);
1110 while (MoreArgs (s));
1114 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
1117 ALIAS *tmp = Aliases;
1121 if (!MoreArgs (s)) {
1122 strfcpy (err->data, _("alias: no address"), err->dsize);
1126 mutt_extract_token (buf, s, 0);
1128 debug_print (2, ("first token is '%s'.\n", buf->data));
1130 /* check to see if an alias with this name already exists */
1131 for (; tmp; tmp = tmp->next) {
1132 if (!str_casecmp (tmp->name, buf->data))
1138 /* create a new alias */
1139 tmp = (ALIAS *) mem_calloc (1, sizeof (ALIAS));
1141 tmp->name = str_dup (buf->data);
1142 /* give the main addressbook code a chance */
1143 if (CurrentMenu == MENU_ALIAS)
1144 set_option (OPTMENUCALLER);
1147 /* override the previous value */
1148 rfc822_free_address (&tmp->addr);
1149 if (CurrentMenu == MENU_ALIAS)
1150 set_option (OPTFORCEREDRAWINDEX);
1153 mutt_extract_token (buf, s,
1154 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1155 debug_print (2, ("second token is '%s'.\n", buf->data));
1156 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1161 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1162 snprintf (err->data, err->dsize,
1163 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1167 if (DebugLevel >= 2) {
1170 for (a = tmp->addr; a; a = a->next) {
1172 debug_print (2, ("%s\n", a->mailbox));
1174 debug_print (2, ("group %s\n", a->mailbox));
1182 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
1185 LIST *tmp = UserHeader;
1190 mutt_extract_token (buf, s, 0);
1191 if (str_cmp ("*", buf->data) == 0)
1192 mutt_free_list (&UserHeader);
1197 l = str_len (buf->data);
1198 if (buf->data[l - 1] == ':')
1202 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1203 && tmp->data[l] == ':') {
1206 last->next = tmp->next;
1208 UserHeader = tmp->next;
1211 mutt_free_list (&ptr);
1220 while (MoreArgs (s));
1224 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
1231 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1232 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1233 strfcpy (err->data, _("invalid header field"), err->dsize);
1236 keylen = p - buf->data + 1;
1239 for (tmp = UserHeader;; tmp = tmp->next) {
1240 /* see if there is already a field by this name */
1241 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1242 /* replace the old value */
1243 mem_free (&tmp->data);
1244 tmp->data = buf->data;
1245 memset (buf, 0, sizeof (BUFFER));
1251 tmp->next = mutt_new_list ();
1255 tmp = mutt_new_list ();
1258 tmp->data = buf->data;
1259 memset (buf, 0, sizeof (BUFFER));
1264 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1265 char* errbuf, size_t errlen) {
1268 if (str_ncmp ("reverse-", s, 8) == 0) {
1270 flags = SORT_REVERSE;
1273 if (str_ncmp ("last-", s, 5) == 0) {
1278 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1280 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1284 *((short*) dst->data) = i | flags;
1288 /* if additional data more == 1, we want to resolve synonyms */
1289 static void mutt_set_default (const char* name, void* p, unsigned long more) {
1290 char buf[LONG_STRING];
1291 struct option_t* ptr = (struct option_t*) p;
1293 if (DTYPE (ptr->type) == DT_SYN) {
1296 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1298 if (!ptr || *ptr->init)
1300 mutt_option_value (ptr->option, buf, sizeof (buf));
1301 if (str_len (ptr->init) == 0 && buf && *buf)
1302 ptr->init = str_dup (buf);
1305 /* if additional data more == 1, we want to resolve synonyms */
1306 static void mutt_restore_default (const char* name, void* p, unsigned long more) {
1307 char errbuf[STRING];
1308 struct option_t* ptr = (struct option_t*) p;
1310 if (DTYPE (ptr->type) == DT_SYN) {
1313 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1317 if (FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, ptr->init, errbuf,
1318 sizeof (errbuf)) < 0) {
1320 fprintf (stderr, _("Invalid default setting found. Please report this "
1321 "error:\n\"%s\"\n"), errbuf);
1325 if (ptr->flags & R_INDEX)
1326 set_option (OPTFORCEREDRAWINDEX);
1327 if (ptr->flags & R_PAGER)
1328 set_option (OPTFORCEREDRAWPAGER);
1329 if (ptr->flags & R_RESORT_SUB)
1330 set_option (OPTSORTSUBTHREADS);
1331 if (ptr->flags & R_RESORT)
1332 set_option (OPTNEEDRESORT);
1333 if (ptr->flags & R_RESORT_INIT)
1334 set_option (OPTRESORTINIT);
1335 if (ptr->flags & R_TREE)
1336 set_option (OPTREDRAWTREE);
1339 /* check whether value for $dsn_return would be valid */
1340 static int check_dsn_return (const char* option, unsigned long p,
1341 char* errbuf, size_t errlen) {
1342 char* val = (char*) p;
1343 if (val && *val && str_ncmp (val, "hdrs", 4) != 0 &&
1344 str_ncmp (val, "full", 4) != 0) {
1346 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1352 /* check whether value for $dsn_notify would be valid */
1353 static int check_dsn_notify (const char* option, unsigned long p,
1354 char* errbuf, size_t errlen) {
1355 list2_t* list = NULL;
1357 char* val = (char*) p;
1361 list = list_from_str (val, ",");
1362 if (list_empty (list))
1365 for (i = 0; i < list->length; i++)
1366 if (str_ncmp (list->data[i], "never", 5) != 0 &&
1367 str_ncmp (list->data[i], "failure", 7) != 0 &&
1368 str_ncmp (list->data[i], "delay", 5) != 0 &&
1369 str_ncmp (list->data[i], "success", 7) != 0) {
1371 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1372 (char*) list->data[i], "dsn_notify");
1376 list_del (&list, (list_del_t*) _mem_free);
1380 static int check_num (const char* option, unsigned long p,
1381 char* errbuf, size_t errlen) {
1384 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1390 static int check_history (const char* option, unsigned long p,
1391 char* errbuf, size_t errlen) {
1392 if (!check_num ("history", p, errbuf, errlen))
1394 mutt_init_history ();
1398 static int check_special (const char* name, unsigned long val,
1399 char* errbuf, size_t errlen) {
1402 for (i = 0; SpecialVars[i].name; i++) {
1403 if (str_cmp (SpecialVars[i].name, name) == 0) {
1404 return (SpecialVars[i].check (SpecialVars[i].name,
1405 val, errbuf, errlen));
1411 static const struct mapping_t* get_sortmap (struct option_t* option) {
1412 const struct mapping_t* map = NULL;
1414 switch (option->type & DT_SUBTYPE_MASK) {
1416 map = SortAliasMethods;
1418 case DT_SORT_BROWSER:
1419 map = SortBrowserMethods;
1422 if ((WithCrypto & APPLICATION_PGP))
1423 map = SortKeyMethods;
1426 map = SortAuxMethods;
1435 /* creates new option_t* of type DT_USER for $user_ var */
1436 static struct option_t* add_user_option (const char* name) {
1437 struct option_t* option = mem_calloc (1, sizeof (struct option_t));
1438 option->option = str_dup (name);
1439 option->type = DT_USER;
1443 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1446 int query, unset, inv, reset, r = 0;
1447 struct option_t* option = NULL;
1449 while (MoreArgs (s)) {
1450 /* reset state variables */
1452 unset = data & M_SET_UNSET;
1453 inv = data & M_SET_INV;
1454 reset = data & M_SET_RESET;
1456 if (*s->dptr == '?') {
1460 else if (str_ncmp ("no", s->dptr, 2) == 0) {
1464 else if (str_ncmp ("inv", s->dptr, 3) == 0) {
1468 else if (*s->dptr == '&') {
1473 /* get the variable name */
1474 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1476 /* resolve synonyms */
1477 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1478 DTYPE (option->type == DT_SYN)) {
1479 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1480 syn_add (newopt, option);
1484 /* see if we need to add $user_ var */
1485 if (!option && !reset && !unset &&
1486 ascii_strncasecmp ("user_", tmp->data, 5) == 0) {
1487 debug_print (1, ("adding user option '%s'\n", tmp->data));
1488 option = add_user_option (tmp->data);
1489 hash_insert (ConfigOptions, option->option, option, 0);
1492 if (!option && !(reset && str_cmp ("all", tmp->data) == 0)) {
1493 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1499 if (query || unset || inv) {
1500 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1504 if (s && *s->dptr == '=') {
1505 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1509 if (!str_cmp ("all", tmp->data)) {
1510 hash_map (ConfigOptions, mutt_restore_default, 1);
1514 mutt_restore_default (NULL, option, 1);
1516 else if (DTYPE (option->type) == DT_BOOL) {
1517 /* XXX this currently ignores the function table
1518 * as we don't get invert and stuff into it */
1519 if (s && *s->dptr == '=') {
1520 if (unset || inv || query) {
1521 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1526 mutt_extract_token (tmp, s, 0);
1527 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1529 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1532 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1538 bool_to_string (err->data, err->dsize, option);
1543 unset_option (option->data);
1545 toggle_option (option->data);
1547 set_option (option->data);
1549 else if (DTYPE (option->type) == DT_STR ||
1550 DTYPE (option->type) == DT_PATH ||
1551 DTYPE (option->type) == DT_ADDR ||
1552 DTYPE (option->type) == DT_MAGIC ||
1553 DTYPE (option->type) == DT_NUM ||
1554 DTYPE (option->type) == DT_SORT ||
1555 DTYPE (option->type) == DT_RX ||
1556 DTYPE (option->type) == DT_USER) {
1558 /* XXX maybe we need to get unset into handlers? */
1559 if (DTYPE (option->type) == DT_STR ||
1560 DTYPE (option->type) == DT_PATH ||
1561 DTYPE (option->type) == DT_ADDR ||
1562 DTYPE (option->type) == DT_USER) {
1564 if (DTYPE (option->type) == DT_ADDR)
1565 rfc822_free_address ((ADDRESS **) option->data);
1566 else if (DTYPE (option->type) == DT_USER) {
1567 void* p = (void*) option->data;
1570 mem_free ((void *) option->data);
1575 if (query || *s->dptr != '=') {
1576 FuncTable[DTYPE (option->type)].opt_to_string
1577 (err->data, err->dsize, option);
1582 mutt_extract_token (tmp, s, 0);
1583 if (!FuncTable[DTYPE (option->type)].opt_from_string
1584 (option, tmp->data, err->data, err->dsize))
1587 else if (DTYPE (option->type) == DT_QUAD) {
1590 quad_to_string (err->data, err->dsize, option);
1594 if (*s->dptr == '=') {
1596 mutt_extract_token (tmp, s, 0);
1597 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1598 set_quadoption (option->data, M_YES);
1599 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1600 set_quadoption (option->data, M_NO);
1601 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1602 set_quadoption (option->data, M_ASKYES);
1603 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1604 set_quadoption (option->data, M_ASKNO);
1606 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1607 tmp->data, option->option);
1614 toggle_quadoption (option->data);
1616 set_quadoption (option->data, M_NO);
1618 set_quadoption (option->data, M_YES);
1622 snprintf (err->data, err->dsize, _("%s: unknown type"),
1628 if (option->flags & R_INDEX)
1629 set_option (OPTFORCEREDRAWINDEX);
1630 if (option->flags & R_PAGER)
1631 set_option (OPTFORCEREDRAWPAGER);
1632 if (option->flags & R_RESORT_SUB)
1633 set_option (OPTSORTSUBTHREADS);
1634 if (option->flags & R_RESORT)
1635 set_option (OPTNEEDRESORT);
1636 if (option->flags & R_RESORT_INIT)
1637 set_option (OPTRESORTINIT);
1638 if (option->flags & R_TREE)
1639 set_option (OPTREDRAWTREE);
1646 /* reads the specified initialization file. returns -1 if errors were found
1647 so that we can pause to let the user know... */
1648 static int source_rc (const char *rcfile, BUFFER * err)
1651 int line = 0, rc = 0, conv = 0;
1653 char *linebuf = NULL;
1654 char *currentline = NULL;
1658 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
1660 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1661 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1665 memset (&token, 0, sizeof (token));
1666 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
1667 conv = ConfigCharset && (*ConfigCharset) && Charset;
1669 currentline = str_dup (linebuf);
1672 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1675 currentline = linebuf;
1680 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1681 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1682 if (--rc < -MAXERRS) {
1684 mem_free (¤tline);
1693 mem_free (¤tline);
1695 mem_free (&token.data);
1696 mem_free (&linebuf);
1699 mutt_wait_filter (pid);
1701 /* the muttrc source keyword */
1702 snprintf (err->data, err->dsize,
1703 rc >= -MAXERRS ? _("source: errors in %s")
1704 : _("source: reading aborted due too many errors in %s"),
1713 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
1716 char path[_POSIX_PATH_MAX];
1720 if (mutt_extract_token (tmp, s, 0) != 0) {
1721 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1725 strfcpy (path, tmp->data, sizeof (path));
1726 mutt_expand_path (path, sizeof (path));
1728 rc += source_rc (path, err);
1730 while (MoreArgs (s));
1732 return ((rc < 0) ? -1 : 0);
1735 /* line command to execute
1737 token scratch buffer to be used by parser. caller should free
1738 token->data when finished. the reason for this variable is
1739 to avoid having to allocate and deallocate a lot of memory
1740 if we are parsing many lines. the caller can pass in the
1741 memory to use, which avoids having to create new space for
1742 every call to this function.
1744 err where to write error messages */
1745 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1750 memset (&expn, 0, sizeof (expn));
1751 expn.data = expn.dptr = line;
1752 expn.dsize = str_len (line);
1757 while (*expn.dptr) {
1758 if (*expn.dptr == '#')
1759 break; /* rest of line is a comment */
1760 if (*expn.dptr == ';') {
1764 mutt_extract_token (token, &expn, 0);
1765 for (i = 0; Commands[i].name; i++) {
1766 if (!str_cmp (token->data, Commands[i].name)) {
1767 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1772 if (!Commands[i].name) {
1773 snprintf (err->data, err->dsize, _("%s: unknown command"),
1774 NONULL (token->data));
1781 mem_free (&expn.data);
1786 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1787 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1788 /* initial string that starts completion. No telling how much crap
1789 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1790 char User_typed[LONG_STRING] = { 0 };
1792 int Num_matched = 0; /* Number of matches for completion */
1793 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1794 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1796 /* helper function for completion. Changes the dest buffer if
1797 necessary/possible to aid completion.
1798 dest == completion result gets here.
1799 src == candidate for completion.
1800 try == user entered data for completion.
1801 len == length of dest buffer.
1803 static void candidate (char *dest, char *try, char *src, int len)
1807 if (strstr (src, try) == src) {
1808 Matches[Num_matched++] = src;
1810 strfcpy (dest, src, len);
1812 for (l = 0; src[l] && src[l] == dest[l]; l++);
1818 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1822 int spaces; /* keep track of the number of leading spaces on the line */
1825 spaces = buffer - pt;
1827 pt = buffer + pos - spaces;
1828 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1831 if (pt == buffer) { /* complete cmd */
1832 /* first TAB. Collect all the matches */
1835 strfcpy (User_typed, pt, sizeof (User_typed));
1836 memset (Matches, 0, sizeof (Matches));
1837 memset (Completed, 0, sizeof (Completed));
1838 for (num = 0; Commands[num].name; num++)
1839 candidate (Completed, User_typed, Commands[num].name,
1840 sizeof (Completed));
1841 Matches[Num_matched++] = User_typed;
1843 /* All matches are stored. Longest non-ambiguous string is ""
1844 * i.e. dont change 'buffer'. Fake successful return this time */
1845 if (User_typed[0] == 0)
1849 if (Completed[0] == 0 && User_typed[0])
1852 /* Num_matched will _always_ be atleast 1 since the initial
1853 * user-typed string is always stored */
1854 if (numtabs == 1 && Num_matched == 2)
1855 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1856 else if (numtabs > 1 && Num_matched > 2)
1857 /* cycle thru all the matches */
1858 snprintf (Completed, sizeof (Completed), "%s",
1859 Matches[(numtabs - 2) % Num_matched]);
1861 /* return the completed command */
1862 strncpy (buffer, Completed, len - spaces);
1864 else if (!str_ncmp (buffer, "set", 3)
1865 || !str_ncmp (buffer, "unset", 5)
1866 || !str_ncmp (buffer, "reset", 5)
1867 || !str_ncmp (buffer, "toggle", 6)) { /* complete variables */
1868 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1871 /* loop through all the possible prefixes (no, inv, ...) */
1872 if (!str_ncmp (buffer, "set", 3)) {
1873 for (num = 0; prefixes[num]; num++) {
1874 if (!str_ncmp (pt, prefixes[num], str_len (prefixes[num]))) {
1875 pt += str_len (prefixes[num]);
1881 /* first TAB. Collect all the matches */
1884 strfcpy (User_typed, pt, sizeof (User_typed));
1885 memset (Matches, 0, sizeof (Matches));
1886 memset (Completed, 0, sizeof (Completed));
1887 for (num = 0; MuttVars[num].option; num++)
1888 candidate (Completed, User_typed, MuttVars[num].option,
1889 sizeof (Completed));
1890 Matches[Num_matched++] = User_typed;
1892 /* All matches are stored. Longest non-ambiguous string is ""
1893 * i.e. dont change 'buffer'. Fake successful return this time */
1894 if (User_typed[0] == 0)
1898 if (Completed[0] == 0 && User_typed[0])
1901 /* Num_matched will _always_ be atleast 1 since the initial
1902 * user-typed string is always stored */
1903 if (numtabs == 1 && Num_matched == 2)
1904 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1905 else if (numtabs > 1 && Num_matched > 2)
1906 /* cycle thru all the matches */
1907 snprintf (Completed, sizeof (Completed), "%s",
1908 Matches[(numtabs - 2) % Num_matched]);
1910 strncpy (pt, Completed, buffer + len - pt - spaces);
1912 else if (!str_ncmp (buffer, "exec", 4)) {
1913 struct binding_t *menu = km_get_table (CurrentMenu);
1915 if (!menu && CurrentMenu != MENU_PAGER)
1919 /* first TAB. Collect all the matches */
1922 strfcpy (User_typed, pt, sizeof (User_typed));
1923 memset (Matches, 0, sizeof (Matches));
1924 memset (Completed, 0, sizeof (Completed));
1925 for (num = 0; menu[num].name; num++)
1926 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1927 /* try the generic menu */
1928 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1930 for (num = 0; menu[num].name; num++)
1931 candidate (Completed, User_typed, menu[num].name,
1932 sizeof (Completed));
1934 Matches[Num_matched++] = User_typed;
1936 /* All matches are stored. Longest non-ambiguous string is ""
1937 * i.e. dont change 'buffer'. Fake successful return this time */
1938 if (User_typed[0] == 0)
1942 if (Completed[0] == 0 && User_typed[0])
1945 /* Num_matched will _always_ be atleast 1 since the initial
1946 * user-typed string is always stored */
1947 if (numtabs == 1 && Num_matched == 2)
1948 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1949 else if (numtabs > 1 && Num_matched > 2)
1950 /* cycle thru all the matches */
1951 snprintf (Completed, sizeof (Completed), "%s",
1952 Matches[(numtabs - 2) % Num_matched]);
1954 strncpy (pt, Completed, buffer + len - pt - spaces);
1962 int mutt_var_value_complete (char *buffer, size_t len, int pos)
1964 char var[STRING], *pt = buffer;
1966 struct option_t* option = NULL;
1972 spaces = buffer - pt;
1974 pt = buffer + pos - spaces;
1975 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1977 pt++; /* move past the space */
1978 if (*pt == '=') /* abort if no var before the '=' */
1981 if (str_ncmp (buffer, "set", 3) == 0) {
1982 strfcpy (var, pt, sizeof (var));
1983 /* ignore the trailing '=' when comparing */
1984 var[str_len (var) - 1] = 0;
1985 if (!(option = hash_find (ConfigOptions, var)))
1986 return 0; /* no such variable. */
1988 char tmp[LONG_STRING], tmp2[LONG_STRING];
1990 size_t dlen = buffer + len - pt - spaces;
1991 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1995 if ((DTYPE (option->type) == DT_STR) ||
1996 (DTYPE (option->type) == DT_PATH) ||
1997 (DTYPE (option->type) == DT_RX)) {
1998 strfcpy (tmp, NONULL (*((char **) option->data)), sizeof (tmp));
1999 if (DTYPE (option->type) == DT_PATH)
2000 mutt_pretty_mailbox (tmp);
2002 else if (DTYPE (option->type) == DT_ADDR) {
2003 rfc822_write_address (tmp, sizeof (tmp),
2004 *((ADDRESS **) option->data), 0);
2006 else if (DTYPE (option->type) == DT_QUAD)
2007 strfcpy (tmp, vals[quadoption (option->data)], sizeof (tmp));
2008 else if (DTYPE (option->type) == DT_NUM)
2009 snprintf (tmp, sizeof (tmp), "%d", (*((short *) option->data)));
2010 else if (DTYPE (option->type) == DT_SORT) {
2011 const struct mapping_t *map;
2014 switch (option->type & DT_SUBTYPE_MASK) {
2016 map = SortAliasMethods;
2018 case DT_SORT_BROWSER:
2019 map = SortBrowserMethods;
2022 if ((WithCrypto & APPLICATION_PGP))
2023 map = SortKeyMethods;
2032 mutt_getnamebyvalue (*((short *) option->data) & SORT_MASK,
2034 snprintf (tmp, sizeof (tmp), "%s%s%s",
2035 (*((short *) option->data) & SORT_REVERSE) ?
2037 (*((short *) option->data) & SORT_LAST) ? "last-" :
2040 else if (DTYPE (option->type) == DT_MAGIC) {
2042 switch (DefaultMagic) {
2058 strfcpy (tmp, p, sizeof (tmp));
2060 else if (DTYPE (option->type) == DT_BOOL)
2061 strfcpy (tmp, option (option->data) ? "yes" : "no",
2066 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
2067 if (*s == '\\' || *s == '"')
2073 strfcpy (tmp, pt, sizeof (tmp));
2074 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2082 /* Implement the -Q command line flag */
2083 int mutt_query_variables (LIST * queries)
2087 char errbuff[STRING];
2088 char command[STRING];
2092 memset (&err, 0, sizeof (err));
2093 memset (&token, 0, sizeof (token));
2096 err.dsize = sizeof (errbuff);
2098 for (p = queries; p; p = p->next) {
2099 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2100 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2101 fprintf (stderr, "%s\n", err.data);
2102 mem_free (&token.data);
2105 printf ("%s\n", err.data);
2108 mem_free (&token.data);
2112 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2116 for (i = 0; map[i].name; i++)
2117 if (map[i].value == val)
2118 return (map[i].name);
2122 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2126 for (i = 0; map[i].name; i++)
2127 if (ascii_strcasecmp (map[i].name, name) == 0)
2128 return (map[i].value);
2132 static int mutt_execute_commands (LIST * p)
2135 char errstr[SHORT_STRING];
2137 memset (&err, 0, sizeof (err));
2139 err.dsize = sizeof (errstr);
2140 memset (&token, 0, sizeof (token));
2141 for (; p; p = p->next) {
2142 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2143 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2144 mem_free (&token.data);
2148 mem_free (&token.data);
2152 void mutt_init (int skip_sys_rc, LIST * commands)
2155 struct utsname utsname;
2156 char *p, buffer[STRING], error[STRING];
2157 int i, default_rc = 0, need_pause = 0;
2160 memset (&err, 0, sizeof (err));
2162 err.dsize = sizeof (error);
2164 /* use 3*sizeof(muttvars) to have some room for $user_ vars */
2165 ConfigOptions = hash_create (sizeof (MuttVars) * 3);
2166 for (i = 0; MuttVars[i].option; i++)
2167 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2170 * XXX - use something even more difficult to predict?
2172 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2173 "\033]9;%ld\a", (long) time (NULL));
2175 /* on one of the systems I use, getcwd() does not return the same prefix
2176 as is listed in the passwd file */
2177 if ((p = getenv ("HOME")))
2178 Homedir = str_dup (p);
2180 /* Get some information about the user */
2181 if ((pw = getpwuid (getuid ()))) {
2184 Username = str_dup (pw->pw_name);
2186 Homedir = str_dup (pw->pw_dir);
2188 Realname = str_dup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2189 Shell = str_dup (pw->pw_shell);
2194 fputs (_("unable to determine home directory"), stderr);
2197 if ((p = getenv ("USER")))
2198 Username = str_dup (p);
2201 fputs (_("unable to determine username"), stderr);
2204 Shell = str_dup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2207 debug_start(Homedir);
2209 /* And about the host... */
2211 /* some systems report the FQDN instead of just the hostname */
2212 if ((p = strchr (utsname.nodename, '.'))) {
2213 Hostname = str_substrdup (utsname.nodename, p);
2215 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2218 Hostname = str_dup (utsname.nodename);
2221 #define DOMAIN buffer
2222 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2223 Fqdn = str_dup ("@");
2226 if (*DOMAIN != '@') {
2227 Fqdn = mem_malloc (str_len (DOMAIN) + str_len (Hostname) + 2);
2228 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2231 Fqdn = str_dup (NONULL (Hostname));
2238 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2240 fgets (buffer, sizeof (buffer), f);
2241 p = (char*) &buffer;
2244 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r')
2248 NewsServer = str_dup (p);
2252 if ((p = getenv ("NNTPSERVER")))
2253 NewsServer = str_dup (p);
2256 if ((p = getenv ("MAIL")))
2257 Spoolfile = str_dup (p);
2258 else if ((p = getenv ("MAILDIR")))
2259 Spoolfile = str_dup (p);
2262 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2264 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
2266 Spoolfile = str_dup (buffer);
2269 if ((p = getenv ("MAILCAPS")))
2270 MailcapPath = str_dup (p);
2272 /* Default search path from RFC1524 */
2274 str_dup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2275 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2278 Tempdir = str_dup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2280 p = getenv ("VISUAL");
2282 p = getenv ("EDITOR");
2286 Editor = str_dup (p);
2287 Visual = str_dup (p);
2289 if ((p = getenv ("REPLYTO")) != NULL) {
2292 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2294 memset (&buf, 0, sizeof (buf));
2295 buf.data = buf.dptr = buffer;
2296 buf.dsize = str_len (buffer);
2298 memset (&token, 0, sizeof (token));
2299 parse_my_hdr (&token, &buf, 0, &err);
2300 mem_free (&token.data);
2303 if ((p = getenv ("EMAIL")) != NULL)
2304 From = rfc822_parse_adrlist (NULL, p);
2306 mutt_set_langinfo_charset ();
2307 mutt_set_charset (Charset);
2310 /* Set standard defaults */
2311 hash_map (ConfigOptions, mutt_set_default, 0);
2312 hash_map (ConfigOptions, mutt_restore_default, 0);
2314 CurrentMenu = MENU_MAIN;
2317 #ifndef LOCALES_HACK
2318 /* Do we have a locale definition? */
2319 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2320 ((p = getenv ("LANG")) != NULL && p[0]) ||
2321 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2322 set_option (OPTLOCALES);
2326 /* Unset suspend by default if we're the session leader */
2327 if (getsid (0) == getpid ())
2328 unset_option (OPTSUSPEND);
2331 mutt_init_history ();
2340 * When changing the code which looks for a configuration file,
2341 * please also change the corresponding code in muttbug.sh.in.
2351 snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2353 if (access (buffer, F_OK) == -1)
2355 snprintf (buffer, sizeof (buffer), "%s/.muttngrc", NONULL (Homedir));
2356 if (access (buffer, F_OK) == -1)
2358 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s",
2359 NONULL (Homedir), MUTT_VERSION);
2360 if (access (buffer, F_OK) == -1)
2362 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc",
2366 Muttrc = str_dup (buffer);
2369 strfcpy (buffer, Muttrc, sizeof (buffer));
2371 mutt_expand_path (buffer, sizeof (buffer));
2372 Muttrc = str_dup (buffer);
2374 mem_free (&AliasFile);
2375 AliasFile = str_dup (NONULL (Muttrc));
2377 /* Process the global rc file if it exists and the user hasn't explicity
2378 requested not to via "-n". */
2380 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2382 if (access (buffer, F_OK) == -1)
2383 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", SYSCONFDIR);
2384 if (access (buffer, F_OK) == -1)
2385 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR,
2387 if (access (buffer, F_OK) == -1)
2388 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2389 if (access (buffer, F_OK) != -1) {
2390 if (source_rc (buffer, &err) != 0) {
2391 fputs (err.data, stderr);
2392 fputc ('\n', stderr);
2398 /* Read the user's initialization file. */
2399 if (access (Muttrc, F_OK) != -1) {
2400 if (!option (OPTNOCURSES))
2402 if (source_rc (Muttrc, &err) != 0) {
2403 fputs (err.data, stderr);
2404 fputc ('\n', stderr);
2408 else if (!default_rc) {
2409 /* file specified by -F does not exist */
2410 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2411 mutt_endwin (buffer);
2415 if (mutt_execute_commands (commands) != 0)
2418 /* warn about synonym variables */
2419 if (!list_empty(Synonyms)) {
2421 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2422 for (i = 0; i < Synonyms->length; i++) {
2423 struct option_t* newopt = NULL, *oldopt = NULL;
2424 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2425 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2426 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2427 oldopt ? NONULL (oldopt->option) : "",
2428 newopt ? NONULL (newopt->option) : "",
2429 NONULL(((syn_t*) Synonyms->data[i])->f),
2430 ((syn_t*) Synonyms->data[i])->l);
2432 fprintf (stderr, _("Warning: synonym variables are scheduled"
2433 " for removal.\n"));
2434 list_del (&Synonyms, syn_del);
2438 if (need_pause && !option (OPTNOCURSES)) {
2439 if (mutt_any_key_to_continue (NULL) == -1)
2444 set_option (OPTWEED); /* turn weeding on by default */
2448 int mutt_get_hook_type (const char *name)
2450 struct command_t *c;
2452 for (c = Commands; c->name; c++)
2453 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2458 /* compare two option_t*'s for sorting -t/-T output */
2459 static int opt_cmp (const void* a, const void* b) {
2460 return (str_cmp ((*(struct option_t**) a)->option,
2461 (*(struct option_t**) b)->option));
2464 /* callback for hash_map() to put all non-synonym vars into list */
2465 static void opt_sel_full (const char* key, void* data,
2466 unsigned long more) {
2467 list2_t** l = (list2_t**) more;
2468 struct option_t* option = (struct option_t*) data;
2470 if (DTYPE (option->type) == DT_SYN)
2472 list_push_back (l, option);
2475 /* callback for hash_map() to put all changed non-synonym vars into list */
2476 static void opt_sel_diff (const char* key, void* data,
2477 unsigned long more) {
2478 list2_t** l = (list2_t**) more;
2479 struct option_t* option = (struct option_t*) data;
2480 char buf[LONG_STRING];
2482 if (DTYPE (option->type) == DT_SYN)
2485 mutt_option_value (option->option, buf, sizeof (buf));
2486 if (str_cmp (buf, option->init) != 0)
2487 list_push_back (l, option);
2490 /* dump out the value of all the variables we have */
2491 int mutt_dump_variables (int full) {
2493 char outbuf[STRING];
2494 list2_t* tmp = NULL;
2495 struct option_t* option = NULL;
2497 /* get all non-synonyms into list... */
2498 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2499 (unsigned long) &tmp);
2501 if (!list_empty(tmp)) {
2502 /* ...and dump list sorted */
2503 qsort (tmp->data, tmp->length, sizeof (void*), opt_cmp);
2504 for (i = 0; i < tmp->length; i++) {
2505 option = (struct option_t*) tmp->data[i];
2506 FuncTable[DTYPE (option->type)].opt_to_string
2507 (outbuf, sizeof (outbuf), option);
2508 printf ("%s\n", outbuf);
2511 list_del (&tmp, NULL);