2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
5 * This file is part of mutt-ng, see http://www.muttng.org/.
6 * It's licensed under the GNU General Public License,
7 * please see the file GPL in the top level source directory.
18 #include "mutt_curses.h"
23 #include "mutt_crypt.h"
24 #include "mutt_idna.h"
26 #if defined(USE_SSL) || defined(USE_GNUTLS)
30 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
31 #include "mutt_libesmtp.h"
42 #include "lib/debug.h"
48 #include <sys/utsname.h>
52 /* for synonym warning reports: synonym found during parsing */
56 int n; /* new name (index) */
57 int o; /* old name (index) */
60 /* for synonym warning reports: list of synonyms found */
62 /* for synonym warning reports: current rc file */
63 char* CurRCFile = NULL;
64 /* for synonym warning reports: current rc line */
67 /* prototypes for checking for special vars */
68 static int check_dsn_return (const char*);
69 static int check_dsn_notify (const char*);
73 int (*check) (const char*);
75 { "dsn_notify", check_dsn_notify },
76 { "dsn_return", check_dsn_return },
77 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
78 { "smtp_use_tls", mutt_libesmtp_check_usetls },
84 /* for synonym warning reports: adds synonym to end of list */
85 static void syn_add (int n, int o) {
86 syn_t* tmp = mem_malloc (sizeof (syn_t));
87 tmp->f = str_dup (CurRCFile);
91 list_push_back (&Synonyms, tmp);
94 /* for synonym warning reports: free single item (for list_del()) */
95 static void syn_del (void** p) {
96 mem_free(&(*(syn_t**) p)->f);
100 void toggle_quadoption (int opt)
103 int b = (opt % 4) * 2;
105 QuadOptions[n] ^= (1 << b);
108 void set_quadoption (int opt, int flag)
111 int b = (opt % 4) * 2;
113 QuadOptions[n] &= ~(0x3 << b);
114 QuadOptions[n] |= (flag & 0x3) << b;
117 int quadoption (int opt)
120 int b = (opt % 4) * 2;
122 return (QuadOptions[n] >> b) & 0x3;
125 int query_quadoption (int opt, const char *prompt)
127 int v = quadoption (opt);
135 v = mutt_yesorno (prompt, (v == M_ASKYES));
136 CLEARLINE (LINES - 1);
143 /* given the variable ``s'', return the index into the rc_vars array which
144 matches, or -1 if the variable is not found. */
145 int mutt_option_index (char *s)
149 for (i = 0; MuttVars[i].option; i++)
150 if (str_cmp (s, MuttVars[i].option) == 0) {
151 if (MuttVars[i].type == DT_SYN)
152 syn_add (mutt_option_index ((char *) MuttVars[i].data), i);
153 return (MuttVars[i].type ==
154 DT_SYN ? mutt_option_index ((char *) MuttVars[i].data) : i);
159 static void add_to_list (LIST ** list, const char *str)
161 LIST *t, *last = NULL;
163 /* don't add a NULL or empty string to the list */
164 if (!str || *str == '\0')
167 /* check to make sure the item is not already on this list */
168 for (last = *list; last; last = last->next) {
169 if (ascii_strcasecmp (str, last->data) == 0) {
170 /* already on the list, so just ignore it */
178 if (!*list || last) {
179 t = (LIST *) mem_calloc (1, sizeof (LIST));
180 t->data = str_dup (str);
190 static int add_to_rx_list (list2_t** list, const char *s, int flags,
199 if (!(rx = rx_compile (s, flags))) {
200 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
204 i = rx_lookup ((*list), rx->pattern);
208 list_push_back (list, rx);
212 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
213 const char *templ, BUFFER * err)
215 SPAM_LIST *t = NULL, *last = NULL;
220 if (!pat || !*pat || !templ)
223 if (!(rx = rx_compile (pat, REG_ICASE))) {
224 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
228 /* check to make sure the item is not already on this list */
229 for (last = *list; last; last = last->next) {
230 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
231 /* Already on the list. Formerly we just skipped this case, but
232 * now we're supporting removals, which means we're supporting
233 * re-adds conceptually. So we probably want this to imply a
234 * removal, then do an add. We can achieve the removal by freeing
235 * the template, and leaving t pointed at the current item.
238 mem_free(t->template);
245 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
246 * update. Otherwise we want to make a new one to link at the list's end.
249 t = mutt_new_spam_list ();
257 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
258 t->template = str_dup (templ);
260 /* Find highest match number in template string */
262 for (p = templ; *p;) {
267 while (*p && isdigit ((int) *p))
273 t->nmatch++; /* match 0 is always the whole expr */
278 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
280 SPAM_LIST *spam, *prev;
283 /* Being first is a special case. */
287 if (spam->rx && !str_cmp (spam->rx->pattern, pat)) {
290 mem_free(&spam->template);
296 for (spam = prev->next; spam;) {
297 if (!str_cmp (spam->rx->pattern, pat)) {
298 prev->next = spam->next;
300 mem_free(spam->template);
313 static void remove_from_list (LIST ** l, const char *str)
315 LIST *p, *last = NULL;
317 if (str_cmp ("*", str) == 0)
318 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
323 if (ascii_strcasecmp (str, p->data) == 0) {
326 last->next = p->next;
339 static int remove_from_rx_list (list2_t** l, const char *str)
343 if (str_cmp ("*", str) == 0) {
344 list_del (l, (list_del_t*) rx_free);
348 i = rx_lookup ((*l), str);
350 rx_t* r = list_pop_idx ((*l), i);
358 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
364 memset (&token, 0, sizeof (token));
365 mutt_extract_token (tmp, s, 0);
367 /* is the item defined as a variable or a function? */
368 if (!(res = (mutt_option_index (tmp->data) != -1)))
369 for (i = 0; !res && i < MENU_MAX; i++) {
370 struct binding_t *b = km_get_table (Menus[i].value);
375 for (j = 0; b[j].name; j++)
376 if (!ascii_strncasecmp (tmp->data, b[j].name, str_len (tmp->data))
377 && (str_len (b[j].name) == str_len (tmp->data))) {
382 /* check for feature_* */
387 j = str_len (tmp->data);
388 /* need at least input of 'feature_X' */
392 while (Features[i].name) {
393 if (str_len (Features[i].name) == j &&
394 ascii_strncasecmp (Features[i].name, p, j)) {
405 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
407 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
410 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
412 if ((data && res) || (!data && !res)) {
413 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
414 mutt_error ("Error: %s", err->data);
415 mem_free (&token.data);
418 mem_free (&token.data);
423 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
427 mutt_extract_token (buf, s, 0);
429 /* don't add "*" to the unignore list */
430 if (strcmp (buf->data, "*"))
431 add_to_list (&UnIgnore, buf->data);
433 remove_from_list (&Ignore, buf->data);
435 while (MoreArgs (s));
440 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
444 mutt_extract_token (buf, s, 0);
445 remove_from_list (&UnIgnore, buf->data);
446 add_to_list (&Ignore, buf->data);
448 while (MoreArgs (s));
453 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
457 mutt_extract_token (buf, s, 0);
458 add_to_list ((LIST **) data, buf->data);
460 while (MoreArgs (s));
465 static void _alternates_clean (void)
469 if (Context && Context->msgcount) {
470 for (i = 0; i < Context->msgcount; i++)
471 Context->hdrs[i]->recip_valid = 0;
475 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
478 _alternates_clean ();
480 mutt_extract_token (buf, s, 0);
481 remove_from_rx_list (&UnAlternates, buf->data);
483 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
486 while (MoreArgs (s));
491 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
494 _alternates_clean ();
496 mutt_extract_token (buf, s, 0);
497 remove_from_rx_list (&Alternates, buf->data);
499 if (str_cmp (buf->data, "*") &&
500 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
504 while (MoreArgs (s));
509 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
514 memset (&templ, 0, sizeof (templ));
516 /* Insist on at least one parameter */
519 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
521 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
525 /* Extract the first token, a regexp */
526 mutt_extract_token (buf, s, 0);
528 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
529 if (data == M_SPAM) {
530 /* If there's a second parameter, it's a template for the spam tag. */
532 mutt_extract_token (&templ, s, 0);
534 /* Add to the spam list. */
535 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
536 mem_free (&templ.data);
539 mem_free (&templ.data);
542 /* If not, try to remove from the nospam list. */
544 remove_from_rx_list (&NoSpamList, buf->data);
550 /* M_NOSPAM is for nospam commands. */
551 else if (data == M_NOSPAM) {
552 /* nospam only ever has one parameter. */
554 /* "*" is a special case. */
555 if (!str_cmp (buf->data, "*")) {
556 mutt_free_spam_list (&SpamList);
557 list_del (&NoSpamList, (list_del_t*) rx_free);
561 /* If it's on the spam list, just remove it. */
562 if (remove_from_spam_list (&SpamList, buf->data) != 0)
565 /* Otherwise, add it to the nospam list. */
566 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
572 /* This should not happen. */
573 strfcpy (err->data, "This is no good at all.", err->dsize);
577 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
581 mutt_extract_token (buf, s, 0);
583 * Check for deletion of entire list
585 if (str_cmp (buf->data, "*") == 0) {
586 mutt_free_list ((LIST **) data);
589 remove_from_list ((LIST **) data, buf->data);
591 while (MoreArgs (s));
596 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
600 mutt_extract_token (buf, s, 0);
601 remove_from_rx_list (&UnMailLists, buf->data);
603 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
606 while (MoreArgs (s));
611 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
615 mutt_extract_token (buf, s, 0);
616 remove_from_rx_list (&SubscribedLists, buf->data);
617 remove_from_rx_list (&MailLists, buf->data);
619 if (str_cmp (buf->data, "*") &&
620 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
623 while (MoreArgs (s));
628 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
632 mutt_extract_token (buf, s, 0);
633 remove_from_rx_list (&UnMailLists, buf->data);
634 remove_from_rx_list (&UnSubscribedLists, buf->data);
636 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
638 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
641 while (MoreArgs (s));
646 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
650 mutt_extract_token (buf, s, 0);
651 remove_from_rx_list (&SubscribedLists, buf->data);
653 if (str_cmp (buf->data, "*") &&
654 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
657 while (MoreArgs (s));
662 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
665 ALIAS *tmp, *last = NULL;
668 mutt_extract_token (buf, s, 0);
670 if (str_cmp ("*", buf->data) == 0) {
671 if (CurrentMenu == MENU_ALIAS) {
672 for (tmp = Aliases; tmp; tmp = tmp->next)
674 set_option (OPTFORCEREDRAWINDEX);
677 mutt_free_alias (&Aliases);
681 for (tmp = Aliases; tmp; tmp = tmp->next) {
682 if (str_casecmp (buf->data, tmp->name) == 0) {
683 if (CurrentMenu == MENU_ALIAS) {
685 set_option (OPTFORCEREDRAWINDEX);
690 last->next = tmp->next;
694 mutt_free_alias (&tmp);
700 while (MoreArgs (s));
704 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
707 ALIAS *tmp = Aliases;
712 strfcpy (err->data, _("alias: no address"), err->dsize);
716 mutt_extract_token (buf, s, 0);
718 debug_print (2, ("first token is '%s'.\n", buf->data));
720 /* check to see if an alias with this name already exists */
721 for (; tmp; tmp = tmp->next) {
722 if (!str_casecmp (tmp->name, buf->data))
728 /* create a new alias */
729 tmp = (ALIAS *) mem_calloc (1, sizeof (ALIAS));
731 tmp->name = str_dup (buf->data);
732 /* give the main addressbook code a chance */
733 if (CurrentMenu == MENU_ALIAS)
734 set_option (OPTMENUCALLER);
737 /* override the previous value */
738 rfc822_free_address (&tmp->addr);
739 if (CurrentMenu == MENU_ALIAS)
740 set_option (OPTFORCEREDRAWINDEX);
743 mutt_extract_token (buf, s,
744 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
745 debug_print (2, ("second token is '%s'.\n", buf->data));
746 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
751 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
752 snprintf (err->data, err->dsize,
753 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
757 if (DebugLevel >= 2) {
760 for (a = tmp->addr; a; a = a->next) {
762 debug_print (2, ("%s\n", a->mailbox));
764 debug_print (2, ("group %s\n", a->mailbox));
772 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
775 LIST *tmp = UserHeader;
780 mutt_extract_token (buf, s, 0);
781 if (str_cmp ("*", buf->data) == 0)
782 mutt_free_list (&UserHeader);
787 l = str_len (buf->data);
788 if (buf->data[l - 1] == ':')
792 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
793 && tmp->data[l] == ':') {
796 last->next = tmp->next;
798 UserHeader = tmp->next;
801 mutt_free_list (&ptr);
810 while (MoreArgs (s));
814 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
821 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
822 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
823 strfcpy (err->data, _("invalid header field"), err->dsize);
826 keylen = p - buf->data + 1;
829 for (tmp = UserHeader;; tmp = tmp->next) {
830 /* see if there is already a field by this name */
831 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
832 /* replace the old value */
833 mem_free (&tmp->data);
834 tmp->data = buf->data;
835 memset (buf, 0, sizeof (BUFFER));
841 tmp->next = mutt_new_list ();
845 tmp = mutt_new_list ();
848 tmp->data = buf->data;
849 memset (buf, 0, sizeof (BUFFER));
854 parse_sort (short *val, const char *s, const struct mapping_t *map,
859 if (str_ncmp ("reverse-", s, 8) == 0) {
861 flags = SORT_REVERSE;
864 if (str_ncmp ("last-", s, 5) == 0) {
869 if ((i = mutt_getvaluebyname (s, map)) == -1) {
870 snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s);
879 static void mutt_set_default (struct option_t *p)
881 switch (p->type & DT_MASK) {
883 if (!p->init && *((char **) p->data))
884 p->init = (unsigned long) str_dup (*((char **) p->data));
887 if (!p->init && *((char **) p->data)) {
888 char *cp = str_dup (*((char **) p->data));
890 /* mutt_pretty_mailbox (cp); */
891 p->init = (unsigned long) cp;
895 if (!p->init && *((ADDRESS **) p->data)) {
896 char tmp[HUGE_STRING];
899 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data), 0);
900 p->init = (unsigned long) str_dup (tmp);
905 rx_t* pp = (rx_t*) p->data;
907 if (!p->init && pp->pattern)
908 p->init = (unsigned long) str_dup (pp->pattern);
914 static void mutt_restore_default (struct option_t *p)
916 switch (p->type & DT_MASK) {
919 str_replace ((char **) p->data, (char *) p->init);
923 char path[_POSIX_PATH_MAX];
925 strfcpy (path, (char *) p->init, sizeof (path));
926 mutt_expand_path (path, sizeof (path));
927 str_replace ((char **) p->data, path);
932 rfc822_free_address ((ADDRESS **) p->data);
933 *((ADDRESS **) p->data) = rfc822_parse_adrlist (NULL, (char *) p->init);
938 set_option (p->data);
940 unset_option (p->data);
943 set_quadoption (p->data, p->init);
948 *((short *) p->data) = p->init;
952 rx_t *pp = (rx_t *) p->data;
955 mem_free (&pp->pattern);
962 char *s = (char *) p->init;
964 pp->rx = mem_calloc (1, sizeof (regex_t));
965 if (str_cmp (p->option, "mask") != 0)
966 flags |= mutt_which_case ((const char *) p->init);
967 if (str_cmp (p->option, "mask") == 0 && *s == '!') {
971 if (REGCOMP (pp->rx, s, flags) != 0) {
973 _("mutt_restore_default(%s): error in regexp: %s\n"),
974 p->option, pp->pattern);
975 mem_free (&pp->pattern);
980 str_replace (&pp->pattern, (char *) p->init);
986 if (p->flags & R_INDEX)
987 set_option (OPTFORCEREDRAWINDEX);
988 if (p->flags & R_PAGER)
989 set_option (OPTFORCEREDRAWPAGER);
990 if (p->flags & R_RESORT_SUB)
991 set_option (OPTSORTSUBTHREADS);
992 if (p->flags & R_RESORT)
993 set_option (OPTNEEDRESORT);
994 if (p->flags & R_RESORT_INIT)
995 set_option (OPTRESORTINIT);
996 if (p->flags & R_TREE)
997 set_option (OPTREDRAWTREE);
1000 /* check whether value for $dsn_return would be valid */
1001 static int check_dsn_return (const char* val) {
1002 if (val && *val && str_ncmp (val, "hdrs", 4) != 0 &&
1003 str_ncmp (val, "full", 4) != 0)
1008 /* check whether value for $dsn_notify would be valid */
1009 static int check_dsn_notify (const char* val) {
1010 list2_t* list = NULL;
1015 list = list_from_str (val, ",");
1016 if (list_empty (list))
1019 for (i = 0; i < list->length; i++)
1020 if (str_ncmp (list->data[i], "never", 5) != 0 &&
1021 str_ncmp (list->data[i], "failure", 7) != 0 &&
1022 str_ncmp (list->data[i], "delay", 5) != 0 &&
1023 str_ncmp (list->data[i], "success", 7) != 0) {
1027 list_del (&list, (list_del_t*) _mem_free);
1031 static int check_special (const char* name, const char* val) {
1034 for (i = 0; SpecialVars[i].name; i++) {
1035 if (str_cmp (SpecialVars[i].name, name) == 0)
1036 return (SpecialVars[i].check (val));
1041 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1044 int idx, query, unset, inv, reset, r = 0;
1045 char *p, scratch[_POSIX_PATH_MAX];
1047 while (MoreArgs (s)) {
1048 /* reset state variables */
1050 unset = data & M_SET_UNSET;
1051 inv = data & M_SET_INV;
1052 reset = data & M_SET_RESET;
1054 if (*s->dptr == '?') {
1058 else if (str_ncmp ("no", s->dptr, 2) == 0) {
1062 else if (str_ncmp ("inv", s->dptr, 3) == 0) {
1066 else if (*s->dptr == '&') {
1071 /* get the variable name */
1072 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1074 if ((idx = mutt_option_index (tmp->data)) == -1 &&
1075 !(reset && !str_cmp ("all", tmp->data))) {
1076 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1082 if (query || unset || inv) {
1083 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1087 if (s && *s->dptr == '=') {
1088 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1092 if (!str_cmp ("all", tmp->data)) {
1093 for (idx = 0; MuttVars[idx].option; idx++)
1094 mutt_restore_default (&MuttVars[idx]);
1098 mutt_restore_default (&MuttVars[idx]);
1100 else if (DTYPE (MuttVars[idx].type) == DT_BOOL) {
1101 if (s && *s->dptr == '=') {
1102 if (unset || inv || query) {
1103 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1108 mutt_extract_token (tmp, s, 0);
1109 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1111 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1114 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1120 snprintf (err->data, err->dsize, option (MuttVars[idx].data)
1121 ? _("%s is set") : _("%s is unset"), tmp->data);
1126 unset_option (MuttVars[idx].data);
1128 toggle_option (MuttVars[idx].data);
1130 set_option (MuttVars[idx].data);
1132 else if (DTYPE (MuttVars[idx].type) == DT_STR ||
1133 DTYPE (MuttVars[idx].type) == DT_PATH ||
1134 DTYPE (MuttVars[idx].type) == DT_ADDR) {
1136 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1137 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1139 mem_free ((void *) MuttVars[idx].data);
1141 else if (query || *s->dptr != '=') {
1145 if (DTYPE (MuttVars[idx].type) == DT_ADDR) {
1147 rfc822_write_address (_tmp, sizeof (_tmp),
1148 *((ADDRESS **) MuttVars[idx].data), 0);
1152 val = *((char **) MuttVars[idx].data);
1154 /* user requested the value of this variable */
1155 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1162 /* copy the value of the string */
1163 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1164 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1166 mem_free ((void *) MuttVars[idx].data);
1168 mutt_extract_token (tmp, s, 0);
1169 if (DTYPE (MuttVars[idx].type) == DT_PATH) {
1170 strfcpy (scratch, tmp->data, sizeof (scratch));
1171 mutt_expand_path (scratch, sizeof (scratch));
1172 *((char **) MuttVars[idx].data) = str_dup (scratch);
1174 else if (DTYPE (MuttVars[idx].type) == DT_STR) {
1175 /* see if the value may only be a certain value... */
1176 if (check_special (MuttVars[idx].option, tmp->data)) {
1177 *((char **) MuttVars[idx].data) = str_dup (tmp->data);
1178 if (str_cmp (MuttVars[idx].option, "charset") == 0)
1179 mutt_set_charset (Charset);
1181 /* ... and abort if it fails */
1182 snprintf (err->data, err->dsize, "'%s' is invalid for $%s",
1183 tmp->data, MuttVars[idx].option);
1188 *((ADDRESS **) MuttVars[idx].data) =
1189 rfc822_parse_adrlist (NULL, tmp->data);
1193 else if (DTYPE (MuttVars[idx].type) == DT_RX) {
1194 rx_t *ptr = (rx_t *) MuttVars[idx].data;
1198 if (query || *s->dptr != '=') {
1199 /* user requested the value of this variable */
1200 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1201 NONULL (ptr->pattern));
1205 if (option (OPTATTACHMSG)
1206 && !str_cmp (MuttVars[idx].option, "reply_regexp")) {
1207 snprintf (err->data, err->dsize,
1208 "Operation not permitted when in attach-message mode.");
1215 /* copy the value of the string */
1216 mutt_extract_token (tmp, s, 0);
1218 if (!ptr->pattern || str_cmp (ptr->pattern, tmp->data) != 0) {
1221 /* $mask is case-sensitive */
1222 if (str_cmp (MuttVars[idx].option, "mask") != 0)
1223 flags |= mutt_which_case (tmp->data);
1226 if (str_cmp (MuttVars[idx].option, "mask") == 0) {
1233 rx = (regex_t *) mem_malloc (sizeof (regex_t));
1234 if ((e = REGCOMP (rx, p, flags)) != 0) {
1235 regerror (e, rx, err->data, err->dsize);
1241 /* get here only if everything went smootly */
1243 mem_free (&ptr->pattern);
1244 regfree ((regex_t *) ptr->rx);
1245 mem_free (&ptr->rx);
1248 ptr->pattern = str_dup (tmp->data);
1252 /* $reply_regexp and $alterantes require special treatment */
1254 if (Context && Context->msgcount &&
1255 str_cmp (MuttVars[idx].option, "reply_regexp") == 0) {
1256 regmatch_t pmatch[1];
1259 #define CUR_ENV Context->hdrs[i]->env
1260 for (i = 0; i < Context->msgcount; i++) {
1261 if (CUR_ENV && CUR_ENV->subject) {
1262 CUR_ENV->real_subj = (regexec (ReplyRegexp.rx,
1263 CUR_ENV->subject, 1, pmatch,
1265 subject : CUR_ENV->subject + pmatch[0].rm_eo;
1272 else if (DTYPE (MuttVars[idx].type) == DT_MAGIC) {
1273 if (query || *s->dptr != '=') {
1274 switch (DefaultMagic) {
1291 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p);
1297 /* copy the value of the string */
1298 mutt_extract_token (tmp, s, 0);
1299 if (mx_set_magic (tmp->data)) {
1300 snprintf (err->data, err->dsize, _("%s: invalid mailbox type"),
1306 else if (DTYPE (MuttVars[idx].type) == DT_NUM) {
1307 short *ptr = (short *) MuttVars[idx].data;
1311 if (query || *s->dptr != '=') {
1312 /* user requested the value of this variable */
1313 snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, *ptr);
1319 mutt_extract_token (tmp, s, 0);
1320 val = strtol (tmp->data, &t, 0);
1322 if (!*tmp->data || *t || (short) val != val) {
1323 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1330 /* these ones need a sanity check */
1331 if (str_cmp (MuttVars[idx].option, "history") == 0) {
1334 mutt_init_history ();
1336 else if (str_cmp (MuttVars[idx].option, "pager_index_lines") == 0) {
1341 else if (DTYPE (MuttVars[idx].type) == DT_QUAD) {
1343 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1345 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
1346 vals[quadoption (MuttVars[idx].data)]);
1350 if (*s->dptr == '=') {
1352 mutt_extract_token (tmp, s, 0);
1353 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1354 set_quadoption (MuttVars[idx].data, M_YES);
1355 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1356 set_quadoption (MuttVars[idx].data, M_NO);
1357 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1358 set_quadoption (MuttVars[idx].data, M_ASKYES);
1359 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1360 set_quadoption (MuttVars[idx].data, M_ASKNO);
1362 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1369 toggle_quadoption (MuttVars[idx].data);
1371 set_quadoption (MuttVars[idx].data, M_NO);
1373 set_quadoption (MuttVars[idx].data, M_YES);
1376 else if (DTYPE (MuttVars[idx].type) == DT_SORT) {
1377 const struct mapping_t *map = NULL;
1379 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) {
1381 map = SortAliasMethods;
1383 case DT_SORT_BROWSER:
1384 map = SortBrowserMethods;
1387 if ((WithCrypto & APPLICATION_PGP))
1388 map = SortKeyMethods;
1391 map = SortAuxMethods;
1399 snprintf (err->data, err->dsize, _("%s: Unknown type."),
1400 MuttVars[idx].option);
1405 if (query || *s->dptr != '=') {
1407 mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK,
1410 snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
1411 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ?
1413 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" :
1418 mutt_extract_token (tmp, s, 0);
1420 if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) ==
1427 snprintf (err->data, err->dsize, _("%s: unknown type"),
1428 MuttVars[idx].option);
1433 if (MuttVars[idx].flags & R_INDEX)
1434 set_option (OPTFORCEREDRAWINDEX);
1435 if (MuttVars[idx].flags & R_PAGER)
1436 set_option (OPTFORCEREDRAWPAGER);
1437 if (MuttVars[idx].flags & R_RESORT_SUB)
1438 set_option (OPTSORTSUBTHREADS);
1439 if (MuttVars[idx].flags & R_RESORT)
1440 set_option (OPTNEEDRESORT);
1441 if (MuttVars[idx].flags & R_RESORT_INIT)
1442 set_option (OPTRESORTINIT);
1443 if (MuttVars[idx].flags & R_TREE)
1444 set_option (OPTREDRAWTREE);
1451 /* reads the specified initialization file. returns -1 if errors were found
1452 so that we can pause to let the user know... */
1453 static int source_rc (const char *rcfile, BUFFER * err)
1456 int line = 0, rc = 0, conv = 0;
1458 char *linebuf = NULL;
1459 char *currentline = NULL;
1463 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
1464 str_replace (&CurRCFile, rcfile);
1467 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1468 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1472 memset (&token, 0, sizeof (token));
1473 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
1475 conv = ConfigCharset && (*ConfigCharset) && Charset;
1477 currentline = str_dup (linebuf);
1480 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1483 currentline = linebuf;
1485 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1486 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1487 if (--rc < -MAXERRS) {
1489 mem_free (¤tline);
1498 mem_free (¤tline);
1500 mem_free (&token.data);
1501 mem_free (&linebuf);
1504 mutt_wait_filter (pid);
1506 /* the muttrc source keyword */
1507 snprintf (err->data, err->dsize,
1508 rc >= -MAXERRS ? _("source: errors in %s")
1509 : _("source: reading aborted due too many errors in %s"),
1518 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
1521 char path[_POSIX_PATH_MAX];
1525 if (mutt_extract_token (tmp, s, 0) != 0) {
1526 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1530 strfcpy (path, tmp->data, sizeof (path));
1531 mutt_expand_path (path, sizeof (path));
1533 rc += source_rc (path, err);
1535 while (MoreArgs (s));
1537 return ((rc < 0) ? -1 : 0);
1540 /* line command to execute
1542 token scratch buffer to be used by parser. caller should free
1543 token->data when finished. the reason for this variable is
1544 to avoid having to allocate and deallocate a lot of memory
1545 if we are parsing many lines. the caller can pass in the
1546 memory to use, which avoids having to create new space for
1547 every call to this function.
1549 err where to write error messages */
1550 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1555 memset (&expn, 0, sizeof (expn));
1556 expn.data = expn.dptr = line;
1557 expn.dsize = str_len (line);
1562 while (*expn.dptr) {
1563 if (*expn.dptr == '#')
1564 break; /* rest of line is a comment */
1565 if (*expn.dptr == ';') {
1569 mutt_extract_token (token, &expn, 0);
1570 for (i = 0; Commands[i].name; i++) {
1571 if (!str_cmp (token->data, Commands[i].name)) {
1572 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1577 if (!Commands[i].name) {
1578 snprintf (err->data, err->dsize, _("%s: unknown command"),
1579 NONULL (token->data));
1586 mem_free (&expn.data);
1591 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1592 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1593 /* initial string that starts completion. No telling how much crap
1594 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1595 char User_typed[LONG_STRING] = { 0 };
1597 int Num_matched = 0; /* Number of matches for completion */
1598 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1599 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1601 /* helper function for completion. Changes the dest buffer if
1602 necessary/possible to aid completion.
1603 dest == completion result gets here.
1604 src == candidate for completion.
1605 try == user entered data for completion.
1606 len == length of dest buffer.
1608 static void candidate (char *dest, char *try, char *src, int len)
1612 if (strstr (src, try) == src) {
1613 Matches[Num_matched++] = src;
1615 strfcpy (dest, src, len);
1617 for (l = 0; src[l] && src[l] == dest[l]; l++);
1623 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1627 int spaces; /* keep track of the number of leading spaces on the line */
1630 spaces = buffer - pt;
1632 pt = buffer + pos - spaces;
1633 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1636 if (pt == buffer) { /* complete cmd */
1637 /* first TAB. Collect all the matches */
1640 strfcpy (User_typed, pt, sizeof (User_typed));
1641 memset (Matches, 0, sizeof (Matches));
1642 memset (Completed, 0, sizeof (Completed));
1643 for (num = 0; Commands[num].name; num++)
1644 candidate (Completed, User_typed, Commands[num].name,
1645 sizeof (Completed));
1646 Matches[Num_matched++] = User_typed;
1648 /* All matches are stored. Longest non-ambiguous string is ""
1649 * i.e. dont change 'buffer'. Fake successful return this time */
1650 if (User_typed[0] == 0)
1654 if (Completed[0] == 0 && User_typed[0])
1657 /* Num_matched will _always_ be atleast 1 since the initial
1658 * user-typed string is always stored */
1659 if (numtabs == 1 && Num_matched == 2)
1660 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1661 else if (numtabs > 1 && Num_matched > 2)
1662 /* cycle thru all the matches */
1663 snprintf (Completed, sizeof (Completed), "%s",
1664 Matches[(numtabs - 2) % Num_matched]);
1666 /* return the completed command */
1667 strncpy (buffer, Completed, len - spaces);
1669 else if (!str_ncmp (buffer, "set", 3)
1670 || !str_ncmp (buffer, "unset", 5)
1671 || !str_ncmp (buffer, "reset", 5)
1672 || !str_ncmp (buffer, "toggle", 6)) { /* complete variables */
1673 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1676 /* loop through all the possible prefixes (no, inv, ...) */
1677 if (!str_ncmp (buffer, "set", 3)) {
1678 for (num = 0; prefixes[num]; num++) {
1679 if (!str_ncmp (pt, prefixes[num], str_len (prefixes[num]))) {
1680 pt += str_len (prefixes[num]);
1686 /* first TAB. Collect all the matches */
1689 strfcpy (User_typed, pt, sizeof (User_typed));
1690 memset (Matches, 0, sizeof (Matches));
1691 memset (Completed, 0, sizeof (Completed));
1692 for (num = 0; MuttVars[num].option; num++)
1693 candidate (Completed, User_typed, MuttVars[num].option,
1694 sizeof (Completed));
1695 Matches[Num_matched++] = User_typed;
1697 /* All matches are stored. Longest non-ambiguous string is ""
1698 * i.e. dont change 'buffer'. Fake successful return this time */
1699 if (User_typed[0] == 0)
1703 if (Completed[0] == 0 && User_typed[0])
1706 /* Num_matched will _always_ be atleast 1 since the initial
1707 * user-typed string is always stored */
1708 if (numtabs == 1 && Num_matched == 2)
1709 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1710 else if (numtabs > 1 && Num_matched > 2)
1711 /* cycle thru all the matches */
1712 snprintf (Completed, sizeof (Completed), "%s",
1713 Matches[(numtabs - 2) % Num_matched]);
1715 strncpy (pt, Completed, buffer + len - pt - spaces);
1717 else if (!str_ncmp (buffer, "exec", 4)) {
1718 struct binding_t *menu = km_get_table (CurrentMenu);
1720 if (!menu && CurrentMenu != MENU_PAGER)
1724 /* first TAB. Collect all the matches */
1727 strfcpy (User_typed, pt, sizeof (User_typed));
1728 memset (Matches, 0, sizeof (Matches));
1729 memset (Completed, 0, sizeof (Completed));
1730 for (num = 0; menu[num].name; num++)
1731 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1732 /* try the generic menu */
1733 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1735 for (num = 0; menu[num].name; num++)
1736 candidate (Completed, User_typed, menu[num].name,
1737 sizeof (Completed));
1739 Matches[Num_matched++] = User_typed;
1741 /* All matches are stored. Longest non-ambiguous string is ""
1742 * i.e. dont change 'buffer'. Fake successful return this time */
1743 if (User_typed[0] == 0)
1747 if (Completed[0] == 0 && User_typed[0])
1750 /* Num_matched will _always_ be atleast 1 since the initial
1751 * user-typed string is always stored */
1752 if (numtabs == 1 && Num_matched == 2)
1753 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1754 else if (numtabs > 1 && Num_matched > 2)
1755 /* cycle thru all the matches */
1756 snprintf (Completed, sizeof (Completed), "%s",
1757 Matches[(numtabs - 2) % Num_matched]);
1759 strncpy (pt, Completed, buffer + len - pt - spaces);
1767 int mutt_var_value_complete (char *buffer, size_t len, int pos)
1769 char var[STRING], *pt = buffer;
1776 spaces = buffer - pt;
1778 pt = buffer + pos - spaces;
1779 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1781 pt++; /* move past the space */
1782 if (*pt == '=') /* abort if no var before the '=' */
1785 if (str_ncmp (buffer, "set", 3) == 0) {
1788 strfcpy (var, pt, sizeof (var));
1789 /* ignore the trailing '=' when comparing */
1790 var[str_len (var) - 1] = 0;
1791 if ((idx = mutt_option_index (var)) == -1)
1792 return 0; /* no such variable. */
1794 char tmp[LONG_STRING], tmp2[LONG_STRING];
1796 size_t dlen = buffer + len - pt - spaces;
1797 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1801 if ((DTYPE (MuttVars[idx].type) == DT_STR) ||
1802 (DTYPE (MuttVars[idx].type) == DT_PATH) ||
1803 (DTYPE (MuttVars[idx].type) == DT_RX)) {
1804 strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data)), sizeof (tmp));
1805 if (DTYPE (MuttVars[idx].type) == DT_PATH)
1806 mutt_pretty_mailbox (tmp);
1808 else if (DTYPE (MuttVars[idx].type) == DT_ADDR) {
1809 rfc822_write_address (tmp, sizeof (tmp),
1810 *((ADDRESS **) MuttVars[idx].data), 0);
1812 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
1813 strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp));
1814 else if (DTYPE (MuttVars[idx].type) == DT_NUM)
1815 snprintf (tmp, sizeof (tmp), "%d", (*((short *) MuttVars[idx].data)));
1816 else if (DTYPE (MuttVars[idx].type) == DT_SORT) {
1817 const struct mapping_t *map;
1820 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) {
1822 map = SortAliasMethods;
1824 case DT_SORT_BROWSER:
1825 map = SortBrowserMethods;
1828 if ((WithCrypto & APPLICATION_PGP))
1829 map = SortKeyMethods;
1838 mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK,
1840 snprintf (tmp, sizeof (tmp), "%s%s%s",
1841 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ?
1843 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" :
1846 else if (DTYPE (MuttVars[idx].type) == DT_MAGIC) {
1848 switch (DefaultMagic) {
1864 strfcpy (tmp, p, sizeof (tmp));
1866 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
1867 strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no",
1872 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
1873 if (*s == '\\' || *s == '"')
1879 strfcpy (tmp, pt, sizeof (tmp));
1880 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
1888 /* Implement the -Q command line flag */
1889 int mutt_query_variables (LIST * queries)
1893 char errbuff[STRING];
1894 char command[STRING];
1898 memset (&err, 0, sizeof (err));
1899 memset (&token, 0, sizeof (token));
1902 err.dsize = sizeof (errbuff);
1904 for (p = queries; p; p = p->next) {
1905 snprintf (command, sizeof (command), "set ?%s\n", p->data);
1906 if (mutt_parse_rc_line (command, &token, &err) == -1) {
1907 fprintf (stderr, "%s\n", err.data);
1908 mem_free (&token.data);
1911 printf ("%s\n", err.data);
1914 mem_free (&token.data);
1918 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
1922 for (i = 0; map[i].name; i++)
1923 if (map[i].value == val)
1924 return (map[i].name);
1928 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
1932 for (i = 0; map[i].name; i++)
1933 if (ascii_strcasecmp (map[i].name, name) == 0)
1934 return (map[i].value);
1938 static int mutt_execute_commands (LIST * p)
1941 char errstr[SHORT_STRING];
1943 memset (&err, 0, sizeof (err));
1945 err.dsize = sizeof (errstr);
1946 memset (&token, 0, sizeof (token));
1947 for (; p; p = p->next) {
1948 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
1949 fprintf (stderr, _("Error in command line: %s\n"), err.data);
1950 mem_free (&token.data);
1954 mem_free (&token.data);
1958 void mutt_init (int skip_sys_rc, LIST * commands)
1961 struct utsname utsname;
1962 char *p, buffer[STRING], error[STRING];
1963 int i, default_rc = 0, need_pause = 0;
1966 memset (&err, 0, sizeof (err));
1968 err.dsize = sizeof (error);
1971 * XXX - use something even more difficult to predict?
1973 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
1974 "\033]9;%ld\a", (long) time (NULL));
1976 /* on one of the systems I use, getcwd() does not return the same prefix
1977 as is listed in the passwd file */
1978 if ((p = getenv ("HOME")))
1979 Homedir = str_dup (p);
1981 /* Get some information about the user */
1982 if ((pw = getpwuid (getuid ()))) {
1985 Username = str_dup (pw->pw_name);
1987 Homedir = str_dup (pw->pw_dir);
1989 Realname = str_dup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
1990 Shell = str_dup (pw->pw_shell);
1995 fputs (_("unable to determine home directory"), stderr);
1998 if ((p = getenv ("USER")))
1999 Username = str_dup (p);
2002 fputs (_("unable to determine username"), stderr);
2005 Shell = str_dup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2008 debug_start(Homedir);
2010 /* And about the host... */
2012 /* some systems report the FQDN instead of just the hostname */
2013 if ((p = strchr (utsname.nodename, '.'))) {
2014 Hostname = str_substrdup (utsname.nodename, p);
2016 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2019 Hostname = str_dup (utsname.nodename);
2022 #define DOMAIN buffer
2023 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2024 Fqdn = str_dup ("@");
2027 if (*DOMAIN != '@') {
2028 Fqdn = mem_malloc (str_len (DOMAIN) + str_len (Hostname) + 2);
2029 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2032 Fqdn = str_dup (NONULL (Hostname));
2039 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2041 fgets (buffer, sizeof (buffer), f);
2042 p = (char*) &buffer;
2045 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r')
2049 NewsServer = str_dup (p);
2053 if ((p = getenv ("NNTPSERVER")))
2054 NewsServer = str_dup (p);
2057 if ((p = getenv ("MAIL")))
2058 Spoolfile = str_dup (p);
2059 else if ((p = getenv ("MAILDIR")))
2060 Spoolfile = str_dup (p);
2063 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2065 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
2067 Spoolfile = str_dup (buffer);
2070 if ((p = getenv ("MAILCAPS")))
2071 MailcapPath = str_dup (p);
2073 /* Default search path from RFC1524 */
2075 str_dup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2076 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2079 Tempdir = str_dup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2081 p = getenv ("VISUAL");
2083 p = getenv ("EDITOR");
2087 Editor = str_dup (p);
2088 Visual = str_dup (p);
2090 if ((p = getenv ("REPLYTO")) != NULL) {
2093 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2095 memset (&buf, 0, sizeof (buf));
2096 buf.data = buf.dptr = buffer;
2097 buf.dsize = str_len (buffer);
2099 memset (&token, 0, sizeof (token));
2100 parse_my_hdr (&token, &buf, 0, &err);
2101 mem_free (&token.data);
2104 if ((p = getenv ("EMAIL")) != NULL)
2105 From = rfc822_parse_adrlist (NULL, p);
2107 mutt_set_langinfo_charset ();
2108 mutt_set_charset (Charset);
2111 /* Set standard defaults */
2112 for (i = 0; MuttVars[i].option; i++) {
2113 mutt_set_default (&MuttVars[i]);
2114 mutt_restore_default (&MuttVars[i]);
2117 CurrentMenu = MENU_MAIN;
2120 #ifndef LOCALES_HACK
2121 /* Do we have a locale definition? */
2122 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2123 ((p = getenv ("LANG")) != NULL && p[0]) ||
2124 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2125 set_option (OPTLOCALES);
2129 /* Unset suspend by default if we're the session leader */
2130 if (getsid (0) == getpid ())
2131 unset_option (OPTSUSPEND);
2134 mutt_init_history ();
2143 * When changing the code which looks for a configuration file,
2144 * please also change the corresponding code in muttbug.sh.in.
2154 snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2156 if (access (buffer, F_OK) == -1)
2158 snprintf (buffer, sizeof (buffer), "%s/.muttngrc", NONULL (Homedir));
2159 if (access (buffer, F_OK) == -1)
2161 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s",
2162 NONULL (Homedir), MUTT_VERSION);
2163 if (access (buffer, F_OK) == -1)
2165 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc",
2169 Muttrc = str_dup (buffer);
2172 strfcpy (buffer, Muttrc, sizeof (buffer));
2174 mutt_expand_path (buffer, sizeof (buffer));
2175 Muttrc = str_dup (buffer);
2177 mem_free (&AliasFile);
2178 AliasFile = str_dup (NONULL (Muttrc));
2180 /* Process the global rc file if it exists and the user hasn't explicity
2181 requested not to via "-n". */
2183 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2185 if (access (buffer, F_OK) == -1)
2186 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", SYSCONFDIR);
2187 if (access (buffer, F_OK) == -1)
2188 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR,
2190 if (access (buffer, F_OK) == -1)
2191 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2192 if (access (buffer, F_OK) != -1) {
2193 if (source_rc (buffer, &err) != 0) {
2194 fputs (err.data, stderr);
2195 fputc ('\n', stderr);
2201 /* Read the user's initialization file. */
2202 if (access (Muttrc, F_OK) != -1) {
2203 if (!option (OPTNOCURSES))
2205 if (source_rc (Muttrc, &err) != 0) {
2206 fputs (err.data, stderr);
2207 fputc ('\n', stderr);
2211 else if (!default_rc) {
2212 /* file specified by -F does not exist */
2213 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2214 mutt_endwin (buffer);
2218 if (mutt_execute_commands (commands) != 0)
2221 /* warn about synonym variables */
2222 if (!list_empty(Synonyms)) {
2224 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2225 for (i = 0; i < Synonyms->length; i++)
2226 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2227 MuttVars[((syn_t*) Synonyms->data[i])->o].option,
2228 MuttVars[((syn_t*) Synonyms->data[i])->n].option,
2229 NONULL(((syn_t*) Synonyms->data[i])->f),
2230 ((syn_t*) Synonyms->data[i])->l);
2231 fprintf (stderr, _("Warning: Synonym variables are scheduled for removal.\n"));
2232 list_del (&Synonyms, syn_del);
2235 /* this is not needed during runtime */
2236 mem_free(&CurRCFile);
2238 if (need_pause && !option (OPTNOCURSES)) {
2239 if (mutt_any_key_to_continue (NULL) == -1)
2244 set_option (OPTWEED); /* turn weeding on by default */
2248 int mutt_get_hook_type (const char *name)
2250 struct command_t *c;
2252 for (c = Commands; c->name; c++)
2253 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2258 static int opt_cmp (const void* a, const void* b) {
2259 return (str_cmp ((*(struct option_t**) a)->option,
2260 (*(struct option_t**) b)->option));
2263 /* dump out the value of all the variables we have */
2264 int mutt_dump_variables (void) {
2267 char errbuff[STRING];
2268 char command[STRING];
2269 list2_t* tmp = NULL;
2273 memset (&err, 0, sizeof (err));
2274 memset (&token, 0, sizeof (token));
2277 err.dsize = sizeof (errbuff);
2279 /* get all non-synonyms into list... */
2280 for (i = 0; MuttVars[i].option; i++) {
2281 if (MuttVars[i].type == DT_SYN)
2283 list_push_back (&tmp, &MuttVars[i]);
2285 if (!list_empty(tmp)) {
2286 /* ...and dump list sorted */
2287 qsort (tmp->data, tmp->length, sizeof (void*), opt_cmp);
2288 for (i = 0; i < tmp->length; i++) {
2289 snprintf (command, sizeof (command), "set ?%s\n",
2290 ((struct option_t*) tmp->data[i])->option);
2291 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2292 fprintf (stderr, "%s\n", err.data);
2293 mem_free (&token.data);
2294 list_del (&tmp, NULL);
2297 printf("%s\n", err.data);
2300 mem_free (&token.data);
2301 list_del (&tmp, NULL);