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"
49 #include <sys/utsname.h>
54 if ((CurrentMenu == MENU_PAGER) && (idx >= 0) && \
55 (MuttVars[idx].flags & R_RESORT)) \
57 snprintf (err->data, err->dsize, \
58 _("Not available in this menu.")); \
69 static myvar_t* MyVars;
71 static int var_to_string (int idx, char* val, size_t len);
73 static void myvar_set (const char* var, const char* val);
74 static const char* myvar_get (const char* var);
75 static void myvar_del (const char* var);
77 static void toggle_quadoption (int opt)
80 int b = (opt % 4) * 2;
82 QuadOptions[n] ^= (1 << b);
85 void set_quadoption (int opt, int flag)
88 int b = (opt % 4) * 2;
90 QuadOptions[n] &= ~(0x3 << b);
91 QuadOptions[n] |= (flag & 0x3) << b;
94 int quadoption (int opt)
97 int b = (opt % 4) * 2;
99 return (QuadOptions[n] >> b) & 0x3;
102 int query_quadoption (int opt, const char *prompt)
104 int v = quadoption (opt);
113 v = mutt_yesorno (prompt, (v == M_ASKYES));
114 CLEARLINE (LINES - 1);
121 /* given the variable ``s'', return the index into the rc_vars array which
122 matches, or -1 if the variable is not found. */
123 static int mutt_option_index (char *s)
127 for (i = 0; MuttVars[i].option; i++)
128 if (mutt_strcmp (s, MuttVars[i].option) == 0)
129 return (MuttVars[i].type == DT_SYN ? mutt_option_index ((char *) MuttVars[i].data) : i);
133 int mutt_extract_token (BUFFER *dest, BUFFER *tok, int flags)
136 char qc = 0; /* quote char */
139 /* reset the destination pointer to the beginning of the buffer */
140 dest->dptr = dest->data;
143 while ((ch = *tok->dptr))
147 if ((ISSPACE (ch) && !(flags & M_TOKEN_SPACE)) ||
148 (ch == '#' && !(flags & M_TOKEN_COMMENT)) ||
149 (ch == '=' && (flags & M_TOKEN_EQUAL)) ||
150 (ch == ';' && !(flags & M_TOKEN_SEMICOLON)) ||
151 ((flags & M_TOKEN_PATTERN) && strchr ("~%=!|", ch)))
158 qc = 0; /* end of quote */
159 else if (!qc && (ch == '\'' || ch == '"') && !(flags & M_TOKEN_QUOTE))
161 else if (ch == '\\' && qc != '\'')
164 return -1; /* premature end of token */
165 switch (ch = *tok->dptr++)
170 return -1; /* premature end of token */
171 mutt_buffer_addch (dest, (toupper ((unsigned char) *tok->dptr)
176 mutt_buffer_addch (dest, '\r');
179 mutt_buffer_addch (dest, '\n');
182 mutt_buffer_addch (dest, '\t');
185 mutt_buffer_addch (dest, '\f');
188 mutt_buffer_addch (dest, '\033');
191 if (isdigit ((unsigned char) ch) &&
192 isdigit ((unsigned char) *tok->dptr) &&
193 isdigit ((unsigned char) *(tok->dptr + 1)))
196 mutt_buffer_addch (dest, (ch << 6) + (*tok->dptr << 3) + *(tok->dptr + 1) - 3504);
200 mutt_buffer_addch (dest, ch);
203 else if (ch == '^' && (flags & M_TOKEN_CONDENSE))
206 return -1; /* premature end of token */
209 mutt_buffer_addch (dest, ch);
211 mutt_buffer_addch (dest, '\033');
212 else if (isalpha ((unsigned char) ch))
213 mutt_buffer_addch (dest, toupper ((unsigned char) ch) - '@');
216 mutt_buffer_addch (dest, '^');
217 mutt_buffer_addch (dest, ch);
220 else if (ch == '`' && (!qc || qc == '"'))
231 if ((pc = strpbrk (pc, "\\`")))
233 /* skip any quoted chars */
237 } while (pc && *pc != '`');
240 dprint (1, (debugfile, "mutt_get_token: mismatched backticks\n"));
243 cmd = mutt_substrdup (tok->dptr, pc);
244 if ((pid = mutt_create_filter (cmd, NULL, &fp, NULL)) < 0)
246 dprint (1, (debugfile, "mutt_get_token: unable to fork command: %s", cmd));
255 memset (&expn, 0, sizeof (expn));
256 expn.data = mutt_read_line (NULL, &expn.dsize, fp, &line, 0);
258 mutt_wait_filter (pid);
260 /* if we got output, make a new string consiting of the shell ouptput
261 plus whatever else was left on the original line */
262 /* BUT: If this is inside a quoted string, directly add output to
266 mutt_buffer_addstr (dest, expn.data);
271 expnlen = mutt_strlen (expn.data);
272 tok->dsize = expnlen + mutt_strlen (tok->dptr) + 1;
273 ptr = safe_malloc (tok->dsize);
274 memcpy (ptr, expn.data, expnlen);
275 strcpy (ptr + expnlen, tok->dptr); /* __STRCPY_CHECKED__ */
280 tok->destroy = 1; /* mark that the caller should destroy this data */
285 else if (ch == '$' && (!qc || qc == '"') && (*tok->dptr == '{' || isalpha ((unsigned char) *tok->dptr)))
287 const char *env = NULL;
291 if (*tok->dptr == '{')
294 if ((pc = strchr (tok->dptr, '}')))
296 var = mutt_substrdup (tok->dptr, pc);
302 for (pc = tok->dptr; isalnum ((unsigned char) *pc) || *pc == '_'; pc++)
304 var = mutt_substrdup (tok->dptr, pc);
309 if ((env = getenv (var)) || (env = myvar_get (var)))
310 mutt_buffer_addstr (dest, env);
311 else if ((idx = mutt_option_index (var)) != -1)
313 /* expand settable mutt variables */
314 char val[LONG_STRING];
316 if (var_to_string (idx, val, sizeof (val)))
317 mutt_buffer_addstr (dest, val);
323 mutt_buffer_addch (dest, ch);
325 mutt_buffer_addch (dest, 0); /* terminate the string */
330 static void mutt_free_opt (struct option_t* p)
334 switch (p->type & DT_MASK)
337 rfc822_free_address ((ADDRESS**)p->data);
340 pp = (REGEXP*)p->data;
350 FREE ((char**)p->data); /* __FREE_CHECKED__ */
355 /* clean up before quitting */
356 void mutt_free_opts (void)
360 for (i = 0; MuttVars[i].option; i++)
361 mutt_free_opt (MuttVars + i);
363 mutt_free_rx_list (&Alternates);
364 mutt_free_rx_list (&UnAlternates);
365 mutt_free_rx_list (&MailLists);
366 mutt_free_rx_list (&UnMailLists);
367 mutt_free_rx_list (&SubscribedLists);
368 mutt_free_rx_list (&UnSubscribedLists);
369 mutt_free_rx_list (&NoSpamList);
372 static void add_to_list (LIST **list, const char *str)
374 LIST *t, *last = NULL;
376 /* don't add a NULL or empty string to the list */
377 if (!str || *str == '\0')
380 /* check to make sure the item is not already on this list */
381 for (last = *list; last; last = last->next)
383 if (ascii_strcasecmp (str, last->data) == 0)
385 /* already on the list, so just ignore it */
395 t = (LIST *) safe_calloc (1, sizeof (LIST));
396 t->data = safe_strdup (str);
407 int mutt_add_to_rx_list (RX_LIST **list, const char *s, int flags, BUFFER *err)
409 RX_LIST *t, *last = NULL;
415 if (!(rx = mutt_compile_regexp (s, flags)))
417 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
421 /* check to make sure the item is not already on this list */
422 for (last = *list; last; last = last->next)
424 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0)
426 /* already on the list, so just ignore it */
436 t = mutt_new_rx_list();
447 mutt_free_regexp (&rx);
452 static int remove_from_spam_list (SPAM_LIST **list, const char *pat);
454 static int add_to_spam_list (SPAM_LIST **list, const char *pat, const char *templ, BUFFER *err)
456 SPAM_LIST *t = NULL, *last = NULL;
461 if (!pat || !*pat || !templ)
464 if (!(rx = mutt_compile_regexp (pat, REG_ICASE)))
466 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
470 /* check to make sure the item is not already on this list */
471 for (last = *list; last; last = last->next)
473 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0)
475 /* Already on the list. Formerly we just skipped this case, but
476 * now we're supporting removals, which means we're supporting
477 * re-adds conceptually. So we probably want this to imply a
478 * removal, then do an add. We can achieve the removal by freeing
479 * the template, and leaving t pointed at the current item.
489 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
490 * update. Otherwise we want to make a new one to link at the list's end.
494 t = mutt_new_spam_list();
502 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
503 t->template = safe_strdup(templ);
505 /* Find highest match number in template string */
514 while (*p && isdigit((int)*p))
521 if (t->nmatch > t->rx->rx->re_nsub)
523 snprintf (err->data, err->dsize, _("Not enough subexpressions for spam "
525 remove_from_spam_list(list, pat);
529 t->nmatch++; /* match 0 is always the whole expr */
534 static int remove_from_spam_list (SPAM_LIST **list, const char *pat)
536 SPAM_LIST *spam, *prev;
539 /* Being first is a special case. */
543 if (spam->rx && !mutt_strcmp(spam->rx->pattern, pat))
546 mutt_free_regexp(&spam->rx);
547 FREE(&spam->template);
553 for (spam = prev->next; spam;)
555 if (!mutt_strcmp(spam->rx->pattern, pat))
557 prev->next = spam->next;
558 mutt_free_regexp(&spam->rx);
559 FREE(&spam->template);
572 static void remove_from_list (LIST **l, const char *str)
574 LIST *p, *last = NULL;
576 if (mutt_strcmp ("*", str) == 0)
577 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
584 if (ascii_strcasecmp (str, p->data) == 0)
588 last->next = p->next;
602 static int remove_from_rx_list (RX_LIST **l, const char *str)
604 RX_LIST *p, *last = NULL;
607 if (mutt_strcmp ("*", str) == 0)
609 mutt_free_rx_list (l); /* ``unCMD *'' means delete all current entries */
618 if (ascii_strcasecmp (str, p->rx->pattern) == 0)
620 mutt_free_regexp (&p->rx);
622 last->next = p->next;
638 static int parse_unignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
642 mutt_extract_token (buf, s, 0);
644 /* don't add "*" to the unignore list */
645 if (strcmp (buf->data, "*"))
646 add_to_list (&UnIgnore, buf->data);
648 remove_from_list (&Ignore, buf->data);
650 while (MoreArgs (s));
655 static int parse_ignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
659 mutt_extract_token (buf, s, 0);
660 remove_from_list (&UnIgnore, buf->data);
661 add_to_list (&Ignore, buf->data);
663 while (MoreArgs (s));
668 static int parse_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
672 mutt_extract_token (buf, s, 0);
673 add_to_list ((LIST **) data, buf->data);
675 while (MoreArgs (s));
680 static void _alternates_clean (void)
683 if (Context && Context->msgcount)
685 for (i = 0; i < Context->msgcount; i++)
686 Context->hdrs[i]->recip_valid = 0;
690 static int parse_alternates (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
692 group_context_t *gc = NULL;
698 mutt_extract_token (buf, s, 0);
700 if (parse_group_context (&gc, buf, s, data, err) == -1)
703 remove_from_rx_list (&UnAlternates, buf->data);
705 if (mutt_add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
708 if (mutt_group_context_add_rx (gc, buf->data, REG_ICASE, err) != 0)
711 while (MoreArgs (s));
713 mutt_group_context_destroy (&gc);
717 mutt_group_context_destroy (&gc);
721 static int parse_unalternates (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
726 mutt_extract_token (buf, s, 0);
727 remove_from_rx_list (&Alternates, buf->data);
729 if (mutt_strcmp (buf->data, "*") &&
730 mutt_add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
734 while (MoreArgs (s));
739 static int parse_spam_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
743 memset(&templ, 0, sizeof(templ));
745 /* Insist on at least one parameter */
749 strfcpy(err->data, _("spam: no matching pattern"), err->dsize);
751 strfcpy(err->data, _("nospam: no matching pattern"), err->dsize);
755 /* Extract the first token, a regexp */
756 mutt_extract_token (buf, s, 0);
758 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
761 /* If there's a second parameter, it's a template for the spam tag. */
764 mutt_extract_token (&templ, s, 0);
766 /* Add to the spam list. */
767 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
774 /* If not, try to remove from the nospam list. */
777 remove_from_rx_list(&NoSpamList, buf->data);
783 /* M_NOSPAM is for nospam commands. */
784 else if (data == M_NOSPAM)
786 /* nospam only ever has one parameter. */
788 /* "*" is a special case. */
789 if (!mutt_strcmp(buf->data, "*"))
791 mutt_free_spam_list (&SpamList);
792 mutt_free_rx_list (&NoSpamList);
796 /* If it's on the spam list, just remove it. */
797 if (remove_from_spam_list(&SpamList, buf->data) != 0)
800 /* Otherwise, add it to the nospam list. */
801 if (mutt_add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
807 /* This should not happen. */
808 strfcpy(err->data, "This is no good at all.", err->dsize);
813 static int parse_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
817 mutt_extract_token (buf, s, 0);
819 * Check for deletion of entire list
821 if (mutt_strcmp (buf->data, "*") == 0)
823 mutt_free_list ((LIST **) data);
826 remove_from_list ((LIST **) data, buf->data);
828 while (MoreArgs (s));
833 static int parse_lists (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
835 group_context_t *gc = NULL;
839 mutt_extract_token (buf, s, 0);
841 if (parse_group_context (&gc, buf, s, data, err) == -1)
844 remove_from_rx_list (&UnMailLists, buf->data);
846 if (mutt_add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
849 if (mutt_group_context_add_rx (gc, buf->data, REG_ICASE, err) != 0)
852 while (MoreArgs (s));
854 mutt_group_context_destroy (&gc);
858 mutt_group_context_destroy (&gc);
862 typedef enum group_state_t {
866 static int parse_group (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
868 group_context_t *gc = NULL;
869 group_state_t state = NONE;
870 ADDRESS *addr = NULL;
875 mutt_extract_token (buf, s, 0);
876 if (parse_group_context (&gc, buf, s, data, err) == -1)
879 if (!mutt_strcasecmp (buf->data, "-rx"))
881 else if (!mutt_strcasecmp (buf->data, "-addr"))
888 strfcpy (err->data, _("Missing -rx or -addr."), err->dsize);
892 if (mutt_group_context_add_rx (gc, buf->data, REG_ICASE, err) != 0)
897 if ((addr = mutt_parse_adrlist (NULL, buf->data)) == NULL)
899 if (mutt_addrlist_to_idna (addr, &estr))
901 snprintf (err->data, err->dsize, _("Warning: Bad IDN '%s'.\n"),
905 mutt_group_context_add_adrlist (gc, addr);
906 rfc822_free_address (&addr);
910 } while (MoreArgs (s));
912 mutt_group_context_destroy (&gc);
916 mutt_group_context_destroy (&gc);
920 static int parse_ungroup (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
922 strfcpy (err->data, "not implemented", err->dsize);
926 /* always wise to do what someone else did before */
927 static void _attachments_clean (void)
930 if (Context && Context->msgcount)
932 for (i = 0; i < Context->msgcount; i++)
933 Context->hdrs[i]->attach_valid = 0;
937 static int parse_attach_list (BUFFER *buf, BUFFER *s, LIST **ldata, BUFFER *err)
945 /* Find the last item in the list that data points to. */
947 dprint(5, (debugfile, "parse_attach_list: ldata = %p, *ldata = %p\n",
948 (void *)ldata, (void *)*ldata));
949 for (listp = *ldata; listp; listp = listp->next)
951 a = (ATTACH_MATCH *)listp->data;
952 dprint(5, (debugfile, "parse_attach_list: skipping %s/%s\n",
953 a->major, a->minor));
959 mutt_extract_token (buf, s, 0);
961 if (!buf->data || *buf->data == '\0')
964 a = safe_malloc(sizeof(ATTACH_MATCH));
966 /* some cheap hacks that I expect to remove */
967 if (!ascii_strcasecmp(buf->data, "any"))
968 a->major = safe_strdup("*/.*");
969 else if (!ascii_strcasecmp(buf->data, "none"))
970 a->major = safe_strdup("cheap_hack/this_should_never_match");
972 a->major = safe_strdup(buf->data);
974 if ((p = strchr(a->major, '/')))
982 a->minor = "unknown";
985 len = strlen(a->minor);
986 tmpminor = safe_malloc(len+3);
987 strcpy(&tmpminor[1], a->minor); /* __STRCPY_CHECKED__ */
989 tmpminor[len+1] = '$';
990 tmpminor[len+2] = '\0';
992 a->major_int = mutt_check_mime_type(a->major);
993 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
997 dprint(5, (debugfile, "parse_attach_list: added %s/%s [%d]\n",
998 a->major, a->minor, a->major_int));
1000 listp = safe_malloc(sizeof(LIST));
1001 listp->data = (char *)a;
1005 lastp->next = listp;
1013 while (MoreArgs (s));
1015 _attachments_clean();
1019 static int parse_unattach_list (BUFFER *buf, BUFFER *s, LIST **ldata, BUFFER *err)
1022 LIST *lp, *lastp, *newlp;
1029 mutt_extract_token (buf, s, 0);
1031 if (!ascii_strcasecmp(buf->data, "any"))
1032 tmp = safe_strdup("*/.*");
1033 else if (!ascii_strcasecmp(buf->data, "none"))
1034 tmp = safe_strdup("cheap_hack/this_should_never_match");
1036 tmp = safe_strdup(buf->data);
1038 if ((minor = strchr(tmp, '/')))
1047 major = mutt_check_mime_type(tmp);
1049 /* We must do our own walk here because remove_from_list() will only
1050 * remove the LIST->data, not anything pointed to by the LIST->data. */
1052 for(lp = *ldata; lp; )
1054 a = (ATTACH_MATCH *)lp->data;
1055 dprint(5, (debugfile, "parse_unattach_list: check %s/%s [%d] : %s/%s [%d]\n",
1056 a->major, a->minor, a->major_int, tmp, minor, major));
1057 if (a->major_int == major && !mutt_strcasecmp(minor, a->minor))
1059 dprint(5, (debugfile, "parse_unattach_list: removed %s/%s [%d]\n",
1060 a->major, a->minor, a->major_int));
1061 regfree(&a->minor_rx);
1064 /* Relink backward */
1066 lastp->next = lp->next;
1071 FREE(&lp->data); /* same as a */
1082 while (MoreArgs (s));
1085 _attachments_clean();
1089 static int print_attach_list (LIST *lp, char op, char *name)
1092 printf("attachments %c%s %s/%s\n", op, name,
1093 ((ATTACH_MATCH *)lp->data)->major,
1094 ((ATTACH_MATCH *)lp->data)->minor);
1102 static int parse_attachments (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1107 mutt_extract_token(buf, s, 0);
1108 if (!buf->data || *buf->data == '\0') {
1109 strfcpy(err->data, _("attachments: no disposition"), err->dsize);
1113 category = buf->data;
1119 printf("\nCurrent attachments settings:\n\n");
1120 print_attach_list(AttachAllow, '+', "A");
1121 print_attach_list(AttachExclude, '-', "A");
1122 print_attach_list(InlineAllow, '+', "I");
1123 print_attach_list(InlineExclude, '-', "I");
1124 set_option (OPTFORCEREDRAWINDEX);
1125 set_option (OPTFORCEREDRAWPAGER);
1126 mutt_any_key_to_continue (NULL);
1130 if (op != '+' && op != '-') {
1134 if (!ascii_strncasecmp(category, "attachment", strlen(category))) {
1136 listp = &AttachAllow;
1138 listp = &AttachExclude;
1140 else if (!ascii_strncasecmp(category, "inline", strlen(category))) {
1142 listp = &InlineAllow;
1144 listp = &InlineExclude;
1147 strfcpy(err->data, _("attachments: invalid disposition"), err->dsize);
1151 return parse_attach_list(buf, s, listp, err);
1154 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1159 mutt_extract_token(buf, s, 0);
1160 if (!buf->data || *buf->data == '\0') {
1161 strfcpy(err->data, _("unattachments: no disposition"), err->dsize);
1167 if (op != '+' && op != '-') {
1171 if (!ascii_strncasecmp(p, "attachment", strlen(p))) {
1173 listp = &AttachAllow;
1175 listp = &AttachExclude;
1177 else if (!ascii_strncasecmp(p, "inline", strlen(p))) {
1179 listp = &InlineAllow;
1181 listp = &InlineExclude;
1184 strfcpy(err->data, _("unattachments: invalid disposition"), err->dsize);
1188 return parse_unattach_list(buf, s, listp, err);
1191 static int parse_unlists (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1195 mutt_extract_token (buf, s, 0);
1196 remove_from_rx_list (&SubscribedLists, buf->data);
1197 remove_from_rx_list (&MailLists, buf->data);
1199 if (mutt_strcmp (buf->data, "*") &&
1200 mutt_add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1203 while (MoreArgs (s));
1208 static int parse_subscribe (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1210 group_context_t *gc = NULL;
1214 mutt_extract_token (buf, s, 0);
1216 if (parse_group_context (&gc, buf, s, data, err) == -1)
1219 remove_from_rx_list (&UnMailLists, buf->data);
1220 remove_from_rx_list (&UnSubscribedLists, buf->data);
1222 if (mutt_add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1224 if (mutt_add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1226 if (mutt_group_context_add_rx (gc, buf->data, REG_ICASE, err) != 0)
1229 while (MoreArgs (s));
1231 mutt_group_context_destroy (&gc);
1235 mutt_group_context_destroy (&gc);
1239 static int parse_unsubscribe (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1243 mutt_extract_token (buf, s, 0);
1244 remove_from_rx_list (&SubscribedLists, buf->data);
1246 if (mutt_strcmp (buf->data, "*") &&
1247 mutt_add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1250 while (MoreArgs (s));
1255 static int parse_unalias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1257 ALIAS *tmp, *last = NULL;
1261 mutt_extract_token (buf, s, 0);
1263 if (mutt_strcmp ("*", buf->data) == 0)
1265 if (CurrentMenu == MENU_ALIAS)
1267 for (tmp = Aliases; tmp ; tmp = tmp->next)
1269 set_option (OPTFORCEREDRAWINDEX);
1272 mutt_free_alias (&Aliases);
1276 for (tmp = Aliases; tmp; tmp = tmp->next)
1278 if (mutt_strcasecmp (buf->data, tmp->name) == 0)
1280 if (CurrentMenu == MENU_ALIAS)
1283 set_option (OPTFORCEREDRAWINDEX);
1288 last->next = tmp->next;
1290 Aliases = tmp->next;
1292 mutt_free_alias (&tmp);
1298 while (MoreArgs (s));
1302 static int parse_alias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1304 ALIAS *tmp = Aliases;
1307 group_context_t *gc = NULL;
1311 strfcpy (err->data, _("alias: no address"), err->dsize);
1315 mutt_extract_token (buf, s, 0);
1317 if (parse_group_context (&gc, buf, s, data, err) == -1)
1320 /* check to see if an alias with this name already exists */
1321 for (; tmp; tmp = tmp->next)
1323 if (!mutt_strcasecmp (tmp->name, buf->data))
1330 /* create a new alias */
1331 tmp = (ALIAS *) safe_calloc (1, sizeof (ALIAS));
1333 tmp->name = safe_strdup (buf->data);
1334 /* give the main addressbook code a chance */
1335 if (CurrentMenu == MENU_ALIAS)
1336 set_option (OPTMENUCALLER);
1340 mutt_alias_delete_reverse (tmp);
1341 /* override the previous value */
1342 rfc822_free_address (&tmp->addr);
1343 if (CurrentMenu == MENU_ALIAS)
1344 set_option (OPTFORCEREDRAWINDEX);
1347 mutt_extract_token (buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1348 dprint (3, (debugfile, "parse_alias: Second token is '%s'.\n",
1351 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1357 if (mutt_addrlist_to_idna (tmp->addr, &estr))
1359 snprintf (err->data, err->dsize, _("Warning: Bad IDN '%s' in alias '%s'.\n"),
1364 mutt_group_context_add_adrlist (gc, tmp->addr);
1365 mutt_alias_add_reverse (tmp);
1368 if (debuglevel >= 2)
1371 /* A group is terminated with an empty address, so check a->mailbox */
1372 for (a = tmp->addr; a && a->mailbox; a = a->next)
1375 dprint (3, (debugfile, "parse_alias: %s\n",
1378 dprint (3, (debugfile, "parse_alias: Group %s\n",
1383 mutt_group_context_destroy (&gc);
1387 mutt_group_context_destroy (&gc);
1392 parse_unmy_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1395 LIST *tmp = UserHeader;
1401 mutt_extract_token (buf, s, 0);
1402 if (mutt_strcmp ("*", buf->data) == 0)
1403 mutt_free_list (&UserHeader);
1409 l = mutt_strlen (buf->data);
1410 if (buf->data[l - 1] == ':')
1415 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0 && tmp->data[l] == ':')
1419 last->next = tmp->next;
1421 UserHeader = tmp->next;
1424 mutt_free_list (&ptr);
1434 while (MoreArgs (s));
1438 static int parse_my_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1444 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1445 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':')
1447 strfcpy (err->data, _("invalid header field"), err->dsize);
1450 keylen = p - buf->data + 1;
1454 for (tmp = UserHeader; ; tmp = tmp->next)
1456 /* see if there is already a field by this name */
1457 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0)
1459 /* replace the old value */
1461 tmp->data = buf->data;
1462 memset (buf, 0, sizeof (BUFFER));
1468 tmp->next = mutt_new_list ();
1473 tmp = mutt_new_list ();
1476 tmp->data = buf->data;
1477 memset (buf, 0, sizeof (BUFFER));
1482 parse_sort (short *val, const char *s, const struct mapping_t *map, BUFFER *err)
1486 if (mutt_strncmp ("reverse-", s, 8) == 0)
1489 flags = SORT_REVERSE;
1492 if (mutt_strncmp ("last-", s, 5) == 0)
1498 if ((i = mutt_getvaluebyname (s, map)) == -1)
1500 snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s);
1509 static void mutt_set_default (struct option_t *p)
1511 switch (p->type & DT_MASK)
1514 if (!p->init && *((char **) p->data))
1515 p->init = (unsigned long) safe_strdup (* ((char **) p->data));
1518 if (!p->init && *((char **) p->data))
1520 char *cp = safe_strdup (*((char **) p->data));
1521 /* mutt_pretty_mailbox (cp); */
1522 p->init = (unsigned long) cp;
1526 if (!p->init && *((ADDRESS **) p->data))
1528 char tmp[HUGE_STRING];
1530 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data), 0);
1531 p->init = (unsigned long) safe_strdup (tmp);
1536 REGEXP *pp = (REGEXP *) p->data;
1537 if (!p->init && pp->pattern)
1538 p->init = (unsigned long) safe_strdup (pp->pattern);
1544 static void mutt_restore_default (struct option_t *p)
1546 switch (p->type & DT_MASK)
1550 mutt_str_replace ((char **) p->data, (char *) p->init);
1555 char path[_POSIX_PATH_MAX];
1557 strfcpy (path, (char *) p->init, sizeof (path));
1558 mutt_expand_path (path, sizeof (path));
1559 mutt_str_replace ((char **) p->data, path);
1565 rfc822_free_address ((ADDRESS **) p->data);
1566 *((ADDRESS **) p->data) = rfc822_parse_adrlist (NULL, (char *) p->init);
1571 set_option (p->data);
1573 unset_option (p->data);
1576 set_quadoption (p->data, p->init);
1581 *((short *) p->data) = p->init;
1585 REGEXP *pp = (REGEXP *) p->data;
1588 FREE (&pp->pattern);
1597 char *s = (char *) p->init;
1599 pp->rx = safe_calloc (1, sizeof (regex_t));
1600 pp->pattern = safe_strdup ((char *) p->init);
1601 if (mutt_strcmp (p->option, "mask") != 0)
1602 flags |= mutt_which_case ((const char *) p->init);
1603 if (mutt_strcmp (p->option, "mask") == 0 && *s == '!')
1608 if (REGCOMP (pp->rx, s, flags) != 0)
1610 fprintf (stderr, _("mutt_restore_default(%s): error in regexp: %s\n"),
1611 p->option, pp->pattern);
1612 FREE (&pp->pattern);
1621 if (p->flags & R_INDEX)
1622 set_option (OPTFORCEREDRAWINDEX);
1623 if (p->flags & R_PAGER)
1624 set_option (OPTFORCEREDRAWPAGER);
1625 if (p->flags & R_RESORT_SUB)
1626 set_option (OPTSORTSUBTHREADS);
1627 if (p->flags & R_RESORT)
1628 set_option (OPTNEEDRESORT);
1629 if (p->flags & R_RESORT_INIT)
1630 set_option (OPTRESORTINIT);
1631 if (p->flags & R_TREE)
1632 set_option (OPTREDRAWTREE);
1635 static size_t escape_string (char *dst, size_t len, const char* src)
1641 len--; /* save room for \0 */
1642 #define ESC_CHAR(C) do { *p++ = '\\'; if (p - dst < len) *p++ = C; } while(0)
1643 while (p - dst < len && src && *src)
1657 if ((*src == '\\' || *src == '"') && p - dst < len - 1)
1668 static void pretty_var (char *dst, size_t len, const char *option, const char *val)
1675 strfcpy (dst, option, len);
1676 len--; /* save room for \0 */
1677 p = dst + mutt_strlen (dst);
1683 p += escape_string (p, len - (p - dst) + 1, val); /* \0 terminate it */
1689 static int check_charset (struct option_t *opt, const char *val)
1691 char *p, *q = NULL, *s = safe_strdup (val);
1692 int rc = 0, strict = strcmp (opt->option, "send_charset") == 0;
1694 for (p = strtok_r (s, ":", &q); p; p = strtok_r (NULL, ":", &q))
1698 if (mutt_check_charset (p, strict) < 0)
1709 static int parse_set (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
1711 int query, unset, inv, reset, r = 0;
1713 char *p, scratch[_POSIX_PATH_MAX];
1716 while (MoreArgs (s))
1718 /* reset state variables */
1720 unset = data & M_SET_UNSET;
1721 inv = data & M_SET_INV;
1722 reset = data & M_SET_RESET;
1725 if (*s->dptr == '?')
1730 else if (mutt_strncmp ("no", s->dptr, 2) == 0)
1735 else if (mutt_strncmp ("inv", s->dptr, 3) == 0)
1740 else if (*s->dptr == '&')
1746 /* get the variable name */
1747 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1749 if (!mutt_strncmp ("my_", tmp->data, 3))
1751 else if ((idx = mutt_option_index (tmp->data)) == -1 &&
1752 !(reset && !mutt_strcmp ("all", tmp->data)))
1754 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1761 if (query || unset || inv)
1763 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1767 if (s && *s->dptr == '=')
1769 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1773 if (!mutt_strcmp ("all", tmp->data))
1775 if (CurrentMenu == MENU_PAGER)
1777 snprintf (err->data, err->dsize, _("Not available in this menu."));
1780 for (idx = 0; MuttVars[idx].option; idx++)
1781 mutt_restore_default (&MuttVars[idx]);
1782 set_option (OPTFORCEREDRAWINDEX);
1783 set_option (OPTFORCEREDRAWPAGER);
1784 set_option (OPTSORTSUBTHREADS);
1785 set_option (OPTNEEDRESORT);
1786 set_option (OPTRESORTINIT);
1787 set_option (OPTREDRAWTREE);
1796 mutt_restore_default (&MuttVars[idx]);
1799 else if (!myvar && DTYPE (MuttVars[idx].type) == DT_BOOL)
1801 if (s && *s->dptr == '=')
1803 if (unset || inv || query)
1805 snprintf (err->data, err->dsize, _("Usage: set variable=yes|no"));
1810 mutt_extract_token (tmp, s, 0);
1811 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1813 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1817 snprintf (err->data, err->dsize, _("Usage: set variable=yes|no"));
1824 snprintf (err->data, err->dsize, option (MuttVars[idx].data)
1825 ? _("%s is set") : _("%s is unset"), tmp->data);
1831 unset_option (MuttVars[idx].data);
1833 toggle_option (MuttVars[idx].data);
1835 set_option (MuttVars[idx].data);
1837 else if (myvar || DTYPE (MuttVars[idx].type) == DT_STR ||
1838 DTYPE (MuttVars[idx].type) == DT_PATH ||
1839 DTYPE (MuttVars[idx].type) == DT_ADDR)
1846 else if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1847 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1849 /* MuttVars[idx].data is already 'char**' (or some 'void**') or...
1850 * so cast to 'void*' is okay */
1851 FREE ((void *) MuttVars[idx].data); /* __FREE_CHECKED__ */
1853 else if (query || *s->dptr != '=')
1855 char _tmp[LONG_STRING];
1856 const char *val = NULL;
1860 if ((val = myvar_get (myvar)))
1862 pretty_var (err->data, err->dsize, myvar, val);
1867 snprintf (err->data, err->dsize, _("%s: unknown variable"), myvar);
1871 else if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1874 rfc822_write_address (_tmp, sizeof (_tmp), *((ADDRESS **) MuttVars[idx].data), 0);
1877 else if (DTYPE (MuttVars[idx].type) == DT_PATH)
1880 strfcpy (_tmp, NONULL(*((char **) MuttVars[idx].data)), sizeof (_tmp));
1881 mutt_pretty_mailbox (_tmp, sizeof (_tmp));
1885 val = *((char **) MuttVars[idx].data);
1887 /* user requested the value of this variable */
1888 pretty_var (err->data, err->dsize, MuttVars[idx].option, NONULL(val));
1898 /* myvar is a pointer to tmp and will be lost on extract_token */
1899 myvar = safe_strdup (myvar);
1903 mutt_extract_token (tmp, s, 0);
1907 myvar_set (myvar, tmp->data);
1909 myvar="don't resort";
1911 else if (DTYPE (MuttVars[idx].type) == DT_PATH)
1913 /* MuttVars[idx].data is already 'char**' (or some 'void**') or...
1914 * so cast to 'void*' is okay */
1915 FREE ((void *) MuttVars[idx].data); /* __FREE_CHECKED__ */
1917 strfcpy (scratch, tmp->data, sizeof (scratch));
1918 mutt_expand_path (scratch, sizeof (scratch));
1919 *((char **) MuttVars[idx].data) = safe_strdup (scratch);
1921 else if (DTYPE (MuttVars[idx].type) == DT_STR)
1923 if (strstr (MuttVars[idx].option, "charset") &&
1924 check_charset (&MuttVars[idx], tmp->data) < 0)
1926 snprintf (err->data, err->dsize, _("Invalid value for option %s: \"%s\""),
1927 MuttVars[idx].option, tmp->data);
1931 FREE ((void *) MuttVars[idx].data); /* __FREE_CHECKED__ */
1932 *((char **) MuttVars[idx].data) = safe_strdup (tmp->data);
1933 if (mutt_strcmp (MuttVars[idx].option, "charset") == 0)
1934 mutt_set_charset (Charset);
1938 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1939 *((ADDRESS **) MuttVars[idx].data) = rfc822_parse_adrlist (NULL, tmp->data);
1943 else if (DTYPE(MuttVars[idx].type) == DT_RX)
1945 REGEXP *ptr = (REGEXP *) MuttVars[idx].data;
1949 if (query || *s->dptr != '=')
1951 /* user requested the value of this variable */
1952 pretty_var (err->data, err->dsize, MuttVars[idx].option, NONULL(ptr->pattern));
1956 if (option(OPTATTACHMSG) && !mutt_strcmp(MuttVars[idx].option, "reply_regexp"))
1958 snprintf (err->data, err->dsize, "Operation not permitted when in attach-message mode.");
1966 /* copy the value of the string */
1967 mutt_extract_token (tmp, s, 0);
1969 if (!ptr->pattern || mutt_strcmp (ptr->pattern, tmp->data) != 0)
1973 /* $mask is case-sensitive */
1974 if (mutt_strcmp (MuttVars[idx].option, "mask") != 0)
1975 flags |= mutt_which_case (tmp->data);
1978 if (mutt_strcmp (MuttVars[idx].option, "mask") == 0)
1987 rx = (regex_t *) safe_malloc (sizeof (regex_t));
1988 if ((e = REGCOMP (rx, p, flags)) != 0)
1990 regerror (e, rx, err->data, err->dsize);
1996 /* get here only if everything went smootly */
1999 FREE (&ptr->pattern);
2000 regfree ((regex_t *) ptr->rx);
2004 ptr->pattern = safe_strdup (tmp->data);
2008 /* $reply_regexp and $alterantes require special treatment */
2010 if (Context && Context->msgcount &&
2011 mutt_strcmp (MuttVars[idx].option, "reply_regexp") == 0)
2013 regmatch_t pmatch[1];
2016 #define CUR_ENV Context->hdrs[i]->env
2017 for (i = 0; i < Context->msgcount; i++)
2019 if (CUR_ENV && CUR_ENV->subject)
2021 CUR_ENV->real_subj = (regexec (ReplyRegexp.rx,
2022 CUR_ENV->subject, 1, pmatch, 0)) ?
2024 CUR_ENV->subject + pmatch[0].rm_eo;
2031 else if (DTYPE(MuttVars[idx].type) == DT_MAGIC)
2033 if (query || *s->dptr != '=')
2035 switch (DefaultMagic)
2053 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p);
2060 /* copy the value of the string */
2061 mutt_extract_token (tmp, s, 0);
2062 if (mx_set_magic (tmp->data))
2064 snprintf (err->data, err->dsize, _("%s: invalid mailbox type"), tmp->data);
2069 else if (DTYPE(MuttVars[idx].type) == DT_NUM)
2071 short *ptr = (short *) MuttVars[idx].data;
2075 if (query || *s->dptr != '=')
2078 /* compatibility alias */
2079 if (mutt_strcmp (MuttVars[idx].option, "wrapmargin") == 0)
2080 val = *ptr < 0 ? -*ptr : 0;
2082 /* user requested the value of this variable */
2083 snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, val);
2090 mutt_extract_token (tmp, s, 0);
2091 rc = mutt_atos (tmp->data, (short *) &val);
2093 if (rc < 0 || !*tmp->data)
2095 snprintf (err->data, err->dsize, _("%s: invalid value (%s)"), tmp->data,
2096 rc == -1 ? _("format error") : _("number overflow"));
2103 /* these ones need a sanity check */
2104 if (mutt_strcmp (MuttVars[idx].option, "history") == 0)
2108 mutt_init_history ();
2110 else if (mutt_strcmp (MuttVars[idx].option, "pager_index_lines") == 0)
2115 else if (mutt_strcmp (MuttVars[idx].option, "wrapmargin") == 0)
2123 else if (mutt_strcmp (MuttVars[idx].option, "imap_pipeline_depth") == 0)
2130 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
2134 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2136 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
2137 vals [ quadoption (MuttVars[idx].data) ]);
2142 if (*s->dptr == '=')
2145 mutt_extract_token (tmp, s, 0);
2146 if (ascii_strcasecmp ("yes", tmp->data) == 0)
2147 set_quadoption (MuttVars[idx].data, M_YES);
2148 else if (ascii_strcasecmp ("no", tmp->data) == 0)
2149 set_quadoption (MuttVars[idx].data, M_NO);
2150 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
2151 set_quadoption (MuttVars[idx].data, M_ASKYES);
2152 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
2153 set_quadoption (MuttVars[idx].data, M_ASKNO);
2156 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
2164 toggle_quadoption (MuttVars[idx].data);
2166 set_quadoption (MuttVars[idx].data, M_NO);
2168 set_quadoption (MuttVars[idx].data, M_YES);
2171 else if (DTYPE (MuttVars[idx].type) == DT_SORT)
2173 const struct mapping_t *map = NULL;
2175 switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
2178 map = SortAliasMethods;
2180 case DT_SORT_BROWSER:
2181 map = SortBrowserMethods;
2184 if ((WithCrypto & APPLICATION_PGP))
2185 map = SortKeyMethods;
2188 map = SortAuxMethods;
2197 snprintf (err->data, err->dsize, _("%s: Unknown type."), MuttVars[idx].option);
2202 if (query || *s->dptr != '=')
2204 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
2206 snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
2207 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
2208 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
2214 mutt_extract_token (tmp, s , 0);
2216 if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) == -1)
2224 snprintf (err->data, err->dsize, _("%s: unknown type"), MuttVars[idx].option);
2231 if (MuttVars[idx].flags & R_INDEX)
2232 set_option (OPTFORCEREDRAWINDEX);
2233 if (MuttVars[idx].flags & R_PAGER)
2234 set_option (OPTFORCEREDRAWPAGER);
2235 if (MuttVars[idx].flags & R_RESORT_SUB)
2236 set_option (OPTSORTSUBTHREADS);
2237 if (MuttVars[idx].flags & R_RESORT)
2238 set_option (OPTNEEDRESORT);
2239 if (MuttVars[idx].flags & R_RESORT_INIT)
2240 set_option (OPTRESORTINIT);
2241 if (MuttVars[idx].flags & R_TREE)
2242 set_option (OPTREDRAWTREE);
2250 /* reads the specified initialization file. returns -1 if errors were found
2251 so that we can pause to let the user know... */
2252 static int source_rc (const char *rcfile, BUFFER *err)
2255 int line = 0, rc = 0, conv = 0;
2257 char *linebuf = NULL;
2258 char *currentline = NULL;
2262 dprint (2, (debugfile, "Reading configuration file '%s'.\n",
2265 if ((f = mutt_open_read (rcfile, &pid)) == NULL)
2267 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
2271 memset (&token, 0, sizeof (token));
2272 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line, M_CONT)) != NULL)
2274 conv=ConfigCharset && (*ConfigCharset) && Charset;
2277 currentline=safe_strdup(linebuf);
2278 if (!currentline) continue;
2279 mutt_convert_string(¤tline, ConfigCharset, Charset, 0);
2282 currentline=linebuf;
2284 if (mutt_parse_rc_line (currentline, &token, err) == -1)
2286 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
2287 if (--rc < -MAXERRS)
2289 if (conv) FREE(¤tline);
2305 mutt_wait_filter (pid);
2308 /* the muttrc source keyword */
2309 snprintf (err->data, err->dsize, rc >= -MAXERRS ? _("source: errors in %s")
2310 : _("source: reading aborted due too many errors in %s"), rcfile);
2318 static int parse_source (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
2320 char path[_POSIX_PATH_MAX];
2322 if (mutt_extract_token (tmp, s, 0) != 0)
2324 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
2329 strfcpy (err->data, _("source: too many arguments"), err->dsize);
2332 strfcpy (path, tmp->data, sizeof (path));
2333 mutt_expand_path (path, sizeof (path));
2334 return (source_rc (path, err));
2337 /* line command to execute
2339 token scratch buffer to be used by parser. caller should free
2340 token->data when finished. the reason for this variable is
2341 to avoid having to allocate and deallocate a lot of memory
2342 if we are parsing many lines. the caller can pass in the
2343 memory to use, which avoids having to create new space for
2344 every call to this function.
2346 err where to write error messages */
2347 int mutt_parse_rc_line (/* const */ char *line, BUFFER *token, BUFFER *err)
2352 if (!line || !*line)
2355 memset (&expn, 0, sizeof (expn));
2356 expn.data = expn.dptr = line;
2357 expn.dsize = mutt_strlen (line);
2364 if (*expn.dptr == '#')
2365 break; /* rest of line is a comment */
2366 if (*expn.dptr == ';')
2371 mutt_extract_token (token, &expn, 0);
2372 for (i = 0; Commands[i].name; i++)
2374 if (!mutt_strcmp (token->data, Commands[i].name))
2376 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
2381 if (!Commands[i].name)
2383 snprintf (err->data, err->dsize, _("%s: unknown command"), NONULL (token->data));
2395 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
2396 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
2397 /* initial string that starts completion. No telling how much crap
2398 * the user has typed so far. Allocate LONG_STRING just to be sure! */
2399 static char User_typed [LONG_STRING] = {0};
2401 static int Num_matched = 0; /* Number of matches for completion */
2402 static char Completed [STRING] = {0}; /* completed string (command or variable) */
2403 static const char **Matches;
2404 /* this is a lie until mutt_init runs: */
2405 static int Matches_listsize = MAX(NUMVARS,NUMCOMMANDS) + 10;
2407 static void matches_ensure_morespace(int current)
2409 int base_space, extra_space, space;
2411 if (current > Matches_listsize - 2)
2413 base_space = MAX(NUMVARS,NUMCOMMANDS) + 1;
2414 extra_space = Matches_listsize - base_space;
2416 space = base_space + extra_space;
2417 safe_realloc (&Matches, space * sizeof (char *));
2418 memset (&Matches[current + 1], 0, space - current);
2419 Matches_listsize = space;
2423 /* helper function for completion. Changes the dest buffer if
2424 necessary/possible to aid completion.
2425 dest == completion result gets here.
2426 src == candidate for completion.
2427 try == user entered data for completion.
2428 len == length of dest buffer.
2430 static void candidate (char *dest, char *try, const char *src, int len)
2434 if (strstr (src, try) == src)
2436 matches_ensure_morespace (Num_matched);
2437 Matches[Num_matched++] = src;
2439 strfcpy (dest, src, len);
2442 for (l = 0; src[l] && src[l] == dest[l]; l++);
2448 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
2452 int spaces; /* keep track of the number of leading spaces on the line */
2456 spaces = buffer - pt;
2458 pt = buffer + pos - spaces;
2459 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2462 if (pt == buffer) /* complete cmd */
2464 /* first TAB. Collect all the matches */
2468 strfcpy (User_typed, pt, sizeof (User_typed));
2469 memset (Matches, 0, Matches_listsize);
2470 memset (Completed, 0, sizeof (Completed));
2471 for (num = 0; Commands[num].name; num++)
2472 candidate (Completed, User_typed, Commands[num].name, sizeof (Completed));
2473 matches_ensure_morespace (Num_matched);
2474 Matches[Num_matched++] = User_typed;
2476 /* All matches are stored. Longest non-ambiguous string is ""
2477 * i.e. dont change 'buffer'. Fake successful return this time */
2478 if (User_typed[0] == 0)
2482 if (Completed[0] == 0 && User_typed[0])
2485 /* Num_matched will _always_ be atleast 1 since the initial
2486 * user-typed string is always stored */
2487 if (numtabs == 1 && Num_matched == 2)
2488 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
2489 else if (numtabs > 1 && Num_matched > 2)
2490 /* cycle thru all the matches */
2491 snprintf(Completed, sizeof(Completed), "%s",
2492 Matches[(numtabs - 2) % Num_matched]);
2494 /* return the completed command */
2495 strncpy (buffer, Completed, len - spaces);
2497 else if (!mutt_strncmp (buffer, "set", 3)
2498 || !mutt_strncmp (buffer, "unset", 5)
2499 || !mutt_strncmp (buffer, "reset", 5)
2500 || !mutt_strncmp (buffer, "toggle", 6))
2501 { /* complete variables */
2502 char *prefixes[] = { "no", "inv", "?", "&", 0 };
2505 /* loop through all the possible prefixes (no, inv, ...) */
2506 if (!mutt_strncmp (buffer, "set", 3))
2508 for (num = 0; prefixes[num]; num++)
2510 if (!mutt_strncmp (pt, prefixes[num], mutt_strlen (prefixes[num])))
2512 pt += mutt_strlen (prefixes[num]);
2518 /* first TAB. Collect all the matches */
2522 strfcpy (User_typed, pt, sizeof (User_typed));
2523 memset (Matches, 0, Matches_listsize);
2524 memset (Completed, 0, sizeof (Completed));
2525 for (num = 0; MuttVars[num].option; num++)
2526 candidate (Completed, User_typed, MuttVars[num].option, sizeof (Completed));
2527 for (myv = MyVars; myv; myv = myv->next)
2528 candidate (Completed, User_typed, myv->name, sizeof (Completed));
2529 matches_ensure_morespace (Num_matched);
2530 Matches[Num_matched++] = User_typed;
2532 /* All matches are stored. Longest non-ambiguous string is ""
2533 * i.e. dont change 'buffer'. Fake successful return this time */
2534 if (User_typed[0] == 0)
2538 if (Completed[0] == 0 && User_typed[0])
2541 /* Num_matched will _always_ be atleast 1 since the initial
2542 * user-typed string is always stored */
2543 if (numtabs == 1 && Num_matched == 2)
2544 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
2545 else if (numtabs > 1 && Num_matched > 2)
2546 /* cycle thru all the matches */
2547 snprintf(Completed, sizeof(Completed), "%s",
2548 Matches[(numtabs - 2) % Num_matched]);
2550 strncpy (pt, Completed, buffer + len - pt - spaces);
2552 else if (!mutt_strncmp (buffer, "exec", 4))
2554 struct binding_t *menu = km_get_table (CurrentMenu);
2556 if (!menu && CurrentMenu != MENU_PAGER)
2560 /* first TAB. Collect all the matches */
2564 strfcpy (User_typed, pt, sizeof (User_typed));
2565 memset (Matches, 0, Matches_listsize);
2566 memset (Completed, 0, sizeof (Completed));
2567 for (num = 0; menu[num].name; num++)
2568 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
2569 /* try the generic menu */
2570 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER)
2573 for (num = 0; menu[num].name; num++)
2574 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
2576 matches_ensure_morespace (Num_matched);
2577 Matches[Num_matched++] = User_typed;
2579 /* All matches are stored. Longest non-ambiguous string is ""
2580 * i.e. dont change 'buffer'. Fake successful return this time */
2581 if (User_typed[0] == 0)
2585 if (Completed[0] == 0 && User_typed[0])
2588 /* Num_matched will _always_ be atleast 1 since the initial
2589 * user-typed string is always stored */
2590 if (numtabs == 1 && Num_matched == 2)
2591 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
2592 else if (numtabs > 1 && Num_matched > 2)
2593 /* cycle thru all the matches */
2594 snprintf(Completed, sizeof(Completed), "%s",
2595 Matches[(numtabs - 2) % Num_matched]);
2597 strncpy (pt, Completed, buffer + len - pt - spaces);
2605 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2607 char var[STRING], *pt = buffer;
2614 spaces = buffer - pt;
2616 pt = buffer + pos - spaces;
2617 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2619 pt++; /* move past the space */
2620 if (*pt == '=') /* abort if no var before the '=' */
2623 if (mutt_strncmp (buffer, "set", 3) == 0)
2626 char val[LONG_STRING];
2627 const char *myvarval;
2629 strfcpy (var, pt, sizeof (var));
2630 /* ignore the trailing '=' when comparing */
2631 var[mutt_strlen (var) - 1] = 0;
2632 if ((idx = mutt_option_index (var)) == -1)
2634 if ((myvarval = myvar_get(var)) != NULL)
2636 pretty_var (pt, len - (pt - buffer), var, myvarval);
2639 return 0; /* no such variable. */
2641 else if (var_to_string (idx, val, sizeof (val)))
2643 snprintf (pt, len - (pt - buffer), "%s=\"%s\"", var, val);
2650 static int var_to_string (int idx, char* val, size_t len)
2652 char tmp[LONG_STRING];
2653 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2657 if ((DTYPE(MuttVars[idx].type) == DT_STR) ||
2658 (DTYPE(MuttVars[idx].type) == DT_PATH) ||
2659 (DTYPE(MuttVars[idx].type) == DT_RX))
2661 strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data)), sizeof (tmp));
2662 if (DTYPE (MuttVars[idx].type) == DT_PATH)
2663 mutt_pretty_mailbox (tmp, sizeof (tmp));
2665 else if (DTYPE (MuttVars[idx].type) == DT_ADDR)
2667 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) MuttVars[idx].data), 0);
2669 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
2670 strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp));
2671 else if (DTYPE (MuttVars[idx].type) == DT_NUM)
2673 short sval = *((short *) MuttVars[idx].data);
2675 /* avert your eyes, gentle reader */
2676 if (mutt_strcmp (MuttVars[idx].option, "wrapmargin") == 0)
2677 sval = sval > 0 ? 0 : -sval;
2679 snprintf (tmp, sizeof (tmp), "%d", sval);
2681 else if (DTYPE (MuttVars[idx].type) == DT_SORT)
2683 const struct mapping_t *map;
2686 switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
2689 map = SortAliasMethods;
2691 case DT_SORT_BROWSER:
2692 map = SortBrowserMethods;
2695 if ((WithCrypto & APPLICATION_PGP))
2696 map = SortKeyMethods;
2704 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
2705 snprintf (tmp, sizeof (tmp), "%s%s%s",
2706 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
2707 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
2710 else if (DTYPE (MuttVars[idx].type) == DT_MAGIC)
2714 switch (DefaultMagic)
2731 strfcpy (tmp, p, sizeof (tmp));
2733 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
2734 strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no", sizeof (tmp));
2738 escape_string (val, len - 1, tmp);
2743 /* Implement the -Q command line flag */
2744 int mutt_query_variables (LIST *queries)
2748 char errbuff[LONG_STRING];
2749 char command[STRING];
2753 memset (&err, 0, sizeof (err));
2754 memset (&token, 0, sizeof (token));
2757 err.dsize = sizeof (errbuff);
2759 for (p = queries; p; p = p->next)
2761 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2762 if (mutt_parse_rc_line (command, &token, &err) == -1)
2764 fprintf (stderr, "%s\n", err.data);
2768 printf ("%s\n", err.data);
2775 /* dump out the value of all the variables we have */
2776 int mutt_dump_variables (void)
2780 char errbuff[LONG_STRING];
2781 char command[STRING];
2785 memset (&err, 0, sizeof (err));
2786 memset (&token, 0, sizeof (token));
2789 err.dsize = sizeof (errbuff);
2791 for (i = 0; MuttVars[i].option; i++)
2793 if (MuttVars[i].type == DT_SYN)
2796 snprintf (command, sizeof (command), "set ?%s\n", MuttVars[i].option);
2797 if (mutt_parse_rc_line (command, &token, &err) == -1)
2799 fprintf (stderr, "%s\n", err.data);
2803 printf("%s\n", err.data);
2810 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2814 for (i=0; map[i].name; i++)
2815 if (map[i].value == val)
2816 return (map[i].name);
2820 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2824 for (i = 0; map[i].name; i++)
2825 if (ascii_strcasecmp (map[i].name, name) == 0)
2826 return (map[i].value);
2831 static void start_debug (void)
2835 char buf[_POSIX_PATH_MAX];
2836 char buf2[_POSIX_PATH_MAX];
2838 /* rotate the old debug logs */
2839 for (i=3; i>=0; i--)
2841 snprintf (buf, sizeof(buf), "%s/.muttdebug%d", NONULL(Homedir), i);
2842 snprintf (buf2, sizeof(buf2), "%s/.muttdebug%d", NONULL(Homedir), i+1);
2845 if ((debugfile = safe_fopen(buf, "w")) != NULL)
2848 setbuf (debugfile, NULL); /* don't buffer the debugging output! */
2849 fprintf (debugfile, "Mutt %s started at %s.\nDebugging at level %d.\n\n",
2850 MUTT_VERSION, asctime (localtime (&t)), debuglevel);
2855 static int mutt_execute_commands (LIST *p)
2858 char errstr[SHORT_STRING];
2860 memset (&err, 0, sizeof (err));
2862 err.dsize = sizeof (errstr);
2863 memset (&token, 0, sizeof (token));
2864 for (; p; p = p->next)
2866 if (mutt_parse_rc_line (p->data, &token, &err) != 0)
2868 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2877 void mutt_init (int skip_sys_rc, LIST *commands)
2880 struct utsname utsname;
2881 char *p, buffer[STRING], error[STRING];
2882 int i, default_rc = 0, need_pause = 0;
2885 memset (&err, 0, sizeof (err));
2887 err.dsize = sizeof (error);
2889 Groups = hash_create (1031, 0);
2890 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);