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);
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 val = strtol (tmp->data, &t, 0);
2093 if (!*tmp->data || *t || (short) val != val)
2095 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
2102 /* these ones need a sanity check */
2103 if (mutt_strcmp (MuttVars[idx].option, "history") == 0)
2107 mutt_init_history ();
2109 else if (mutt_strcmp (MuttVars[idx].option, "pager_index_lines") == 0)
2114 else if (mutt_strcmp (MuttVars[idx].option, "wrapmargin") == 0)
2122 else if (mutt_strcmp (MuttVars[idx].option, "imap_pipeline_depth") == 0)
2129 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
2133 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2135 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
2136 vals [ quadoption (MuttVars[idx].data) ]);
2141 if (*s->dptr == '=')
2144 mutt_extract_token (tmp, s, 0);
2145 if (ascii_strcasecmp ("yes", tmp->data) == 0)
2146 set_quadoption (MuttVars[idx].data, M_YES);
2147 else if (ascii_strcasecmp ("no", tmp->data) == 0)
2148 set_quadoption (MuttVars[idx].data, M_NO);
2149 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
2150 set_quadoption (MuttVars[idx].data, M_ASKYES);
2151 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
2152 set_quadoption (MuttVars[idx].data, M_ASKNO);
2155 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
2163 toggle_quadoption (MuttVars[idx].data);
2165 set_quadoption (MuttVars[idx].data, M_NO);
2167 set_quadoption (MuttVars[idx].data, M_YES);
2170 else if (DTYPE (MuttVars[idx].type) == DT_SORT)
2172 const struct mapping_t *map = NULL;
2174 switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
2177 map = SortAliasMethods;
2179 case DT_SORT_BROWSER:
2180 map = SortBrowserMethods;
2183 if ((WithCrypto & APPLICATION_PGP))
2184 map = SortKeyMethods;
2187 map = SortAuxMethods;
2196 snprintf (err->data, err->dsize, _("%s: Unknown type."), MuttVars[idx].option);
2201 if (query || *s->dptr != '=')
2203 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
2205 snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
2206 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
2207 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
2213 mutt_extract_token (tmp, s , 0);
2215 if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) == -1)
2223 snprintf (err->data, err->dsize, _("%s: unknown type"), MuttVars[idx].option);
2230 if (MuttVars[idx].flags & R_INDEX)
2231 set_option (OPTFORCEREDRAWINDEX);
2232 if (MuttVars[idx].flags & R_PAGER)
2233 set_option (OPTFORCEREDRAWPAGER);
2234 if (MuttVars[idx].flags & R_RESORT_SUB)
2235 set_option (OPTSORTSUBTHREADS);
2236 if (MuttVars[idx].flags & R_RESORT)
2237 set_option (OPTNEEDRESORT);
2238 if (MuttVars[idx].flags & R_RESORT_INIT)
2239 set_option (OPTRESORTINIT);
2240 if (MuttVars[idx].flags & R_TREE)
2241 set_option (OPTREDRAWTREE);
2249 /* reads the specified initialization file. returns -1 if errors were found
2250 so that we can pause to let the user know... */
2251 static int source_rc (const char *rcfile, BUFFER *err)
2254 int line = 0, rc = 0, conv = 0;
2256 char *linebuf = NULL;
2257 char *currentline = NULL;
2261 dprint (2, (debugfile, "Reading configuration file '%s'.\n",
2264 if ((f = mutt_open_read (rcfile, &pid)) == NULL)
2266 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
2270 memset (&token, 0, sizeof (token));
2271 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL)
2273 conv=ConfigCharset && (*ConfigCharset) && Charset;
2276 currentline=safe_strdup(linebuf);
2277 if (!currentline) continue;
2278 mutt_convert_string(¤tline, ConfigCharset, Charset, 0);
2281 currentline=linebuf;
2283 if (mutt_parse_rc_line (currentline, &token, err) == -1)
2285 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
2286 if (--rc < -MAXERRS)
2288 if (conv) FREE(¤tline);
2304 mutt_wait_filter (pid);
2307 /* the muttrc source keyword */
2308 snprintf (err->data, err->dsize, rc >= -MAXERRS ? _("source: errors in %s")
2309 : _("source: reading aborted due too many errors in %s"), rcfile);
2317 static int parse_source (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
2319 char path[_POSIX_PATH_MAX];
2321 if (mutt_extract_token (tmp, s, 0) != 0)
2323 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
2328 strfcpy (err->data, _("source: too many arguments"), err->dsize);
2331 strfcpy (path, tmp->data, sizeof (path));
2332 mutt_expand_path (path, sizeof (path));
2333 return (source_rc (path, err));
2336 /* line command to execute
2338 token scratch buffer to be used by parser. caller should free
2339 token->data when finished. the reason for this variable is
2340 to avoid having to allocate and deallocate a lot of memory
2341 if we are parsing many lines. the caller can pass in the
2342 memory to use, which avoids having to create new space for
2343 every call to this function.
2345 err where to write error messages */
2346 int mutt_parse_rc_line (/* const */ char *line, BUFFER *token, BUFFER *err)
2351 if (!line || !*line)
2354 memset (&expn, 0, sizeof (expn));
2355 expn.data = expn.dptr = line;
2356 expn.dsize = mutt_strlen (line);
2363 if (*expn.dptr == '#')
2364 break; /* rest of line is a comment */
2365 if (*expn.dptr == ';')
2370 mutt_extract_token (token, &expn, 0);
2371 for (i = 0; Commands[i].name; i++)
2373 if (!mutt_strcmp (token->data, Commands[i].name))
2375 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
2380 if (!Commands[i].name)
2382 snprintf (err->data, err->dsize, _("%s: unknown command"), NONULL (token->data));
2394 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
2395 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
2396 /* initial string that starts completion. No telling how much crap
2397 * the user has typed so far. Allocate LONG_STRING just to be sure! */
2398 static char User_typed [LONG_STRING] = {0};
2400 static int Num_matched = 0; /* Number of matches for completion */
2401 static char Completed [STRING] = {0}; /* completed string (command or variable) */
2402 static const char **Matches;
2403 /* this is a lie until mutt_init runs: */
2404 static int Matches_listsize = MAX(NUMVARS,NUMCOMMANDS) + 10;
2406 static void matches_ensure_morespace(int current)
2408 int base_space, extra_space, space;
2410 if (current > Matches_listsize - 2)
2412 base_space = MAX(NUMVARS,NUMCOMMANDS) + 1;
2413 extra_space = Matches_listsize - base_space;
2415 space = base_space + extra_space;
2416 safe_realloc (&Matches, space * sizeof (char *));
2417 memset (&Matches[current + 1], 0, space - current);
2418 Matches_listsize = space;
2422 /* helper function for completion. Changes the dest buffer if
2423 necessary/possible to aid completion.
2424 dest == completion result gets here.
2425 src == candidate for completion.
2426 try == user entered data for completion.
2427 len == length of dest buffer.
2429 static void candidate (char *dest, char *try, const char *src, int len)
2433 if (strstr (src, try) == src)
2435 matches_ensure_morespace (Num_matched);
2436 Matches[Num_matched++] = src;
2438 strfcpy (dest, src, len);
2441 for (l = 0; src[l] && src[l] == dest[l]; l++);
2447 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
2451 int spaces; /* keep track of the number of leading spaces on the line */
2455 spaces = buffer - pt;
2457 pt = buffer + pos - spaces;
2458 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2461 if (pt == buffer) /* complete cmd */
2463 /* first TAB. Collect all the matches */
2467 strfcpy (User_typed, pt, sizeof (User_typed));
2468 memset (Matches, 0, Matches_listsize);
2469 memset (Completed, 0, sizeof (Completed));
2470 for (num = 0; Commands[num].name; num++)
2471 candidate (Completed, User_typed, Commands[num].name, sizeof (Completed));
2472 matches_ensure_morespace (Num_matched);
2473 Matches[Num_matched++] = User_typed;
2475 /* All matches are stored. Longest non-ambiguous string is ""
2476 * i.e. dont change 'buffer'. Fake successful return this time */
2477 if (User_typed[0] == 0)
2481 if (Completed[0] == 0 && User_typed[0])
2484 /* Num_matched will _always_ be atleast 1 since the initial
2485 * user-typed string is always stored */
2486 if (numtabs == 1 && Num_matched == 2)
2487 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
2488 else if (numtabs > 1 && Num_matched > 2)
2489 /* cycle thru all the matches */
2490 snprintf(Completed, sizeof(Completed), "%s",
2491 Matches[(numtabs - 2) % Num_matched]);
2493 /* return the completed command */
2494 strncpy (buffer, Completed, len - spaces);
2496 else if (!mutt_strncmp (buffer, "set", 3)
2497 || !mutt_strncmp (buffer, "unset", 5)
2498 || !mutt_strncmp (buffer, "reset", 5)
2499 || !mutt_strncmp (buffer, "toggle", 6))
2500 { /* complete variables */
2501 char *prefixes[] = { "no", "inv", "?", "&", 0 };
2504 /* loop through all the possible prefixes (no, inv, ...) */
2505 if (!mutt_strncmp (buffer, "set", 3))
2507 for (num = 0; prefixes[num]; num++)
2509 if (!mutt_strncmp (pt, prefixes[num], mutt_strlen (prefixes[num])))
2511 pt += mutt_strlen (prefixes[num]);
2517 /* first TAB. Collect all the matches */
2521 strfcpy (User_typed, pt, sizeof (User_typed));
2522 memset (Matches, 0, Matches_listsize);
2523 memset (Completed, 0, sizeof (Completed));
2524 for (num = 0; MuttVars[num].option; num++)
2525 candidate (Completed, User_typed, MuttVars[num].option, sizeof (Completed));
2526 for (myv = MyVars; myv; myv = myv->next)
2527 candidate (Completed, User_typed, myv->name, sizeof (Completed));
2528 matches_ensure_morespace (Num_matched);
2529 Matches[Num_matched++] = User_typed;
2531 /* All matches are stored. Longest non-ambiguous string is ""
2532 * i.e. dont change 'buffer'. Fake successful return this time */
2533 if (User_typed[0] == 0)
2537 if (Completed[0] == 0 && User_typed[0])
2540 /* Num_matched will _always_ be atleast 1 since the initial
2541 * user-typed string is always stored */
2542 if (numtabs == 1 && Num_matched == 2)
2543 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
2544 else if (numtabs > 1 && Num_matched > 2)
2545 /* cycle thru all the matches */
2546 snprintf(Completed, sizeof(Completed), "%s",
2547 Matches[(numtabs - 2) % Num_matched]);
2549 strncpy (pt, Completed, buffer + len - pt - spaces);
2551 else if (!mutt_strncmp (buffer, "exec", 4))
2553 struct binding_t *menu = km_get_table (CurrentMenu);
2555 if (!menu && CurrentMenu != MENU_PAGER)
2559 /* first TAB. Collect all the matches */
2563 strfcpy (User_typed, pt, sizeof (User_typed));
2564 memset (Matches, 0, Matches_listsize);
2565 memset (Completed, 0, sizeof (Completed));
2566 for (num = 0; menu[num].name; num++)
2567 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
2568 /* try the generic menu */
2569 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER)
2572 for (num = 0; menu[num].name; num++)
2573 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
2575 matches_ensure_morespace (Num_matched);
2576 Matches[Num_matched++] = User_typed;
2578 /* All matches are stored. Longest non-ambiguous string is ""
2579 * i.e. dont change 'buffer'. Fake successful return this time */
2580 if (User_typed[0] == 0)
2584 if (Completed[0] == 0 && User_typed[0])
2587 /* Num_matched will _always_ be atleast 1 since the initial
2588 * user-typed string is always stored */
2589 if (numtabs == 1 && Num_matched == 2)
2590 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
2591 else if (numtabs > 1 && Num_matched > 2)
2592 /* cycle thru all the matches */
2593 snprintf(Completed, sizeof(Completed), "%s",
2594 Matches[(numtabs - 2) % Num_matched]);
2596 strncpy (pt, Completed, buffer + len - pt - spaces);
2604 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2606 char var[STRING], *pt = buffer;
2613 spaces = buffer - pt;
2615 pt = buffer + pos - spaces;
2616 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2618 pt++; /* move past the space */
2619 if (*pt == '=') /* abort if no var before the '=' */
2622 if (mutt_strncmp (buffer, "set", 3) == 0)
2625 char val[LONG_STRING];
2626 const char *myvarval;
2628 strfcpy (var, pt, sizeof (var));
2629 /* ignore the trailing '=' when comparing */
2630 var[mutt_strlen (var) - 1] = 0;
2631 if ((idx = mutt_option_index (var)) == -1)
2633 if ((myvarval = myvar_get(var)) != NULL)
2635 pretty_var (pt, len - (pt - buffer), var, myvarval);
2638 return 0; /* no such variable. */
2640 else if (var_to_string (idx, val, sizeof (val)))
2642 snprintf (pt, len - (pt - buffer), "%s=\"%s\"", var, val);
2649 static int var_to_string (int idx, char* val, size_t len)
2651 char tmp[LONG_STRING];
2652 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2656 if ((DTYPE(MuttVars[idx].type) == DT_STR) ||
2657 (DTYPE(MuttVars[idx].type) == DT_PATH) ||
2658 (DTYPE(MuttVars[idx].type) == DT_RX))
2660 strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data)), sizeof (tmp));
2661 if (DTYPE (MuttVars[idx].type) == DT_PATH)
2662 mutt_pretty_mailbox (tmp, sizeof (tmp));
2664 else if (DTYPE (MuttVars[idx].type) == DT_ADDR)
2666 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) MuttVars[idx].data), 0);
2668 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
2669 strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp));
2670 else if (DTYPE (MuttVars[idx].type) == DT_NUM)
2672 short sval = *((short *) MuttVars[idx].data);
2674 /* avert your eyes, gentle reader */
2675 if (mutt_strcmp (MuttVars[idx].option, "wrapmargin") == 0)
2676 sval = sval > 0 ? 0 : -sval;
2678 snprintf (tmp, sizeof (tmp), "%d", sval);
2680 else if (DTYPE (MuttVars[idx].type) == DT_SORT)
2682 const struct mapping_t *map;
2685 switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
2688 map = SortAliasMethods;
2690 case DT_SORT_BROWSER:
2691 map = SortBrowserMethods;
2694 if ((WithCrypto & APPLICATION_PGP))
2695 map = SortKeyMethods;
2703 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
2704 snprintf (tmp, sizeof (tmp), "%s%s%s",
2705 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
2706 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
2709 else if (DTYPE (MuttVars[idx].type) == DT_MAGIC)
2713 switch (DefaultMagic)
2730 strfcpy (tmp, p, sizeof (tmp));
2732 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
2733 strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no", sizeof (tmp));
2737 escape_string (val, len - 1, tmp);
2742 /* Implement the -Q command line flag */
2743 int mutt_query_variables (LIST *queries)
2747 char errbuff[LONG_STRING];
2748 char command[STRING];
2752 memset (&err, 0, sizeof (err));
2753 memset (&token, 0, sizeof (token));
2756 err.dsize = sizeof (errbuff);
2758 for (p = queries; p; p = p->next)
2760 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2761 if (mutt_parse_rc_line (command, &token, &err) == -1)
2763 fprintf (stderr, "%s\n", err.data);
2767 printf ("%s\n", err.data);
2774 /* dump out the value of all the variables we have */
2775 int mutt_dump_variables (void)
2779 char errbuff[LONG_STRING];
2780 char command[STRING];
2784 memset (&err, 0, sizeof (err));
2785 memset (&token, 0, sizeof (token));
2788 err.dsize = sizeof (errbuff);
2790 for (i = 0; MuttVars[i].option; i++)
2792 if (MuttVars[i].type == DT_SYN)
2795 snprintf (command, sizeof (command), "set ?%s\n", MuttVars[i].option);
2796 if (mutt_parse_rc_line (command, &token, &err) == -1)
2798 fprintf (stderr, "%s\n", err.data);
2802 printf("%s\n", err.data);
2809 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2813 for (i=0; map[i].name; i++)
2814 if (map[i].value == val)
2815 return (map[i].name);
2819 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2823 for (i = 0; map[i].name; i++)
2824 if (ascii_strcasecmp (map[i].name, name) == 0)
2825 return (map[i].value);
2830 static void start_debug (void)
2834 char buf[_POSIX_PATH_MAX];
2835 char buf2[_POSIX_PATH_MAX];
2837 /* rotate the old debug logs */
2838 for (i=3; i>=0; i--)
2840 snprintf (buf, sizeof(buf), "%s/.muttdebug%d", NONULL(Homedir), i);
2841 snprintf (buf2, sizeof(buf2), "%s/.muttdebug%d", NONULL(Homedir), i+1);
2844 if ((debugfile = safe_fopen(buf, "w")) != NULL)
2847 setbuf (debugfile, NULL); /* don't buffer the debugging output! */
2848 fprintf (debugfile, "Mutt %s started at %s.\nDebugging at level %d.\n\n",
2849 MUTT_VERSION, asctime (localtime (&t)), debuglevel);
2854 static int mutt_execute_commands (LIST *p)
2857 char errstr[SHORT_STRING];
2859 memset (&err, 0, sizeof (err));
2861 err.dsize = sizeof (errstr);
2862 memset (&token, 0, sizeof (token));
2863 for (; p; p = p->next)
2865 if (mutt_parse_rc_line (p->data, &token, &err) != 0)
2867 fprintf (stderr, _("Error in command line: %s\n"), err.data);
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);
2889 ReverseAlias = hash_create (1031);
2894 * XXX - use something even more difficult to predict?
2896 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2897 "\033]9;%ld\a", (long) time (NULL));
2899 /* on one of the systems I use, getcwd() does not return the same prefix
2900 as is listed in the passwd file */
2901 if ((p = getenv ("HOME")))
2902 Homedir = safe_strdup (p);
2904 /* Get some information about the user */
2905 if ((pw = getpwuid (getuid ())))
2909 Username = safe_strdup (pw->pw_name);
2911 Homedir = safe_strdup (pw->pw_dir);
2913 Realname = safe_strdup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2914 Shell = safe_strdup (pw->pw_shell);
2922 fputs (_("unable to determine home directory"), stderr);
2925 if ((p = getenv ("USER")))
2926 Username = safe_strdup (p);
2930 fputs (_("unable to determine username"), stderr);
2933 Shell = safe_strdup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2937 /* Start up debugging mode if requested */
2942 /* And about the host... */
2944 /* some systems report the FQDN instead of just the hostname */
2945 if ((p = strchr (utsname.nodename, '.')))
2947 Hostname = mutt_substrdup (utsname.nodename, p);
2949 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2952 Hostname = safe_strdup (utsname.nodename);
2955 #define DOMAIN buffer
2956 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2957 Fqdn = safe_strdup ("@");
2962 Fqdn = safe_malloc (mutt_strlen (DOMAIN) + mutt_strlen (Hostname) + 2);
2963 sprintf (Fqdn, "%s.%s", NONULL(Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2966 Fqdn = safe_strdup(NONULL(Hostname));
2968 if ((p = getenv ("MAIL")))
2969 Spoolfile = safe_strdup (p);
2970 else if ((p = getenv ("MAILDIR")))
2971 Spoolfile = safe_strdup (p);
2975 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2977 mutt_concat_path (buffer, MAILPATH, NONULL(Username), sizeof (buffer));
2979 Spoolfile = safe_strdup (buffer);
2982 if ((p = getenv ("MAILCAPS")))
2983 MailcapPath = safe_strdup (p);
2986 /* Default search path from RFC1524 */
2987 MailcapPath = safe_strdup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2990 Tempdir = safe_strdup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2992 p = getenv ("VISUAL");
2995 p = getenv ("EDITOR");
2999 Editor = safe_strdup (p);
3000 Visual = safe_strdup (p);
3002 if ((p = getenv ("REPLYTO")) != NULL)
3006 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
3008 memset (&buf, 0, sizeof (buf));
3009 buf.data = buf.dptr = buffer;
3010 buf.dsize = mutt_strlen (buffer);
3012 memset (&token, 0, sizeof (token));
3013 parse_my_hdr (&token, &buf, 0, &err);
3017 if ((p = getenv ("EMAIL")) != NULL)
3018 From = rfc822_parse_adrlist (NULL, p);
3020 mutt_set_langinfo_charset ();
3021 mutt_set_charset (Charset);
3023 Matches = safe_calloc (Matches_listsize, sizeof (char *));
3025 /* Set standard defaults */
3026 for (i = 0; MuttVars[i].option; i++)
3028 mutt_set_default (&MuttVars[i]);
3029 mutt_restore_default (&MuttVars[i]);
3032 CurrentMenu = MENU_MAIN;
3035 #ifndef LOCALES_HACK
3036 /* Do we have a locale definition? */
3037 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
3038 ((p = getenv ("LANG")) != NULL && p[0]) ||
3039 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
3040 set_option (OPTLOCALES);
3044 /* Unset suspend by default if we're the session leader */
3045 if (getsid(0) == getpid())
3046 unset_option (OPTSUSPEND);
3049 mutt_init_history ();
3058 * When changing the code which looks for a configuration file,
3059 * please also change the corresponding code in muttbug.sh.in.
3069 snprintf (buffer, sizeof(buffer), "%s/.muttrc-%s", NONULL(Homedir), MUTT_VERSION);
3070 if (access(buffer, F_OK) == -1)
3071 snprintf (buffer, sizeof(buffer), "%s/.muttrc", NONULL(Homedir));
3072 if (access(buffer, F_OK) == -1)
3073 snprintf (buffer, sizeof (buffer), "%s/.mutt/muttrc-%s", NONULL(Homedir), MUTT_VERSION);
3074 if (access(buffer, F_OK) == -1)
3075 snprintf (buffer, sizeof (buffer), "%s/.mutt/muttrc", NONULL(Homedir));
3076 if (access(buffer, F_OK) == -1) /* default to .muttrc for alias_file */
3077 snprintf (buffer, sizeof(buffer), "%s/.muttrc", NONULL(Homedir));
3080 Muttrc = safe_strdup (buffer);
3084 strfcpy (buffer, Muttrc, sizeof (buffer));
3086 mutt_expand_path (buffer, sizeof (buffer));
3087 Muttrc = safe_strdup (buffer);
3090 AliasFile = safe_strdup (NONULL(Muttrc));
3092 /* Process the global rc file if it exists and the user hasn't explicity
3093 requested not to via "-n". */
3096 snprintf (buffer, sizeof(buffer), "%s/Muttrc-%s", SYSCONFDIR, MUTT_VERSION);
3097 if (access (buffer, F_OK) == -1)
3098 snprintf (buffer, sizeof(buffer), "%s/Muttrc", SYSCONFDIR);
3099 if (access (buffer, F_OK) == -1)
3100 snprintf (buffer, sizeof (buffer), "%s/Muttrc-%s", PKGDATADIR, MUTT_VERSION);
3101 if (access (buffer, F_OK) == -1)
3102 snprintf (buffer, sizeof (buffer), "%s/Muttrc", PKGDATADIR);
3103 if (access (buffer, F_OK) != -1)
3105 if (source_rc (buffer, &err) != 0)
3107 fputs (err.data, stderr);
3108 fputc ('\n', stderr);
3114 /* Read the user's initialization file. */
3115 if (access (Muttrc, F_OK) != -1)
3117 if (!option (OPTNOCURSES))
3119 if (source_rc (Muttrc, &err) != 0)
3121 fputs (err.data, stderr);
3122 fputc ('\n', stderr);
3126 else if (!default_rc)
3128 /* file specified by -F does not exist */
3129 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
3130 mutt_endwin (buffer);
3134 if (mutt_execute_commands (commands) != 0)
3137 if (need_pause && !option (OPTNOCURSES))
3139 if (mutt_any_key_to_continue (NULL) == -1)
3143 mutt_read_histfile ();
3146 set_option (OPTWEED); /* turn weeding on by default */
3150 int mutt_get_hook_type (const char *name)
3152 struct command_t *c;
3154 for (c = Commands ; c->name ; c++)
3155 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
3160 static int parse_group_context (group_context_t **ctx, BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
3162 while (!mutt_strcasecmp (buf->data, "-group"))
3166 strfcpy (err->data, _("-group: no group name"), err->dsize);
3170 mutt_extract_token (buf, s, 0);
3172 mutt_group_context_add (ctx, mutt_pattern_group (buf->data));
3176 strfcpy (err->data, _("out of arguments"), err->dsize);
3180 mutt_extract_token (buf, s, 0);
3186 mutt_group_context_destroy (ctx);
3190 static void myvar_set (const char* var, const char* val)
3194 for (cur = &MyVars; *cur; cur = &((*cur)->next))
3195 if (!mutt_strcmp ((*cur)->name, var))
3199 *cur = safe_calloc (1, sizeof (myvar_t));
3202 (*cur)->name = safe_strdup (var);
3204 mutt_str_replace (&(*cur)->value, val);
3207 static void myvar_del (const char* var)
3213 for (cur = &MyVars; *cur; cur = &((*cur)->next))
3214 if (!mutt_strcmp ((*cur)->name, var))
3220 FREE (&(*cur)->name);
3221 FREE (&(*cur)->value);
3222 FREE (cur); /* __FREE_CHECKED__ */
3227 static const char* myvar_get (const char* var)
3231 for (cur = MyVars; cur; cur = cur->next)
3232 if (!mutt_strcmp (cur->name, var))
3233 return NONULL(cur->value);