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*);
83 static int check_dsn_notify (const char*);
85 /* variable <-> sanity check function mappings */
88 int (*check) (const char*);
90 { "dsn_notify", check_dsn_notify },
91 { "dsn_return", check_dsn_return },
92 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
93 { "smtp_use_tls", mutt_libesmtp_check_usetls },
99 /* protos for config type handles */
100 static void bool_to_string (char* dst, size_t dstlen, int idx);
101 static void num_to_string (char* dst, size_t dstlen, int idx);
102 static void str_to_string (char* dst, size_t dstlen, int idx);
103 static void quad_to_string (char* dst, size_t dstlen, int idx);
104 static void sort_to_string (char* dst, size_t dstlen, int idx);
105 static void rx_to_string (char* dst, size_t dstlen, int idx);
106 static void magic_to_string (char* dst, size_t dstlen, int idx);
107 static void syn_to_string (char* dst, size_t dstlen, int idx);
108 static void addr_to_string (char* dst, size_t dstlen, int idx);
112 void (*opt_to_string) (char* dst, size_t dstlen, int idx);
114 { 0, NULL }, /* there's no DT_ type with 0 */
115 { DT_BOOL, bool_to_string },
116 { DT_NUM, num_to_string },
117 { DT_STR, str_to_string },
118 { DT_PATH, str_to_string },
119 { DT_QUAD, quad_to_string },
120 { DT_SORT, sort_to_string },
121 { DT_RX, rx_to_string },
122 { DT_MAGIC, magic_to_string },
123 { DT_SYN, syn_to_string },
124 { DT_ADDR, addr_to_string }
127 static void bool_to_string (char* dst, size_t dstlen, int idx) {
128 snprintf (dst, dstlen, "%s=%s", MuttVars[idx].option,
129 option (MuttVars[idx].data) ? "yes" : "no");
132 static void num_to_string (char* dst, size_t dstlen, int idx) {
134 const char* fmt = (idx == mutt_option_index ("umask")) ? "%s=%04o" : "%s=%d";
135 snprintf (dst, dstlen, fmt, MuttVars[idx].option,
136 *((short*) MuttVars[idx].data));
139 static void str_to_string (char* dst, size_t dstlen, int idx) {
140 snprintf (dst, dstlen, "%s=\"%s\"", MuttVars[idx].option,
141 NONULL (*((char**) MuttVars[idx].data)));
144 static void quad_to_string (char* dst, size_t dstlen, int idx) {
145 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
146 snprintf (dst, dstlen, "%s=%s", MuttVars[idx].option,
147 vals[quadoption (MuttVars[idx].data)]);
150 static void sort_to_string (char* dst, size_t dstlen, int idx) {
151 const struct mapping_t *map = get_sortmap (idx);
155 snprintf (dst, sizeof (dst), "%s=unknown", MuttVars[idx].option);
159 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK,
162 snprintf (dst, dstlen, "%s=%s%s%s", MuttVars[idx].option,
163 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ?
165 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" :
169 static void rx_to_string (char* dst, size_t dstlen, int idx) {
170 rx_t* p = (rx_t*) MuttVars[idx].data;
171 snprintf (dst, dstlen, "%s=\"%s\"", MuttVars[idx].option,
172 NONULL (p->pattern));
175 static void magic_to_string (char* dst, size_t dstlen, int idx) {
176 const char* s = NULL;
177 switch (MuttVars[idx].data) {
178 case M_MBOX: s = "mbox"; break;
179 case M_MMDF: s = "MMDF"; break;
180 case M_MH: s = "MH"; break;
181 case M_MAILDIR: s = "Maildir"; break;
182 default: s = "unknown"; break;
184 snprintf (dst, dstlen, "%s=%s", MuttVars[idx].option, s);
187 static void syn_to_string (char* dst, size_t dstlen, int idx) {
188 int i = mutt_option_index ((char*) MuttVars[idx].data);
189 FuncTable[MuttVars[i].type].opt_to_string (dst, dstlen, i);
192 static void addr_to_string (char* dst, size_t dstlen, int idx) {
195 rfc822_write_address (s, sizeof (s), *((ADDRESS**) MuttVars[idx].data), 0);
196 snprintf (dst, dstlen, "%s=\"%s\"", MuttVars[idx].option, NONULL (s));
199 int mutt_option_value (const char* val, char* dst, size_t dstlen) {
200 int i = mutt_option_index ((char*) val);
201 char* tmp = NULL, *t = NULL;
205 debug_print (1, ("var '%s' not found, i = %d\n", val, i));
209 tmp = mem_malloc (dstlen+1);
210 FuncTable[DTYPE (MuttVars[i].type)].opt_to_string (tmp, dstlen, i);
212 /* as we get things of type $var=value and don't want to bloat the
213 * above "just" for expansion, we do the stripping here */
214 debug_print (1, ("orig == '%s'\n", tmp));
215 t = strchr (tmp, '=');
219 if (t[l-1] == '"' && *t == '"') {
224 memcpy (dst, t, l+1);
226 debug_print (1, ("stripped == '%s'\n", dst));
231 /* for synonym warning reports: adds synonym to end of list */
232 static void syn_add (struct option_t* n, struct option_t* o) {
233 syn_t* tmp = mem_malloc (sizeof (syn_t));
234 tmp->f = str_dup (CurRCFile);
238 list_push_back (&Synonyms, tmp);
241 /* for synonym warning reports: free single item (for list_del()) */
242 static void syn_del (void** p) {
243 mem_free(&(*(syn_t**) p)->f);
247 void toggle_quadoption (int opt)
250 int b = (opt % 4) * 2;
252 QuadOptions[n] ^= (1 << b);
255 void set_quadoption (int opt, int flag)
258 int b = (opt % 4) * 2;
260 QuadOptions[n] &= ~(0x3 << b);
261 QuadOptions[n] |= (flag & 0x3) << b;
264 int quadoption (int opt)
267 int b = (opt % 4) * 2;
269 return (QuadOptions[n] >> b) & 0x3;
272 int query_quadoption (int opt, const char *prompt)
274 int v = quadoption (opt);
282 v = mutt_yesorno (prompt, (v == M_ASKYES));
283 CLEARLINE (LINES - 1);
290 static void add_to_list (LIST ** list, const char *str)
292 LIST *t, *last = NULL;
294 /* don't add a NULL or empty string to the list */
295 if (!str || *str == '\0')
298 /* check to make sure the item is not already on this list */
299 for (last = *list; last; last = last->next) {
300 if (ascii_strcasecmp (str, last->data) == 0) {
301 /* already on the list, so just ignore it */
309 if (!*list || last) {
310 t = (LIST *) mem_calloc (1, sizeof (LIST));
311 t->data = str_dup (str);
321 static int add_to_rx_list (list2_t** list, const char *s, int flags,
330 if (!(rx = rx_compile (s, flags))) {
331 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
335 i = rx_lookup ((*list), rx->pattern);
339 list_push_back (list, rx);
343 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
344 const char *templ, BUFFER * err)
346 SPAM_LIST *t = NULL, *last = NULL;
351 if (!pat || !*pat || !templ)
354 if (!(rx = rx_compile (pat, REG_ICASE))) {
355 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
359 /* check to make sure the item is not already on this list */
360 for (last = *list; last; last = last->next) {
361 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
362 /* Already on the list. Formerly we just skipped this case, but
363 * now we're supporting removals, which means we're supporting
364 * re-adds conceptually. So we probably want this to imply a
365 * removal, then do an add. We can achieve the removal by freeing
366 * the template, and leaving t pointed at the current item.
369 mem_free(t->template);
376 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
377 * update. Otherwise we want to make a new one to link at the list's end.
380 t = mutt_new_spam_list ();
388 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
389 t->template = str_dup (templ);
391 /* Find highest match number in template string */
393 for (p = templ; *p;) {
398 while (*p && isdigit ((int) *p))
404 t->nmatch++; /* match 0 is always the whole expr */
409 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
411 SPAM_LIST *spam, *prev;
414 /* Being first is a special case. */
418 if (spam->rx && !str_cmp (spam->rx->pattern, pat)) {
421 mem_free(&spam->template);
427 for (spam = prev->next; spam;) {
428 if (!str_cmp (spam->rx->pattern, pat)) {
429 prev->next = spam->next;
431 mem_free(spam->template);
444 static void remove_from_list (LIST ** l, const char *str)
446 LIST *p, *last = NULL;
448 if (str_cmp ("*", str) == 0)
449 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
454 if (ascii_strcasecmp (str, p->data) == 0) {
457 last->next = p->next;
470 static int remove_from_rx_list (list2_t** l, const char *str)
474 if (str_cmp ("*", str) == 0) {
475 list_del (l, (list_del_t*) rx_free);
479 i = rx_lookup ((*l), str);
481 rx_t* r = list_pop_idx ((*l), i);
489 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
494 struct option_t* option = NULL;
496 memset (&token, 0, sizeof (token));
497 mutt_extract_token (tmp, s, 0);
499 /* is the item defined as a variable or a function? */
500 if ((option = hash_find (ConfigOptions, tmp->data)))
503 for (i = 0; !res && i < MENU_MAX; i++) {
504 struct binding_t *b = km_get_table (Menus[i].value);
509 for (j = 0; b[j].name; j++)
510 if (!ascii_strncasecmp (tmp->data, b[j].name, str_len (tmp->data))
511 && (str_len (b[j].name) == str_len (tmp->data))) {
517 /* check for feature_* */
522 j = str_len (tmp->data);
523 /* need at least input of 'feature_X' */
527 while (Features[i].name) {
528 if (str_len (Features[i].name) == j &&
529 ascii_strncasecmp (Features[i].name, p, j)) {
540 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
542 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
545 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
547 if ((data && res) || (!data && !res)) {
548 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
549 mutt_error ("Error: %s", err->data);
550 mem_free (&token.data);
553 mem_free (&token.data);
558 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
562 mutt_extract_token (buf, s, 0);
564 /* don't add "*" to the unignore list */
565 if (strcmp (buf->data, "*"))
566 add_to_list (&UnIgnore, buf->data);
568 remove_from_list (&Ignore, buf->data);
570 while (MoreArgs (s));
575 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
579 mutt_extract_token (buf, s, 0);
580 remove_from_list (&UnIgnore, buf->data);
581 add_to_list (&Ignore, buf->data);
583 while (MoreArgs (s));
588 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
592 mutt_extract_token (buf, s, 0);
593 add_to_list ((LIST **) data, buf->data);
595 while (MoreArgs (s));
600 static void _alternates_clean (void)
604 if (Context && Context->msgcount) {
605 for (i = 0; i < Context->msgcount; i++)
606 Context->hdrs[i]->recip_valid = 0;
610 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
613 _alternates_clean ();
615 mutt_extract_token (buf, s, 0);
616 remove_from_rx_list (&UnAlternates, buf->data);
618 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
621 while (MoreArgs (s));
626 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
629 _alternates_clean ();
631 mutt_extract_token (buf, s, 0);
632 remove_from_rx_list (&Alternates, buf->data);
634 if (str_cmp (buf->data, "*") &&
635 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
639 while (MoreArgs (s));
644 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
649 memset (&templ, 0, sizeof (templ));
651 /* Insist on at least one parameter */
654 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
656 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
660 /* Extract the first token, a regexp */
661 mutt_extract_token (buf, s, 0);
663 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
664 if (data == M_SPAM) {
665 /* If there's a second parameter, it's a template for the spam tag. */
667 mutt_extract_token (&templ, s, 0);
669 /* Add to the spam list. */
670 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
671 mem_free (&templ.data);
674 mem_free (&templ.data);
677 /* If not, try to remove from the nospam list. */
679 remove_from_rx_list (&NoSpamList, buf->data);
685 /* M_NOSPAM is for nospam commands. */
686 else if (data == M_NOSPAM) {
687 /* nospam only ever has one parameter. */
689 /* "*" is a special case. */
690 if (!str_cmp (buf->data, "*")) {
691 mutt_free_spam_list (&SpamList);
692 list_del (&NoSpamList, (list_del_t*) rx_free);
696 /* If it's on the spam list, just remove it. */
697 if (remove_from_spam_list (&SpamList, buf->data) != 0)
700 /* Otherwise, add it to the nospam list. */
701 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
707 /* This should not happen. */
708 strfcpy (err->data, "This is no good at all.", err->dsize);
712 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
716 mutt_extract_token (buf, s, 0);
718 * Check for deletion of entire list
720 if (str_cmp (buf->data, "*") == 0) {
721 mutt_free_list ((LIST **) data);
724 remove_from_list ((LIST **) data, buf->data);
726 while (MoreArgs (s));
731 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
735 mutt_extract_token (buf, s, 0);
736 remove_from_rx_list (&UnMailLists, buf->data);
738 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
741 while (MoreArgs (s));
746 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
750 mutt_extract_token (buf, s, 0);
751 remove_from_rx_list (&SubscribedLists, buf->data);
752 remove_from_rx_list (&MailLists, buf->data);
754 if (str_cmp (buf->data, "*") &&
755 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
758 while (MoreArgs (s));
763 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
767 mutt_extract_token (buf, s, 0);
768 remove_from_rx_list (&UnMailLists, buf->data);
769 remove_from_rx_list (&UnSubscribedLists, buf->data);
771 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
773 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
776 while (MoreArgs (s));
781 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
785 mutt_extract_token (buf, s, 0);
786 remove_from_rx_list (&SubscribedLists, buf->data);
788 if (str_cmp (buf->data, "*") &&
789 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
792 while (MoreArgs (s));
797 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
800 ALIAS *tmp, *last = NULL;
803 mutt_extract_token (buf, s, 0);
805 if (str_cmp ("*", buf->data) == 0) {
806 if (CurrentMenu == MENU_ALIAS) {
807 for (tmp = Aliases; tmp; tmp = tmp->next)
809 set_option (OPTFORCEREDRAWINDEX);
812 mutt_free_alias (&Aliases);
816 for (tmp = Aliases; tmp; tmp = tmp->next) {
817 if (str_casecmp (buf->data, tmp->name) == 0) {
818 if (CurrentMenu == MENU_ALIAS) {
820 set_option (OPTFORCEREDRAWINDEX);
825 last->next = tmp->next;
829 mutt_free_alias (&tmp);
835 while (MoreArgs (s));
839 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
842 ALIAS *tmp = Aliases;
847 strfcpy (err->data, _("alias: no address"), err->dsize);
851 mutt_extract_token (buf, s, 0);
853 debug_print (2, ("first token is '%s'.\n", buf->data));
855 /* check to see if an alias with this name already exists */
856 for (; tmp; tmp = tmp->next) {
857 if (!str_casecmp (tmp->name, buf->data))
863 /* create a new alias */
864 tmp = (ALIAS *) mem_calloc (1, sizeof (ALIAS));
866 tmp->name = str_dup (buf->data);
867 /* give the main addressbook code a chance */
868 if (CurrentMenu == MENU_ALIAS)
869 set_option (OPTMENUCALLER);
872 /* override the previous value */
873 rfc822_free_address (&tmp->addr);
874 if (CurrentMenu == MENU_ALIAS)
875 set_option (OPTFORCEREDRAWINDEX);
878 mutt_extract_token (buf, s,
879 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
880 debug_print (2, ("second token is '%s'.\n", buf->data));
881 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
886 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
887 snprintf (err->data, err->dsize,
888 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
892 if (DebugLevel >= 2) {
895 for (a = tmp->addr; a; a = a->next) {
897 debug_print (2, ("%s\n", a->mailbox));
899 debug_print (2, ("group %s\n", a->mailbox));
907 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
910 LIST *tmp = UserHeader;
915 mutt_extract_token (buf, s, 0);
916 if (str_cmp ("*", buf->data) == 0)
917 mutt_free_list (&UserHeader);
922 l = str_len (buf->data);
923 if (buf->data[l - 1] == ':')
927 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
928 && tmp->data[l] == ':') {
931 last->next = tmp->next;
933 UserHeader = tmp->next;
936 mutt_free_list (&ptr);
945 while (MoreArgs (s));
949 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
956 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
957 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
958 strfcpy (err->data, _("invalid header field"), err->dsize);
961 keylen = p - buf->data + 1;
964 for (tmp = UserHeader;; tmp = tmp->next) {
965 /* see if there is already a field by this name */
966 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
967 /* replace the old value */
968 mem_free (&tmp->data);
969 tmp->data = buf->data;
970 memset (buf, 0, sizeof (BUFFER));
976 tmp->next = mutt_new_list ();
980 tmp = mutt_new_list ();
983 tmp->data = buf->data;
984 memset (buf, 0, sizeof (BUFFER));
989 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
990 char* errbuf, size_t errlen) {
993 if (str_ncmp ("reverse-", s, 8) == 0) {
995 flags = SORT_REVERSE;
998 if (str_ncmp ("last-", s, 5) == 0) {
1003 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1005 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1009 *((short*) dst->data) = i | flags;
1013 /* if additional data more == 1, we want to resolve synonyms */
1014 static void mutt_set_default (const char* name, void* p, unsigned long more) {
1015 char buf[LONG_STRING];
1016 struct option_t* ptr = (struct option_t*) p;
1018 if (DTYPE (ptr->type) == DT_SYN) {
1021 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1023 if (!ptr || *ptr->init)
1025 mutt_option_value (ptr->option, buf, sizeof (buf));
1026 if (str_len (ptr->init) == 0 && buf && *buf)
1027 ptr->init = str_dup (buf);
1030 /* if additional data more == 1, we want to resolve synonyms */
1031 static void mutt_restore_default (const char* name, void* p, unsigned long more) {
1032 char errbuf[STRING];
1033 struct option_t* ptr = (struct option_t*) p;
1035 if (DTYPE (ptr->type) == DT_SYN) {
1038 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1042 if (FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, ptr->init, errbuf,
1043 sizeof (errbuf)) < 0) {
1045 fprintf (stderr, _("Invalid default setting found. Please report this "
1046 "error:\n\"%s\"\n"), errbuf);
1050 if (ptr->flags & R_INDEX)
1051 set_option (OPTFORCEREDRAWINDEX);
1052 if (ptr->flags & R_PAGER)
1053 set_option (OPTFORCEREDRAWPAGER);
1054 if (ptr->flags & R_RESORT_SUB)
1055 set_option (OPTSORTSUBTHREADS);
1056 if (ptr->flags & R_RESORT)
1057 set_option (OPTNEEDRESORT);
1058 if (ptr->flags & R_RESORT_INIT)
1059 set_option (OPTRESORTINIT);
1060 if (ptr->flags & R_TREE)
1061 set_option (OPTREDRAWTREE);
1064 /* check whether value for $dsn_return would be valid */
1065 static int check_dsn_return (const char* option, unsigned long p,
1066 char* errbuf, size_t errlen) {
1067 char* val = (char*) p;
1068 if (val && *val && str_ncmp (val, "hdrs", 4) != 0 &&
1069 str_ncmp (val, "full", 4) != 0) {
1071 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1077 /* check whether value for $dsn_notify would be valid */
1078 static int check_dsn_notify (const char* option, unsigned long p,
1079 char* errbuf, size_t errlen) {
1080 list2_t* list = NULL;
1082 char* val = (char*) p;
1086 list = list_from_str (val, ",");
1087 if (list_empty (list))
1090 for (i = 0; i < list->length; i++)
1091 if (str_ncmp (list->data[i], "never", 5) != 0 &&
1092 str_ncmp (list->data[i], "failure", 7) != 0 &&
1093 str_ncmp (list->data[i], "delay", 5) != 0 &&
1094 str_ncmp (list->data[i], "success", 7) != 0) {
1096 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1097 (char*) list->data[i], "dsn_notify");
1101 list_del (&list, (list_del_t*) _mem_free);
1105 static int check_num (const char* option, unsigned long p,
1106 char* errbuf, size_t errlen) {
1109 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1115 static int check_history (const char* option, unsigned long p,
1116 char* errbuf, size_t errlen) {
1117 if (!check_num ("history", p, errbuf, errlen))
1119 mutt_init_history ();
1123 static int check_special (const char* name, unsigned long val,
1124 char* errbuf, size_t errlen) {
1127 for (i = 0; SpecialVars[i].name; i++) {
1128 if (str_cmp (SpecialVars[i].name, name) == 0) {
1129 return (SpecialVars[i].check (SpecialVars[i].name,
1130 val, errbuf, errlen));
1136 static const struct mapping_t* get_sortmap (struct option_t* option) {
1137 const struct mapping_t* map = NULL;
1139 switch (option->type & DT_SUBTYPE_MASK) {
1141 map = SortAliasMethods;
1143 case DT_SORT_BROWSER:
1144 map = SortBrowserMethods;
1147 if ((WithCrypto & APPLICATION_PGP))
1148 map = SortKeyMethods;
1151 map = SortAuxMethods;
1160 /* creates new option_t* of type DT_USER for $user_ var */
1161 static struct option_t* add_user_option (const char* name) {
1162 struct option_t* option = mem_calloc (1, sizeof (struct option_t));
1163 option->option = str_dup (name);
1164 option->type = DT_USER;
1168 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1171 int query, unset, inv, reset, r = 0;
1172 struct option_t* option = NULL;
1174 while (MoreArgs (s)) {
1175 /* reset state variables */
1177 unset = data & M_SET_UNSET;
1178 inv = data & M_SET_INV;
1179 reset = data & M_SET_RESET;
1181 if (*s->dptr == '?') {
1185 else if (str_ncmp ("no", s->dptr, 2) == 0) {
1189 else if (str_ncmp ("inv", s->dptr, 3) == 0) {
1193 else if (*s->dptr == '&') {
1198 /* get the variable name */
1199 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1201 /* resolve synonyms */
1202 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1203 DTYPE (option->type == DT_SYN)) {
1204 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1205 syn_add (newopt, option);
1209 /* see if we need to add $user_ var */
1210 if (!option && !reset && !unset &&
1211 ascii_strncasecmp ("user_", tmp->data, 5) == 0) {
1212 debug_print (1, ("adding user option '%s'\n", tmp->data));
1213 option = add_user_option (tmp->data);
1214 hash_insert (ConfigOptions, option->option, option, 0);
1217 if (!option && !(reset && str_cmp ("all", tmp->data) == 0)) {
1218 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1224 if (query || unset || inv) {
1225 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1229 if (s && *s->dptr == '=') {
1230 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1234 if (!str_cmp ("all", tmp->data)) {
1235 hash_map (ConfigOptions, mutt_restore_default, 1);
1239 mutt_restore_default (NULL, option, 1);
1241 else if (DTYPE (option->type) == DT_BOOL) {
1242 /* XXX this currently ignores the function table
1243 * as we don't get invert and stuff into it */
1244 if (s && *s->dptr == '=') {
1245 if (unset || inv || query) {
1246 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1251 mutt_extract_token (tmp, s, 0);
1252 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1254 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1257 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1263 bool_to_string (err->data, err->dsize, option);
1268 unset_option (option->data);
1270 toggle_option (option->data);
1272 set_option (option->data);
1274 else if (DTYPE (option->type) == DT_STR ||
1275 DTYPE (option->type) == DT_PATH ||
1276 DTYPE (option->type) == DT_ADDR ||
1277 DTYPE (option->type) == DT_MAGIC ||
1278 DTYPE (option->type) == DT_NUM ||
1279 DTYPE (option->type) == DT_SORT ||
1280 DTYPE (option->type) == DT_RX ||
1281 DTYPE (option->type) == DT_USER) {
1283 /* XXX maybe we need to get unset into handlers? */
1284 if (DTYPE (option->type) == DT_STR ||
1285 DTYPE (option->type) == DT_PATH ||
1286 DTYPE (option->type) == DT_ADDR ||
1287 DTYPE (option->type) == DT_USER) {
1289 if (DTYPE (option->type) == DT_ADDR)
1290 rfc822_free_address ((ADDRESS **) option->data);
1291 else if (DTYPE (option->type == DT_USER)) {
1292 void* p = (void*) option->data;
1295 mem_free ((void *) option->data);
1300 if (query || *s->dptr != '=') {
1301 FuncTable[DTYPE (option->type)].opt_to_string
1302 (err->data, err->dsize, option);
1307 mutt_extract_token (tmp, s, 0);
1308 if (!FuncTable[DTYPE (option->type)].opt_from_string
1309 (option, tmp->data, err->data, err->dsize))
1312 else if (DTYPE (option->type) == DT_QUAD) {
1315 quad_to_string (err->data, err->dsize, option);
1319 if (*s->dptr == '=') {
1321 mutt_extract_token (tmp, s, 0);
1322 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1323 set_quadoption (option->data, M_YES);
1324 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1325 set_quadoption (option->data, M_NO);
1326 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1327 set_quadoption (option->data, M_ASKYES);
1328 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1329 set_quadoption (option->data, M_ASKNO);
1331 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1332 tmp->data, option->option);
1339 toggle_quadoption (option->data);
1341 set_quadoption (option->data, M_NO);
1343 set_quadoption (option->data, M_YES);
1347 snprintf (err->data, err->dsize, _("%s: unknown type"),
1353 if (option->flags & R_INDEX)
1354 set_option (OPTFORCEREDRAWINDEX);
1355 if (option->flags & R_PAGER)
1356 set_option (OPTFORCEREDRAWPAGER);
1357 if (option->flags & R_RESORT_SUB)
1358 set_option (OPTSORTSUBTHREADS);
1359 if (option->flags & R_RESORT)
1360 set_option (OPTNEEDRESORT);
1361 if (option->flags & R_RESORT_INIT)
1362 set_option (OPTRESORTINIT);
1363 if (option->flags & R_TREE)
1364 set_option (OPTREDRAWTREE);
1371 /* reads the specified initialization file. returns -1 if errors were found
1372 so that we can pause to let the user know... */
1373 static int source_rc (const char *rcfile, BUFFER * err)
1376 int line = 0, rc = 0, conv = 0;
1378 char *linebuf = NULL;
1379 char *currentline = NULL;
1383 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
1385 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1386 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1390 memset (&token, 0, sizeof (token));
1391 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
1392 conv = ConfigCharset && (*ConfigCharset) && Charset;
1394 currentline = str_dup (linebuf);
1397 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1400 currentline = linebuf;
1405 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1406 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1407 if (--rc < -MAXERRS) {
1409 mem_free (¤tline);
1418 mem_free (¤tline);
1420 mem_free (&token.data);
1421 mem_free (&linebuf);
1424 mutt_wait_filter (pid);
1426 /* the muttrc source keyword */
1427 snprintf (err->data, err->dsize,
1428 rc >= -MAXERRS ? _("source: errors in %s")
1429 : _("source: reading aborted due too many errors in %s"),
1438 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
1441 char path[_POSIX_PATH_MAX];
1445 if (mutt_extract_token (tmp, s, 0) != 0) {
1446 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1450 strfcpy (path, tmp->data, sizeof (path));
1451 mutt_expand_path (path, sizeof (path));
1453 rc += source_rc (path, err);
1455 while (MoreArgs (s));
1457 return ((rc < 0) ? -1 : 0);
1460 /* line command to execute
1462 token scratch buffer to be used by parser. caller should free
1463 token->data when finished. the reason for this variable is
1464 to avoid having to allocate and deallocate a lot of memory
1465 if we are parsing many lines. the caller can pass in the
1466 memory to use, which avoids having to create new space for
1467 every call to this function.
1469 err where to write error messages */
1470 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1475 memset (&expn, 0, sizeof (expn));
1476 expn.data = expn.dptr = line;
1477 expn.dsize = str_len (line);
1482 while (*expn.dptr) {
1483 if (*expn.dptr == '#')
1484 break; /* rest of line is a comment */
1485 if (*expn.dptr == ';') {
1489 mutt_extract_token (token, &expn, 0);
1490 for (i = 0; Commands[i].name; i++) {
1491 if (!str_cmp (token->data, Commands[i].name)) {
1492 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1497 if (!Commands[i].name) {
1498 snprintf (err->data, err->dsize, _("%s: unknown command"),
1499 NONULL (token->data));
1506 mem_free (&expn.data);
1511 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1512 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1513 /* initial string that starts completion. No telling how much crap
1514 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1515 char User_typed[LONG_STRING] = { 0 };
1517 int Num_matched = 0; /* Number of matches for completion */
1518 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1519 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1521 /* helper function for completion. Changes the dest buffer if
1522 necessary/possible to aid completion.
1523 dest == completion result gets here.
1524 src == candidate for completion.
1525 try == user entered data for completion.
1526 len == length of dest buffer.
1528 static void candidate (char *dest, char *try, char *src, int len)
1532 if (strstr (src, try) == src) {
1533 Matches[Num_matched++] = src;
1535 strfcpy (dest, src, len);
1537 for (l = 0; src[l] && src[l] == dest[l]; l++);
1543 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1547 int spaces; /* keep track of the number of leading spaces on the line */
1550 spaces = buffer - pt;
1552 pt = buffer + pos - spaces;
1553 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1556 if (pt == buffer) { /* complete cmd */
1557 /* first TAB. Collect all the matches */
1560 strfcpy (User_typed, pt, sizeof (User_typed));
1561 memset (Matches, 0, sizeof (Matches));
1562 memset (Completed, 0, sizeof (Completed));
1563 for (num = 0; Commands[num].name; num++)
1564 candidate (Completed, User_typed, Commands[num].name,
1565 sizeof (Completed));
1566 Matches[Num_matched++] = User_typed;
1568 /* All matches are stored. Longest non-ambiguous string is ""
1569 * i.e. dont change 'buffer'. Fake successful return this time */
1570 if (User_typed[0] == 0)
1574 if (Completed[0] == 0 && User_typed[0])
1577 /* Num_matched will _always_ be atleast 1 since the initial
1578 * user-typed string is always stored */
1579 if (numtabs == 1 && Num_matched == 2)
1580 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1581 else if (numtabs > 1 && Num_matched > 2)
1582 /* cycle thru all the matches */
1583 snprintf (Completed, sizeof (Completed), "%s",
1584 Matches[(numtabs - 2) % Num_matched]);
1586 /* return the completed command */
1587 strncpy (buffer, Completed, len - spaces);
1589 else if (!str_ncmp (buffer, "set", 3)
1590 || !str_ncmp (buffer, "unset", 5)
1591 || !str_ncmp (buffer, "reset", 5)
1592 || !str_ncmp (buffer, "toggle", 6)) { /* complete variables */
1593 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1596 /* loop through all the possible prefixes (no, inv, ...) */
1597 if (!str_ncmp (buffer, "set", 3)) {
1598 for (num = 0; prefixes[num]; num++) {
1599 if (!str_ncmp (pt, prefixes[num], str_len (prefixes[num]))) {
1600 pt += str_len (prefixes[num]);
1606 /* first TAB. Collect all the matches */
1609 strfcpy (User_typed, pt, sizeof (User_typed));
1610 memset (Matches, 0, sizeof (Matches));
1611 memset (Completed, 0, sizeof (Completed));
1612 for (num = 0; MuttVars[num].option; num++)
1613 candidate (Completed, User_typed, MuttVars[num].option,
1614 sizeof (Completed));
1615 Matches[Num_matched++] = User_typed;
1617 /* All matches are stored. Longest non-ambiguous string is ""
1618 * i.e. dont change 'buffer'. Fake successful return this time */
1619 if (User_typed[0] == 0)
1623 if (Completed[0] == 0 && User_typed[0])
1626 /* Num_matched will _always_ be atleast 1 since the initial
1627 * user-typed string is always stored */
1628 if (numtabs == 1 && Num_matched == 2)
1629 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1630 else if (numtabs > 1 && Num_matched > 2)
1631 /* cycle thru all the matches */
1632 snprintf (Completed, sizeof (Completed), "%s",
1633 Matches[(numtabs - 2) % Num_matched]);
1635 strncpy (pt, Completed, buffer + len - pt - spaces);
1637 else if (!str_ncmp (buffer, "exec", 4)) {
1638 struct binding_t *menu = km_get_table (CurrentMenu);
1640 if (!menu && CurrentMenu != MENU_PAGER)
1644 /* first TAB. Collect all the matches */
1647 strfcpy (User_typed, pt, sizeof (User_typed));
1648 memset (Matches, 0, sizeof (Matches));
1649 memset (Completed, 0, sizeof (Completed));
1650 for (num = 0; menu[num].name; num++)
1651 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1652 /* try the generic menu */
1653 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1655 for (num = 0; menu[num].name; num++)
1656 candidate (Completed, User_typed, menu[num].name,
1657 sizeof (Completed));
1659 Matches[Num_matched++] = User_typed;
1661 /* All matches are stored. Longest non-ambiguous string is ""
1662 * i.e. dont change 'buffer'. Fake successful return this time */
1663 if (User_typed[0] == 0)
1667 if (Completed[0] == 0 && User_typed[0])
1670 /* Num_matched will _always_ be atleast 1 since the initial
1671 * user-typed string is always stored */
1672 if (numtabs == 1 && Num_matched == 2)
1673 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1674 else if (numtabs > 1 && Num_matched > 2)
1675 /* cycle thru all the matches */
1676 snprintf (Completed, sizeof (Completed), "%s",
1677 Matches[(numtabs - 2) % Num_matched]);
1679 strncpy (pt, Completed, buffer + len - pt - spaces);
1687 int mutt_var_value_complete (char *buffer, size_t len, int pos)
1689 char var[STRING], *pt = buffer;
1691 struct option_t* option = NULL;
1697 spaces = buffer - pt;
1699 pt = buffer + pos - spaces;
1700 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1702 pt++; /* move past the space */
1703 if (*pt == '=') /* abort if no var before the '=' */
1706 if (str_ncmp (buffer, "set", 3) == 0) {
1707 strfcpy (var, pt, sizeof (var));
1708 /* ignore the trailing '=' when comparing */
1709 var[str_len (var) - 1] = 0;
1710 if (!(option = hash_find (ConfigOptions, var)))
1711 return 0; /* no such variable. */
1713 char tmp[LONG_STRING], tmp2[LONG_STRING];
1715 size_t dlen = buffer + len - pt - spaces;
1716 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1720 if ((DTYPE (option->type) == DT_STR) ||
1721 (DTYPE (option->type) == DT_PATH) ||
1722 (DTYPE (option->type) == DT_RX)) {
1723 strfcpy (tmp, NONULL (*((char **) option->data)), sizeof (tmp));
1724 if (DTYPE (option->type) == DT_PATH)
1725 mutt_pretty_mailbox (tmp);
1727 else if (DTYPE (option->type) == DT_ADDR) {
1728 rfc822_write_address (tmp, sizeof (tmp),
1729 *((ADDRESS **) option->data), 0);
1731 else if (DTYPE (option->type) == DT_QUAD)
1732 strfcpy (tmp, vals[quadoption (option->data)], sizeof (tmp));
1733 else if (DTYPE (option->type) == DT_NUM)
1734 snprintf (tmp, sizeof (tmp), "%d", (*((short *) option->data)));
1735 else if (DTYPE (option->type) == DT_SORT) {
1736 const struct mapping_t *map;
1739 switch (option->type & DT_SUBTYPE_MASK) {
1741 map = SortAliasMethods;
1743 case DT_SORT_BROWSER:
1744 map = SortBrowserMethods;
1747 if ((WithCrypto & APPLICATION_PGP))
1748 map = SortKeyMethods;
1757 mutt_getnamebyvalue (*((short *) option->data) & SORT_MASK,
1759 snprintf (tmp, sizeof (tmp), "%s%s%s",
1760 (*((short *) option->data) & SORT_REVERSE) ?
1762 (*((short *) option->data) & SORT_LAST) ? "last-" :
1765 else if (DTYPE (option->type) == DT_MAGIC) {
1767 switch (DefaultMagic) {
1783 strfcpy (tmp, p, sizeof (tmp));
1785 else if (DTYPE (option->type) == DT_BOOL)
1786 strfcpy (tmp, option (option->data) ? "yes" : "no",
1791 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
1792 if (*s == '\\' || *s == '"')
1798 strfcpy (tmp, pt, sizeof (tmp));
1799 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
1807 /* Implement the -Q command line flag */
1808 int mutt_query_variables (LIST * queries)
1812 char errbuff[STRING];
1813 char command[STRING];
1817 memset (&err, 0, sizeof (err));
1818 memset (&token, 0, sizeof (token));
1821 err.dsize = sizeof (errbuff);
1823 for (p = queries; p; p = p->next) {
1824 snprintf (command, sizeof (command), "set ?%s\n", p->data);
1825 if (mutt_parse_rc_line (command, &token, &err) == -1) {
1826 fprintf (stderr, "%s\n", err.data);
1827 mem_free (&token.data);
1830 printf ("%s\n", err.data);
1833 mem_free (&token.data);
1837 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
1841 for (i = 0; map[i].name; i++)
1842 if (map[i].value == val)
1843 return (map[i].name);
1847 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
1851 for (i = 0; map[i].name; i++)
1852 if (ascii_strcasecmp (map[i].name, name) == 0)
1853 return (map[i].value);
1857 static int mutt_execute_commands (LIST * p)
1860 char errstr[SHORT_STRING];
1862 memset (&err, 0, sizeof (err));
1864 err.dsize = sizeof (errstr);
1865 memset (&token, 0, sizeof (token));
1866 for (; p; p = p->next) {
1867 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
1868 fprintf (stderr, _("Error in command line: %s\n"), err.data);
1869 mem_free (&token.data);
1873 mem_free (&token.data);
1877 void mutt_init (int skip_sys_rc, LIST * commands)
1880 struct utsname utsname;
1881 char *p, buffer[STRING], error[STRING];
1882 int i, default_rc = 0, need_pause = 0;
1885 memset (&err, 0, sizeof (err));
1887 err.dsize = sizeof (error);
1889 /* use 3*sizeof(muttvars) to have some room for $user_ vars */
1890 ConfigOptions = hash_create (sizeof (MuttVars) * 3);
1891 for (i = 0; MuttVars[i].option; i++)
1892 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
1895 * XXX - use something even more difficult to predict?
1897 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
1898 "\033]9;%ld\a", (long) time (NULL));
1900 /* on one of the systems I use, getcwd() does not return the same prefix
1901 as is listed in the passwd file */
1902 if ((p = getenv ("HOME")))
1903 Homedir = str_dup (p);
1905 /* Get some information about the user */
1906 if ((pw = getpwuid (getuid ()))) {
1909 Username = str_dup (pw->pw_name);
1911 Homedir = str_dup (pw->pw_dir);
1913 Realname = str_dup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
1914 Shell = str_dup (pw->pw_shell);
1919 fputs (_("unable to determine home directory"), stderr);
1922 if ((p = getenv ("USER")))
1923 Username = str_dup (p);
1926 fputs (_("unable to determine username"), stderr);
1929 Shell = str_dup ((p = getenv ("SHELL")) ? p : "/bin/sh");
1932 debug_start(Homedir);
1934 /* And about the host... */
1936 /* some systems report the FQDN instead of just the hostname */
1937 if ((p = strchr (utsname.nodename, '.'))) {
1938 Hostname = str_substrdup (utsname.nodename, p);
1940 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
1943 Hostname = str_dup (utsname.nodename);
1946 #define DOMAIN buffer
1947 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
1948 Fqdn = str_dup ("@");
1951 if (*DOMAIN != '@') {
1952 Fqdn = mem_malloc (str_len (DOMAIN) + str_len (Hostname) + 2);
1953 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
1956 Fqdn = str_dup (NONULL (Hostname));
1963 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
1965 fgets (buffer, sizeof (buffer), f);
1966 p = (char*) &buffer;
1969 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r')
1973 NewsServer = str_dup (p);
1977 if ((p = getenv ("NNTPSERVER")))
1978 NewsServer = str_dup (p);
1981 if ((p = getenv ("MAIL")))
1982 Spoolfile = str_dup (p);
1983 else if ((p = getenv ("MAILDIR")))
1984 Spoolfile = str_dup (p);
1987 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
1989 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
1991 Spoolfile = str_dup (buffer);
1994 if ((p = getenv ("MAILCAPS")))
1995 MailcapPath = str_dup (p);
1997 /* Default search path from RFC1524 */
1999 str_dup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2000 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2003 Tempdir = str_dup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2005 p = getenv ("VISUAL");
2007 p = getenv ("EDITOR");
2011 Editor = str_dup (p);
2012 Visual = str_dup (p);
2014 if ((p = getenv ("REPLYTO")) != NULL) {
2017 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2019 memset (&buf, 0, sizeof (buf));
2020 buf.data = buf.dptr = buffer;
2021 buf.dsize = str_len (buffer);
2023 memset (&token, 0, sizeof (token));
2024 parse_my_hdr (&token, &buf, 0, &err);
2025 mem_free (&token.data);
2028 if ((p = getenv ("EMAIL")) != NULL)
2029 From = rfc822_parse_adrlist (NULL, p);
2031 mutt_set_langinfo_charset ();
2032 mutt_set_charset (Charset);
2035 /* Set standard defaults */
2036 hash_map (ConfigOptions, mutt_set_default, 0);
2037 hash_map (ConfigOptions, mutt_restore_default, 0);
2039 CurrentMenu = MENU_MAIN;
2042 #ifndef LOCALES_HACK
2043 /* Do we have a locale definition? */
2044 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2045 ((p = getenv ("LANG")) != NULL && p[0]) ||
2046 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2047 set_option (OPTLOCALES);
2051 /* Unset suspend by default if we're the session leader */
2052 if (getsid (0) == getpid ())
2053 unset_option (OPTSUSPEND);
2056 mutt_init_history ();
2065 * When changing the code which looks for a configuration file,
2066 * please also change the corresponding code in muttbug.sh.in.
2076 snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2078 if (access (buffer, F_OK) == -1)
2080 snprintf (buffer, sizeof (buffer), "%s/.muttngrc", NONULL (Homedir));
2081 if (access (buffer, F_OK) == -1)
2083 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s",
2084 NONULL (Homedir), MUTT_VERSION);
2085 if (access (buffer, F_OK) == -1)
2087 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc",
2091 Muttrc = str_dup (buffer);
2094 strfcpy (buffer, Muttrc, sizeof (buffer));
2096 mutt_expand_path (buffer, sizeof (buffer));
2097 Muttrc = str_dup (buffer);
2099 mem_free (&AliasFile);
2100 AliasFile = str_dup (NONULL (Muttrc));
2102 /* Process the global rc file if it exists and the user hasn't explicity
2103 requested not to via "-n". */
2105 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2107 if (access (buffer, F_OK) == -1)
2108 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", SYSCONFDIR);
2109 if (access (buffer, F_OK) == -1)
2110 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR,
2112 if (access (buffer, F_OK) == -1)
2113 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2114 if (access (buffer, F_OK) != -1) {
2115 if (source_rc (buffer, &err) != 0) {
2116 fputs (err.data, stderr);
2117 fputc ('\n', stderr);
2123 /* Read the user's initialization file. */
2124 if (access (Muttrc, F_OK) != -1) {
2125 if (!option (OPTNOCURSES))
2127 if (source_rc (Muttrc, &err) != 0) {
2128 fputs (err.data, stderr);
2129 fputc ('\n', stderr);
2133 else if (!default_rc) {
2134 /* file specified by -F does not exist */
2135 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2136 mutt_endwin (buffer);
2140 if (mutt_execute_commands (commands) != 0)
2143 /* warn about synonym variables */
2144 if (!list_empty(Synonyms)) {
2146 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2147 for (i = 0; i < Synonyms->length; i++) {
2148 struct option_t* newopt = NULL, *oldopt = NULL;
2149 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2150 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2151 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2152 oldopt ? NONULL (oldopt->option) : "",
2153 newopt ? NONULL (newopt->option) : "",
2154 NONULL(((syn_t*) Synonyms->data[i])->f),
2155 ((syn_t*) Synonyms->data[i])->l);
2157 fprintf (stderr, _("Warning: synonym variables are scheduled"
2158 " for removal.\n"));
2159 list_del (&Synonyms, syn_del);
2163 if (need_pause && !option (OPTNOCURSES)) {
2164 if (mutt_any_key_to_continue (NULL) == -1)
2169 set_option (OPTWEED); /* turn weeding on by default */
2173 int mutt_get_hook_type (const char *name)
2175 struct command_t *c;
2177 for (c = Commands; c->name; c++)
2178 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2183 /* compare two option_t*'s for sorting -t/-T output */
2184 static int opt_cmp (const void* a, const void* b) {
2185 return (str_cmp ((*(struct option_t**) a)->option,
2186 (*(struct option_t**) b)->option));
2189 /* callback for hash_map() to put all non-synonym vars into list */
2190 static void opt_sel_full (const char* key, void* data,
2191 unsigned long more) {
2192 list2_t** l = (list2_t**) more;
2193 struct option_t* option = (struct option_t*) data;
2195 if (DTYPE (option->type) == DT_SYN)
2197 list_push_back (l, option);
2200 /* callback for hash_map() to put all changed non-synonym vars into list */
2201 static void opt_sel_diff (const char* key, void* data,
2202 unsigned long more) {
2203 list2_t** l = (list2_t**) more;
2204 struct option_t* option = (struct option_t*) data;
2205 char buf[LONG_STRING];
2207 if (DTYPE (option->type) == DT_SYN)
2210 mutt_option_value (option->option, buf, sizeof (buf));
2211 if (str_cmp (buf, option->init) != 0)
2212 list_push_back (l, option);
2215 /* dump out the value of all the variables we have */
2216 int mutt_dump_variables (int full) {
2218 char outbuf[STRING];
2219 list2_t* tmp = NULL;
2220 struct option_t* option = NULL;
2222 /* get all non-synonyms into list... */
2223 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2224 (unsigned long) &tmp);
2226 if (!list_empty(tmp)) {
2227 /* ...and dump list sorted */
2228 qsort (tmp->data, tmp->length, sizeof (void*), opt_cmp);
2229 for (i = 0; i < tmp->length; i++) {
2230 option = (struct option_t*) tmp->data[i];
2231 FuncTable[DTYPE (option->type)].opt_to_string
2232 (outbuf, sizeof (outbuf), option);
2233 printf ("%s\n", outbuf);
2236 list_del (&tmp, NULL);