2 * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 #include "mutt_curses.h"
26 #include "mutt_menu.h"
27 #include "mutt_regex.h"
32 #include "mutt_crypt.h"
33 #include "mutt_idna.h"
50 #include <sys/utsname.h>
56 if ((CurrentMenu == MENU_PAGER) && (idx >= 0) && \
57 (MuttVars[idx].flags & R_RESORT)) \
59 snprintf (err->data, err->dsize, \
60 _("Not available in this menu.")); \
71 static myvar_t* MyVars;
73 static int var_to_string (int idx, char* val, size_t len);
75 static void myvar_set (const char* var, const char* val);
76 static const char* myvar_get (const char* var);
77 static void myvar_del (const char* var);
79 static 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);
115 v = mutt_yesorno (prompt, (v == M_ASKYES));
116 CLEARLINE (LINES - 1);
123 /* given the variable ``s'', return the index into the rc_vars array which
124 matches, or -1 if the variable is not found. */
125 static int mutt_option_index (char *s)
129 for (i = 0; MuttVars[i].option; i++)
130 if (mutt_strcmp (s, MuttVars[i].option) == 0)
131 return (MuttVars[i].type == DT_SYN ? mutt_option_index ((char *) MuttVars[i].data) : i);
135 int mutt_extract_token (BUFFER *dest, BUFFER *tok, int flags)
138 char qc = 0; /* quote char */
141 /* reset the destination pointer to the beginning of the buffer */
142 dest->dptr = dest->data;
145 while ((ch = *tok->dptr))
149 if ((ISSPACE (ch) && !(flags & M_TOKEN_SPACE)) ||
150 (ch == '#' && !(flags & M_TOKEN_COMMENT)) ||
151 (ch == '=' && (flags & M_TOKEN_EQUAL)) ||
152 (ch == ';' && !(flags & M_TOKEN_SEMICOLON)) ||
153 ((flags & M_TOKEN_PATTERN) && strchr ("~%=!|", ch)))
160 qc = 0; /* end of quote */
161 else if (!qc && (ch == '\'' || ch == '"') && !(flags & M_TOKEN_QUOTE))
163 else if (ch == '\\' && qc != '\'')
166 return -1; /* premature end of token */
167 switch (ch = *tok->dptr++)
172 return -1; /* premature end of token */
173 mutt_buffer_addch (dest, (toupper ((unsigned char) *tok->dptr)
178 mutt_buffer_addch (dest, '\r');
181 mutt_buffer_addch (dest, '\n');
184 mutt_buffer_addch (dest, '\t');
187 mutt_buffer_addch (dest, '\f');
190 mutt_buffer_addch (dest, '\033');
193 if (isdigit ((unsigned char) ch) &&
194 isdigit ((unsigned char) *tok->dptr) &&
195 isdigit ((unsigned char) *(tok->dptr + 1)))
198 mutt_buffer_addch (dest, (ch << 6) + (*tok->dptr << 3) + *(tok->dptr + 1) - 3504);
202 mutt_buffer_addch (dest, ch);
205 else if (ch == '^' && (flags & M_TOKEN_CONDENSE))
208 return -1; /* premature end of token */
211 mutt_buffer_addch (dest, ch);
213 mutt_buffer_addch (dest, '\033');
214 else if (isalpha ((unsigned char) ch))
215 mutt_buffer_addch (dest, toupper ((unsigned char) ch) - '@');
218 mutt_buffer_addch (dest, '^');
219 mutt_buffer_addch (dest, ch);
222 else if (ch == '`' && (!qc || qc == '"'))
233 if ((pc = strpbrk (pc, "\\`")))
235 /* skip any quoted chars */
239 } while (pc && *pc != '`');
242 dprint (1, (debugfile, "mutt_get_token: mismatched backticks\n"));
245 cmd = mutt_substrdup (tok->dptr, pc);
246 if ((pid = mutt_create_filter (cmd, NULL, &fp, NULL)) < 0)
248 dprint (1, (debugfile, "mutt_get_token: unable to fork command: %s", cmd));
257 memset (&expn, 0, sizeof (expn));
258 expn.data = mutt_read_line (NULL, &expn.dsize, fp, &line, 0);
260 mutt_wait_filter (pid);
262 /* if we got output, make a new string consiting of the shell ouptput
263 plus whatever else was left on the original line */
264 /* BUT: If this is inside a quoted string, directly add output to
268 mutt_buffer_addstr (dest, expn.data);
273 expnlen = mutt_strlen (expn.data);
274 tok->dsize = expnlen + mutt_strlen (tok->dptr) + 1;
275 ptr = safe_malloc (tok->dsize);
276 memcpy (ptr, expn.data, expnlen);
277 strcpy (ptr + expnlen, tok->dptr); /* __STRCPY_CHECKED__ */
282 tok->destroy = 1; /* mark that the caller should destroy this data */
287 else if (ch == '$' && (!qc || qc == '"') && (*tok->dptr == '{' || isalpha ((unsigned char) *tok->dptr)))
289 const char *env = NULL;
293 if (*tok->dptr == '{')
296 if ((pc = strchr (tok->dptr, '}')))
298 var = mutt_substrdup (tok->dptr, pc);
304 for (pc = tok->dptr; isalnum ((unsigned char) *pc) || *pc == '_'; pc++)
306 var = mutt_substrdup (tok->dptr, pc);
311 if ((env = getenv (var)) || (env = myvar_get (var)))
312 mutt_buffer_addstr (dest, env);
313 else if ((idx = mutt_option_index (var)) != -1)
315 /* expand settable mutt variables */
316 char val[LONG_STRING];
318 if (var_to_string (idx, val, sizeof (val)))
319 mutt_buffer_addstr (dest, val);
325 mutt_buffer_addch (dest, ch);
327 mutt_buffer_addch (dest, 0); /* terminate the string */
332 static void mutt_free_opt (struct option_t* p)
336 switch (p->type & DT_MASK)
339 rfc822_free_address ((ADDRESS**)p->data);
342 pp = (REGEXP*)p->data;
352 FREE ((char**)p->data); /* __FREE_CHECKED__ */
357 /* clean up before quitting */
358 void mutt_free_opts (void)
362 for (i = 0; MuttVars[i].option; i++)
363 mutt_free_opt (MuttVars + i);
365 mutt_free_rx_list (&Alternates);
366 mutt_free_rx_list (&UnAlternates);
367 mutt_free_rx_list (&MailLists);
368 mutt_free_rx_list (&UnMailLists);
369 mutt_free_rx_list (&SubscribedLists);
370 mutt_free_rx_list (&UnSubscribedLists);
371 mutt_free_rx_list (&NoSpamList);
374 static void add_to_list (LIST **list, const char *str)
376 LIST *t, *last = NULL;
378 /* don't add a NULL or empty string to the list */
379 if (!str || *str == '\0')
382 /* check to make sure the item is not already on this list */
383 for (last = *list; last; last = last->next)
385 if (ascii_strcasecmp (str, last->data) == 0)
387 /* already on the list, so just ignore it */
397 t = (LIST *) safe_calloc (1, sizeof (LIST));
398 t->data = safe_strdup (str);
409 int mutt_add_to_rx_list (RX_LIST **list, const char *s, int flags, BUFFER *err)
411 RX_LIST *t, *last = NULL;
417 if (!(rx = mutt_compile_regexp (s, flags)))
419 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
423 /* check to make sure the item is not already on this list */
424 for (last = *list; last; last = last->next)
426 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0)
428 /* already on the list, so just ignore it */
438 t = mutt_new_rx_list();
449 mutt_free_regexp (&rx);
454 static int remove_from_spam_list (SPAM_LIST **list, const char *pat);
456 static int add_to_spam_list (SPAM_LIST **list, const char *pat, const char *templ, BUFFER *err)
458 SPAM_LIST *t = NULL, *last = NULL;
463 if (!pat || !*pat || !templ)
466 if (!(rx = mutt_compile_regexp (pat, REG_ICASE)))
468 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
472 /* check to make sure the item is not already on this list */
473 for (last = *list; last; last = last->next)
475 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0)
477 /* Already on the list. Formerly we just skipped this case, but
478 * now we're supporting removals, which means we're supporting
479 * re-adds conceptually. So we probably want this to imply a
480 * removal, then do an add. We can achieve the removal by freeing
481 * the template, and leaving t pointed at the current item.
491 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
492 * update. Otherwise we want to make a new one to link at the list's end.
496 t = mutt_new_spam_list();
504 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
505 t->template = safe_strdup(templ);
507 /* Find highest match number in template string */
516 while (*p && isdigit((int)*p))
523 if (t->nmatch > t->rx->rx->re_nsub)
525 snprintf (err->data, err->dsize, _("Not enough subexpressions for spam "
527 remove_from_spam_list(list, pat);
531 t->nmatch++; /* match 0 is always the whole expr */
536 static int remove_from_spam_list (SPAM_LIST **list, const char *pat)
538 SPAM_LIST *spam, *prev;
541 /* Being first is a special case. */
545 if (spam->rx && !mutt_strcmp(spam->rx->pattern, pat))
548 mutt_free_regexp(&spam->rx);
549 FREE(&spam->template);
555 for (spam = prev->next; spam;)
557 if (!mutt_strcmp(spam->rx->pattern, pat))
559 prev->next = spam->next;
560 mutt_free_regexp(&spam->rx);
561 FREE(&spam->template);
574 static void remove_from_list (LIST **l, const char *str)
576 LIST *p, *last = NULL;
578 if (mutt_strcmp ("*", str) == 0)
579 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
586 if (ascii_strcasecmp (str, p->data) == 0)
590 last->next = p->next;
604 static int parse_unignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
608 mutt_extract_token (buf, s, 0);
610 /* don't add "*" to the unignore list */
611 if (strcmp (buf->data, "*"))
612 add_to_list (&UnIgnore, buf->data);
614 remove_from_list (&Ignore, buf->data);
616 while (MoreArgs (s));
621 static int parse_ignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
625 mutt_extract_token (buf, s, 0);
626 remove_from_list (&UnIgnore, buf->data);
627 add_to_list (&Ignore, buf->data);
629 while (MoreArgs (s));
634 static int parse_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
638 mutt_extract_token (buf, s, 0);
639 add_to_list ((LIST **) data, buf->data);
641 while (MoreArgs (s));
646 static void _alternates_clean (void)
649 if (Context && Context->msgcount)
651 for (i = 0; i < Context->msgcount; i++)
652 Context->hdrs[i]->recip_valid = 0;
656 static int parse_alternates (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
658 group_context_t *gc = NULL;
664 mutt_extract_token (buf, s, 0);
666 if (parse_group_context (&gc, buf, s, data, err) == -1)
669 mutt_remove_from_rx_list (&UnAlternates, buf->data);
671 if (mutt_add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
674 if (mutt_group_context_add_rx (gc, buf->data, REG_ICASE, err) != 0)
677 while (MoreArgs (s));
679 mutt_group_context_destroy (&gc);
683 mutt_group_context_destroy (&gc);
687 static int parse_unalternates (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
692 mutt_extract_token (buf, s, 0);
693 mutt_remove_from_rx_list (&Alternates, buf->data);
695 if (mutt_strcmp (buf->data, "*") &&
696 mutt_add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
700 while (MoreArgs (s));
705 static int parse_spam_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
709 memset(&templ, 0, sizeof(templ));
711 /* Insist on at least one parameter */
715 strfcpy(err->data, _("spam: no matching pattern"), err->dsize);
717 strfcpy(err->data, _("nospam: no matching pattern"), err->dsize);
721 /* Extract the first token, a regexp */
722 mutt_extract_token (buf, s, 0);
724 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
727 /* If there's a second parameter, it's a template for the spam tag. */
730 mutt_extract_token (&templ, s, 0);
732 /* Add to the spam list. */
733 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
740 /* If not, try to remove from the nospam list. */
743 mutt_remove_from_rx_list(&NoSpamList, buf->data);
749 /* M_NOSPAM is for nospam commands. */
750 else if (data == M_NOSPAM)
752 /* nospam only ever has one parameter. */
754 /* "*" is a special case. */
755 if (!mutt_strcmp(buf->data, "*"))
757 mutt_free_spam_list (&SpamList);
758 mutt_free_rx_list (&NoSpamList);
762 /* If it's on the spam list, just remove it. */
763 if (remove_from_spam_list(&SpamList, buf->data) != 0)
766 /* Otherwise, add it to the nospam list. */
767 if (mutt_add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
773 /* This should not happen. */
774 strfcpy(err->data, "This is no good at all.", err->dsize);
779 static int parse_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
783 mutt_extract_token (buf, s, 0);
785 * Check for deletion of entire list
787 if (mutt_strcmp (buf->data, "*") == 0)
789 mutt_free_list ((LIST **) data);
792 remove_from_list ((LIST **) data, buf->data);
794 while (MoreArgs (s));
799 static int parse_lists (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
801 group_context_t *gc = NULL;
805 mutt_extract_token (buf, s, 0);
807 if (parse_group_context (&gc, buf, s, data, err) == -1)
810 mutt_remove_from_rx_list (&UnMailLists, buf->data);
812 if (mutt_add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
815 if (mutt_group_context_add_rx (gc, buf->data, REG_ICASE, err) != 0)
818 while (MoreArgs (s));
820 mutt_group_context_destroy (&gc);
824 mutt_group_context_destroy (&gc);
828 typedef enum group_state_t {
832 static int parse_group (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
834 group_context_t *gc = NULL;
835 group_state_t state = NONE;
836 ADDRESS *addr = NULL;
841 mutt_extract_token (buf, s, 0);
842 if (parse_group_context (&gc, buf, s, data, err) == -1)
845 if (data == M_UNGROUP && !mutt_strcasecmp (buf->data, "*"))
847 if (mutt_group_context_clear (&gc) < 0)
852 if (!mutt_strcasecmp (buf->data, "-rx"))
854 else if (!mutt_strcasecmp (buf->data, "-addr"))
861 snprintf (err->data, err->dsize, _("%sgroup: missing -rx or -addr."),
862 data == M_UNGROUP ? "un" : "");
866 if (data == M_GROUP &&
867 mutt_group_context_add_rx (gc, buf->data, REG_ICASE, err) != 0)
869 else if (data == M_UNGROUP &&
870 mutt_group_context_remove_rx (gc, buf->data) < 0)
875 if ((addr = mutt_parse_adrlist (NULL, buf->data)) == NULL)
877 if (mutt_addrlist_to_idna (addr, &estr))
879 snprintf (err->data, err->dsize, _("%sgroup: warning: bad IDN '%s'.\n"),
880 data == 1 ? "un" : "", estr);
884 mutt_group_context_add_adrlist (gc, addr);
885 else if (data == M_UNGROUP)
886 mutt_group_context_remove_adrlist (gc, addr);
887 rfc822_free_address (&addr);
891 } while (MoreArgs (s));
894 mutt_group_context_destroy (&gc);
898 mutt_group_context_destroy (&gc);
902 /* always wise to do what someone else did before */
903 static void _attachments_clean (void)
906 if (Context && Context->msgcount)
908 for (i = 0; i < Context->msgcount; i++)
909 Context->hdrs[i]->attach_valid = 0;
913 static int parse_attach_list (BUFFER *buf, BUFFER *s, LIST **ldata, BUFFER *err)
922 /* Find the last item in the list that data points to. */
924 dprint(5, (debugfile, "parse_attach_list: ldata = %p, *ldata = %p\n",
925 (void *)ldata, (void *)*ldata));
926 for (listp = *ldata; listp; listp = listp->next)
928 a = (ATTACH_MATCH *)listp->data;
929 dprint(5, (debugfile, "parse_attach_list: skipping %s/%s\n",
930 a->major, a->minor));
936 mutt_extract_token (buf, s, 0);
938 if (!buf->data || *buf->data == '\0')
941 a = safe_malloc(sizeof(ATTACH_MATCH));
943 /* some cheap hacks that I expect to remove */
944 if (!ascii_strcasecmp(buf->data, "any"))
945 a->major = safe_strdup("*/.*");
946 else if (!ascii_strcasecmp(buf->data, "none"))
947 a->major = safe_strdup("cheap_hack/this_should_never_match");
949 a->major = safe_strdup(buf->data);
951 if ((p = strchr(a->major, '/')))
959 a->minor = "unknown";
962 len = strlen(a->minor);
963 tmpminor = safe_malloc(len+3);
964 strcpy(&tmpminor[1], a->minor); /* __STRCPY_CHECKED__ */
966 tmpminor[len+1] = '$';
967 tmpminor[len+2] = '\0';
969 a->major_int = mutt_check_mime_type(a->major);
970 ret = REGCOMP(&a->minor_rx, tmpminor, REG_ICASE);
976 regerror(ret, &a->minor_rx, err->data, err->dsize);
982 dprint(5, (debugfile, "parse_attach_list: added %s/%s [%d]\n",
983 a->major, a->minor, a->major_int));
985 listp = safe_malloc(sizeof(LIST));
986 listp->data = (char *)a;
998 while (MoreArgs (s));
1000 _attachments_clean();
1004 static int parse_unattach_list (BUFFER *buf, BUFFER *s, LIST **ldata, BUFFER *err)
1007 LIST *lp, *lastp, *newlp;
1014 mutt_extract_token (buf, s, 0);
1016 if (!ascii_strcasecmp(buf->data, "any"))
1017 tmp = safe_strdup("*/.*");
1018 else if (!ascii_strcasecmp(buf->data, "none"))
1019 tmp = safe_strdup("cheap_hack/this_should_never_match");
1021 tmp = safe_strdup(buf->data);
1023 if ((minor = strchr(tmp, '/')))
1032 major = mutt_check_mime_type(tmp);
1034 /* We must do our own walk here because remove_from_list() will only
1035 * remove the LIST->data, not anything pointed to by the LIST->data. */
1037 for(lp = *ldata; lp; )
1039 a = (ATTACH_MATCH *)lp->data;
1040 dprint(5, (debugfile, "parse_unattach_list: check %s/%s [%d] : %s/%s [%d]\n",
1041 a->major, a->minor, a->major_int, tmp, minor, major));
1042 if (a->major_int == major && !mutt_strcasecmp(minor, a->minor))
1044 dprint(5, (debugfile, "parse_unattach_list: removed %s/%s [%d]\n",
1045 a->major, a->minor, a->major_int));
1046 regfree(&a->minor_rx);
1049 /* Relink backward */
1051 lastp->next = lp->next;
1056 FREE(&lp->data); /* same as a */
1067 while (MoreArgs (s));
1070 _attachments_clean();
1074 static int print_attach_list (LIST *lp, char op, char *name)
1077 printf("attachments %c%s %s/%s\n", op, name,
1078 ((ATTACH_MATCH *)lp->data)->major,
1079 ((ATTACH_MATCH *)lp->data)->minor);
1087 static int parse_attachments (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1092 mutt_extract_token(buf, s, 0);
1093 if (!buf->data || *buf->data == '\0') {
1094 strfcpy(err->data, _("attachments: no disposition"), err->dsize);
1098 category = buf->data;
1104 printf("\nCurrent attachments settings:\n\n");
1105 print_attach_list(AttachAllow, '+', "A");
1106 print_attach_list(AttachExclude, '-', "A");
1107 print_attach_list(InlineAllow, '+', "I");
1108 print_attach_list(InlineExclude, '-', "I");
1109 set_option (OPTFORCEREDRAWINDEX);
1110 set_option (OPTFORCEREDRAWPAGER);
1111 mutt_any_key_to_continue (NULL);
1115 if (op != '+' && op != '-') {
1119 if (!ascii_strncasecmp(category, "attachment", strlen(category))) {
1121 listp = &AttachAllow;
1123 listp = &AttachExclude;
1125 else if (!ascii_strncasecmp(category, "inline", strlen(category))) {
1127 listp = &InlineAllow;
1129 listp = &InlineExclude;
1132 strfcpy(err->data, _("attachments: invalid disposition"), err->dsize);
1136 return parse_attach_list(buf, s, listp, err);
1139 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1144 mutt_extract_token(buf, s, 0);
1145 if (!buf->data || *buf->data == '\0') {
1146 strfcpy(err->data, _("unattachments: no disposition"), err->dsize);
1152 if (op != '+' && op != '-') {
1156 if (!ascii_strncasecmp(p, "attachment", strlen(p))) {
1158 listp = &AttachAllow;
1160 listp = &AttachExclude;
1162 else if (!ascii_strncasecmp(p, "inline", strlen(p))) {
1164 listp = &InlineAllow;
1166 listp = &InlineExclude;
1169 strfcpy(err->data, _("unattachments: invalid disposition"), err->dsize);
1173 return parse_unattach_list(buf, s, listp, err);
1176 static int parse_unlists (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1180 mutt_extract_token (buf, s, 0);
1181 mutt_remove_from_rx_list (&SubscribedLists, buf->data);
1182 mutt_remove_from_rx_list (&MailLists, buf->data);
1184 if (mutt_strcmp (buf->data, "*") &&
1185 mutt_add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1188 while (MoreArgs (s));
1193 static int parse_subscribe (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1195 group_context_t *gc = NULL;
1199 mutt_extract_token (buf, s, 0);
1201 if (parse_group_context (&gc, buf, s, data, err) == -1)
1204 mutt_remove_from_rx_list (&UnMailLists, buf->data);
1205 mutt_remove_from_rx_list (&UnSubscribedLists, buf->data);
1207 if (mutt_add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1209 if (mutt_add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1211 if (mutt_group_context_add_rx (gc, buf->data, REG_ICASE, err) != 0)
1214 while (MoreArgs (s));
1216 mutt_group_context_destroy (&gc);
1220 mutt_group_context_destroy (&gc);
1224 static int parse_unsubscribe (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1228 mutt_extract_token (buf, s, 0);
1229 mutt_remove_from_rx_list (&SubscribedLists, buf->data);
1231 if (mutt_strcmp (buf->data, "*") &&
1232 mutt_add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1235 while (MoreArgs (s));
1240 static int parse_unalias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1242 ALIAS *tmp, *last = NULL;
1246 mutt_extract_token (buf, s, 0);
1248 if (mutt_strcmp ("*", buf->data) == 0)
1250 if (CurrentMenu == MENU_ALIAS)
1252 for (tmp = Aliases; tmp ; tmp = tmp->next)
1254 set_option (OPTFORCEREDRAWINDEX);
1257 mutt_free_alias (&Aliases);
1261 for (tmp = Aliases; tmp; tmp = tmp->next)
1263 if (mutt_strcasecmp (buf->data, tmp->name) == 0)
1265 if (CurrentMenu == MENU_ALIAS)
1268 set_option (OPTFORCEREDRAWINDEX);
1273 last->next = tmp->next;
1275 Aliases = tmp->next;
1277 mutt_free_alias (&tmp);
1283 while (MoreArgs (s));
1287 static int parse_alias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1289 ALIAS *tmp = Aliases;
1292 group_context_t *gc = NULL;
1296 strfcpy (err->data, _("alias: no address"), err->dsize);
1300 mutt_extract_token (buf, s, 0);
1302 if (parse_group_context (&gc, buf, s, data, err) == -1)
1305 /* check to see if an alias with this name already exists */
1306 for (; tmp; tmp = tmp->next)
1308 if (!mutt_strcasecmp (tmp->name, buf->data))
1315 /* create a new alias */
1316 tmp = (ALIAS *) safe_calloc (1, sizeof (ALIAS));
1318 tmp->name = safe_strdup (buf->data);
1319 /* give the main addressbook code a chance */
1320 if (CurrentMenu == MENU_ALIAS)
1321 set_option (OPTMENUCALLER);
1325 mutt_alias_delete_reverse (tmp);
1326 /* override the previous value */
1327 rfc822_free_address (&tmp->addr);
1328 if (CurrentMenu == MENU_ALIAS)
1329 set_option (OPTFORCEREDRAWINDEX);
1332 mutt_extract_token (buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1333 dprint (3, (debugfile, "parse_alias: Second token is '%s'.\n",
1336 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1342 if (mutt_addrlist_to_idna (tmp->addr, &estr))
1344 snprintf (err->data, err->dsize, _("Warning: Bad IDN '%s' in alias '%s'.\n"),
1349 mutt_group_context_add_adrlist (gc, tmp->addr);
1350 mutt_alias_add_reverse (tmp);
1353 if (debuglevel >= 2)
1356 /* A group is terminated with an empty address, so check a->mailbox */
1357 for (a = tmp->addr; a && a->mailbox; a = a->next)
1360 dprint (3, (debugfile, "parse_alias: %s\n",
1363 dprint (3, (debugfile, "parse_alias: Group %s\n",
1368 mutt_group_context_destroy (&gc);
1372 mutt_group_context_destroy (&gc);
1377 parse_unmy_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1380 LIST *tmp = UserHeader;
1386 mutt_extract_token (buf, s, 0);
1387 if (mutt_strcmp ("*", buf->data) == 0)
1388 mutt_free_list (&UserHeader);
1394 l = mutt_strlen (buf->data);
1395 if (buf->data[l - 1] == ':')
1400 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0 && tmp->data[l] == ':')
1404 last->next = tmp->next;
1406 UserHeader = tmp->next;
1409 mutt_free_list (&ptr);
1419 while (MoreArgs (s));
1423 static int parse_my_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1429 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1430 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':')
1432 strfcpy (err->data, _("invalid header field"), err->dsize);
1435 keylen = p - buf->data + 1;
1439 for (tmp = UserHeader; ; tmp = tmp->next)
1441 /* see if there is already a field by this name */
1442 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0)
1444 /* replace the old value */
1446 tmp->data = buf->data;
1447 memset (buf, 0, sizeof (BUFFER));
1453 tmp->next = mutt_new_list ();
1458 tmp = mutt_new_list ();
1461 tmp->data = buf->data;
1462 memset (buf, 0, sizeof (BUFFER));
1467 parse_sort (short *val, const char *s, const struct mapping_t *map, BUFFER *err)
1471 if (mutt_strncmp ("reverse-", s, 8) == 0)
1474 flags = SORT_REVERSE;
1477 if (mutt_strncmp ("last-", s, 5) == 0)
1483 if ((i = mutt_getvaluebyname (s, map)) == -1)
1485 snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s);
1494 static void mutt_set_default (struct option_t *p)
1496 switch (p->type & DT_MASK)
1499 if (!p->init && *((char **) p->data))
1500 p->init = (unsigned long) safe_strdup (* ((char **) p->data));
1503 if (!p->init && *((char **) p->data))
1505 char *cp = safe_strdup (*((char **) p->data));
1506 /* mutt_pretty_mailbox (cp); */
1507 p->init = (unsigned long) cp;
1511 if (!p->init && *((ADDRESS **) p->data))
1513 char tmp[HUGE_STRING];
1515 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data), 0);
1516 p->init = (unsigned long) safe_strdup (tmp);
1521 REGEXP *pp = (REGEXP *) p->data;
1522 if (!p->init && pp->pattern)
1523 p->init = (unsigned long) safe_strdup (pp->pattern);
1529 static void mutt_restore_default (struct option_t *p)
1531 switch (p->type & DT_MASK)
1534 mutt_str_replace ((char **) p->data, (char *) p->init);
1537 FREE((char **) p->data); /* __FREE_CHECKED__ */
1540 char path[_POSIX_PATH_MAX];
1541 strfcpy (path, (char *) p->init, sizeof (path));
1542 mutt_expand_path (path, sizeof (path));
1543 *((char **) p->data) = safe_strdup (path);
1547 rfc822_free_address ((ADDRESS **) p->data);
1549 *((ADDRESS **) p->data) = rfc822_parse_adrlist (NULL, (char *) p->init);
1553 set_option (p->data);
1555 unset_option (p->data);
1558 set_quadoption (p->data, p->init);
1563 *((short *) p->data) = p->init;
1567 REGEXP *pp = (REGEXP *) p->data;
1570 FREE (&pp->pattern);
1579 char *s = (char *) p->init;
1581 pp->rx = safe_calloc (1, sizeof (regex_t));
1582 pp->pattern = safe_strdup ((char *) p->init);
1583 if (mutt_strcmp (p->option, "mask") != 0)
1584 flags |= mutt_which_case ((const char *) p->init);
1585 if (mutt_strcmp (p->option, "mask") == 0 && *s == '!')
1590 if (REGCOMP (pp->rx, s, flags) != 0)
1592 fprintf (stderr, _("mutt_restore_default(%s): error in regexp: %s\n"),
1593 p->option, pp->pattern);
1594 FREE (&pp->pattern);
1602 if (p->flags & R_INDEX)
1603 set_option (OPTFORCEREDRAWINDEX);
1604 if (p->flags & R_PAGER)
1605 set_option (OPTFORCEREDRAWPAGER);
1606 if (p->flags & R_RESORT_SUB)
1607 set_option (OPTSORTSUBTHREADS);
1608 if (p->flags & R_RESORT)
1609 set_option (OPTNEEDRESORT);
1610 if (p->flags & R_RESORT_INIT)
1611 set_option (OPTRESORTINIT);
1612 if (p->flags & R_TREE)
1613 set_option (OPTREDRAWTREE);
1616 static size_t escape_string (char *dst, size_t len, const char* src)
1622 len--; /* save room for \0 */
1623 #define ESC_CHAR(C) do { *p++ = '\\'; if (p - dst < len) *p++ = C; } while(0)
1624 while (p - dst < len && src && *src)
1638 if ((*src == '\\' || *src == '"') && p - dst < len - 1)
1649 static void pretty_var (char *dst, size_t len, const char *option, const char *val)
1656 strfcpy (dst, option, len);
1657 len--; /* save room for \0 */
1658 p = dst + mutt_strlen (dst);
1664 p += escape_string (p, len - (p - dst) + 1, val); /* \0 terminate it */
1670 static int check_charset (struct option_t *opt, const char *val)
1672 char *p, *q = NULL, *s = safe_strdup (val);
1673 int rc = 0, strict = strcmp (opt->option, "send_charset") == 0;
1675 for (p = strtok_r (s, ":", &q); p; p = strtok_r (NULL, ":", &q))
1679 if (mutt_check_charset (p, strict) < 0)
1690 static int parse_set (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
1692 int query, unset, inv, reset, r = 0;
1694 char *p, scratch[_POSIX_PATH_MAX];
1697 while (MoreArgs (s))
1699 /* reset state variables */
1701 unset = data & M_SET_UNSET;
1702 inv = data & M_SET_INV;
1703 reset = data & M_SET_RESET;
1706 if (*s->dptr == '?')
1711 else if (mutt_strncmp ("no", s->dptr, 2) == 0)
1716 else if (mutt_strncmp ("inv", s->dptr, 3) == 0)
1721 else if (*s->dptr == '&')
1727 /* get the variable name */
1728 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1730 if (!mutt_strncmp ("my_", tmp->data, 3))
1732 else if ((idx = mutt_option_index (tmp->data)) == -1 &&
1733 !(reset && !mutt_strcmp ("all", tmp->data)))
1735 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1742 if (query || unset || inv)
1744 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1748 if (s && *s->dptr == '=')
1750 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1754 if (!mutt_strcmp ("all", tmp->data))
1756 if (CurrentMenu == MENU_PAGER)
1758 snprintf (err->data, err->dsize, _("Not available in this menu."));
1761 for (idx = 0; MuttVars[idx].option; idx++)
1762 mutt_restore_default (&MuttVars[idx]);
1763 set_option (OPTFORCEREDRAWINDEX);
1764 set_option (OPTFORCEREDRAWPAGER);
1765 set_option (OPTSORTSUBTHREADS);
1766 set_option (OPTNEEDRESORT);
1767 set_option (OPTRESORTINIT);
1768 set_option (OPTREDRAWTREE);
1777 mutt_restore_default (&MuttVars[idx]);
1780 else if (!myvar && DTYPE (MuttVars[idx].type) == DT_BOOL)
1782 if (s && *s->dptr == '=')
1784 if (unset || inv || query)
1786 snprintf (err->data, err->dsize, _("Usage: set variable=yes|no"));
1791 mutt_extract_token (tmp, s, 0);
1792 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1794 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1798 snprintf (err->data, err->dsize, _("Usage: set variable=yes|no"));
1805 snprintf (err->data, err->dsize, option (MuttVars[idx].data)
1806 ? _("%s is set") : _("%s is unset"), tmp->data);
1812 unset_option (MuttVars[idx].data);
1814 toggle_option (MuttVars[idx].data);
1816 set_option (MuttVars[idx].data);
1818 else if (myvar || DTYPE (MuttVars[idx].type) == DT_STR ||
1819 DTYPE (MuttVars[idx].type) == DT_PATH ||
1820 DTYPE (MuttVars[idx].type) == DT_ADDR)
1827 else if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1828 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1830 /* MuttVars[idx].data is already 'char**' (or some 'void**') or...
1831 * so cast to 'void*' is okay */
1832 FREE ((void *) MuttVars[idx].data); /* __FREE_CHECKED__ */
1834 else if (query || *s->dptr != '=')
1836 char _tmp[LONG_STRING];
1837 const char *val = NULL;
1841 if ((val = myvar_get (myvar)))
1843 pretty_var (err->data, err->dsize, myvar, val);
1848 snprintf (err->data, err->dsize, _("%s: unknown variable"), myvar);
1852 else if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1855 rfc822_write_address (_tmp, sizeof (_tmp), *((ADDRESS **) MuttVars[idx].data), 0);
1858 else if (DTYPE (MuttVars[idx].type) == DT_PATH)
1861 strfcpy (_tmp, NONULL(*((char **) MuttVars[idx].data)), sizeof (_tmp));
1862 mutt_pretty_mailbox (_tmp, sizeof (_tmp));
1866 val = *((char **) MuttVars[idx].data);
1868 /* user requested the value of this variable */
1869 pretty_var (err->data, err->dsize, MuttVars[idx].option, NONULL(val));
1879 /* myvar is a pointer to tmp and will be lost on extract_token */
1880 myvar = safe_strdup (myvar);
1884 mutt_extract_token (tmp, s, 0);
1888 myvar_set (myvar, tmp->data);
1890 myvar="don't resort";
1892 else if (DTYPE (MuttVars[idx].type) == DT_PATH)
1894 /* MuttVars[idx].data is already 'char**' (or some 'void**') or...
1895 * so cast to 'void*' is okay */
1896 FREE ((void *) MuttVars[idx].data); /* __FREE_CHECKED__ */
1898 strfcpy (scratch, tmp->data, sizeof (scratch));
1899 mutt_expand_path (scratch, sizeof (scratch));
1900 *((char **) MuttVars[idx].data) = safe_strdup (scratch);
1902 else if (DTYPE (MuttVars[idx].type) == DT_STR)
1904 if ((strstr (MuttVars[idx].option, "charset") &&
1905 check_charset (&MuttVars[idx], tmp->data) < 0) |
1906 /* $charset can't be empty, others can */
1907 (strcmp(MuttVars[idx].option, "charset") == 0 && ! *tmp->data))
1909 snprintf (err->data, err->dsize, _("Invalid value for option %s: \"%s\""),
1910 MuttVars[idx].option, tmp->data);
1914 FREE ((void *) MuttVars[idx].data); /* __FREE_CHECKED__ */
1915 *((char **) MuttVars[idx].data) = safe_strdup (tmp->data);
1916 if (mutt_strcmp (MuttVars[idx].option, "charset") == 0)
1917 mutt_set_charset (Charset);
1921 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1922 *((ADDRESS **) MuttVars[idx].data) = rfc822_parse_adrlist (NULL, tmp->data);
1926 else if (DTYPE(MuttVars[idx].type) == DT_RX)
1928 REGEXP *ptr = (REGEXP *) MuttVars[idx].data;
1932 if (query || *s->dptr != '=')
1934 /* user requested the value of this variable */
1935 pretty_var (err->data, err->dsize, MuttVars[idx].option, NONULL(ptr->pattern));
1939 if (option(OPTATTACHMSG) && !mutt_strcmp(MuttVars[idx].option, "reply_regexp"))
1941 snprintf (err->data, err->dsize, "Operation not permitted when in attach-message mode.");
1949 /* copy the value of the string */
1950 mutt_extract_token (tmp, s, 0);
1952 if (!ptr->pattern || mutt_strcmp (ptr->pattern, tmp->data) != 0)
1956 /* $mask is case-sensitive */
1957 if (mutt_strcmp (MuttVars[idx].option, "mask") != 0)
1958 flags |= mutt_which_case (tmp->data);
1961 if (mutt_strcmp (MuttVars[idx].option, "mask") == 0)
1970 rx = (regex_t *) safe_malloc (sizeof (regex_t));
1971 if ((e = REGCOMP (rx, p, flags)) != 0)
1973 regerror (e, rx, err->data, err->dsize);
1978 /* get here only if everything went smootly */
1981 FREE (&ptr->pattern);
1982 regfree ((regex_t *) ptr->rx);
1986 ptr->pattern = safe_strdup (tmp->data);
1990 /* $reply_regexp and $alterantes require special treatment */
1992 if (Context && Context->msgcount &&
1993 mutt_strcmp (MuttVars[idx].option, "reply_regexp") == 0)
1995 regmatch_t pmatch[1];
1998 #define CUR_ENV Context->hdrs[i]->env
1999 for (i = 0; i < Context->msgcount; i++)
2001 if (CUR_ENV && CUR_ENV->subject)
2003 CUR_ENV->real_subj = (regexec (ReplyRegexp.rx,
2004 CUR_ENV->subject, 1, pmatch, 0)) ?
2006 CUR_ENV->subject + pmatch[0].rm_eo;
2013 else if (DTYPE(MuttVars[idx].type) == DT_MAGIC)
2015 if (query || *s->dptr != '=')
2017 switch (DefaultMagic)
2035 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p);
2042 /* copy the value of the string */
2043 mutt_extract_token (tmp, s, 0);
2044 if (mx_set_magic (tmp->data))
2046 snprintf (err->data, err->dsize, _("%s: invalid mailbox type"), tmp->data);
2051 else if (DTYPE(MuttVars[idx].type) == DT_NUM)
2053 short *ptr = (short *) MuttVars[idx].data;
2057 if (query || *s->dptr != '=')
2060 /* compatibility alias */
2061 if (mutt_strcmp (MuttVars[idx].option, "wrapmargin") == 0)
2062 val = *ptr < 0 ? -*ptr : 0;
2064 /* user requested the value of this variable */
2065 snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, val);
2072 mutt_extract_token (tmp, s, 0);
2073 rc = mutt_atos (tmp->data, (short *) &val);
2075 if (rc < 0 || !*tmp->data)
2077 snprintf (err->data, err->dsize, _("%s: invalid value (%s)"), tmp->data,
2078 rc == -1 ? _("format error") : _("number overflow"));
2085 /* these ones need a sanity check */
2086 if (mutt_strcmp (MuttVars[idx].option, "history") == 0)
2090 mutt_init_history ();
2092 else if (mutt_strcmp (MuttVars[idx].option, "pager_index_lines") == 0)
2097 else if (mutt_strcmp (MuttVars[idx].option, "wrapmargin") == 0)
2105 else if (mutt_strcmp (MuttVars[idx].option, "imap_pipeline_depth") == 0)
2112 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
2116 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2118 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
2119 vals [ quadoption (MuttVars[idx].data) ]);
2124 if (*s->dptr == '=')
2127 mutt_extract_token (tmp, s, 0);
2128 if (ascii_strcasecmp ("yes", tmp->data) == 0)
2129 set_quadoption (MuttVars[idx].data, M_YES);
2130 else if (ascii_strcasecmp ("no", tmp->data) == 0)
2131 set_quadoption (MuttVars[idx].data, M_NO);
2132 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
2133 set_quadoption (MuttVars[idx].data, M_ASKYES);
2134 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
2135 set_quadoption (MuttVars[idx].data, M_ASKNO);
2138 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
2146 toggle_quadoption (MuttVars[idx].data);
2148 set_quadoption (MuttVars[idx].data, M_NO);
2150 set_quadoption (MuttVars[idx].data, M_YES);
2153 else if (DTYPE (MuttVars[idx].type) == DT_SORT)
2155 const struct mapping_t *map = NULL;
2157 switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
2160 map = SortAliasMethods;
2162 case DT_SORT_BROWSER:
2163 map = SortBrowserMethods;
2166 if ((WithCrypto & APPLICATION_PGP))
2167 map = SortKeyMethods;
2170 map = SortAuxMethods;
2179 snprintf (err->data, err->dsize, _("%s: Unknown type."), MuttVars[idx].option);
2184 if (query || *s->dptr != '=')
2186 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
2188 snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
2189 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
2190 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
2196 mutt_extract_token (tmp, s , 0);
2198 if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) == -1)
2206 snprintf (err->data, err->dsize, _("%s: unknown type"), MuttVars[idx].option);
2213 if (MuttVars[idx].flags & R_INDEX)
2214 set_option (OPTFORCEREDRAWINDEX);
2215 if (MuttVars[idx].flags & R_PAGER)
2216 set_option (OPTFORCEREDRAWPAGER);
2217 if (MuttVars[idx].flags & R_RESORT_SUB)
2218 set_option (OPTSORTSUBTHREADS);
2219 if (MuttVars[idx].flags & R_RESORT)
2220 set_option (OPTNEEDRESORT);
2221 if (MuttVars[idx].flags & R_RESORT_INIT)
2222 set_option (OPTRESORTINIT);
2223 if (MuttVars[idx].flags & R_TREE)
2224 set_option (OPTREDRAWTREE);
2232 /* reads the specified initialization file. returns -1 if errors were found
2233 so that we can pause to let the user know... */
2234 static int source_rc (const char *rcfile, BUFFER *err)
2237 int line = 0, rc = 0, conv = 0;
2239 char *linebuf = NULL;
2240 char *currentline = NULL;
2244 dprint (2, (debugfile, "Reading configuration file '%s'.\n",
2247 if ((f = mutt_open_read (rcfile, &pid)) == NULL)
2249 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
2253 memset (&token, 0, sizeof (token));
2254 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line, M_CONT)) != NULL)
2256 conv=ConfigCharset && (*ConfigCharset) && Charset;
2259 currentline=safe_strdup(linebuf);
2260 if (!currentline) continue;
2261 mutt_convert_string(¤tline, ConfigCharset, Charset, 0);
2264 currentline=linebuf;
2266 if (mutt_parse_rc_line (currentline, &token, err) == -1)
2268 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
2269 if (--rc < -MAXERRS)
2271 if (conv) FREE(¤tline);
2287 mutt_wait_filter (pid);
2290 /* the muttrc source keyword */
2291 snprintf (err->data, err->dsize, rc >= -MAXERRS ? _("source: errors in %s")
2292 : _("source: reading aborted due too many errors in %s"), rcfile);
2300 static int parse_source (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
2302 char path[_POSIX_PATH_MAX];
2304 if (mutt_extract_token (tmp, s, 0) != 0)
2306 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
2311 strfcpy (err->data, _("source: too many arguments"), err->dsize);
2314 strfcpy (path, tmp->data, sizeof (path));
2315 mutt_expand_path (path, sizeof (path));
2316 return (source_rc (path, err));
2319 /* line command to execute
2321 token scratch buffer to be used by parser. caller should free
2322 token->data when finished. the reason for this variable is
2323 to avoid having to allocate and deallocate a lot of memory
2324 if we are parsing many lines. the caller can pass in the
2325 memory to use, which avoids having to create new space for
2326 every call to this function.
2328 err where to write error messages */
2329 int mutt_parse_rc_line (/* const */ char *line, BUFFER *token, BUFFER *err)
2334 if (!line || !*line)
2337 memset (&expn, 0, sizeof (expn));
2338 expn.data = expn.dptr = line;
2339 expn.dsize = mutt_strlen (line);
2346 if (*expn.dptr == '#')
2347 break; /* rest of line is a comment */
2348 if (*expn.dptr == ';')
2353 mutt_extract_token (token, &expn, 0);
2354 for (i = 0; Commands[i].name; i++)
2356 if (!mutt_strcmp (token->data, Commands[i].name))
2358 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
2363 if (!Commands[i].name)
2365 snprintf (err->data, err->dsize, _("%s: unknown command"), NONULL (token->data));
2377 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
2378 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
2379 /* initial string that starts completion. No telling how much crap
2380 * the user has typed so far. Allocate LONG_STRING just to be sure! */
2381 static char User_typed [LONG_STRING] = {0};
2383 static int Num_matched = 0; /* Number of matches for completion */
2384 static char Completed [STRING] = {0}; /* completed string (command or variable) */
2385 static const char **Matches;
2386 /* this is a lie until mutt_init runs: */
2387 static int Matches_listsize = MAX(NUMVARS,NUMCOMMANDS) + 10;
2389 static void matches_ensure_morespace(int current)
2391 int base_space, extra_space, space;
2393 if (current > Matches_listsize - 2)
2395 base_space = MAX(NUMVARS,NUMCOMMANDS) + 1;
2396 extra_space = Matches_listsize - base_space;
2398 space = base_space + extra_space;
2399 safe_realloc (&Matches, space * sizeof (char *));
2400 memset (&Matches[current + 1], 0, space - current);
2401 Matches_listsize = space;
2405 /* helper function for completion. Changes the dest buffer if
2406 necessary/possible to aid completion.
2407 dest == completion result gets here.
2408 src == candidate for completion.
2409 try == user entered data for completion.
2410 len == length of dest buffer.
2412 static void candidate (char *dest, char *try, const char *src, int len)
2416 if (strstr (src, try) == src)
2418 matches_ensure_morespace (Num_matched);
2419 Matches[Num_matched++] = src;
2421 strfcpy (dest, src, len);
2424 for (l = 0; src[l] && src[l] == dest[l]; l++);
2430 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
2434 int spaces; /* keep track of the number of leading spaces on the line */
2438 spaces = buffer - pt;
2440 pt = buffer + pos - spaces;
2441 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2444 if (pt == buffer) /* complete cmd */
2446 /* first TAB. Collect all the matches */
2450 strfcpy (User_typed, pt, sizeof (User_typed));
2451 memset (Matches, 0, Matches_listsize);
2452 memset (Completed, 0, sizeof (Completed));
2453 for (num = 0; Commands[num].name; num++)
2454 candidate (Completed, User_typed, Commands[num].name, sizeof (Completed));
2455 matches_ensure_morespace (Num_matched);
2456 Matches[Num_matched++] = User_typed;
2458 /* All matches are stored. Longest non-ambiguous string is ""
2459 * i.e. dont change 'buffer'. Fake successful return this time */
2460 if (User_typed[0] == 0)
2464 if (Completed[0] == 0 && User_typed[0])
2467 /* Num_matched will _always_ be atleast 1 since the initial
2468 * user-typed string is always stored */
2469 if (numtabs == 1 && Num_matched == 2)
2470 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
2471 else if (numtabs > 1 && Num_matched > 2)
2472 /* cycle thru all the matches */
2473 snprintf(Completed, sizeof(Completed), "%s",
2474 Matches[(numtabs - 2) % Num_matched]);
2476 /* return the completed command */
2477 strncpy (buffer, Completed, len - spaces);
2479 else if (!mutt_strncmp (buffer, "set", 3)
2480 || !mutt_strncmp (buffer, "unset", 5)
2481 || !mutt_strncmp (buffer, "reset", 5)
2482 || !mutt_strncmp (buffer, "toggle", 6))
2483 { /* complete variables */
2484 char *prefixes[] = { "no", "inv", "?", "&", 0 };
2487 /* loop through all the possible prefixes (no, inv, ...) */
2488 if (!mutt_strncmp (buffer, "set", 3))
2490 for (num = 0; prefixes[num]; num++)
2492 if (!mutt_strncmp (pt, prefixes[num], mutt_strlen (prefixes[num])))
2494 pt += mutt_strlen (prefixes[num]);
2500 /* first TAB. Collect all the matches */
2504 strfcpy (User_typed, pt, sizeof (User_typed));
2505 memset (Matches, 0, Matches_listsize);
2506 memset (Completed, 0, sizeof (Completed));
2507 for (num = 0; MuttVars[num].option; num++)
2508 candidate (Completed, User_typed, MuttVars[num].option, sizeof (Completed));
2509 for (myv = MyVars; myv; myv = myv->next)
2510 candidate (Completed, User_typed, myv->name, sizeof (Completed));
2511 matches_ensure_morespace (Num_matched);
2512 Matches[Num_matched++] = User_typed;
2514 /* All matches are stored. Longest non-ambiguous string is ""
2515 * i.e. dont change 'buffer'. Fake successful return this time */
2516 if (User_typed[0] == 0)
2520 if (Completed[0] == 0 && User_typed[0])
2523 /* Num_matched will _always_ be atleast 1 since the initial
2524 * user-typed string is always stored */
2525 if (numtabs == 1 && Num_matched == 2)
2526 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
2527 else if (numtabs > 1 && Num_matched > 2)
2528 /* cycle thru all the matches */
2529 snprintf(Completed, sizeof(Completed), "%s",
2530 Matches[(numtabs - 2) % Num_matched]);
2532 strncpy (pt, Completed, buffer + len - pt - spaces);
2534 else if (!mutt_strncmp (buffer, "exec", 4))
2536 struct binding_t *menu = km_get_table (CurrentMenu);
2538 if (!menu && CurrentMenu != MENU_PAGER)
2542 /* first TAB. Collect all the matches */
2546 strfcpy (User_typed, pt, sizeof (User_typed));
2547 memset (Matches, 0, Matches_listsize);
2548 memset (Completed, 0, sizeof (Completed));
2549 for (num = 0; menu[num].name; num++)
2550 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
2551 /* try the generic menu */
2552 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER)
2555 for (num = 0; menu[num].name; num++)
2556 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
2558 matches_ensure_morespace (Num_matched);
2559 Matches[Num_matched++] = User_typed;
2561 /* All matches are stored. Longest non-ambiguous string is ""
2562 * i.e. dont change 'buffer'. Fake successful return this time */
2563 if (User_typed[0] == 0)
2567 if (Completed[0] == 0 && User_typed[0])
2570 /* Num_matched will _always_ be atleast 1 since the initial
2571 * user-typed string is always stored */
2572 if (numtabs == 1 && Num_matched == 2)
2573 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
2574 else if (numtabs > 1 && Num_matched > 2)
2575 /* cycle thru all the matches */
2576 snprintf(Completed, sizeof(Completed), "%s",
2577 Matches[(numtabs - 2) % Num_matched]);
2579 strncpy (pt, Completed, buffer + len - pt - spaces);
2587 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2589 char var[STRING], *pt = buffer;
2596 spaces = buffer - pt;
2598 pt = buffer + pos - spaces;
2599 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2601 pt++; /* move past the space */
2602 if (*pt == '=') /* abort if no var before the '=' */
2605 if (mutt_strncmp (buffer, "set", 3) == 0)
2608 char val[LONG_STRING];
2609 const char *myvarval;
2611 strfcpy (var, pt, sizeof (var));
2612 /* ignore the trailing '=' when comparing */
2613 var[mutt_strlen (var) - 1] = 0;
2614 if ((idx = mutt_option_index (var)) == -1)
2616 if ((myvarval = myvar_get(var)) != NULL)
2618 pretty_var (pt, len - (pt - buffer), var, myvarval);
2621 return 0; /* no such variable. */
2623 else if (var_to_string (idx, val, sizeof (val)))
2625 snprintf (pt, len - (pt - buffer), "%s=\"%s\"", var, val);
2632 static int var_to_string (int idx, char* val, size_t len)
2634 char tmp[LONG_STRING];
2635 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2639 if ((DTYPE(MuttVars[idx].type) == DT_STR) ||
2640 (DTYPE(MuttVars[idx].type) == DT_PATH) ||
2641 (DTYPE(MuttVars[idx].type) == DT_RX))
2643 strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data)), sizeof (tmp));
2644 if (DTYPE (MuttVars[idx].type) == DT_PATH)
2645 mutt_pretty_mailbox (tmp, sizeof (tmp));
2647 else if (DTYPE (MuttVars[idx].type) == DT_ADDR)
2649 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) MuttVars[idx].data), 0);
2651 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
2652 strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp));
2653 else if (DTYPE (MuttVars[idx].type) == DT_NUM)
2655 short sval = *((short *) MuttVars[idx].data);
2657 /* avert your eyes, gentle reader */
2658 if (mutt_strcmp (MuttVars[idx].option, "wrapmargin") == 0)
2659 sval = sval > 0 ? 0 : -sval;
2661 snprintf (tmp, sizeof (tmp), "%d", sval);
2663 else if (DTYPE (MuttVars[idx].type) == DT_SORT)
2665 const struct mapping_t *map;
2668 switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
2671 map = SortAliasMethods;
2673 case DT_SORT_BROWSER:
2674 map = SortBrowserMethods;
2677 if ((WithCrypto & APPLICATION_PGP))
2678 map = SortKeyMethods;
2686 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
2687 snprintf (tmp, sizeof (tmp), "%s%s%s",
2688 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
2689 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
2692 else if (DTYPE (MuttVars[idx].type) == DT_MAGIC)
2696 switch (DefaultMagic)
2713 strfcpy (tmp, p, sizeof (tmp));
2715 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
2716 strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no", sizeof (tmp));
2720 escape_string (val, len - 1, tmp);
2725 /* Implement the -Q command line flag */
2726 int mutt_query_variables (LIST *queries)
2730 char errbuff[LONG_STRING];
2731 char command[STRING];
2735 memset (&err, 0, sizeof (err));
2736 memset (&token, 0, sizeof (token));
2739 err.dsize = sizeof (errbuff);
2741 for (p = queries; p; p = p->next)
2743 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2744 if (mutt_parse_rc_line (command, &token, &err) == -1)
2746 fprintf (stderr, "%s\n", err.data);
2750 printf ("%s\n", err.data);
2757 /* dump out the value of all the variables we have */
2758 int mutt_dump_variables (void)
2762 char errbuff[LONG_STRING];
2763 char command[STRING];
2767 memset (&err, 0, sizeof (err));
2768 memset (&token, 0, sizeof (token));
2771 err.dsize = sizeof (errbuff);
2773 for (i = 0; MuttVars[i].option; i++)
2775 if (MuttVars[i].type == DT_SYN)
2778 snprintf (command, sizeof (command), "set ?%s\n", MuttVars[i].option);
2779 if (mutt_parse_rc_line (command, &token, &err) == -1)
2781 fprintf (stderr, "%s\n", err.data);
2785 printf("%s\n", err.data);
2792 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2796 for (i=0; map[i].name; i++)
2797 if (map[i].value == val)
2798 return (map[i].name);
2802 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2806 for (i = 0; map[i].name; i++)
2807 if (ascii_strcasecmp (map[i].name, name) == 0)
2808 return (map[i].value);
2813 static void start_debug (void)
2817 char buf[_POSIX_PATH_MAX];
2818 char buf2[_POSIX_PATH_MAX];
2820 /* rotate the old debug logs */
2821 for (i=3; i>=0; i--)
2823 snprintf (buf, sizeof(buf), "%s/.muttdebug%d", NONULL(Homedir), i);
2824 snprintf (buf2, sizeof(buf2), "%s/.muttdebug%d", NONULL(Homedir), i+1);
2827 if ((debugfile = safe_fopen(buf, "w")) != NULL)
2830 setbuf (debugfile, NULL); /* don't buffer the debugging output! */
2831 dprint(1,(debugfile,"Mutt/%s (%s) debugging at level %d\n",
2832 MUTT_VERSION, ReleaseDate, debuglevel));
2837 static int mutt_execute_commands (LIST *p)
2840 char errstr[SHORT_STRING];
2842 memset (&err, 0, sizeof (err));
2844 err.dsize = sizeof (errstr);
2845 memset (&token, 0, sizeof (token));
2846 for (; p; p = p->next)
2848 if (mutt_parse_rc_line (p->data, &token, &err) != 0)
2850 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2859 static void mutt_srandom (void)
2864 gettimeofday(&tv, NULL);
2865 /* POSIX.1-2008 states that seed is 'unsigned' without specifying its width.
2866 * Use as many of the lower order bits from the current time of day as the seed.
2867 * If the upper bound is truncated, that is fine.
2869 * tv_sec is integral of type integer or float. Cast to 'long long' before
2870 * bitshift in case it is a float.
2872 seed = ((LONGLONG) tv.tv_sec << 20) | tv.tv_usec;
2876 void mutt_init (int skip_sys_rc, LIST *commands)
2879 struct utsname utsname;
2880 char *p, buffer[STRING], error[STRING];
2881 int i, default_rc = 0, need_pause = 0;
2884 memset (&err, 0, sizeof (err));
2886 err.dsize = sizeof (error);
2888 Groups = hash_create (1031, 0);
2889 ReverseAlias = hash_create (1031, 1);
2895 * XXX - use something even more difficult to predict?
2897 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2898 "\033]9;%ld\a", (long) time (NULL));
2900 /* on one of the systems I use, getcwd() does not return the same prefix
2901 as is listed in the passwd file */
2902 if ((p = getenv ("HOME")))
2903 Homedir = safe_strdup (p);
2905 /* Get some information about the user */
2906 if ((pw = getpwuid (getuid ())))
2910 Username = safe_strdup (pw->pw_name);
2912 Homedir = safe_strdup (pw->pw_dir);
2914 Realname = safe_strdup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2915 Shell = safe_strdup (pw->pw_shell);
2923 fputs (_("unable to determine home directory"), stderr);
2926 if ((p = getenv ("USER")))
2927 Username = safe_strdup (p);
2931 fputs (_("unable to determine username"), stderr);
2934 Shell = safe_strdup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2938 /* Start up debugging mode if requested */
2943 /* And about the host... */
2945 /* some systems report the FQDN instead of just the hostname */
2946 if ((p = strchr (utsname.nodename, '.')))
2948 Hostname = mutt_substrdup (utsname.nodename, p);
2950 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2953 Hostname = safe_strdup (utsname.nodename);
2956 #define DOMAIN buffer
2957 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2958 Fqdn = safe_strdup ("@");
2963 Fqdn = safe_malloc (mutt_strlen (DOMAIN) + mutt_strlen (Hostname) + 2);
2964 sprintf (Fqdn, "%s.%s", NONULL(Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2967 Fqdn = safe_strdup(NONULL(Hostname));
2969 if ((p = getenv ("MAIL")))
2970 Spoolfile = safe_strdup (p);
2971 else if ((p = getenv ("MAILDIR")))
2972 Spoolfile = safe_strdup (p);
2976 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2978 mutt_concat_path (buffer, MAILPATH, NONULL(Username), sizeof (buffer));
2980 Spoolfile = safe_strdup (buffer);
2983 if ((p = getenv ("MAILCAPS")))
2984 MailcapPath = safe_strdup (p);
2987 /* Default search path from RFC1524 */
2988 MailcapPath = safe_strdup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2991 Tempdir = safe_strdup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2993 p = getenv ("VISUAL");
2996 p = getenv ("EDITOR");
3000 Editor = safe_strdup (p);
3001 Visual = safe_strdup (p);
3003 if ((p = getenv ("REPLYTO")) != NULL)
3007 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
3009 memset (&buf, 0, sizeof (buf));
3010 buf.data = buf.dptr = buffer;
3011 buf.dsize = mutt_strlen (buffer);
3013 memset (&token, 0, sizeof (token));
3014 parse_my_hdr (&token, &buf, 0, &err);
3018 if ((p = getenv ("EMAIL")) != NULL)
3019 From = rfc822_parse_adrlist (NULL, p);
3021 mutt_set_langinfo_charset ();
3022 mutt_set_charset (Charset);
3024 Matches = safe_calloc (Matches_listsize, sizeof (char *));
3026 /* Set standard defaults */
3027 for (i = 0; MuttVars[i].option; i++)
3029 mutt_set_default (&MuttVars[i]);
3030 mutt_restore_default (&MuttVars[i]);
3033 CurrentMenu = MENU_MAIN;
3036 #ifndef LOCALES_HACK
3037 /* Do we have a locale definition? */
3038 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
3039 ((p = getenv ("LANG")) != NULL && p[0]) ||
3040 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
3041 set_option (OPTLOCALES);
3045 /* Unset suspend by default if we're the session leader */
3046 if (getsid(0) == getpid())
3047 unset_option (OPTSUSPEND);
3050 mutt_init_history ();
3059 * When changing the code which looks for a configuration file,
3060 * please also change the corresponding code in muttbug.sh.in.
3070 snprintf (buffer, sizeof(buffer), "%s/.muttrc-%s", NONULL(Homedir), MUTT_VERSION);
3071 if (access(buffer, F_OK) == -1)
3072 snprintf (buffer, sizeof(buffer), "%s/.muttrc", NONULL(Homedir));
3073 if (access(buffer, F_OK) == -1)
3074 snprintf (buffer, sizeof (buffer), "%s/.mutt/muttrc-%s", NONULL(Homedir), MUTT_VERSION);
3075 if (access(buffer, F_OK) == -1)
3076 snprintf (buffer, sizeof (buffer), "%s/.mutt/muttrc", NONULL(Homedir));
3077 if (access(buffer, F_OK) == -1) /* default to .muttrc for alias_file */
3078 snprintf (buffer, sizeof(buffer), "%s/.muttrc", NONULL(Homedir));
3081 Muttrc = safe_strdup (buffer);
3085 strfcpy (buffer, Muttrc, sizeof (buffer));
3087 mutt_expand_path (buffer, sizeof (buffer));
3088 Muttrc = safe_strdup (buffer);
3091 AliasFile = safe_strdup (NONULL(Muttrc));
3093 /* Process the global rc file if it exists and the user hasn't explicity
3094 requested not to via "-n". */
3097 snprintf (buffer, sizeof(buffer), "%s/Muttrc-%s", SYSCONFDIR, MUTT_VERSION);
3098 if (access (buffer, F_OK) == -1)
3099 snprintf (buffer, sizeof(buffer), "%s/Muttrc", SYSCONFDIR);
3100 if (access (buffer, F_OK) == -1)
3101 snprintf (buffer, sizeof (buffer), "%s/Muttrc-%s", PKGDATADIR, MUTT_VERSION);
3102 if (access (buffer, F_OK) == -1)
3103 snprintf (buffer, sizeof (buffer), "%s/Muttrc", PKGDATADIR);
3104 if (access (buffer, F_OK) != -1)
3106 if (source_rc (buffer, &err) != 0)
3108 fputs (err.data, stderr);
3109 fputc ('\n', stderr);
3115 /* Read the user's initialization file. */
3116 if (access (Muttrc, F_OK) != -1)
3118 if (!option (OPTNOCURSES))
3120 if (source_rc (Muttrc, &err) != 0)
3122 fputs (err.data, stderr);
3123 fputc ('\n', stderr);
3127 else if (!default_rc)
3129 /* file specified by -F does not exist */
3130 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
3131 mutt_endwin (buffer);
3135 if (mutt_execute_commands (commands) != 0)
3138 if (need_pause && !option (OPTNOCURSES))
3140 if (mutt_any_key_to_continue (NULL) == -1)
3144 mutt_read_histfile ();
3147 set_option (OPTWEED); /* turn weeding on by default */
3151 int mutt_get_hook_type (const char *name)
3153 struct command_t *c;
3155 for (c = Commands ; c->name ; c++)
3156 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
3161 static int parse_group_context (group_context_t **ctx, BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
3163 while (!mutt_strcasecmp (buf->data, "-group"))
3167 strfcpy (err->data, _("-group: no group name"), err->dsize);
3171 mutt_extract_token (buf, s, 0);
3173 mutt_group_context_add (ctx, mutt_pattern_group (buf->data));
3177 strfcpy (err->data, _("out of arguments"), err->dsize);
3181 mutt_extract_token (buf, s, 0);
3187 mutt_group_context_destroy (ctx);
3191 static void myvar_set (const char* var, const char* val)
3195 for (cur = &MyVars; *cur; cur = &((*cur)->next))
3196 if (!mutt_strcmp ((*cur)->name, var))
3200 *cur = safe_calloc (1, sizeof (myvar_t));
3203 (*cur)->name = safe_strdup (var);
3205 mutt_str_replace (&(*cur)->value, val);
3208 static void myvar_del (const char* var)
3214 for (cur = &MyVars; *cur; cur = &((*cur)->next))
3215 if (!mutt_strcmp ((*cur)->name, var))
3221 FREE (&(*cur)->name);
3222 FREE (&(*cur)->value);
3223 FREE (cur); /* __FREE_CHECKED__ */
3228 static const char* myvar_get (const char* var)
3232 for (cur = MyVars; cur; cur = cur->next)
3233 if (!mutt_strcmp (cur->name, var))
3234 return NONULL(cur->value);