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>
55 static int mutt_option_index (char*);
56 static const struct mapping_t* get_sortmap (int idx);
58 /* for synonym warning reports: synonym found during parsing */
62 int n; /* new name (index) */
63 int o; /* old name (index) */
66 /* for synonym warning reports: list of synonyms found */
67 static list2_t* Synonyms;
68 /* for synonym warning reports: current rc file */
69 static const char* CurRCFile = NULL;
70 /* for synonym warning reports: current rc line */
71 static int CurRCLine = 0;
73 /* prototypes for checking for special vars */
74 static int check_dsn_return (const char*);
75 static int check_dsn_notify (const char*);
77 /* variable <-> sanity check function mappings */
80 int (*check) (const char*);
82 { "dsn_notify", check_dsn_notify },
83 { "dsn_return", check_dsn_return },
84 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
85 { "smtp_use_tls", mutt_libesmtp_check_usetls },
91 /* protos for config type handles */
92 static void bool_to_string (char* dst, size_t dstlen, int idx);
93 static void num_to_string (char* dst, size_t dstlen, int idx);
94 static void str_to_string (char* dst, size_t dstlen, int idx);
95 static void quad_to_string (char* dst, size_t dstlen, int idx);
96 static void sort_to_string (char* dst, size_t dstlen, int idx);
97 static void rx_to_string (char* dst, size_t dstlen, int idx);
98 static void magic_to_string (char* dst, size_t dstlen, int idx);
99 static void syn_to_string (char* dst, size_t dstlen, int idx);
100 static void addr_to_string (char* dst, size_t dstlen, int idx);
104 void (*opt_to_string) (char* dst, size_t dstlen, int idx);
106 { 0, NULL }, /* there's no DT_ type with 0 */
107 { DT_BOOL, bool_to_string },
108 { DT_NUM, num_to_string },
109 { DT_STR, str_to_string },
110 { DT_PATH, str_to_string },
111 { DT_QUAD, quad_to_string },
112 { DT_SORT, sort_to_string },
113 { DT_RX, rx_to_string },
114 { DT_MAGIC, magic_to_string },
115 { DT_SYN, syn_to_string },
116 { DT_ADDR, addr_to_string }
119 static void bool_to_string (char* dst, size_t dstlen, int idx) {
120 snprintf (dst, dstlen, "%s=%s", MuttVars[idx].option,
121 MuttVars[idx].data ? "yes" : "no");
124 static void num_to_string (char* dst, size_t dstlen, int idx) {
126 const char* fmt = (idx == mutt_option_index ("umask")) ? "%s=%04o" : "%s=%d";
127 snprintf (dst, dstlen, fmt, MuttVars[idx].option,
128 *((short*) MuttVars[idx].data));
131 static void str_to_string (char* dst, size_t dstlen, int idx) {
132 snprintf (dst, dstlen, "%s=\"%s\"", MuttVars[idx].option,
133 NONULL (*((char**) MuttVars[idx].data)));
136 static void quad_to_string (char* dst, size_t dstlen, int idx) {
137 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
138 snprintf (dst, dstlen, "%s=%s", MuttVars[idx].option,
139 vals[quadoption (MuttVars[idx].data)]);
142 static void sort_to_string (char* dst, size_t dstlen, int idx) {
143 const struct mapping_t *map = get_sortmap (idx);
147 snprintf (dst, sizeof (dst), "%s=unknown", MuttVars[idx].option);
151 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK,
154 snprintf (dst, dstlen, "%s=%s%s%s", MuttVars[idx].option,
155 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ?
157 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" :
161 static void rx_to_string (char* dst, size_t dstlen, int idx) {
162 rx_t* p = (rx_t*) MuttVars[idx].data;
163 snprintf (dst, dstlen, "%s=\"%s\"", MuttVars[idx].option,
164 NONULL (p->pattern));
167 static void magic_to_string (char* dst, size_t dstlen, int idx) {
168 const char* s = NULL;
169 switch (MuttVars[idx].data) {
170 case M_MBOX: s = "mbox"; break;
171 case M_MMDF: s = "MMDF"; break;
172 case M_MH: s = "MH"; break;
173 case M_MAILDIR: s = "Maildir"; break;
174 default: s = "unknown"; break;
176 snprintf (dst, dstlen, "%s=%s", MuttVars[idx].option, s);
179 static void syn_to_string (char* dst, size_t dstlen, int idx) {
180 int i = mutt_option_index ((char*) MuttVars[idx].data);
181 FuncTable[MuttVars[i].type].opt_to_string (dst, dstlen, i);
184 static void addr_to_string (char* dst, size_t dstlen, int idx) {
187 rfc822_write_address (s, sizeof (s), *((ADDRESS**) MuttVars[idx].data), 0);
188 snprintf (dst, dstlen, "%s=\"%s\"", MuttVars[idx].option, NONULL (s));
191 int mutt_option_value (const char* val, char* dst, size_t dstlen) {
192 int i = mutt_option_index ((char*) val);
193 char* tmp = NULL, *t = NULL;
197 debug_print (1, ("var '%s' not found, i = %d\n", val, i));
201 tmp = mem_malloc (dstlen+1);
202 FuncTable[DTYPE (MuttVars[i].type)].opt_to_string (tmp, dstlen, i);
204 /* as we get things of type $var=value and don't want to bloat the
205 * above "just" for expansion, we do the stripping here */
206 debug_print (1, ("orig == '%s'\n", tmp));
207 t = strchr (tmp, '=');
211 if (t[l-1] == '"' && *t == '"') {
216 memcpy (dst, t, l+1);
218 debug_print (1, ("stripped == '%s'\n", dst));
223 /* for synonym warning reports: adds synonym to end of list */
224 static void syn_add (int n, int o) {
225 syn_t* tmp = mem_malloc (sizeof (syn_t));
226 tmp->f = str_dup (CurRCFile);
230 list_push_back (&Synonyms, tmp);
233 /* for synonym warning reports: free single item (for list_del()) */
234 static void syn_del (void** p) {
235 mem_free(&(*(syn_t**) p)->f);
239 void toggle_quadoption (int opt)
242 int b = (opt % 4) * 2;
244 QuadOptions[n] ^= (1 << b);
247 void set_quadoption (int opt, int flag)
250 int b = (opt % 4) * 2;
252 QuadOptions[n] &= ~(0x3 << b);
253 QuadOptions[n] |= (flag & 0x3) << b;
256 int quadoption (int opt)
259 int b = (opt % 4) * 2;
261 return (QuadOptions[n] >> b) & 0x3;
264 int query_quadoption (int opt, const char *prompt)
266 int v = quadoption (opt);
274 v = mutt_yesorno (prompt, (v == M_ASKYES));
275 CLEARLINE (LINES - 1);
282 /* given the variable ``s'', return the index into the rc_vars array which
283 matches, or -1 if the variable is not found. */
284 static int mutt_option_index (char *s)
288 for (i = 0; MuttVars[i].option; i++)
289 if (str_cmp (s, MuttVars[i].option) == 0) {
290 if (MuttVars[i].type == DT_SYN)
291 syn_add (mutt_option_index ((char *) MuttVars[i].data), i);
292 return (MuttVars[i].type ==
293 DT_SYN ? mutt_option_index ((char *) MuttVars[i].data) : i);
298 static void add_to_list (LIST ** list, const char *str)
300 LIST *t, *last = NULL;
302 /* don't add a NULL or empty string to the list */
303 if (!str || *str == '\0')
306 /* check to make sure the item is not already on this list */
307 for (last = *list; last; last = last->next) {
308 if (ascii_strcasecmp (str, last->data) == 0) {
309 /* already on the list, so just ignore it */
317 if (!*list || last) {
318 t = (LIST *) mem_calloc (1, sizeof (LIST));
319 t->data = str_dup (str);
329 static int add_to_rx_list (list2_t** list, const char *s, int flags,
338 if (!(rx = rx_compile (s, flags))) {
339 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
343 i = rx_lookup ((*list), rx->pattern);
347 list_push_back (list, rx);
351 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
352 const char *templ, BUFFER * err)
354 SPAM_LIST *t = NULL, *last = NULL;
359 if (!pat || !*pat || !templ)
362 if (!(rx = rx_compile (pat, REG_ICASE))) {
363 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
367 /* check to make sure the item is not already on this list */
368 for (last = *list; last; last = last->next) {
369 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
370 /* Already on the list. Formerly we just skipped this case, but
371 * now we're supporting removals, which means we're supporting
372 * re-adds conceptually. So we probably want this to imply a
373 * removal, then do an add. We can achieve the removal by freeing
374 * the template, and leaving t pointed at the current item.
377 mem_free(t->template);
384 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
385 * update. Otherwise we want to make a new one to link at the list's end.
388 t = mutt_new_spam_list ();
396 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
397 t->template = str_dup (templ);
399 /* Find highest match number in template string */
401 for (p = templ; *p;) {
406 while (*p && isdigit ((int) *p))
412 t->nmatch++; /* match 0 is always the whole expr */
417 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
419 SPAM_LIST *spam, *prev;
422 /* Being first is a special case. */
426 if (spam->rx && !str_cmp (spam->rx->pattern, pat)) {
429 mem_free(&spam->template);
435 for (spam = prev->next; spam;) {
436 if (!str_cmp (spam->rx->pattern, pat)) {
437 prev->next = spam->next;
439 mem_free(spam->template);
452 static void remove_from_list (LIST ** l, const char *str)
454 LIST *p, *last = NULL;
456 if (str_cmp ("*", str) == 0)
457 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
462 if (ascii_strcasecmp (str, p->data) == 0) {
465 last->next = p->next;
478 static int remove_from_rx_list (list2_t** l, const char *str)
482 if (str_cmp ("*", str) == 0) {
483 list_del (l, (list_del_t*) rx_free);
487 i = rx_lookup ((*l), str);
489 rx_t* r = list_pop_idx ((*l), i);
497 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
503 memset (&token, 0, sizeof (token));
504 mutt_extract_token (tmp, s, 0);
506 /* is the item defined as a variable or a function? */
507 if (!(res = (mutt_option_index (tmp->data) != -1)))
508 for (i = 0; !res && i < MENU_MAX; i++) {
509 struct binding_t *b = km_get_table (Menus[i].value);
514 for (j = 0; b[j].name; j++)
515 if (!ascii_strncasecmp (tmp->data, b[j].name, str_len (tmp->data))
516 && (str_len (b[j].name) == str_len (tmp->data))) {
521 /* check for feature_* */
526 j = str_len (tmp->data);
527 /* need at least input of 'feature_X' */
531 while (Features[i].name) {
532 if (str_len (Features[i].name) == j &&
533 ascii_strncasecmp (Features[i].name, p, j)) {
544 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
546 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
549 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
551 if ((data && res) || (!data && !res)) {
552 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
553 mutt_error ("Error: %s", err->data);
554 mem_free (&token.data);
557 mem_free (&token.data);
562 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
566 mutt_extract_token (buf, s, 0);
568 /* don't add "*" to the unignore list */
569 if (strcmp (buf->data, "*"))
570 add_to_list (&UnIgnore, buf->data);
572 remove_from_list (&Ignore, buf->data);
574 while (MoreArgs (s));
579 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
583 mutt_extract_token (buf, s, 0);
584 remove_from_list (&UnIgnore, buf->data);
585 add_to_list (&Ignore, buf->data);
587 while (MoreArgs (s));
592 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
596 mutt_extract_token (buf, s, 0);
597 add_to_list ((LIST **) data, buf->data);
599 while (MoreArgs (s));
604 static void _alternates_clean (void)
608 if (Context && Context->msgcount) {
609 for (i = 0; i < Context->msgcount; i++)
610 Context->hdrs[i]->recip_valid = 0;
614 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
617 _alternates_clean ();
619 mutt_extract_token (buf, s, 0);
620 remove_from_rx_list (&UnAlternates, buf->data);
622 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
625 while (MoreArgs (s));
630 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
633 _alternates_clean ();
635 mutt_extract_token (buf, s, 0);
636 remove_from_rx_list (&Alternates, buf->data);
638 if (str_cmp (buf->data, "*") &&
639 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
643 while (MoreArgs (s));
648 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
653 memset (&templ, 0, sizeof (templ));
655 /* Insist on at least one parameter */
658 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
660 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
664 /* Extract the first token, a regexp */
665 mutt_extract_token (buf, s, 0);
667 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
668 if (data == M_SPAM) {
669 /* If there's a second parameter, it's a template for the spam tag. */
671 mutt_extract_token (&templ, s, 0);
673 /* Add to the spam list. */
674 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
675 mem_free (&templ.data);
678 mem_free (&templ.data);
681 /* If not, try to remove from the nospam list. */
683 remove_from_rx_list (&NoSpamList, buf->data);
689 /* M_NOSPAM is for nospam commands. */
690 else if (data == M_NOSPAM) {
691 /* nospam only ever has one parameter. */
693 /* "*" is a special case. */
694 if (!str_cmp (buf->data, "*")) {
695 mutt_free_spam_list (&SpamList);
696 list_del (&NoSpamList, (list_del_t*) rx_free);
700 /* If it's on the spam list, just remove it. */
701 if (remove_from_spam_list (&SpamList, buf->data) != 0)
704 /* Otherwise, add it to the nospam list. */
705 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
711 /* This should not happen. */
712 strfcpy (err->data, "This is no good at all.", err->dsize);
716 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
720 mutt_extract_token (buf, s, 0);
722 * Check for deletion of entire list
724 if (str_cmp (buf->data, "*") == 0) {
725 mutt_free_list ((LIST **) data);
728 remove_from_list ((LIST **) data, buf->data);
730 while (MoreArgs (s));
735 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
739 mutt_extract_token (buf, s, 0);
740 remove_from_rx_list (&UnMailLists, buf->data);
742 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
745 while (MoreArgs (s));
750 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
754 mutt_extract_token (buf, s, 0);
755 remove_from_rx_list (&SubscribedLists, buf->data);
756 remove_from_rx_list (&MailLists, buf->data);
758 if (str_cmp (buf->data, "*") &&
759 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
762 while (MoreArgs (s));
767 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
771 mutt_extract_token (buf, s, 0);
772 remove_from_rx_list (&UnMailLists, buf->data);
773 remove_from_rx_list (&UnSubscribedLists, buf->data);
775 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
777 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
780 while (MoreArgs (s));
785 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
789 mutt_extract_token (buf, s, 0);
790 remove_from_rx_list (&SubscribedLists, buf->data);
792 if (str_cmp (buf->data, "*") &&
793 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
796 while (MoreArgs (s));
801 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
804 ALIAS *tmp, *last = NULL;
807 mutt_extract_token (buf, s, 0);
809 if (str_cmp ("*", buf->data) == 0) {
810 if (CurrentMenu == MENU_ALIAS) {
811 for (tmp = Aliases; tmp; tmp = tmp->next)
813 set_option (OPTFORCEREDRAWINDEX);
816 mutt_free_alias (&Aliases);
820 for (tmp = Aliases; tmp; tmp = tmp->next) {
821 if (str_casecmp (buf->data, tmp->name) == 0) {
822 if (CurrentMenu == MENU_ALIAS) {
824 set_option (OPTFORCEREDRAWINDEX);
829 last->next = tmp->next;
833 mutt_free_alias (&tmp);
839 while (MoreArgs (s));
843 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
846 ALIAS *tmp = Aliases;
851 strfcpy (err->data, _("alias: no address"), err->dsize);
855 mutt_extract_token (buf, s, 0);
857 debug_print (2, ("first token is '%s'.\n", buf->data));
859 /* check to see if an alias with this name already exists */
860 for (; tmp; tmp = tmp->next) {
861 if (!str_casecmp (tmp->name, buf->data))
867 /* create a new alias */
868 tmp = (ALIAS *) mem_calloc (1, sizeof (ALIAS));
870 tmp->name = str_dup (buf->data);
871 /* give the main addressbook code a chance */
872 if (CurrentMenu == MENU_ALIAS)
873 set_option (OPTMENUCALLER);
876 /* override the previous value */
877 rfc822_free_address (&tmp->addr);
878 if (CurrentMenu == MENU_ALIAS)
879 set_option (OPTFORCEREDRAWINDEX);
882 mutt_extract_token (buf, s,
883 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
884 debug_print (2, ("second token is '%s'.\n", buf->data));
885 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
890 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
891 snprintf (err->data, err->dsize,
892 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
896 if (DebugLevel >= 2) {
899 for (a = tmp->addr; a; a = a->next) {
901 debug_print (2, ("%s\n", a->mailbox));
903 debug_print (2, ("group %s\n", a->mailbox));
911 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
914 LIST *tmp = UserHeader;
919 mutt_extract_token (buf, s, 0);
920 if (str_cmp ("*", buf->data) == 0)
921 mutt_free_list (&UserHeader);
926 l = str_len (buf->data);
927 if (buf->data[l - 1] == ':')
931 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
932 && tmp->data[l] == ':') {
935 last->next = tmp->next;
937 UserHeader = tmp->next;
940 mutt_free_list (&ptr);
949 while (MoreArgs (s));
953 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
960 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
961 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
962 strfcpy (err->data, _("invalid header field"), err->dsize);
965 keylen = p - buf->data + 1;
968 for (tmp = UserHeader;; tmp = tmp->next) {
969 /* see if there is already a field by this name */
970 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
971 /* replace the old value */
972 mem_free (&tmp->data);
973 tmp->data = buf->data;
974 memset (buf, 0, sizeof (BUFFER));
980 tmp->next = mutt_new_list ();
984 tmp = mutt_new_list ();
987 tmp->data = buf->data;
988 memset (buf, 0, sizeof (BUFFER));
993 parse_sort (short *val, const char *s, const struct mapping_t *map,
998 if (str_ncmp ("reverse-", s, 8) == 0) {
1000 flags = SORT_REVERSE;
1003 if (str_ncmp ("last-", s, 5) == 0) {
1008 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1009 snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s);
1018 static void mutt_set_default (struct option_t *p)
1020 switch (p->type & DT_MASK) {
1022 if (!p->init && *((char **) p->data))
1023 p->init = (unsigned long) str_dup (*((char **) p->data));
1026 if (!p->init && *((char **) p->data)) {
1027 char *cp = str_dup (*((char **) p->data));
1029 /* mutt_pretty_mailbox (cp); */
1030 p->init = (unsigned long) cp;
1034 if (!p->init && *((ADDRESS **) p->data)) {
1035 char tmp[HUGE_STRING];
1038 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data), 0);
1039 p->init = (unsigned long) str_dup (tmp);
1044 rx_t* pp = (rx_t*) p->data;
1046 if (!p->init && pp->pattern)
1047 p->init = (unsigned long) str_dup (pp->pattern);
1053 static void mutt_restore_default (struct option_t *p)
1055 switch (p->type & DT_MASK) {
1058 str_replace ((char **) p->data, (char *) p->init);
1062 char path[_POSIX_PATH_MAX];
1064 strfcpy (path, (char *) p->init, sizeof (path));
1065 mutt_expand_path (path, sizeof (path));
1066 str_replace ((char **) p->data, path);
1071 rfc822_free_address ((ADDRESS **) p->data);
1072 *((ADDRESS **) p->data) = rfc822_parse_adrlist (NULL, (char *) p->init);
1077 set_option (p->data);
1079 unset_option (p->data);
1082 set_quadoption (p->data, p->init);
1087 *((short *) p->data) = p->init;
1091 rx_t *pp = (rx_t *) p->data;
1094 mem_free (&pp->pattern);
1101 char *s = (char *) p->init;
1103 pp->rx = mem_calloc (1, sizeof (regex_t));
1104 if (str_cmp (p->option, "mask") != 0)
1105 flags |= mutt_which_case ((const char *) p->init);
1106 if (str_cmp (p->option, "mask") == 0 && *s == '!') {
1110 if (REGCOMP (pp->rx, s, flags) != 0) {
1112 _("mutt_restore_default(%s): error in regexp: %s\n"),
1113 p->option, pp->pattern);
1114 mem_free (&pp->pattern);
1119 str_replace (&pp->pattern, (char *) p->init);
1125 if (p->flags & R_INDEX)
1126 set_option (OPTFORCEREDRAWINDEX);
1127 if (p->flags & R_PAGER)
1128 set_option (OPTFORCEREDRAWPAGER);
1129 if (p->flags & R_RESORT_SUB)
1130 set_option (OPTSORTSUBTHREADS);
1131 if (p->flags & R_RESORT)
1132 set_option (OPTNEEDRESORT);
1133 if (p->flags & R_RESORT_INIT)
1134 set_option (OPTRESORTINIT);
1135 if (p->flags & R_TREE)
1136 set_option (OPTREDRAWTREE);
1139 /* check whether value for $dsn_return would be valid */
1140 static int check_dsn_return (const char* val) {
1141 if (val && *val && str_ncmp (val, "hdrs", 4) != 0 &&
1142 str_ncmp (val, "full", 4) != 0)
1147 /* check whether value for $dsn_notify would be valid */
1148 static int check_dsn_notify (const char* val) {
1149 list2_t* list = NULL;
1154 list = list_from_str (val, ",");
1155 if (list_empty (list))
1158 for (i = 0; i < list->length; i++)
1159 if (str_ncmp (list->data[i], "never", 5) != 0 &&
1160 str_ncmp (list->data[i], "failure", 7) != 0 &&
1161 str_ncmp (list->data[i], "delay", 5) != 0 &&
1162 str_ncmp (list->data[i], "success", 7) != 0) {
1166 list_del (&list, (list_del_t*) _mem_free);
1170 static int check_special (const char* name, const char* val) {
1173 for (i = 0; SpecialVars[i].name; i++) {
1174 if (str_cmp (SpecialVars[i].name, name) == 0)
1175 return (SpecialVars[i].check (val));
1180 static const struct mapping_t* get_sortmap (int idx) {
1181 const struct mapping_t* map = NULL;
1183 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) {
1185 map = SortAliasMethods;
1187 case DT_SORT_BROWSER:
1188 map = SortBrowserMethods;
1191 if ((WithCrypto & APPLICATION_PGP))
1192 map = SortKeyMethods;
1195 map = SortAuxMethods;
1204 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1207 int idx, query, unset, inv, reset, r = 0;
1208 char *p, scratch[_POSIX_PATH_MAX];
1210 while (MoreArgs (s)) {
1211 /* reset state variables */
1213 unset = data & M_SET_UNSET;
1214 inv = data & M_SET_INV;
1215 reset = data & M_SET_RESET;
1217 if (*s->dptr == '?') {
1221 else if (str_ncmp ("no", s->dptr, 2) == 0) {
1225 else if (str_ncmp ("inv", s->dptr, 3) == 0) {
1229 else if (*s->dptr == '&') {
1234 /* get the variable name */
1235 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1237 if ((idx = mutt_option_index (tmp->data)) == -1 &&
1238 !(reset && !str_cmp ("all", tmp->data))) {
1239 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1245 if (query || unset || inv) {
1246 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1250 if (s && *s->dptr == '=') {
1251 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1255 if (!str_cmp ("all", tmp->data)) {
1256 for (idx = 0; MuttVars[idx].option; idx++)
1257 mutt_restore_default (&MuttVars[idx]);
1261 mutt_restore_default (&MuttVars[idx]);
1263 else if (DTYPE (MuttVars[idx].type) == DT_BOOL) {
1264 if (s && *s->dptr == '=') {
1265 if (unset || inv || query) {
1266 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1271 mutt_extract_token (tmp, s, 0);
1272 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1274 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1277 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1283 bool_to_string (err->data, err->dsize, idx);
1288 unset_option (MuttVars[idx].data);
1290 toggle_option (MuttVars[idx].data);
1292 set_option (MuttVars[idx].data);
1294 else if (DTYPE (MuttVars[idx].type) == DT_STR ||
1295 DTYPE (MuttVars[idx].type) == DT_PATH ||
1296 DTYPE (MuttVars[idx].type) == DT_ADDR) {
1298 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1299 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1301 mem_free ((void *) MuttVars[idx].data);
1303 else if (query || *s->dptr != '=') {
1304 FuncTable[DTYPE (MuttVars[idx].type)].opt_to_string (err->data, err->dsize, idx);
1310 /* copy the value of the string */
1311 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1312 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1314 mem_free ((void *) MuttVars[idx].data);
1316 mutt_extract_token (tmp, s, 0);
1317 if (DTYPE (MuttVars[idx].type) == DT_PATH) {
1318 strfcpy (scratch, tmp->data, sizeof (scratch));
1319 mutt_expand_path (scratch, sizeof (scratch));
1320 *((char **) MuttVars[idx].data) = str_dup (scratch);
1322 else if (DTYPE (MuttVars[idx].type) == DT_STR) {
1323 /* see if the value may only be a certain value... */
1324 if (check_special (MuttVars[idx].option, tmp->data)) {
1325 *((char **) MuttVars[idx].data) = str_dup (tmp->data);
1326 if (str_cmp (MuttVars[idx].option, "charset") == 0)
1327 mutt_set_charset (Charset);
1329 /* ... and abort if it fails */
1330 snprintf (err->data, err->dsize, "'%s' is invalid for $%s",
1331 tmp->data, MuttVars[idx].option);
1336 *((ADDRESS **) MuttVars[idx].data) =
1337 rfc822_parse_adrlist (NULL, tmp->data);
1341 else if (DTYPE (MuttVars[idx].type) == DT_RX) {
1342 rx_t *ptr = (rx_t *) MuttVars[idx].data;
1346 if (query || *s->dptr != '=') {
1347 rx_to_string (err->data, err->dsize, idx);
1351 if (option (OPTATTACHMSG)
1352 && !str_cmp (MuttVars[idx].option, "reply_regexp")) {
1353 snprintf (err->data, err->dsize,
1354 "Operation not permitted when in attach-message mode.");
1361 /* copy the value of the string */
1362 mutt_extract_token (tmp, s, 0);
1364 if (!ptr->pattern || str_cmp (ptr->pattern, tmp->data) != 0) {
1367 /* $mask is case-sensitive */
1368 if (str_cmp (MuttVars[idx].option, "mask") != 0)
1369 flags |= mutt_which_case (tmp->data);
1372 if (str_cmp (MuttVars[idx].option, "mask") == 0) {
1379 rx = (regex_t *) mem_malloc (sizeof (regex_t));
1380 if ((e = REGCOMP (rx, p, flags)) != 0) {
1381 regerror (e, rx, err->data, err->dsize);
1387 /* get here only if everything went smootly */
1389 mem_free (&ptr->pattern);
1390 regfree ((regex_t *) ptr->rx);
1391 mem_free (&ptr->rx);
1394 ptr->pattern = str_dup (tmp->data);
1398 /* $reply_regexp and $alterantes require special treatment */
1400 if (Context && Context->msgcount &&
1401 str_cmp (MuttVars[idx].option, "reply_regexp") == 0) {
1402 regmatch_t pmatch[1];
1405 #define CUR_ENV Context->hdrs[i]->env
1406 for (i = 0; i < Context->msgcount; i++) {
1407 if (CUR_ENV && CUR_ENV->subject) {
1408 CUR_ENV->real_subj = (regexec (ReplyRegexp.rx,
1409 CUR_ENV->subject, 1, pmatch,
1411 subject : CUR_ENV->subject + pmatch[0].rm_eo;
1418 else if (DTYPE (MuttVars[idx].type) == DT_MAGIC) {
1420 if (query || *s->dptr != '=') {
1421 magic_to_string (err->data, err->dsize, idx);
1427 /* copy the value of the string */
1428 mutt_extract_token (tmp, s, 0);
1429 if (mx_set_magic (tmp->data)) {
1430 snprintf (err->data, err->dsize, _("%s: invalid mailbox type"),
1436 else if (DTYPE (MuttVars[idx].type) == DT_NUM) {
1437 short *ptr = (short *) MuttVars[idx].data;
1441 if (query || *s->dptr != '=') {
1442 num_to_string (err->data, err->dsize, idx);
1448 mutt_extract_token (tmp, s, 0);
1449 val = strtol (tmp->data, &t, 0);
1451 if (!*tmp->data || *t || (short) val != val) {
1452 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1459 /* these ones need a sanity check */
1460 if (str_cmp (MuttVars[idx].option, "history") == 0) {
1463 mutt_init_history ();
1465 else if (str_cmp (MuttVars[idx].option, "pager_index_lines") == 0) {
1470 else if (DTYPE (MuttVars[idx].type) == DT_QUAD) {
1473 quad_to_string (err->data, err->dsize, idx);
1477 if (*s->dptr == '=') {
1479 mutt_extract_token (tmp, s, 0);
1480 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1481 set_quadoption (MuttVars[idx].data, M_YES);
1482 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1483 set_quadoption (MuttVars[idx].data, M_NO);
1484 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1485 set_quadoption (MuttVars[idx].data, M_ASKYES);
1486 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1487 set_quadoption (MuttVars[idx].data, M_ASKNO);
1489 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1496 toggle_quadoption (MuttVars[idx].data);
1498 set_quadoption (MuttVars[idx].data, M_NO);
1500 set_quadoption (MuttVars[idx].data, M_YES);
1503 else if (DTYPE (MuttVars[idx].type) == DT_SORT) {
1504 const struct mapping_t *map = NULL;
1506 if (query || *s->dptr != '=') {
1507 sort_to_string (err->data, err->dsize, idx);
1511 /* do this here so we don't ordinarily do it twice for queries */
1512 if (!(map = get_sortmap (idx))) {
1513 snprintf (err->data, err->dsize, _("%s: Unknown type."),
1514 MuttVars[idx].option);
1520 mutt_extract_token (tmp, s, 0);
1522 if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) ==
1529 snprintf (err->data, err->dsize, _("%s: unknown type"),
1530 MuttVars[idx].option);
1535 if (MuttVars[idx].flags & R_INDEX)
1536 set_option (OPTFORCEREDRAWINDEX);
1537 if (MuttVars[idx].flags & R_PAGER)
1538 set_option (OPTFORCEREDRAWPAGER);
1539 if (MuttVars[idx].flags & R_RESORT_SUB)
1540 set_option (OPTSORTSUBTHREADS);
1541 if (MuttVars[idx].flags & R_RESORT)
1542 set_option (OPTNEEDRESORT);
1543 if (MuttVars[idx].flags & R_RESORT_INIT)
1544 set_option (OPTRESORTINIT);
1545 if (MuttVars[idx].flags & R_TREE)
1546 set_option (OPTREDRAWTREE);
1553 /* reads the specified initialization file. returns -1 if errors were found
1554 so that we can pause to let the user know... */
1555 static int source_rc (const char *rcfile, BUFFER * err)
1558 int line = 0, rc = 0, conv = 0;
1560 char *linebuf = NULL;
1561 char *currentline = NULL;
1565 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
1567 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1568 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1572 memset (&token, 0, sizeof (token));
1573 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
1574 conv = ConfigCharset && (*ConfigCharset) && Charset;
1576 currentline = str_dup (linebuf);
1579 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1582 currentline = linebuf;
1587 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1588 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1589 if (--rc < -MAXERRS) {
1591 mem_free (¤tline);
1600 mem_free (¤tline);
1602 mem_free (&token.data);
1603 mem_free (&linebuf);
1606 mutt_wait_filter (pid);
1608 /* the muttrc source keyword */
1609 snprintf (err->data, err->dsize,
1610 rc >= -MAXERRS ? _("source: errors in %s")
1611 : _("source: reading aborted due too many errors in %s"),
1620 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
1623 char path[_POSIX_PATH_MAX];
1627 if (mutt_extract_token (tmp, s, 0) != 0) {
1628 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1632 strfcpy (path, tmp->data, sizeof (path));
1633 mutt_expand_path (path, sizeof (path));
1635 rc += source_rc (path, err);
1637 while (MoreArgs (s));
1639 return ((rc < 0) ? -1 : 0);
1642 /* line command to execute
1644 token scratch buffer to be used by parser. caller should free
1645 token->data when finished. the reason for this variable is
1646 to avoid having to allocate and deallocate a lot of memory
1647 if we are parsing many lines. the caller can pass in the
1648 memory to use, which avoids having to create new space for
1649 every call to this function.
1651 err where to write error messages */
1652 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1657 memset (&expn, 0, sizeof (expn));
1658 expn.data = expn.dptr = line;
1659 expn.dsize = str_len (line);
1664 while (*expn.dptr) {
1665 if (*expn.dptr == '#')
1666 break; /* rest of line is a comment */
1667 if (*expn.dptr == ';') {
1671 mutt_extract_token (token, &expn, 0);
1672 for (i = 0; Commands[i].name; i++) {
1673 if (!str_cmp (token->data, Commands[i].name)) {
1674 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1679 if (!Commands[i].name) {
1680 snprintf (err->data, err->dsize, _("%s: unknown command"),
1681 NONULL (token->data));
1688 mem_free (&expn.data);
1693 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1694 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1695 /* initial string that starts completion. No telling how much crap
1696 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1697 char User_typed[LONG_STRING] = { 0 };
1699 int Num_matched = 0; /* Number of matches for completion */
1700 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1701 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1703 /* helper function for completion. Changes the dest buffer if
1704 necessary/possible to aid completion.
1705 dest == completion result gets here.
1706 src == candidate for completion.
1707 try == user entered data for completion.
1708 len == length of dest buffer.
1710 static void candidate (char *dest, char *try, char *src, int len)
1714 if (strstr (src, try) == src) {
1715 Matches[Num_matched++] = src;
1717 strfcpy (dest, src, len);
1719 for (l = 0; src[l] && src[l] == dest[l]; l++);
1725 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1729 int spaces; /* keep track of the number of leading spaces on the line */
1732 spaces = buffer - pt;
1734 pt = buffer + pos - spaces;
1735 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1738 if (pt == buffer) { /* complete cmd */
1739 /* first TAB. Collect all the matches */
1742 strfcpy (User_typed, pt, sizeof (User_typed));
1743 memset (Matches, 0, sizeof (Matches));
1744 memset (Completed, 0, sizeof (Completed));
1745 for (num = 0; Commands[num].name; num++)
1746 candidate (Completed, User_typed, Commands[num].name,
1747 sizeof (Completed));
1748 Matches[Num_matched++] = User_typed;
1750 /* All matches are stored. Longest non-ambiguous string is ""
1751 * i.e. dont change 'buffer'. Fake successful return this time */
1752 if (User_typed[0] == 0)
1756 if (Completed[0] == 0 && User_typed[0])
1759 /* Num_matched will _always_ be atleast 1 since the initial
1760 * user-typed string is always stored */
1761 if (numtabs == 1 && Num_matched == 2)
1762 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1763 else if (numtabs > 1 && Num_matched > 2)
1764 /* cycle thru all the matches */
1765 snprintf (Completed, sizeof (Completed), "%s",
1766 Matches[(numtabs - 2) % Num_matched]);
1768 /* return the completed command */
1769 strncpy (buffer, Completed, len - spaces);
1771 else if (!str_ncmp (buffer, "set", 3)
1772 || !str_ncmp (buffer, "unset", 5)
1773 || !str_ncmp (buffer, "reset", 5)
1774 || !str_ncmp (buffer, "toggle", 6)) { /* complete variables */
1775 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1778 /* loop through all the possible prefixes (no, inv, ...) */
1779 if (!str_ncmp (buffer, "set", 3)) {
1780 for (num = 0; prefixes[num]; num++) {
1781 if (!str_ncmp (pt, prefixes[num], str_len (prefixes[num]))) {
1782 pt += str_len (prefixes[num]);
1788 /* first TAB. Collect all the matches */
1791 strfcpy (User_typed, pt, sizeof (User_typed));
1792 memset (Matches, 0, sizeof (Matches));
1793 memset (Completed, 0, sizeof (Completed));
1794 for (num = 0; MuttVars[num].option; num++)
1795 candidate (Completed, User_typed, MuttVars[num].option,
1796 sizeof (Completed));
1797 Matches[Num_matched++] = User_typed;
1799 /* All matches are stored. Longest non-ambiguous string is ""
1800 * i.e. dont change 'buffer'. Fake successful return this time */
1801 if (User_typed[0] == 0)
1805 if (Completed[0] == 0 && User_typed[0])
1808 /* Num_matched will _always_ be atleast 1 since the initial
1809 * user-typed string is always stored */
1810 if (numtabs == 1 && Num_matched == 2)
1811 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1812 else if (numtabs > 1 && Num_matched > 2)
1813 /* cycle thru all the matches */
1814 snprintf (Completed, sizeof (Completed), "%s",
1815 Matches[(numtabs - 2) % Num_matched]);
1817 strncpy (pt, Completed, buffer + len - pt - spaces);
1819 else if (!str_ncmp (buffer, "exec", 4)) {
1820 struct binding_t *menu = km_get_table (CurrentMenu);
1822 if (!menu && CurrentMenu != MENU_PAGER)
1826 /* first TAB. Collect all the matches */
1829 strfcpy (User_typed, pt, sizeof (User_typed));
1830 memset (Matches, 0, sizeof (Matches));
1831 memset (Completed, 0, sizeof (Completed));
1832 for (num = 0; menu[num].name; num++)
1833 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1834 /* try the generic menu */
1835 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1837 for (num = 0; menu[num].name; num++)
1838 candidate (Completed, User_typed, menu[num].name,
1839 sizeof (Completed));
1841 Matches[Num_matched++] = User_typed;
1843 /* All matches are stored. Longest non-ambiguous string is ""
1844 * i.e. dont change 'buffer'. Fake successful return this time */
1845 if (User_typed[0] == 0)
1849 if (Completed[0] == 0 && User_typed[0])
1852 /* Num_matched will _always_ be atleast 1 since the initial
1853 * user-typed string is always stored */
1854 if (numtabs == 1 && Num_matched == 2)
1855 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1856 else if (numtabs > 1 && Num_matched > 2)
1857 /* cycle thru all the matches */
1858 snprintf (Completed, sizeof (Completed), "%s",
1859 Matches[(numtabs - 2) % Num_matched]);
1861 strncpy (pt, Completed, buffer + len - pt - spaces);
1869 int mutt_var_value_complete (char *buffer, size_t len, int pos)
1871 char var[STRING], *pt = buffer;
1878 spaces = buffer - pt;
1880 pt = buffer + pos - spaces;
1881 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1883 pt++; /* move past the space */
1884 if (*pt == '=') /* abort if no var before the '=' */
1887 if (str_ncmp (buffer, "set", 3) == 0) {
1890 strfcpy (var, pt, sizeof (var));
1891 /* ignore the trailing '=' when comparing */
1892 var[str_len (var) - 1] = 0;
1893 if ((idx = mutt_option_index (var)) == -1)
1894 return 0; /* no such variable. */
1896 char tmp[LONG_STRING], tmp2[LONG_STRING];
1898 size_t dlen = buffer + len - pt - spaces;
1899 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1903 if ((DTYPE (MuttVars[idx].type) == DT_STR) ||
1904 (DTYPE (MuttVars[idx].type) == DT_PATH) ||
1905 (DTYPE (MuttVars[idx].type) == DT_RX)) {
1906 strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data)), sizeof (tmp));
1907 if (DTYPE (MuttVars[idx].type) == DT_PATH)
1908 mutt_pretty_mailbox (tmp);
1910 else if (DTYPE (MuttVars[idx].type) == DT_ADDR) {
1911 rfc822_write_address (tmp, sizeof (tmp),
1912 *((ADDRESS **) MuttVars[idx].data), 0);
1914 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
1915 strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp));
1916 else if (DTYPE (MuttVars[idx].type) == DT_NUM)
1917 snprintf (tmp, sizeof (tmp), "%d", (*((short *) MuttVars[idx].data)));
1918 else if (DTYPE (MuttVars[idx].type) == DT_SORT) {
1919 const struct mapping_t *map;
1922 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) {
1924 map = SortAliasMethods;
1926 case DT_SORT_BROWSER:
1927 map = SortBrowserMethods;
1930 if ((WithCrypto & APPLICATION_PGP))
1931 map = SortKeyMethods;
1940 mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK,
1942 snprintf (tmp, sizeof (tmp), "%s%s%s",
1943 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ?
1945 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" :
1948 else if (DTYPE (MuttVars[idx].type) == DT_MAGIC) {
1950 switch (DefaultMagic) {
1966 strfcpy (tmp, p, sizeof (tmp));
1968 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
1969 strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no",
1974 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
1975 if (*s == '\\' || *s == '"')
1981 strfcpy (tmp, pt, sizeof (tmp));
1982 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
1990 /* Implement the -Q command line flag */
1991 int mutt_query_variables (LIST * queries)
1995 char errbuff[STRING];
1996 char command[STRING];
2000 memset (&err, 0, sizeof (err));
2001 memset (&token, 0, sizeof (token));
2004 err.dsize = sizeof (errbuff);
2006 for (p = queries; p; p = p->next) {
2007 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2008 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2009 fprintf (stderr, "%s\n", err.data);
2010 mem_free (&token.data);
2013 printf ("%s\n", err.data);
2016 mem_free (&token.data);
2020 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2024 for (i = 0; map[i].name; i++)
2025 if (map[i].value == val)
2026 return (map[i].name);
2030 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2034 for (i = 0; map[i].name; i++)
2035 if (ascii_strcasecmp (map[i].name, name) == 0)
2036 return (map[i].value);
2040 static int mutt_execute_commands (LIST * p)
2043 char errstr[SHORT_STRING];
2045 memset (&err, 0, sizeof (err));
2047 err.dsize = sizeof (errstr);
2048 memset (&token, 0, sizeof (token));
2049 for (; p; p = p->next) {
2050 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2051 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2052 mem_free (&token.data);
2056 mem_free (&token.data);
2060 void mutt_init (int skip_sys_rc, LIST * commands)
2063 struct utsname utsname;
2064 char *p, buffer[STRING], error[STRING];
2065 int i, default_rc = 0, need_pause = 0;
2068 memset (&err, 0, sizeof (err));
2070 err.dsize = sizeof (error);
2073 * XXX - use something even more difficult to predict?
2075 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2076 "\033]9;%ld\a", (long) time (NULL));
2078 /* on one of the systems I use, getcwd() does not return the same prefix
2079 as is listed in the passwd file */
2080 if ((p = getenv ("HOME")))
2081 Homedir = str_dup (p);
2083 /* Get some information about the user */
2084 if ((pw = getpwuid (getuid ()))) {
2087 Username = str_dup (pw->pw_name);
2089 Homedir = str_dup (pw->pw_dir);
2091 Realname = str_dup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2092 Shell = str_dup (pw->pw_shell);
2097 fputs (_("unable to determine home directory"), stderr);
2100 if ((p = getenv ("USER")))
2101 Username = str_dup (p);
2104 fputs (_("unable to determine username"), stderr);
2107 Shell = str_dup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2110 debug_start(Homedir);
2112 /* And about the host... */
2114 /* some systems report the FQDN instead of just the hostname */
2115 if ((p = strchr (utsname.nodename, '.'))) {
2116 Hostname = str_substrdup (utsname.nodename, p);
2118 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2121 Hostname = str_dup (utsname.nodename);
2124 #define DOMAIN buffer
2125 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2126 Fqdn = str_dup ("@");
2129 if (*DOMAIN != '@') {
2130 Fqdn = mem_malloc (str_len (DOMAIN) + str_len (Hostname) + 2);
2131 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2134 Fqdn = str_dup (NONULL (Hostname));
2141 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2143 fgets (buffer, sizeof (buffer), f);
2144 p = (char*) &buffer;
2147 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r')
2151 NewsServer = str_dup (p);
2155 if ((p = getenv ("NNTPSERVER")))
2156 NewsServer = str_dup (p);
2159 if ((p = getenv ("MAIL")))
2160 Spoolfile = str_dup (p);
2161 else if ((p = getenv ("MAILDIR")))
2162 Spoolfile = str_dup (p);
2165 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2167 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
2169 Spoolfile = str_dup (buffer);
2172 if ((p = getenv ("MAILCAPS")))
2173 MailcapPath = str_dup (p);
2175 /* Default search path from RFC1524 */
2177 str_dup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2178 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2181 Tempdir = str_dup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2183 p = getenv ("VISUAL");
2185 p = getenv ("EDITOR");
2189 Editor = str_dup (p);
2190 Visual = str_dup (p);
2192 if ((p = getenv ("REPLYTO")) != NULL) {
2195 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2197 memset (&buf, 0, sizeof (buf));
2198 buf.data = buf.dptr = buffer;
2199 buf.dsize = str_len (buffer);
2201 memset (&token, 0, sizeof (token));
2202 parse_my_hdr (&token, &buf, 0, &err);
2203 mem_free (&token.data);
2206 if ((p = getenv ("EMAIL")) != NULL)
2207 From = rfc822_parse_adrlist (NULL, p);
2209 mutt_set_langinfo_charset ();
2210 mutt_set_charset (Charset);
2213 /* Set standard defaults */
2214 for (i = 0; MuttVars[i].option; i++) {
2215 mutt_set_default (&MuttVars[i]);
2216 mutt_restore_default (&MuttVars[i]);
2219 CurrentMenu = MENU_MAIN;
2222 #ifndef LOCALES_HACK
2223 /* Do we have a locale definition? */
2224 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2225 ((p = getenv ("LANG")) != NULL && p[0]) ||
2226 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2227 set_option (OPTLOCALES);
2231 /* Unset suspend by default if we're the session leader */
2232 if (getsid (0) == getpid ())
2233 unset_option (OPTSUSPEND);
2236 mutt_init_history ();
2245 * When changing the code which looks for a configuration file,
2246 * please also change the corresponding code in muttbug.sh.in.
2256 snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2258 if (access (buffer, F_OK) == -1)
2260 snprintf (buffer, sizeof (buffer), "%s/.muttngrc", NONULL (Homedir));
2261 if (access (buffer, F_OK) == -1)
2263 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s",
2264 NONULL (Homedir), MUTT_VERSION);
2265 if (access (buffer, F_OK) == -1)
2267 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc",
2271 Muttrc = str_dup (buffer);
2274 strfcpy (buffer, Muttrc, sizeof (buffer));
2276 mutt_expand_path (buffer, sizeof (buffer));
2277 Muttrc = str_dup (buffer);
2279 mem_free (&AliasFile);
2280 AliasFile = str_dup (NONULL (Muttrc));
2282 /* Process the global rc file if it exists and the user hasn't explicity
2283 requested not to via "-n". */
2285 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2287 if (access (buffer, F_OK) == -1)
2288 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", SYSCONFDIR);
2289 if (access (buffer, F_OK) == -1)
2290 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR,
2292 if (access (buffer, F_OK) == -1)
2293 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2294 if (access (buffer, F_OK) != -1) {
2295 if (source_rc (buffer, &err) != 0) {
2296 fputs (err.data, stderr);
2297 fputc ('\n', stderr);
2303 /* Read the user's initialization file. */
2304 if (access (Muttrc, F_OK) != -1) {
2305 if (!option (OPTNOCURSES))
2307 if (source_rc (Muttrc, &err) != 0) {
2308 fputs (err.data, stderr);
2309 fputc ('\n', stderr);
2313 else if (!default_rc) {
2314 /* file specified by -F does not exist */
2315 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2316 mutt_endwin (buffer);
2320 if (mutt_execute_commands (commands) != 0)
2323 /* warn about synonym variables */
2324 if (!list_empty(Synonyms)) {
2326 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2327 for (i = 0; i < Synonyms->length; i++)
2328 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2329 MuttVars[((syn_t*) Synonyms->data[i])->o].option,
2330 MuttVars[((syn_t*) Synonyms->data[i])->n].option,
2331 NONULL(((syn_t*) Synonyms->data[i])->f),
2332 ((syn_t*) Synonyms->data[i])->l);
2333 fprintf (stderr, _("Warning: synonym variables are scheduled for removal.\n"));
2334 list_del (&Synonyms, syn_del);
2338 if (need_pause && !option (OPTNOCURSES)) {
2339 if (mutt_any_key_to_continue (NULL) == -1)
2344 set_option (OPTWEED); /* turn weeding on by default */
2348 int mutt_get_hook_type (const char *name)
2350 struct command_t *c;
2352 for (c = Commands; c->name; c++)
2353 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2358 static int opt_cmp (const void* a, const void* b) {
2359 return (str_cmp ((*(struct option_t**) a)->option,
2360 (*(struct option_t**) b)->option));
2363 /* dump out the value of all the variables we have */
2364 int mutt_dump_variables (void) {
2366 char outbuf[STRING];
2367 list2_t* tmp = NULL;
2369 /* get all non-synonyms into list... */
2370 for (i = 0; MuttVars[i].option; i++) {
2371 if (MuttVars[i].type == DT_SYN)
2373 list_push_back (&tmp, &MuttVars[i]);
2375 if (!list_empty(tmp)) {
2376 /* ...and dump list sorted */
2377 qsort (tmp->data, tmp->length, sizeof (void*), opt_cmp);
2378 for (i = 0; i < tmp->length; i++) {
2379 idx = mutt_option_index (((struct option_t*) tmp->data[i])->option);
2380 FuncTable[DTYPE (MuttVars[idx].type)].opt_to_string (outbuf, sizeof (outbuf), idx);
2381 printf ("%s\n", outbuf);
2384 list_del (&tmp, NULL);