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)
38 #include "lib/debug.h"
44 #include <sys/utsname.h>
48 /* for synonym warning reports: synonym found during parsing */
52 int n; /* new name (index) */
53 int o; /* old name (index) */
56 /* for synonym warning reports: list of synonyms found */
58 /* for synonym warning reports: current rc file */
59 char* CurRCFile = NULL;
60 /* for synonym warning reports: current rc line */
63 /* for synonym warning reports: adds synonym to end of list */
64 static void syn_add (int n, int o) {
65 syn_t* tmp = mem_malloc (sizeof (syn_t));
66 tmp->f = str_dup (CurRCFile);
70 list_push_back (&Synonyms, tmp);
73 /* for synonym warning reports: free single item (for list_del()) */
74 static void syn_del (void** p) {
75 mem_free(&(*(syn_t**) p)->f);
79 void toggle_quadoption (int opt)
82 int b = (opt % 4) * 2;
84 QuadOptions[n] ^= (1 << b);
87 void set_quadoption (int opt, int flag)
90 int b = (opt % 4) * 2;
92 QuadOptions[n] &= ~(0x3 << b);
93 QuadOptions[n] |= (flag & 0x3) << b;
96 int quadoption (int opt)
99 int b = (opt % 4) * 2;
101 return (QuadOptions[n] >> b) & 0x3;
104 int query_quadoption (int opt, const char *prompt)
106 int v = quadoption (opt);
114 v = mutt_yesorno (prompt, (v == M_ASKYES));
115 CLEARLINE (LINES - 1);
122 /* given the variable ``s'', return the index into the rc_vars array which
123 matches, or -1 if the variable is not found. */
124 int mutt_option_index (char *s)
128 for (i = 0; MuttVars[i].option; i++)
129 if (str_cmp (s, MuttVars[i].option) == 0) {
130 if (MuttVars[i].type == DT_SYN)
131 syn_add (mutt_option_index ((char *) MuttVars[i].data), i);
132 return (MuttVars[i].type ==
133 DT_SYN ? mutt_option_index ((char *) MuttVars[i].data) : i);
138 static void add_to_list (LIST ** list, const char *str)
140 LIST *t, *last = NULL;
142 /* don't add a NULL or empty string to the list */
143 if (!str || *str == '\0')
146 /* check to make sure the item is not already on this list */
147 for (last = *list; last; last = last->next) {
148 if (ascii_strcasecmp (str, last->data) == 0) {
149 /* already on the list, so just ignore it */
157 if (!*list || last) {
158 t = (LIST *) mem_calloc (1, sizeof (LIST));
159 t->data = str_dup (str);
169 static int add_to_rx_list (list2_t** list, const char *s, int flags,
178 if (!(rx = rx_compile (s, flags))) {
179 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
183 i = rx_lookup ((*list), rx->pattern);
187 list_push_back (list, rx);
191 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
192 const char *templ, BUFFER * err)
194 SPAM_LIST *t = NULL, *last = NULL;
199 if (!pat || !*pat || !templ)
202 if (!(rx = rx_compile (pat, REG_ICASE))) {
203 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
207 /* check to make sure the item is not already on this list */
208 for (last = *list; last; last = last->next) {
209 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
210 /* Already on the list. Formerly we just skipped this case, but
211 * now we're supporting removals, which means we're supporting
212 * re-adds conceptually. So we probably want this to imply a
213 * removal, then do an add. We can achieve the removal by freeing
214 * the template, and leaving t pointed at the current item.
217 mem_free(t->template);
224 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
225 * update. Otherwise we want to make a new one to link at the list's end.
228 t = mutt_new_spam_list ();
236 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
237 t->template = str_dup (templ);
239 /* Find highest match number in template string */
241 for (p = templ; *p;) {
246 while (*p && isdigit ((int) *p))
252 t->nmatch++; /* match 0 is always the whole expr */
257 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
259 SPAM_LIST *spam, *prev;
262 /* Being first is a special case. */
266 if (spam->rx && !str_cmp (spam->rx->pattern, pat)) {
269 mem_free(&spam->template);
275 for (spam = prev->next; spam;) {
276 if (!str_cmp (spam->rx->pattern, pat)) {
277 prev->next = spam->next;
279 mem_free(spam->template);
292 static void remove_from_list (LIST ** l, const char *str)
294 LIST *p, *last = NULL;
296 if (str_cmp ("*", str) == 0)
297 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
302 if (ascii_strcasecmp (str, p->data) == 0) {
305 last->next = p->next;
318 static int remove_from_rx_list (list2_t** l, const char *str)
322 if (str_cmp ("*", str) == 0) {
323 list_del (l, (list_del_t*) rx_free);
327 i = rx_lookup ((*l), str);
329 rx_t* r = list_pop_idx ((*l), i);
337 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
343 memset (&token, 0, sizeof (token));
344 mutt_extract_token (tmp, s, 0);
346 /* is the item defined as a variable or a function? */
347 if (!(res = (mutt_option_index (tmp->data) != -1)))
348 for (i = 0; !res && i < MENU_MAX; i++) {
349 struct binding_t *b = km_get_table (Menus[i].value);
354 for (j = 0; b[j].name; j++)
355 if (!ascii_strncasecmp (tmp->data, b[j].name, str_len (tmp->data))
356 && (str_len (b[j].name) == str_len (tmp->data))) {
361 /* check for feature_* */
366 j = str_len (tmp->data);
367 /* need at least input of 'feature_X' */
371 while (Features[i].name) {
372 if (str_len (Features[i].name) == j &&
373 ascii_strncasecmp (Features[i].name, p, j)) {
384 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
386 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
389 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
391 if ((data && res) || (!data && !res)) {
392 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
393 mutt_error ("Error: %s", err->data);
394 mem_free (&token.data);
397 mem_free (&token.data);
402 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
406 mutt_extract_token (buf, s, 0);
408 /* don't add "*" to the unignore list */
409 if (strcmp (buf->data, "*"))
410 add_to_list (&UnIgnore, buf->data);
412 remove_from_list (&Ignore, buf->data);
414 while (MoreArgs (s));
419 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
423 mutt_extract_token (buf, s, 0);
424 remove_from_list (&UnIgnore, buf->data);
425 add_to_list (&Ignore, buf->data);
427 while (MoreArgs (s));
432 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
436 mutt_extract_token (buf, s, 0);
437 add_to_list ((LIST **) data, buf->data);
439 while (MoreArgs (s));
444 static void _alternates_clean (void)
448 if (Context && Context->msgcount) {
449 for (i = 0; i < Context->msgcount; i++)
450 Context->hdrs[i]->recip_valid = 0;
454 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
457 _alternates_clean ();
459 mutt_extract_token (buf, s, 0);
460 remove_from_rx_list (&UnAlternates, buf->data);
462 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
465 while (MoreArgs (s));
470 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
473 _alternates_clean ();
475 mutt_extract_token (buf, s, 0);
476 remove_from_rx_list (&Alternates, buf->data);
478 if (str_cmp (buf->data, "*") &&
479 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
483 while (MoreArgs (s));
488 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
493 memset (&templ, 0, sizeof (templ));
495 /* Insist on at least one parameter */
498 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
500 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
504 /* Extract the first token, a regexp */
505 mutt_extract_token (buf, s, 0);
507 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
508 if (data == M_SPAM) {
509 /* If there's a second parameter, it's a template for the spam tag. */
511 mutt_extract_token (&templ, s, 0);
513 /* Add to the spam list. */
514 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
515 mem_free (&templ.data);
518 mem_free (&templ.data);
521 /* If not, try to remove from the nospam list. */
523 remove_from_rx_list (&NoSpamList, buf->data);
529 /* M_NOSPAM is for nospam commands. */
530 else if (data == M_NOSPAM) {
531 /* nospam only ever has one parameter. */
533 /* "*" is a special case. */
534 if (!str_cmp (buf->data, "*")) {
535 mutt_free_spam_list (&SpamList);
536 list_del (&NoSpamList, (list_del_t*) rx_free);
540 /* If it's on the spam list, just remove it. */
541 if (remove_from_spam_list (&SpamList, buf->data) != 0)
544 /* Otherwise, add it to the nospam list. */
545 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
551 /* This should not happen. */
552 strfcpy (err->data, "This is no good at all.", err->dsize);
556 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
560 mutt_extract_token (buf, s, 0);
562 * Check for deletion of entire list
564 if (str_cmp (buf->data, "*") == 0) {
565 mutt_free_list ((LIST **) data);
568 remove_from_list ((LIST **) data, buf->data);
570 while (MoreArgs (s));
575 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
579 mutt_extract_token (buf, s, 0);
580 remove_from_rx_list (&UnMailLists, buf->data);
582 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
585 while (MoreArgs (s));
590 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
594 mutt_extract_token (buf, s, 0);
595 remove_from_rx_list (&SubscribedLists, buf->data);
596 remove_from_rx_list (&MailLists, buf->data);
598 if (str_cmp (buf->data, "*") &&
599 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
602 while (MoreArgs (s));
607 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
611 mutt_extract_token (buf, s, 0);
612 remove_from_rx_list (&UnMailLists, buf->data);
613 remove_from_rx_list (&UnSubscribedLists, buf->data);
615 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
617 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
620 while (MoreArgs (s));
625 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
629 mutt_extract_token (buf, s, 0);
630 remove_from_rx_list (&SubscribedLists, buf->data);
632 if (str_cmp (buf->data, "*") &&
633 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
636 while (MoreArgs (s));
641 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
644 ALIAS *tmp, *last = NULL;
647 mutt_extract_token (buf, s, 0);
649 if (str_cmp ("*", buf->data) == 0) {
650 if (CurrentMenu == MENU_ALIAS) {
651 for (tmp = Aliases; tmp; tmp = tmp->next)
653 set_option (OPTFORCEREDRAWINDEX);
656 mutt_free_alias (&Aliases);
660 for (tmp = Aliases; tmp; tmp = tmp->next) {
661 if (str_casecmp (buf->data, tmp->name) == 0) {
662 if (CurrentMenu == MENU_ALIAS) {
664 set_option (OPTFORCEREDRAWINDEX);
669 last->next = tmp->next;
673 mutt_free_alias (&tmp);
679 while (MoreArgs (s));
683 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
686 ALIAS *tmp = Aliases;
691 strfcpy (err->data, _("alias: no address"), err->dsize);
695 mutt_extract_token (buf, s, 0);
697 debug_print (2, ("first token is '%s'.\n", buf->data));
699 /* check to see if an alias with this name already exists */
700 for (; tmp; tmp = tmp->next) {
701 if (!str_casecmp (tmp->name, buf->data))
707 /* create a new alias */
708 tmp = (ALIAS *) mem_calloc (1, sizeof (ALIAS));
710 tmp->name = str_dup (buf->data);
711 /* give the main addressbook code a chance */
712 if (CurrentMenu == MENU_ALIAS)
713 set_option (OPTMENUCALLER);
716 /* override the previous value */
717 rfc822_free_address (&tmp->addr);
718 if (CurrentMenu == MENU_ALIAS)
719 set_option (OPTFORCEREDRAWINDEX);
722 mutt_extract_token (buf, s,
723 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
724 debug_print (2, ("second token is '%s'.\n", buf->data));
725 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
730 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
731 snprintf (err->data, err->dsize,
732 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
736 if (DebugLevel >= 2) {
739 for (a = tmp->addr; a; a = a->next) {
741 debug_print (2, ("%s\n", a->mailbox));
743 debug_print (2, ("group %s\n", a->mailbox));
751 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
754 LIST *tmp = UserHeader;
759 mutt_extract_token (buf, s, 0);
760 if (str_cmp ("*", buf->data) == 0)
761 mutt_free_list (&UserHeader);
766 l = str_len (buf->data);
767 if (buf->data[l - 1] == ':')
771 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
772 && tmp->data[l] == ':') {
775 last->next = tmp->next;
777 UserHeader = tmp->next;
780 mutt_free_list (&ptr);
789 while (MoreArgs (s));
793 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
800 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
801 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
802 strfcpy (err->data, _("invalid header field"), err->dsize);
805 keylen = p - buf->data + 1;
808 for (tmp = UserHeader;; tmp = tmp->next) {
809 /* see if there is already a field by this name */
810 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
811 /* replace the old value */
812 mem_free (&tmp->data);
813 tmp->data = buf->data;
814 memset (buf, 0, sizeof (BUFFER));
820 tmp->next = mutt_new_list ();
824 tmp = mutt_new_list ();
827 tmp->data = buf->data;
828 memset (buf, 0, sizeof (BUFFER));
833 parse_sort (short *val, const char *s, const struct mapping_t *map,
838 if (str_ncmp ("reverse-", s, 8) == 0) {
840 flags = SORT_REVERSE;
843 if (str_ncmp ("last-", s, 5) == 0) {
848 if ((i = mutt_getvaluebyname (s, map)) == -1) {
849 snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s);
858 static void mutt_set_default (struct option_t *p)
860 switch (p->type & DT_MASK) {
862 if (!p->init && *((char **) p->data))
863 p->init = (unsigned long) str_dup (*((char **) p->data));
866 if (!p->init && *((char **) p->data)) {
867 char *cp = str_dup (*((char **) p->data));
869 /* mutt_pretty_mailbox (cp); */
870 p->init = (unsigned long) cp;
874 if (!p->init && *((ADDRESS **) p->data)) {
875 char tmp[HUGE_STRING];
878 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data), 0);
879 p->init = (unsigned long) str_dup (tmp);
884 rx_t* pp = (rx_t*) p->data;
886 if (!p->init && pp->pattern)
887 p->init = (unsigned long) str_dup (pp->pattern);
893 static void mutt_restore_default (struct option_t *p)
895 switch (p->type & DT_MASK) {
898 str_replace ((char **) p->data, (char *) p->init);
902 char path[_POSIX_PATH_MAX];
904 strfcpy (path, (char *) p->init, sizeof (path));
905 mutt_expand_path (path, sizeof (path));
906 str_replace ((char **) p->data, path);
911 rfc822_free_address ((ADDRESS **) p->data);
912 *((ADDRESS **) p->data) = rfc822_parse_adrlist (NULL, (char *) p->init);
917 set_option (p->data);
919 unset_option (p->data);
922 set_quadoption (p->data, p->init);
927 *((short *) p->data) = p->init;
931 rx_t *pp = (rx_t *) p->data;
934 mem_free (&pp->pattern);
941 char *s = (char *) p->init;
943 pp->rx = mem_calloc (1, sizeof (regex_t));
944 if (str_cmp (p->option, "mask") != 0)
945 flags |= mutt_which_case ((const char *) p->init);
946 if (str_cmp (p->option, "mask") == 0 && *s == '!') {
950 if (REGCOMP (pp->rx, s, flags) != 0) {
952 _("mutt_restore_default(%s): error in regexp: %s\n"),
953 p->option, pp->pattern);
954 mem_free (&pp->pattern);
959 str_replace (&pp->pattern, (char *) p->init);
965 if (p->flags & R_INDEX)
966 set_option (OPTFORCEREDRAWINDEX);
967 if (p->flags & R_PAGER)
968 set_option (OPTFORCEREDRAWPAGER);
969 if (p->flags & R_RESORT_SUB)
970 set_option (OPTSORTSUBTHREADS);
971 if (p->flags & R_RESORT)
972 set_option (OPTNEEDRESORT);
973 if (p->flags & R_RESORT_INIT)
974 set_option (OPTRESORTINIT);
975 if (p->flags & R_TREE)
976 set_option (OPTREDRAWTREE);
979 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
982 int idx, query, unset, inv, reset, r = 0;
983 char *p, scratch[_POSIX_PATH_MAX];
985 while (MoreArgs (s)) {
986 /* reset state variables */
988 unset = data & M_SET_UNSET;
989 inv = data & M_SET_INV;
990 reset = data & M_SET_RESET;
992 if (*s->dptr == '?') {
996 else if (str_ncmp ("no", s->dptr, 2) == 0) {
1000 else if (str_ncmp ("inv", s->dptr, 3) == 0) {
1004 else if (*s->dptr == '&') {
1009 /* get the variable name */
1010 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1012 if ((idx = mutt_option_index (tmp->data)) == -1 &&
1013 !(reset && !str_cmp ("all", tmp->data))) {
1014 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1020 if (query || unset || inv) {
1021 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1025 if (s && *s->dptr == '=') {
1026 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1030 if (!str_cmp ("all", tmp->data)) {
1031 for (idx = 0; MuttVars[idx].option; idx++)
1032 mutt_restore_default (&MuttVars[idx]);
1036 mutt_restore_default (&MuttVars[idx]);
1038 else if (DTYPE (MuttVars[idx].type) == DT_BOOL) {
1039 if (s && *s->dptr == '=') {
1040 if (unset || inv || query) {
1041 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1046 mutt_extract_token (tmp, s, 0);
1047 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1049 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1052 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1058 snprintf (err->data, err->dsize, option (MuttVars[idx].data)
1059 ? _("%s is set") : _("%s is unset"), tmp->data);
1064 unset_option (MuttVars[idx].data);
1066 toggle_option (MuttVars[idx].data);
1068 set_option (MuttVars[idx].data);
1070 else if (DTYPE (MuttVars[idx].type) == DT_STR ||
1071 DTYPE (MuttVars[idx].type) == DT_PATH ||
1072 DTYPE (MuttVars[idx].type) == DT_ADDR) {
1074 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1075 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1077 mem_free ((void *) MuttVars[idx].data);
1079 else if (query || *s->dptr != '=') {
1083 if (DTYPE (MuttVars[idx].type) == DT_ADDR) {
1085 rfc822_write_address (_tmp, sizeof (_tmp),
1086 *((ADDRESS **) MuttVars[idx].data), 0);
1090 val = *((char **) MuttVars[idx].data);
1092 /* user requested the value of this variable */
1093 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1100 /* copy the value of the string */
1101 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1102 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1104 mem_free ((void *) MuttVars[idx].data);
1106 mutt_extract_token (tmp, s, 0);
1107 if (DTYPE (MuttVars[idx].type) == DT_PATH) {
1108 strfcpy (scratch, tmp->data, sizeof (scratch));
1109 mutt_expand_path (scratch, sizeof (scratch));
1110 *((char **) MuttVars[idx].data) = str_dup (scratch);
1112 else if (DTYPE (MuttVars[idx].type) == DT_STR) {
1113 *((char **) MuttVars[idx].data) = str_dup (tmp->data);
1114 if (str_cmp (MuttVars[idx].option, "charset") == 0)
1115 mutt_set_charset (Charset);
1118 *((ADDRESS **) MuttVars[idx].data) =
1119 rfc822_parse_adrlist (NULL, tmp->data);
1123 else if (DTYPE (MuttVars[idx].type) == DT_RX) {
1124 rx_t *ptr = (rx_t *) MuttVars[idx].data;
1128 if (query || *s->dptr != '=') {
1129 /* user requested the value of this variable */
1130 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1131 NONULL (ptr->pattern));
1135 if (option (OPTATTACHMSG)
1136 && !str_cmp (MuttVars[idx].option, "reply_regexp")) {
1137 snprintf (err->data, err->dsize,
1138 "Operation not permitted when in attach-message mode.");
1145 /* copy the value of the string */
1146 mutt_extract_token (tmp, s, 0);
1148 if (!ptr->pattern || str_cmp (ptr->pattern, tmp->data) != 0) {
1151 /* $mask is case-sensitive */
1152 if (str_cmp (MuttVars[idx].option, "mask") != 0)
1153 flags |= mutt_which_case (tmp->data);
1156 if (str_cmp (MuttVars[idx].option, "mask") == 0) {
1163 rx = (regex_t *) mem_malloc (sizeof (regex_t));
1164 if ((e = REGCOMP (rx, p, flags)) != 0) {
1165 regerror (e, rx, err->data, err->dsize);
1171 /* get here only if everything went smootly */
1173 mem_free (&ptr->pattern);
1174 regfree ((regex_t *) ptr->rx);
1175 mem_free (&ptr->rx);
1178 ptr->pattern = str_dup (tmp->data);
1182 /* $reply_regexp and $alterantes require special treatment */
1184 if (Context && Context->msgcount &&
1185 str_cmp (MuttVars[idx].option, "reply_regexp") == 0) {
1186 regmatch_t pmatch[1];
1189 #define CUR_ENV Context->hdrs[i]->env
1190 for (i = 0; i < Context->msgcount; i++) {
1191 if (CUR_ENV && CUR_ENV->subject) {
1192 CUR_ENV->real_subj = (regexec (ReplyRegexp.rx,
1193 CUR_ENV->subject, 1, pmatch,
1195 subject : CUR_ENV->subject + pmatch[0].rm_eo;
1202 else if (DTYPE (MuttVars[idx].type) == DT_MAGIC) {
1203 if (query || *s->dptr != '=') {
1204 switch (DefaultMagic) {
1221 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p);
1227 /* copy the value of the string */
1228 mutt_extract_token (tmp, s, 0);
1229 if (mx_set_magic (tmp->data)) {
1230 snprintf (err->data, err->dsize, _("%s: invalid mailbox type"),
1236 else if (DTYPE (MuttVars[idx].type) == DT_NUM) {
1237 short *ptr = (short *) MuttVars[idx].data;
1241 if (query || *s->dptr != '=') {
1242 /* user requested the value of this variable */
1243 snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, *ptr);
1249 mutt_extract_token (tmp, s, 0);
1250 val = strtol (tmp->data, &t, 0);
1252 if (!*tmp->data || *t || (short) val != val) {
1253 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1260 /* these ones need a sanity check */
1261 if (str_cmp (MuttVars[idx].option, "history") == 0) {
1264 mutt_init_history ();
1266 else if (str_cmp (MuttVars[idx].option, "pager_index_lines") == 0) {
1271 else if (DTYPE (MuttVars[idx].type) == DT_QUAD) {
1273 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1275 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
1276 vals[quadoption (MuttVars[idx].data)]);
1280 if (*s->dptr == '=') {
1282 mutt_extract_token (tmp, s, 0);
1283 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1284 set_quadoption (MuttVars[idx].data, M_YES);
1285 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1286 set_quadoption (MuttVars[idx].data, M_NO);
1287 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1288 set_quadoption (MuttVars[idx].data, M_ASKYES);
1289 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1290 set_quadoption (MuttVars[idx].data, M_ASKNO);
1292 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1299 toggle_quadoption (MuttVars[idx].data);
1301 set_quadoption (MuttVars[idx].data, M_NO);
1303 set_quadoption (MuttVars[idx].data, M_YES);
1306 else if (DTYPE (MuttVars[idx].type) == DT_SORT) {
1307 const struct mapping_t *map = NULL;
1309 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) {
1311 map = SortAliasMethods;
1313 case DT_SORT_BROWSER:
1314 map = SortBrowserMethods;
1317 if ((WithCrypto & APPLICATION_PGP))
1318 map = SortKeyMethods;
1321 map = SortAuxMethods;
1329 snprintf (err->data, err->dsize, _("%s: Unknown type."),
1330 MuttVars[idx].option);
1335 if (query || *s->dptr != '=') {
1337 mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK,
1340 snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
1341 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ?
1343 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" :
1348 mutt_extract_token (tmp, s, 0);
1350 if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) ==
1357 snprintf (err->data, err->dsize, _("%s: unknown type"),
1358 MuttVars[idx].option);
1363 if (MuttVars[idx].flags & R_INDEX)
1364 set_option (OPTFORCEREDRAWINDEX);
1365 if (MuttVars[idx].flags & R_PAGER)
1366 set_option (OPTFORCEREDRAWPAGER);
1367 if (MuttVars[idx].flags & R_RESORT_SUB)
1368 set_option (OPTSORTSUBTHREADS);
1369 if (MuttVars[idx].flags & R_RESORT)
1370 set_option (OPTNEEDRESORT);
1371 if (MuttVars[idx].flags & R_RESORT_INIT)
1372 set_option (OPTRESORTINIT);
1373 if (MuttVars[idx].flags & R_TREE)
1374 set_option (OPTREDRAWTREE);
1381 /* reads the specified initialization file. returns -1 if errors were found
1382 so that we can pause to let the user know... */
1383 static int source_rc (const char *rcfile, BUFFER * err)
1386 int line = 0, rc = 0, conv = 0;
1388 char *linebuf = NULL;
1389 char *currentline = NULL;
1393 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
1394 str_replace (&CurRCFile, rcfile);
1397 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1398 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1402 memset (&token, 0, sizeof (token));
1403 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
1405 conv = ConfigCharset && (*ConfigCharset) && Charset;
1407 currentline = str_dup (linebuf);
1410 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1413 currentline = linebuf;
1415 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1416 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1417 if (--rc < -MAXERRS) {
1419 mem_free (¤tline);
1428 mem_free (¤tline);
1430 mem_free (&token.data);
1431 mem_free (&linebuf);
1434 mutt_wait_filter (pid);
1436 /* the muttrc source keyword */
1437 snprintf (err->data, err->dsize,
1438 rc >= -MAXERRS ? _("source: errors in %s")
1439 : _("source: reading aborted due too many errors in %s"),
1448 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
1451 char path[_POSIX_PATH_MAX];
1455 if (mutt_extract_token (tmp, s, 0) != 0) {
1456 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1460 strfcpy (path, tmp->data, sizeof (path));
1461 mutt_expand_path (path, sizeof (path));
1463 rc += source_rc (path, err);
1465 while (MoreArgs (s));
1467 return ((rc < 0) ? -1 : 0);
1470 /* line command to execute
1472 token scratch buffer to be used by parser. caller should free
1473 token->data when finished. the reason for this variable is
1474 to avoid having to allocate and deallocate a lot of memory
1475 if we are parsing many lines. the caller can pass in the
1476 memory to use, which avoids having to create new space for
1477 every call to this function.
1479 err where to write error messages */
1480 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1485 memset (&expn, 0, sizeof (expn));
1486 expn.data = expn.dptr = line;
1487 expn.dsize = str_len (line);
1492 while (*expn.dptr) {
1493 if (*expn.dptr == '#')
1494 break; /* rest of line is a comment */
1495 if (*expn.dptr == ';') {
1499 mutt_extract_token (token, &expn, 0);
1500 for (i = 0; Commands[i].name; i++) {
1501 if (!str_cmp (token->data, Commands[i].name)) {
1502 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1507 if (!Commands[i].name) {
1508 snprintf (err->data, err->dsize, _("%s: unknown command"),
1509 NONULL (token->data));
1516 mem_free (&expn.data);
1521 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1522 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1523 /* initial string that starts completion. No telling how much crap
1524 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1525 char User_typed[LONG_STRING] = { 0 };
1527 int Num_matched = 0; /* Number of matches for completion */
1528 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1529 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1531 /* helper function for completion. Changes the dest buffer if
1532 necessary/possible to aid completion.
1533 dest == completion result gets here.
1534 src == candidate for completion.
1535 try == user entered data for completion.
1536 len == length of dest buffer.
1538 static void candidate (char *dest, char *try, char *src, int len)
1542 if (strstr (src, try) == src) {
1543 Matches[Num_matched++] = src;
1545 strfcpy (dest, src, len);
1547 for (l = 0; src[l] && src[l] == dest[l]; l++);
1553 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1557 int spaces; /* keep track of the number of leading spaces on the line */
1560 spaces = buffer - pt;
1562 pt = buffer + pos - spaces;
1563 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1566 if (pt == buffer) { /* complete cmd */
1567 /* first TAB. Collect all the matches */
1570 strfcpy (User_typed, pt, sizeof (User_typed));
1571 memset (Matches, 0, sizeof (Matches));
1572 memset (Completed, 0, sizeof (Completed));
1573 for (num = 0; Commands[num].name; num++)
1574 candidate (Completed, User_typed, Commands[num].name,
1575 sizeof (Completed));
1576 Matches[Num_matched++] = User_typed;
1578 /* All matches are stored. Longest non-ambiguous string is ""
1579 * i.e. dont change 'buffer'. Fake successful return this time */
1580 if (User_typed[0] == 0)
1584 if (Completed[0] == 0 && User_typed[0])
1587 /* Num_matched will _always_ be atleast 1 since the initial
1588 * user-typed string is always stored */
1589 if (numtabs == 1 && Num_matched == 2)
1590 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1591 else if (numtabs > 1 && Num_matched > 2)
1592 /* cycle thru all the matches */
1593 snprintf (Completed, sizeof (Completed), "%s",
1594 Matches[(numtabs - 2) % Num_matched]);
1596 /* return the completed command */
1597 strncpy (buffer, Completed, len - spaces);
1599 else if (!str_ncmp (buffer, "set", 3)
1600 || !str_ncmp (buffer, "unset", 5)
1601 || !str_ncmp (buffer, "reset", 5)
1602 || !str_ncmp (buffer, "toggle", 6)) { /* complete variables */
1603 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1606 /* loop through all the possible prefixes (no, inv, ...) */
1607 if (!str_ncmp (buffer, "set", 3)) {
1608 for (num = 0; prefixes[num]; num++) {
1609 if (!str_ncmp (pt, prefixes[num], str_len (prefixes[num]))) {
1610 pt += str_len (prefixes[num]);
1616 /* first TAB. Collect all the matches */
1619 strfcpy (User_typed, pt, sizeof (User_typed));
1620 memset (Matches, 0, sizeof (Matches));
1621 memset (Completed, 0, sizeof (Completed));
1622 for (num = 0; MuttVars[num].option; num++)
1623 candidate (Completed, User_typed, MuttVars[num].option,
1624 sizeof (Completed));
1625 Matches[Num_matched++] = User_typed;
1627 /* All matches are stored. Longest non-ambiguous string is ""
1628 * i.e. dont change 'buffer'. Fake successful return this time */
1629 if (User_typed[0] == 0)
1633 if (Completed[0] == 0 && User_typed[0])
1636 /* Num_matched will _always_ be atleast 1 since the initial
1637 * user-typed string is always stored */
1638 if (numtabs == 1 && Num_matched == 2)
1639 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1640 else if (numtabs > 1 && Num_matched > 2)
1641 /* cycle thru all the matches */
1642 snprintf (Completed, sizeof (Completed), "%s",
1643 Matches[(numtabs - 2) % Num_matched]);
1645 strncpy (pt, Completed, buffer + len - pt - spaces);
1647 else if (!str_ncmp (buffer, "exec", 4)) {
1648 struct binding_t *menu = km_get_table (CurrentMenu);
1650 if (!menu && CurrentMenu != MENU_PAGER)
1654 /* first TAB. Collect all the matches */
1657 strfcpy (User_typed, pt, sizeof (User_typed));
1658 memset (Matches, 0, sizeof (Matches));
1659 memset (Completed, 0, sizeof (Completed));
1660 for (num = 0; menu[num].name; num++)
1661 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1662 /* try the generic menu */
1663 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1665 for (num = 0; menu[num].name; num++)
1666 candidate (Completed, User_typed, menu[num].name,
1667 sizeof (Completed));
1669 Matches[Num_matched++] = User_typed;
1671 /* All matches are stored. Longest non-ambiguous string is ""
1672 * i.e. dont change 'buffer'. Fake successful return this time */
1673 if (User_typed[0] == 0)
1677 if (Completed[0] == 0 && User_typed[0])
1680 /* Num_matched will _always_ be atleast 1 since the initial
1681 * user-typed string is always stored */
1682 if (numtabs == 1 && Num_matched == 2)
1683 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1684 else if (numtabs > 1 && Num_matched > 2)
1685 /* cycle thru all the matches */
1686 snprintf (Completed, sizeof (Completed), "%s",
1687 Matches[(numtabs - 2) % Num_matched]);
1689 strncpy (pt, Completed, buffer + len - pt - spaces);
1697 int mutt_var_value_complete (char *buffer, size_t len, int pos)
1699 char var[STRING], *pt = buffer;
1706 spaces = buffer - pt;
1708 pt = buffer + pos - spaces;
1709 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1711 pt++; /* move past the space */
1712 if (*pt == '=') /* abort if no var before the '=' */
1715 if (str_ncmp (buffer, "set", 3) == 0) {
1718 strfcpy (var, pt, sizeof (var));
1719 /* ignore the trailing '=' when comparing */
1720 var[str_len (var) - 1] = 0;
1721 if ((idx = mutt_option_index (var)) == -1)
1722 return 0; /* no such variable. */
1724 char tmp[LONG_STRING], tmp2[LONG_STRING];
1726 size_t dlen = buffer + len - pt - spaces;
1727 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1731 if ((DTYPE (MuttVars[idx].type) == DT_STR) ||
1732 (DTYPE (MuttVars[idx].type) == DT_PATH) ||
1733 (DTYPE (MuttVars[idx].type) == DT_RX)) {
1734 strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data)), sizeof (tmp));
1735 if (DTYPE (MuttVars[idx].type) == DT_PATH)
1736 mutt_pretty_mailbox (tmp);
1738 else if (DTYPE (MuttVars[idx].type) == DT_ADDR) {
1739 rfc822_write_address (tmp, sizeof (tmp),
1740 *((ADDRESS **) MuttVars[idx].data), 0);
1742 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
1743 strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp));
1744 else if (DTYPE (MuttVars[idx].type) == DT_NUM)
1745 snprintf (tmp, sizeof (tmp), "%d", (*((short *) MuttVars[idx].data)));
1746 else if (DTYPE (MuttVars[idx].type) == DT_SORT) {
1747 const struct mapping_t *map;
1750 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) {
1752 map = SortAliasMethods;
1754 case DT_SORT_BROWSER:
1755 map = SortBrowserMethods;
1758 if ((WithCrypto & APPLICATION_PGP))
1759 map = SortKeyMethods;
1768 mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK,
1770 snprintf (tmp, sizeof (tmp), "%s%s%s",
1771 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ?
1773 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" :
1776 else if (DTYPE (MuttVars[idx].type) == DT_MAGIC) {
1778 switch (DefaultMagic) {
1794 strfcpy (tmp, p, sizeof (tmp));
1796 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
1797 strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no",
1802 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
1803 if (*s == '\\' || *s == '"')
1809 strfcpy (tmp, pt, sizeof (tmp));
1810 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
1818 /* Implement the -Q command line flag */
1819 int mutt_query_variables (LIST * queries)
1823 char errbuff[STRING];
1824 char command[STRING];
1828 memset (&err, 0, sizeof (err));
1829 memset (&token, 0, sizeof (token));
1832 err.dsize = sizeof (errbuff);
1834 for (p = queries; p; p = p->next) {
1835 snprintf (command, sizeof (command), "set ?%s\n", p->data);
1836 if (mutt_parse_rc_line (command, &token, &err) == -1) {
1837 fprintf (stderr, "%s\n", err.data);
1838 mem_free (&token.data);
1841 printf ("%s\n", err.data);
1844 mem_free (&token.data);
1848 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
1852 for (i = 0; map[i].name; i++)
1853 if (map[i].value == val)
1854 return (map[i].name);
1858 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
1862 for (i = 0; map[i].name; i++)
1863 if (ascii_strcasecmp (map[i].name, name) == 0)
1864 return (map[i].value);
1868 static int mutt_execute_commands (LIST * p)
1871 char errstr[SHORT_STRING];
1873 memset (&err, 0, sizeof (err));
1875 err.dsize = sizeof (errstr);
1876 memset (&token, 0, sizeof (token));
1877 for (; p; p = p->next) {
1878 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
1879 fprintf (stderr, _("Error in command line: %s\n"), err.data);
1880 mem_free (&token.data);
1884 mem_free (&token.data);
1888 void mutt_init (int skip_sys_rc, LIST * commands)
1891 struct utsname utsname;
1892 char *p, buffer[STRING], error[STRING];
1893 int i, default_rc = 0, need_pause = 0;
1896 memset (&err, 0, sizeof (err));
1898 err.dsize = sizeof (error);
1901 * XXX - use something even more difficult to predict?
1903 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
1904 "\033]9;%ld\a", (long) time (NULL));
1906 /* on one of the systems I use, getcwd() does not return the same prefix
1907 as is listed in the passwd file */
1908 if ((p = getenv ("HOME")))
1909 Homedir = str_dup (p);
1911 /* Get some information about the user */
1912 if ((pw = getpwuid (getuid ()))) {
1915 Username = str_dup (pw->pw_name);
1917 Homedir = str_dup (pw->pw_dir);
1919 Realname = str_dup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
1920 Shell = str_dup (pw->pw_shell);
1925 fputs (_("unable to determine home directory"), stderr);
1928 if ((p = getenv ("USER")))
1929 Username = str_dup (p);
1932 fputs (_("unable to determine username"), stderr);
1935 Shell = str_dup ((p = getenv ("SHELL")) ? p : "/bin/sh");
1938 debug_start(Homedir);
1940 /* And about the host... */
1942 /* some systems report the FQDN instead of just the hostname */
1943 if ((p = strchr (utsname.nodename, '.'))) {
1944 Hostname = str_substrdup (utsname.nodename, p);
1946 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
1949 Hostname = str_dup (utsname.nodename);
1952 #define DOMAIN buffer
1953 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
1954 Fqdn = str_dup ("@");
1957 if (*DOMAIN != '@') {
1958 Fqdn = mem_malloc (str_len (DOMAIN) + str_len (Hostname) + 2);
1959 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
1962 Fqdn = str_dup (NONULL (Hostname));
1969 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
1971 fgets (buffer, sizeof (buffer), f);
1972 p = (char*) &buffer;
1975 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r')
1979 NewsServer = str_dup (p);
1983 if ((p = getenv ("NNTPSERVER")))
1984 NewsServer = str_dup (p);
1987 if ((p = getenv ("MAIL")))
1988 Spoolfile = str_dup (p);
1989 else if ((p = getenv ("MAILDIR")))
1990 Spoolfile = str_dup (p);
1993 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
1995 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
1997 Spoolfile = str_dup (buffer);
2000 if ((p = getenv ("MAILCAPS")))
2001 MailcapPath = str_dup (p);
2003 /* Default search path from RFC1524 */
2005 str_dup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2006 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2009 Tempdir = str_dup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2011 p = getenv ("VISUAL");
2013 p = getenv ("EDITOR");
2017 Editor = str_dup (p);
2018 Visual = str_dup (p);
2020 if ((p = getenv ("REPLYTO")) != NULL) {
2023 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2025 memset (&buf, 0, sizeof (buf));
2026 buf.data = buf.dptr = buffer;
2027 buf.dsize = str_len (buffer);
2029 memset (&token, 0, sizeof (token));
2030 parse_my_hdr (&token, &buf, 0, &err);
2031 mem_free (&token.data);
2034 if ((p = getenv ("EMAIL")) != NULL)
2035 From = rfc822_parse_adrlist (NULL, p);
2037 mutt_set_langinfo_charset ();
2038 mutt_set_charset (Charset);
2041 /* Set standard defaults */
2042 for (i = 0; MuttVars[i].option; i++) {
2043 mutt_set_default (&MuttVars[i]);
2044 mutt_restore_default (&MuttVars[i]);
2047 CurrentMenu = MENU_MAIN;
2050 #ifndef LOCALES_HACK
2051 /* Do we have a locale definition? */
2052 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2053 ((p = getenv ("LANG")) != NULL && p[0]) ||
2054 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2055 set_option (OPTLOCALES);
2059 /* Unset suspend by default if we're the session leader */
2060 if (getsid (0) == getpid ())
2061 unset_option (OPTSUSPEND);
2064 mutt_init_history ();
2073 * When changing the code which looks for a configuration file,
2074 * please also change the corresponding code in muttbug.sh.in.
2083 snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2085 if (access (buffer, F_OK) == -1)
2086 snprintf (buffer, sizeof (buffer), "%s/.muttngrc", NONULL (Homedir));
2087 if (access (buffer, F_OK) == -1)
2088 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s",
2089 NONULL (Homedir), MUTT_VERSION);
2090 if (access (buffer, F_OK) == -1)
2091 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc",
2095 Muttrc = str_dup (buffer);
2098 strfcpy (buffer, Muttrc, sizeof (buffer));
2100 mutt_expand_path (buffer, sizeof (buffer));
2101 Muttrc = str_dup (buffer);
2103 mem_free (&AliasFile);
2104 AliasFile = str_dup (NONULL (Muttrc));
2106 /* Process the global rc file if it exists and the user hasn't explicity
2107 requested not to via "-n". */
2109 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2111 if (access (buffer, F_OK) == -1)
2112 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", SYSCONFDIR);
2113 if (access (buffer, F_OK) == -1)
2114 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR,
2116 if (access (buffer, F_OK) == -1)
2117 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2118 if (access (buffer, F_OK) != -1) {
2119 if (source_rc (buffer, &err) != 0) {
2120 fputs (err.data, stderr);
2121 fputc ('\n', stderr);
2127 /* Read the user's initialization file. */
2128 if (access (Muttrc, F_OK) != -1) {
2129 if (!option (OPTNOCURSES))
2131 if (source_rc (Muttrc, &err) != 0) {
2132 fputs (err.data, stderr);
2133 fputc ('\n', stderr);
2137 else if (!default_rc) {
2138 /* file specified by -F does not exist */
2139 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2140 mutt_endwin (buffer);
2144 if (mutt_execute_commands (commands) != 0)
2147 /* warn about synonym variables */
2148 if (!list_empty(Synonyms)) {
2150 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2151 for (i = 0; i < Synonyms->length; i++)
2152 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2153 MuttVars[((syn_t*) Synonyms->data[i])->o].option,
2154 MuttVars[((syn_t*) Synonyms->data[i])->n].option,
2155 NONULL(((syn_t*) Synonyms->data[i])->f),
2156 ((syn_t*) Synonyms->data[i])->l);
2157 fprintf (stderr, _("Warning: Synonym variables are scheduled for removal.\n"));
2158 list_del (&Synonyms, syn_del);
2161 /* this is not needed during runtime */
2162 mem_free(&CurRCFile);
2164 if (need_pause && !option (OPTNOCURSES)) {
2165 if (mutt_any_key_to_continue (NULL) == -1)
2170 set_option (OPTWEED); /* turn weeding on by default */
2174 int mutt_get_hook_type (const char *name)
2176 struct command_t *c;
2178 for (c = Commands; c->name; c++)
2179 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
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 /* dump out the value of all the variables we have */
2190 int mutt_dump_variables (void) {
2193 char errbuff[STRING];
2194 char command[STRING];
2195 list2_t* tmp = NULL;
2199 memset (&err, 0, sizeof (err));
2200 memset (&token, 0, sizeof (token));
2203 err.dsize = sizeof (errbuff);
2205 /* get all non-synonyms into list... */
2206 for (i = 0; MuttVars[i].option; i++) {
2207 if (MuttVars[i].type == DT_SYN)
2209 list_push_back (&tmp, &MuttVars[i]);
2211 if (!list_empty(tmp)) {
2212 /* ...and dump list sorted */
2213 qsort (tmp->data, tmp->length, sizeof (void*), opt_cmp);
2214 for (i = 0; i < tmp->length; i++) {
2215 snprintf (command, sizeof (command), "set ?%s\n",
2216 ((struct option_t*) tmp->data[i])->option);
2217 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2218 fprintf (stderr, "%s\n", err.data);
2219 mem_free (&token.data);
2220 list_del (&tmp, NULL);
2223 printf("%s\n", err.data);
2226 mem_free (&token.data);
2227 list_del (&tmp, NULL);