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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
25 #include "mutt_curses.h"
26 #include "mutt_regex.h"
31 #include "mutt_crypt.h"
32 #include "mutt_idna.h"
34 #if defined(USE_SSL) || defined(USE_NSS) || defined(USE_GNUTLS)
48 #include <sys/utsname.h>
52 void toggle_quadoption (int opt)
55 int b = (opt % 4) * 2;
57 QuadOptions[n] ^= (1 << b);
60 void set_quadoption (int opt, int flag)
63 int b = (opt % 4) * 2;
65 QuadOptions[n] &= ~(0x3 << b);
66 QuadOptions[n] |= (flag & 0x3) << b;
69 int quadoption (int opt)
72 int b = (opt % 4) * 2;
74 return (QuadOptions[n] >> b) & 0x3;
77 int query_quadoption (int opt, const char *prompt)
79 int v = quadoption (opt);
87 v = mutt_yesorno (prompt, (v == M_ASKYES));
88 CLEARLINE (LINES - 1);
95 /* given the variable ``s'', return the index into the rc_vars array which
96 matches, or -1 if the variable is not found. */
97 int mutt_option_index (char *s)
101 for (i = 0; MuttVars[i].option; i++)
102 if (mutt_strcmp (s, MuttVars[i].option) == 0)
103 return (MuttVars[i].type ==
104 DT_SYN ? mutt_option_index ((char *) MuttVars[i].data) : i);
108 int mutt_extract_token (BUFFER * dest, BUFFER * tok, int flags)
111 char qc = 0; /* quote char */
114 /* reset the destination pointer to the beginning of the buffer */
115 dest->dptr = dest->data;
118 while ((ch = *tok->dptr)) {
120 if ((ISSPACE (ch) && !(flags & M_TOKEN_SPACE)) ||
121 (ch == '#' && !(flags & M_TOKEN_COMMENT)) ||
122 (ch == '=' && (flags & M_TOKEN_EQUAL)) ||
123 (ch == ';' && !(flags & M_TOKEN_SEMICOLON)) ||
124 ((flags & M_TOKEN_PATTERN) && strchr ("~!|", ch)))
131 qc = 0; /* end of quote */
132 else if (!qc && (ch == '\'' || ch == '"') && !(flags & M_TOKEN_QUOTE))
134 else if (ch == '\\' && qc != '\'') {
136 return -1; /* premature end of token */
137 switch (ch = *tok->dptr++) {
141 return -1; /* premature end of token */
142 mutt_buffer_addch (dest, (toupper ((unsigned char) *tok->dptr)
147 mutt_buffer_addch (dest, '\r');
150 mutt_buffer_addch (dest, '\n');
153 mutt_buffer_addch (dest, '\t');
156 mutt_buffer_addch (dest, '\f');
159 mutt_buffer_addch (dest, '\033');
162 if (isdigit ((unsigned char) ch) &&
163 isdigit ((unsigned char) *tok->dptr) &&
164 isdigit ((unsigned char) *(tok->dptr + 1))) {
166 mutt_buffer_addch (dest,
167 (ch << 6) + (*tok->dptr << 3) + *(tok->dptr +
172 mutt_buffer_addch (dest, ch);
175 else if (ch == '^' && (flags & M_TOKEN_CONDENSE)) {
177 return -1; /* premature end of token */
180 mutt_buffer_addch (dest, ch);
182 mutt_buffer_addch (dest, '\033');
183 else if (isalpha ((unsigned char) ch))
184 mutt_buffer_addch (dest, toupper ((unsigned char) ch) - '@');
186 mutt_buffer_addch (dest, '^');
187 mutt_buffer_addch (dest, ch);
190 else if (ch == '`' && (!qc || qc == '"')) {
200 if ((pc = strpbrk (pc, "\\`"))) {
201 /* skip any quoted chars */
205 } while (pc && *pc != '`');
207 dprint (1, (debugfile, "mutt_get_token: mismatched backtics\n"));
210 cmd = mutt_substrdup (tok->dptr, pc);
211 if ((pid = mutt_create_filter (cmd, NULL, &fp, NULL)) < 0) {
213 (debugfile, "mutt_get_token: unable to fork command: %s",
223 memset (&expn, 0, sizeof (expn));
224 expn.data = mutt_read_line (NULL, &expn.dsize, fp, &line);
226 mutt_wait_filter (pid);
228 /* if we got output, make a new string consiting of the shell ouptput
229 plus whatever else was left on the original line */
230 /* BUT: If this is inside a quoted string, directly add output to
232 if (expn.data && qc) {
233 mutt_buffer_addstr (dest, expn.data);
236 else if (expn.data) {
237 expnlen = mutt_strlen (expn.data);
238 tok->dsize = expnlen + mutt_strlen (tok->dptr) + 1;
239 ptr = safe_malloc (tok->dsize);
240 memcpy (ptr, expn.data, expnlen);
241 strcpy (ptr + expnlen, tok->dptr); /* __STRCPY_CHECKED__ */
246 tok->destroy = 1; /* mark that the caller should destroy this data */
251 else if (ch == '$' && (!qc || qc == '"')
252 && (*tok->dptr == '{' || isalpha ((unsigned char) *tok->dptr))) {
253 char *env = NULL, *var = NULL;
255 if (*tok->dptr == '{') {
257 if ((pc = strchr (tok->dptr, '}'))) {
258 var = mutt_substrdup (tok->dptr, pc);
263 for (pc = tok->dptr; isalpha ((unsigned char) *pc) || *pc == '_';
265 var = mutt_substrdup (tok->dptr, pc);
268 if (var && (env = getenv (var)))
269 mutt_buffer_addstr (dest, env);
273 mutt_buffer_addch (dest, ch);
275 mutt_buffer_addch (dest, 0); /* terminate the string */
280 static void add_to_list (LIST ** list, const char *str)
282 LIST *t, *last = NULL;
284 /* don't add a NULL or empty string to the list */
285 if (!str || *str == '\0')
288 /* check to make sure the item is not already on this list */
289 for (last = *list; last; last = last->next) {
290 if (ascii_strcasecmp (str, last->data) == 0) {
291 /* already on the list, so just ignore it */
299 if (!*list || last) {
300 t = (LIST *) safe_calloc (1, sizeof (LIST));
301 t->data = safe_strdup (str);
311 static int add_to_rx_list (RX_LIST ** list, const char *s, int flags,
314 RX_LIST *t, *last = NULL;
320 if (!(rx = mutt_compile_regexp (s, flags))) {
321 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
325 /* check to make sure the item is not already on this list */
326 for (last = *list; last; last = last->next) {
327 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
328 /* already on the list, so just ignore it */
336 if (!*list || last) {
337 t = mutt_new_rx_list ();
347 mutt_free_regexp (&rx);
352 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
353 const char *templ, BUFFER * err)
355 SPAM_LIST *t = NULL, *last = NULL;
360 if (!pat || !*pat || !templ)
363 if (!(rx = mutt_compile_regexp (pat, REG_ICASE))) {
364 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
368 /* check to make sure the item is not already on this list */
369 for (last = *list; last; last = last->next) {
370 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
371 /* Already on the list. Formerly we just skipped this case, but
372 * now we're supporting removals, which means we're supporting
373 * re-adds conceptually. So we probably want this to imply a
374 * removal, then do an add. We can achieve the removal by freeing
375 * the template, and leaving t pointed at the current item.
378 safe_free (&t->template);
385 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
386 * update. Otherwise we want to make a new one to link at the list's end.
389 t = mutt_new_spam_list ();
397 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
398 t->template = safe_strdup (templ);
400 /* Find highest match number in template string */
402 for (p = templ; *p;) {
407 while (*p && isdigit ((int) *p))
413 t->nmatch++; /* match 0 is always the whole expr */
418 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
420 SPAM_LIST *spam, *prev;
423 /* Being first is a special case. */
425 if (spam->rx && !mutt_strcmp (spam->rx->pattern, pat)) {
427 mutt_free_regexp (&spam->rx);
428 safe_free (&spam->template);
434 for (spam = prev->next; spam;) {
435 if (!mutt_strcmp (spam->rx->pattern, pat)) {
436 prev->next = spam->next;
437 mutt_free_regexp (&spam->rx);
438 safe_free (&spam->template);
451 static void remove_from_list (LIST ** l, const char *str)
453 LIST *p, *last = NULL;
455 if (mutt_strcmp ("*", str) == 0)
456 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
461 if (ascii_strcasecmp (str, p->data) == 0) {
464 last->next = p->next;
477 static int remove_from_rx_list (RX_LIST ** l, const char *str)
479 RX_LIST *p, *last = NULL;
482 if (mutt_strcmp ("*", str) == 0) {
483 mutt_free_rx_list (l); /* ``unCMD *'' means delete all current entries */
490 if (ascii_strcasecmp (str, p->rx->pattern) == 0) {
491 mutt_free_regexp (&p->rx);
493 last->next = p->next;
508 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
514 memset (&token, 0, sizeof (token));
515 mutt_extract_token (tmp, s, 0);
517 /* is the item defined as a variable or a function? */
518 if (!(res = (mutt_option_index (tmp->data) != -1)))
519 for (i = 0; !res && i < MENU_MAX; i++) {
520 struct binding_t *b = km_get_table (Menus[i].value);
525 for (j = 0; b[j].name; j++)
526 if (!ascii_strncasecmp (tmp->data, b[j].name, mutt_strlen (tmp->data))
527 && (mutt_strlen (b[j].name) == mutt_strlen (tmp->data))) {
532 /* check for feature_* */
537 j = mutt_strlen (tmp->data);
538 /* need at least input of 'feature_X' */
542 while (Features[i].name) {
543 if (mutt_strlen (Features[i].name) == j &&
544 ascii_strncasecmp (Features[i].name, p, j)) {
555 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
557 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
560 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
562 if ((data && res) || (!data && !res)) {
563 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
564 mutt_error ("Error: %s", err->data);
573 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
577 mutt_extract_token (buf, s, 0);
579 /* don't add "*" to the unignore list */
580 if (strcmp (buf->data, "*"))
581 add_to_list (&UnIgnore, buf->data);
583 remove_from_list (&Ignore, buf->data);
585 while (MoreArgs (s));
590 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
594 mutt_extract_token (buf, s, 0);
595 remove_from_list (&UnIgnore, buf->data);
596 add_to_list (&Ignore, buf->data);
598 while (MoreArgs (s));
603 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
607 mutt_extract_token (buf, s, 0);
608 add_to_list ((LIST **) data, buf->data);
610 while (MoreArgs (s));
616 static int _parse_rx_list (BUFFER * buf, BUFFER * s, unsigned long data,
617 BUFFER * err, int flags)
620 mutt_extract_token (buf, s, 0);
621 if (add_to_rx_list ((RX_LIST **) data, buf->data, flags, err) != 0)
625 while (MoreArgs (s));
630 static int parse_rx_list (BUFFER * buf, BUFFER * s, unsigned long data,
633 return _parse_rx_list (buf, s, data, err, REG_ICASE);
636 static int parse_rx_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
640 mutt_extract_token (buf, s, 0);
641 if (mutt_strcmp (buf->data, "*") == 0) {
642 mutt_free_rx_list ((RX_LIST **) data);
645 remove_from_rx_list ((RX_LIST **) data, buf->data);
647 while (MoreArgs (s));
653 static void _alternates_clean (void)
657 if (Context && Context->msgcount) {
658 for (i = 0; i < Context->msgcount; i++)
659 Context->hdrs[i]->recip_valid = 0;
663 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
666 _alternates_clean ();
668 mutt_extract_token (buf, s, 0);
669 remove_from_rx_list (&UnAlternates, buf->data);
671 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
674 while (MoreArgs (s));
679 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
682 _alternates_clean ();
684 mutt_extract_token (buf, s, 0);
685 remove_from_rx_list (&Alternates, buf->data);
687 if (mutt_strcmp (buf->data, "*") &&
688 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
692 while (MoreArgs (s));
697 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
702 memset (&templ, 0, sizeof (templ));
704 /* Insist on at least one parameter */
707 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
709 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
713 /* Extract the first token, a regexp */
714 mutt_extract_token (buf, s, 0);
716 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
717 if (data == M_SPAM) {
718 /* If there's a second parameter, it's a template for the spam tag. */
720 mutt_extract_token (&templ, s, 0);
722 /* Add to the spam list. */
723 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
730 /* If not, try to remove from the nospam list. */
732 remove_from_rx_list (&NoSpamList, buf->data);
738 /* M_NOSPAM is for nospam commands. */
739 else if (data == M_NOSPAM) {
740 /* nospam only ever has one parameter. */
742 /* "*" is a special case. */
743 if (!mutt_strcmp (buf->data, "*")) {
744 mutt_free_spam_list (&SpamList);
745 mutt_free_rx_list (&NoSpamList);
749 /* If it's on the spam list, just remove it. */
750 if (remove_from_spam_list (&SpamList, buf->data) != 0)
753 /* Otherwise, add it to the nospam list. */
754 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
760 /* This should not happen. */
761 strfcpy (err->data, "This is no good at all.", err->dsize);
765 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
769 mutt_extract_token (buf, s, 0);
771 * Check for deletion of entire list
773 if (mutt_strcmp (buf->data, "*") == 0) {
774 mutt_free_list ((LIST **) data);
777 remove_from_list ((LIST **) data, buf->data);
779 while (MoreArgs (s));
784 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
788 mutt_extract_token (buf, s, 0);
789 remove_from_rx_list (&UnMailLists, buf->data);
791 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
794 while (MoreArgs (s));
799 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
803 mutt_extract_token (buf, s, 0);
804 remove_from_rx_list (&SubscribedLists, buf->data);
805 remove_from_rx_list (&MailLists, buf->data);
807 if (mutt_strcmp (buf->data, "*") &&
808 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
811 while (MoreArgs (s));
816 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
820 mutt_extract_token (buf, s, 0);
821 remove_from_rx_list (&UnMailLists, buf->data);
822 remove_from_rx_list (&UnSubscribedLists, buf->data);
824 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
826 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
829 while (MoreArgs (s));
834 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
838 mutt_extract_token (buf, s, 0);
839 remove_from_rx_list (&SubscribedLists, buf->data);
841 if (mutt_strcmp (buf->data, "*") &&
842 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
845 while (MoreArgs (s));
850 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
853 ALIAS *tmp, *last = NULL;
856 mutt_extract_token (buf, s, 0);
858 if (mutt_strcmp ("*", buf->data) == 0) {
859 if (CurrentMenu == MENU_ALIAS) {
860 for (tmp = Aliases; tmp; tmp = tmp->next)
862 set_option (OPTFORCEREDRAWINDEX);
865 mutt_free_alias (&Aliases);
869 for (tmp = Aliases; tmp; tmp = tmp->next) {
870 if (mutt_strcasecmp (buf->data, tmp->name) == 0) {
871 if (CurrentMenu == MENU_ALIAS) {
873 set_option (OPTFORCEREDRAWINDEX);
878 last->next = tmp->next;
882 mutt_free_alias (&tmp);
888 while (MoreArgs (s));
892 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
895 ALIAS *tmp = Aliases;
900 strfcpy (err->data, _("alias: no address"), err->dsize);
904 mutt_extract_token (buf, s, 0);
906 dprint (2, (debugfile, "parse_alias: First token is '%s'.\n", buf->data));
908 /* check to see if an alias with this name already exists */
909 for (; tmp; tmp = tmp->next) {
910 if (!mutt_strcasecmp (tmp->name, buf->data))
916 /* create a new alias */
917 tmp = (ALIAS *) safe_calloc (1, sizeof (ALIAS));
919 tmp->name = safe_strdup (buf->data);
920 /* give the main addressbook code a chance */
921 if (CurrentMenu == MENU_ALIAS)
922 set_option (OPTMENUCALLER);
925 /* override the previous value */
926 rfc822_free_address (&tmp->addr);
927 if (CurrentMenu == MENU_ALIAS)
928 set_option (OPTFORCEREDRAWINDEX);
931 mutt_extract_token (buf, s,
932 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
933 dprint (2, (debugfile, "parse_alias: Second token is '%s'.\n", buf->data));
934 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
939 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
940 snprintf (err->data, err->dsize,
941 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
945 if (debuglevel >= 2) {
948 for (a = tmp->addr; a; a = a->next) {
950 dprint (2, (debugfile, "parse_alias: %s\n", a->mailbox));
952 dprint (2, (debugfile, "parse_alias: Group %s\n", a->mailbox));
960 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
963 LIST *tmp = UserHeader;
968 mutt_extract_token (buf, s, 0);
969 if (mutt_strcmp ("*", buf->data) == 0)
970 mutt_free_list (&UserHeader);
975 l = mutt_strlen (buf->data);
976 if (buf->data[l - 1] == ':')
980 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
981 && tmp->data[l] == ':') {
984 last->next = tmp->next;
986 UserHeader = tmp->next;
989 mutt_free_list (&ptr);
998 while (MoreArgs (s));
1002 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
1009 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1010 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1011 strfcpy (err->data, _("invalid header field"), err->dsize);
1014 keylen = p - buf->data + 1;
1017 for (tmp = UserHeader;; tmp = tmp->next) {
1018 /* see if there is already a field by this name */
1019 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1020 /* replace the old value */
1022 tmp->data = buf->data;
1023 memset (buf, 0, sizeof (BUFFER));
1029 tmp->next = mutt_new_list ();
1033 tmp = mutt_new_list ();
1036 tmp->data = buf->data;
1037 memset (buf, 0, sizeof (BUFFER));
1042 parse_sort (short *val, const char *s, const struct mapping_t *map,
1047 if (mutt_strncmp ("reverse-", s, 8) == 0) {
1049 flags = SORT_REVERSE;
1052 if (mutt_strncmp ("last-", s, 5) == 0) {
1057 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1058 snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s);
1067 static void mutt_set_default (struct option_t *p)
1069 switch (p->type & DT_MASK) {
1071 if (!p->init && *((char **) p->data))
1072 p->init = (unsigned long) safe_strdup (*((char **) p->data));
1075 if (!p->init && *((char **) p->data)) {
1076 char *cp = safe_strdup (*((char **) p->data));
1078 /* mutt_pretty_mailbox (cp); */
1079 p->init = (unsigned long) cp;
1083 if (!p->init && *((ADDRESS **) p->data)) {
1084 char tmp[HUGE_STRING];
1087 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data), 0);
1088 p->init = (unsigned long) safe_strdup (tmp);
1093 REGEXP *pp = (REGEXP *) p->data;
1095 if (!p->init && pp->pattern)
1096 p->init = (unsigned long) safe_strdup (pp->pattern);
1102 static void mutt_restore_default (struct option_t *p)
1104 switch (p->type & DT_MASK) {
1107 mutt_str_replace ((char **) p->data, (char *) p->init);
1111 char path[_POSIX_PATH_MAX];
1113 strfcpy (path, (char *) p->init, sizeof (path));
1114 mutt_expand_path (path, sizeof (path));
1115 mutt_str_replace ((char **) p->data, path);
1120 rfc822_free_address ((ADDRESS **) p->data);
1121 *((ADDRESS **) p->data) = rfc822_parse_adrlist (NULL, (char *) p->init);
1126 set_option (p->data);
1128 unset_option (p->data);
1131 set_quadoption (p->data, p->init);
1136 *((short *) p->data) = p->init;
1140 REGEXP *pp = (REGEXP *) p->data;
1143 FREE (&pp->pattern);
1150 char *s = (char *) p->init;
1152 pp->rx = safe_calloc (1, sizeof (regex_t));
1153 if (mutt_strcmp (p->option, "mask") != 0)
1154 flags |= mutt_which_case ((const char *) p->init);
1155 if (mutt_strcmp (p->option, "mask") == 0 && *s == '!') {
1159 if (REGCOMP (pp->rx, s, flags) != 0) {
1161 _("mutt_restore_default(%s): error in regexp: %s\n"),
1162 p->option, pp->pattern);
1163 FREE (&pp->pattern);
1168 mutt_str_replace (&pp->pattern, (char *) p->init);
1174 if (p->flags & R_INDEX)
1175 set_option (OPTFORCEREDRAWINDEX);
1176 if (p->flags & R_PAGER)
1177 set_option (OPTFORCEREDRAWPAGER);
1178 if (p->flags & R_RESORT_SUB)
1179 set_option (OPTSORTSUBTHREADS);
1180 if (p->flags & R_RESORT)
1181 set_option (OPTNEEDRESORT);
1182 if (p->flags & R_RESORT_INIT)
1183 set_option (OPTRESORTINIT);
1184 if (p->flags & R_TREE)
1185 set_option (OPTREDRAWTREE);
1188 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1191 int idx, query, unset, inv, reset, r = 0;
1192 char *p, scratch[_POSIX_PATH_MAX];
1194 while (MoreArgs (s)) {
1195 /* reset state variables */
1197 unset = data & M_SET_UNSET;
1198 inv = data & M_SET_INV;
1199 reset = data & M_SET_RESET;
1201 if (*s->dptr == '?') {
1205 else if (mutt_strncmp ("no", s->dptr, 2) == 0) {
1209 else if (mutt_strncmp ("inv", s->dptr, 3) == 0) {
1213 else if (*s->dptr == '&') {
1218 /* get the variable name */
1219 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1221 if ((idx = mutt_option_index (tmp->data)) == -1 &&
1222 !(reset && !mutt_strcmp ("all", tmp->data))) {
1223 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1229 if (query || unset || inv) {
1230 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1234 if (s && *s->dptr == '=') {
1235 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1239 if (!mutt_strcmp ("all", tmp->data)) {
1240 for (idx = 0; MuttVars[idx].option; idx++)
1241 mutt_restore_default (&MuttVars[idx]);
1245 mutt_restore_default (&MuttVars[idx]);
1247 else if (DTYPE (MuttVars[idx].type) == DT_BOOL) {
1248 if (s && *s->dptr == '=') {
1249 if (unset || inv || query) {
1250 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1255 mutt_extract_token (tmp, s, 0);
1256 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1258 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1261 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1267 snprintf (err->data, err->dsize, option (MuttVars[idx].data)
1268 ? _("%s is set") : _("%s is unset"), tmp->data);
1273 unset_option (MuttVars[idx].data);
1275 toggle_option (MuttVars[idx].data);
1277 set_option (MuttVars[idx].data);
1279 else if (DTYPE (MuttVars[idx].type) == DT_STR ||
1280 DTYPE (MuttVars[idx].type) == DT_PATH ||
1281 DTYPE (MuttVars[idx].type) == DT_ADDR) {
1283 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1284 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1286 FREE ((void *) MuttVars[idx].data);
1288 else if (query || *s->dptr != '=') {
1292 if (DTYPE (MuttVars[idx].type) == DT_ADDR) {
1294 rfc822_write_address (_tmp, sizeof (_tmp),
1295 *((ADDRESS **) MuttVars[idx].data), 0);
1299 val = *((char **) MuttVars[idx].data);
1301 /* user requested the value of this variable */
1302 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1309 /* copy the value of the string */
1310 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1311 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1313 FREE ((void *) MuttVars[idx].data);
1315 mutt_extract_token (tmp, s, 0);
1316 if (DTYPE (MuttVars[idx].type) == DT_PATH) {
1317 strfcpy (scratch, tmp->data, sizeof (scratch));
1318 mutt_expand_path (scratch, sizeof (scratch));
1319 *((char **) MuttVars[idx].data) = safe_strdup (scratch);
1321 else if (DTYPE (MuttVars[idx].type) == DT_STR) {
1322 *((char **) MuttVars[idx].data) = safe_strdup (tmp->data);
1323 if (mutt_strcmp (MuttVars[idx].option, "charset") == 0)
1324 mutt_set_charset (Charset);
1327 *((ADDRESS **) MuttVars[idx].data) =
1328 rfc822_parse_adrlist (NULL, tmp->data);
1332 else if (DTYPE (MuttVars[idx].type) == DT_RX) {
1333 REGEXP *ptr = (REGEXP *) MuttVars[idx].data;
1337 if (query || *s->dptr != '=') {
1338 /* user requested the value of this variable */
1339 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1340 NONULL (ptr->pattern));
1344 if (option (OPTATTACHMSG)
1345 && !mutt_strcmp (MuttVars[idx].option, "reply_regexp")) {
1346 snprintf (err->data, err->dsize,
1347 "Operation not permitted when in attach-message mode.");
1354 /* copy the value of the string */
1355 mutt_extract_token (tmp, s, 0);
1357 if (!ptr->pattern || mutt_strcmp (ptr->pattern, tmp->data) != 0) {
1360 /* $mask is case-sensitive */
1361 if (mutt_strcmp (MuttVars[idx].option, "mask") != 0)
1362 flags |= mutt_which_case (tmp->data);
1365 if (mutt_strcmp (MuttVars[idx].option, "mask") == 0) {
1372 rx = (regex_t *) safe_malloc (sizeof (regex_t));
1373 if ((e = REGCOMP (rx, p, flags)) != 0) {
1374 regerror (e, rx, err->data, err->dsize);
1380 /* get here only if everything went smootly */
1382 FREE (&ptr->pattern);
1383 regfree ((regex_t *) ptr->rx);
1387 ptr->pattern = safe_strdup (tmp->data);
1391 /* $reply_regexp and $alterantes require special treatment */
1393 if (Context && Context->msgcount &&
1394 mutt_strcmp (MuttVars[idx].option, "reply_regexp") == 0) {
1395 regmatch_t pmatch[1];
1398 #define CUR_ENV Context->hdrs[i]->env
1399 for (i = 0; i < Context->msgcount; i++) {
1400 if (CUR_ENV && CUR_ENV->subject) {
1401 CUR_ENV->real_subj = (regexec (ReplyRegexp.rx,
1402 CUR_ENV->subject, 1, pmatch,
1404 subject : CUR_ENV->subject + pmatch[0].rm_eo;
1411 else if (DTYPE (MuttVars[idx].type) == DT_MAGIC) {
1412 if (query || *s->dptr != '=') {
1413 switch (DefaultMagic) {
1430 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p);
1436 /* copy the value of the string */
1437 mutt_extract_token (tmp, s, 0);
1438 if (mx_set_magic (tmp->data)) {
1439 snprintf (err->data, err->dsize, _("%s: invalid mailbox type"),
1445 else if (DTYPE (MuttVars[idx].type) == DT_NUM) {
1446 short *ptr = (short *) MuttVars[idx].data;
1450 if (query || *s->dptr != '=') {
1451 /* user requested the value of this variable */
1452 snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, *ptr);
1458 mutt_extract_token (tmp, s, 0);
1459 val = strtol (tmp->data, &t, 0);
1461 if (!*tmp->data || *t || (short) val != val) {
1462 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1469 /* these ones need a sanity check */
1470 if (mutt_strcmp (MuttVars[idx].option, "history") == 0) {
1473 mutt_init_history ();
1475 else if (mutt_strcmp (MuttVars[idx].option, "pager_index_lines") == 0) {
1480 else if (DTYPE (MuttVars[idx].type) == DT_QUAD) {
1482 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1484 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
1485 vals[quadoption (MuttVars[idx].data)]);
1489 if (*s->dptr == '=') {
1491 mutt_extract_token (tmp, s, 0);
1492 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1493 set_quadoption (MuttVars[idx].data, M_YES);
1494 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1495 set_quadoption (MuttVars[idx].data, M_NO);
1496 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1497 set_quadoption (MuttVars[idx].data, M_ASKYES);
1498 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1499 set_quadoption (MuttVars[idx].data, M_ASKNO);
1501 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1508 toggle_quadoption (MuttVars[idx].data);
1510 set_quadoption (MuttVars[idx].data, M_NO);
1512 set_quadoption (MuttVars[idx].data, M_YES);
1515 else if (DTYPE (MuttVars[idx].type) == DT_SORT) {
1516 const struct mapping_t *map = NULL;
1518 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) {
1520 map = SortAliasMethods;
1522 case DT_SORT_BROWSER:
1523 map = SortBrowserMethods;
1526 if ((WithCrypto & APPLICATION_PGP))
1527 map = SortKeyMethods;
1530 map = SortAuxMethods;
1538 snprintf (err->data, err->dsize, _("%s: Unknown type."),
1539 MuttVars[idx].option);
1544 if (query || *s->dptr != '=') {
1546 mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK,
1549 snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
1550 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ?
1552 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" :
1557 mutt_extract_token (tmp, s, 0);
1559 if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) ==
1566 snprintf (err->data, err->dsize, _("%s: unknown type"),
1567 MuttVars[idx].option);
1572 if (MuttVars[idx].flags & R_INDEX)
1573 set_option (OPTFORCEREDRAWINDEX);
1574 if (MuttVars[idx].flags & R_PAGER)
1575 set_option (OPTFORCEREDRAWPAGER);
1576 if (MuttVars[idx].flags & R_RESORT_SUB)
1577 set_option (OPTSORTSUBTHREADS);
1578 if (MuttVars[idx].flags & R_RESORT)
1579 set_option (OPTNEEDRESORT);
1580 if (MuttVars[idx].flags & R_RESORT_INIT)
1581 set_option (OPTRESORTINIT);
1582 if (MuttVars[idx].flags & R_TREE)
1583 set_option (OPTREDRAWTREE);
1590 /* reads the specified initialization file. returns -1 if errors were found
1591 so that we can pause to let the user know... */
1592 static int source_rc (const char *rcfile, BUFFER * err)
1595 int line = 0, rc = 0, conv = 0;
1597 char *linebuf = NULL;
1598 char *currentline = NULL;
1602 dprint (2, (debugfile, "Reading configuration file '%s'.\n", rcfile));
1604 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1605 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1609 memset (&token, 0, sizeof (token));
1610 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
1611 conv = ConfigCharset && (*ConfigCharset) && Charset;
1613 currentline = safe_strdup (linebuf);
1616 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1619 currentline = linebuf;
1621 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1622 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1623 if (--rc < -MAXERRS) {
1625 FREE (¤tline);
1634 FREE (¤tline);
1640 mutt_wait_filter (pid);
1642 /* the muttrc source keyword */
1643 snprintf (err->data, err->dsize,
1644 rc >= -MAXERRS ? _("source: errors in %s")
1645 : _("source: reading aborted due too many errors in %s"),
1654 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
1657 char path[_POSIX_PATH_MAX];
1661 if (mutt_extract_token (tmp, s, 0) != 0) {
1662 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1666 strfcpy (path, tmp->data, sizeof (path));
1667 mutt_expand_path (path, sizeof (path));
1669 rc += source_rc (path, err);
1671 while (MoreArgs (s));
1673 return ((rc < 0) ? -1 : 0);
1676 /* line command to execute
1678 token scratch buffer to be used by parser. caller should free
1679 token->data when finished. the reason for this variable is
1680 to avoid having to allocate and deallocate a lot of memory
1681 if we are parsing many lines. the caller can pass in the
1682 memory to use, which avoids having to create new space for
1683 every call to this function.
1685 err where to write error messages */
1686 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1691 memset (&expn, 0, sizeof (expn));
1692 expn.data = expn.dptr = line;
1693 expn.dsize = mutt_strlen (line);
1698 while (*expn.dptr) {
1699 if (*expn.dptr == '#')
1700 break; /* rest of line is a comment */
1701 if (*expn.dptr == ';') {
1705 mutt_extract_token (token, &expn, 0);
1706 for (i = 0; Commands[i].name; i++) {
1707 if (!mutt_strcmp (token->data, Commands[i].name)) {
1708 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1713 if (!Commands[i].name) {
1714 snprintf (err->data, err->dsize, _("%s: unknown command"),
1715 NONULL (token->data));
1727 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1728 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1729 /* initial string that starts completion. No telling how much crap
1730 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1731 char User_typed[LONG_STRING] = { 0 };
1733 int Num_matched = 0; /* Number of matches for completion */
1734 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1735 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1737 /* helper function for completion. Changes the dest buffer if
1738 necessary/possible to aid completion.
1739 dest == completion result gets here.
1740 src == candidate for completion.
1741 try == user entered data for completion.
1742 len == length of dest buffer.
1744 static void candidate (char *dest, char *try, char *src, int len)
1748 if (strstr (src, try) == src) {
1749 Matches[Num_matched++] = src;
1751 strfcpy (dest, src, len);
1753 for (l = 0; src[l] && src[l] == dest[l]; l++);
1759 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1763 int spaces; /* keep track of the number of leading spaces on the line */
1766 spaces = buffer - pt;
1768 pt = buffer + pos - spaces;
1769 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1772 if (pt == buffer) { /* complete cmd */
1773 /* first TAB. Collect all the matches */
1776 strfcpy (User_typed, pt, sizeof (User_typed));
1777 memset (Matches, 0, sizeof (Matches));
1778 memset (Completed, 0, sizeof (Completed));
1779 for (num = 0; Commands[num].name; num++)
1780 candidate (Completed, User_typed, Commands[num].name,
1781 sizeof (Completed));
1782 Matches[Num_matched++] = User_typed;
1784 /* All matches are stored. Longest non-ambiguous string is ""
1785 * i.e. dont change 'buffer'. Fake successful return this time */
1786 if (User_typed[0] == 0)
1790 if (Completed[0] == 0 && User_typed[0])
1793 /* Num_matched will _always_ be atleast 1 since the initial
1794 * user-typed string is always stored */
1795 if (numtabs == 1 && Num_matched == 2)
1796 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1797 else if (numtabs > 1 && Num_matched > 2)
1798 /* cycle thru all the matches */
1799 snprintf (Completed, sizeof (Completed), "%s",
1800 Matches[(numtabs - 2) % Num_matched]);
1802 /* return the completed command */
1803 strncpy (buffer, Completed, len - spaces);
1805 else if (!mutt_strncmp (buffer, "set", 3)
1806 || !mutt_strncmp (buffer, "unset", 5)
1807 || !mutt_strncmp (buffer, "reset", 5)
1808 || !mutt_strncmp (buffer, "toggle", 6)) { /* complete variables */
1809 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1812 /* loop through all the possible prefixes (no, inv, ...) */
1813 if (!mutt_strncmp (buffer, "set", 3)) {
1814 for (num = 0; prefixes[num]; num++) {
1815 if (!mutt_strncmp (pt, prefixes[num], mutt_strlen (prefixes[num]))) {
1816 pt += mutt_strlen (prefixes[num]);
1822 /* first TAB. Collect all the matches */
1825 strfcpy (User_typed, pt, sizeof (User_typed));
1826 memset (Matches, 0, sizeof (Matches));
1827 memset (Completed, 0, sizeof (Completed));
1828 for (num = 0; MuttVars[num].option; num++)
1829 candidate (Completed, User_typed, MuttVars[num].option,
1830 sizeof (Completed));
1831 Matches[Num_matched++] = User_typed;
1833 /* All matches are stored. Longest non-ambiguous string is ""
1834 * i.e. dont change 'buffer'. Fake successful return this time */
1835 if (User_typed[0] == 0)
1839 if (Completed[0] == 0 && User_typed[0])
1842 /* Num_matched will _always_ be atleast 1 since the initial
1843 * user-typed string is always stored */
1844 if (numtabs == 1 && Num_matched == 2)
1845 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1846 else if (numtabs > 1 && Num_matched > 2)
1847 /* cycle thru all the matches */
1848 snprintf (Completed, sizeof (Completed), "%s",
1849 Matches[(numtabs - 2) % Num_matched]);
1851 strncpy (pt, Completed, buffer + len - pt - spaces);
1853 else if (!mutt_strncmp (buffer, "exec", 4)) {
1854 struct binding_t *menu = km_get_table (CurrentMenu);
1856 if (!menu && CurrentMenu != MENU_PAGER)
1860 /* first TAB. Collect all the matches */
1863 strfcpy (User_typed, pt, sizeof (User_typed));
1864 memset (Matches, 0, sizeof (Matches));
1865 memset (Completed, 0, sizeof (Completed));
1866 for (num = 0; menu[num].name; num++)
1867 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1868 /* try the generic menu */
1869 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1871 for (num = 0; menu[num].name; num++)
1872 candidate (Completed, User_typed, menu[num].name,
1873 sizeof (Completed));
1875 Matches[Num_matched++] = User_typed;
1877 /* All matches are stored. Longest non-ambiguous string is ""
1878 * i.e. dont change 'buffer'. Fake successful return this time */
1879 if (User_typed[0] == 0)
1883 if (Completed[0] == 0 && User_typed[0])
1886 /* Num_matched will _always_ be atleast 1 since the initial
1887 * user-typed string is always stored */
1888 if (numtabs == 1 && Num_matched == 2)
1889 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1890 else if (numtabs > 1 && Num_matched > 2)
1891 /* cycle thru all the matches */
1892 snprintf (Completed, sizeof (Completed), "%s",
1893 Matches[(numtabs - 2) % Num_matched]);
1895 strncpy (pt, Completed, buffer + len - pt - spaces);
1903 int mutt_var_value_complete (char *buffer, size_t len, int pos)
1905 char var[STRING], *pt = buffer;
1912 spaces = buffer - pt;
1914 pt = buffer + pos - spaces;
1915 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1917 pt++; /* move past the space */
1918 if (*pt == '=') /* abort if no var before the '=' */
1921 if (mutt_strncmp (buffer, "set", 3) == 0) {
1924 strfcpy (var, pt, sizeof (var));
1925 /* ignore the trailing '=' when comparing */
1926 var[mutt_strlen (var) - 1] = 0;
1927 if ((idx = mutt_option_index (var)) == -1)
1928 return 0; /* no such variable. */
1930 char tmp[LONG_STRING], tmp2[LONG_STRING];
1932 size_t dlen = buffer + len - pt - spaces;
1933 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1937 if ((DTYPE (MuttVars[idx].type) == DT_STR) ||
1938 (DTYPE (MuttVars[idx].type) == DT_PATH) ||
1939 (DTYPE (MuttVars[idx].type) == DT_RX)) {
1940 strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data)), sizeof (tmp));
1941 if (DTYPE (MuttVars[idx].type) == DT_PATH)
1942 mutt_pretty_mailbox (tmp);
1944 else if (DTYPE (MuttVars[idx].type) == DT_ADDR) {
1945 rfc822_write_address (tmp, sizeof (tmp),
1946 *((ADDRESS **) MuttVars[idx].data), 0);
1948 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
1949 strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp));
1950 else if (DTYPE (MuttVars[idx].type) == DT_NUM)
1951 snprintf (tmp, sizeof (tmp), "%d", (*((short *) MuttVars[idx].data)));
1952 else if (DTYPE (MuttVars[idx].type) == DT_SORT) {
1953 const struct mapping_t *map;
1956 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) {
1958 map = SortAliasMethods;
1960 case DT_SORT_BROWSER:
1961 map = SortBrowserMethods;
1964 if ((WithCrypto & APPLICATION_PGP))
1965 map = SortKeyMethods;
1974 mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK,
1976 snprintf (tmp, sizeof (tmp), "%s%s%s",
1977 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ?
1979 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" :
1982 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
1983 strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no",
1988 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
1989 if (*s == '\\' || *s == '"')
1995 strfcpy (tmp, pt, sizeof (tmp));
1996 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2004 /* Implement the -Q command line flag */
2005 int mutt_query_variables (LIST * queries)
2009 char errbuff[STRING];
2010 char command[STRING];
2014 memset (&err, 0, sizeof (err));
2015 memset (&token, 0, sizeof (token));
2018 err.dsize = sizeof (errbuff);
2020 for (p = queries; p; p = p->next) {
2021 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2022 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2023 fprintf (stderr, "%s\n", err.data);
2027 printf ("%s\n", err.data);
2034 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2038 for (i = 0; map[i].name; i++)
2039 if (map[i].value == val)
2040 return (map[i].name);
2044 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2048 for (i = 0; map[i].name; i++)
2049 if (ascii_strcasecmp (map[i].name, name) == 0)
2050 return (map[i].value);
2055 static void start_debug (void)
2059 char buf[_POSIX_PATH_MAX];
2060 char buf2[_POSIX_PATH_MAX];
2062 /* rotate the old debug logs */
2063 for (i = 3; i >= 0; i--) {
2064 snprintf (buf, sizeof (buf), "%s/.muttdebug%d", NONULL (Homedir), i);
2065 snprintf (buf2, sizeof (buf2), "%s/.muttdebug%d", NONULL (Homedir),
2069 if ((debugfile = safe_fopen (buf, "w")) != NULL) {
2071 setbuf (debugfile, NULL); /* don't buffer the debugging output! */
2073 "Mutt-ng %s started at %s.\nDebugging at level %d.\n\n",
2074 MUTT_VERSION, asctime (localtime (&t)), debuglevel);
2079 static int mutt_execute_commands (LIST * p)
2082 char errstr[SHORT_STRING];
2084 memset (&err, 0, sizeof (err));
2086 err.dsize = sizeof (errstr);
2087 memset (&token, 0, sizeof (token));
2088 for (; p; p = p->next) {
2089 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2090 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2099 void mutt_init (int skip_sys_rc, LIST * commands)
2102 struct utsname utsname;
2103 char *p, buffer[STRING], error[STRING];
2104 int i, default_rc = 0, need_pause = 0;
2107 memset (&err, 0, sizeof (err));
2109 err.dsize = sizeof (error);
2112 * XXX - use something even more difficult to predict?
2114 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2115 "\033]9;%ld\a", (long) time (NULL));
2117 /* on one of the systems I use, getcwd() does not return the same prefix
2118 as is listed in the passwd file */
2119 if ((p = getenv ("HOME")))
2120 Homedir = safe_strdup (p);
2122 /* Get some information about the user */
2123 if ((pw = getpwuid (getuid ()))) {
2126 Username = safe_strdup (pw->pw_name);
2128 Homedir = safe_strdup (pw->pw_dir);
2130 Realname = safe_strdup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2131 Shell = safe_strdup (pw->pw_shell);
2136 fputs (_("unable to determine home directory"), stderr);
2139 if ((p = getenv ("USER")))
2140 Username = safe_strdup (p);
2143 fputs (_("unable to determine username"), stderr);
2146 Shell = safe_strdup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2150 /* Start up debugging mode if requested */
2155 /* And about the host... */
2157 /* some systems report the FQDN instead of just the hostname */
2158 if ((p = strchr (utsname.nodename, '.'))) {
2159 Hostname = mutt_substrdup (utsname.nodename, p);
2161 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2164 Hostname = safe_strdup (utsname.nodename);
2167 #define DOMAIN buffer
2168 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2169 Fqdn = safe_strdup ("@");
2172 if (*DOMAIN != '@') {
2173 Fqdn = safe_malloc (mutt_strlen (DOMAIN) + mutt_strlen (Hostname) + 2);
2174 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2177 Fqdn = safe_strdup (NONULL (Hostname));
2184 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2186 fgets (buffer, sizeof (buffer), f);
2190 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r')
2194 NewsServer = safe_strdup (p);
2198 if ((p = getenv ("NNTPSERVER")))
2199 NewsServer = safe_strdup (p);
2202 if ((p = getenv ("MAIL")))
2203 Spoolfile = safe_strdup (p);
2204 else if ((p = getenv ("MAILDIR")))
2205 Spoolfile = safe_strdup (p);
2208 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2210 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
2212 Spoolfile = safe_strdup (buffer);
2215 if ((p = getenv ("MAILCAPS")))
2216 MailcapPath = safe_strdup (p);
2218 /* Default search path from RFC1524 */
2220 safe_strdup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2221 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2224 Tempdir = safe_strdup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2226 p = getenv ("VISUAL");
2228 p = getenv ("EDITOR");
2232 Editor = safe_strdup (p);
2233 Visual = safe_strdup (p);
2235 if ((p = getenv ("REPLYTO")) != NULL) {
2238 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2240 memset (&buf, 0, sizeof (buf));
2241 buf.data = buf.dptr = buffer;
2242 buf.dsize = mutt_strlen (buffer);
2244 memset (&token, 0, sizeof (token));
2245 parse_my_hdr (&token, &buf, 0, &err);
2249 if ((p = getenv ("EMAIL")) != NULL)
2250 From = rfc822_parse_adrlist (NULL, p);
2252 mutt_set_langinfo_charset ();
2253 mutt_set_charset (Charset);
2256 /* Set standard defaults */
2257 for (i = 0; MuttVars[i].option; i++) {
2258 mutt_set_default (&MuttVars[i]);
2259 mutt_restore_default (&MuttVars[i]);
2262 CurrentMenu = MENU_MAIN;
2265 #ifndef LOCALES_HACK
2266 /* Do we have a locale definition? */
2267 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2268 ((p = getenv ("LANG")) != NULL && p[0]) ||
2269 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2270 set_option (OPTLOCALES);
2274 /* Unset suspend by default if we're the session leader */
2275 if (getsid (0) == getpid ())
2276 unset_option (OPTSUSPEND);
2279 mutt_init_history ();
2288 * When changing the code which looks for a configuration file,
2289 * please also change the corresponding code in muttbug.sh.in.
2298 snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2300 if (access (buffer, F_OK) == -1)
2301 snprintf (buffer, sizeof (buffer), "%s/.muttngrc", NONULL (Homedir));
2302 if (access (buffer, F_OK) == -1)
2303 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s",
2304 NONULL (Homedir), MUTT_VERSION);
2305 if (access (buffer, F_OK) == -1)
2306 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc",
2310 Muttrc = safe_strdup (buffer);
2313 strfcpy (buffer, Muttrc, sizeof (buffer));
2315 mutt_expand_path (buffer, sizeof (buffer));
2316 Muttrc = safe_strdup (buffer);
2319 AliasFile = safe_strdup (NONULL (Muttrc));
2321 /* Process the global rc file if it exists and the user hasn't explicity
2322 requested not to via "-n". */
2324 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2326 if (access (buffer, F_OK) == -1)
2327 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", SYSCONFDIR);
2328 if (access (buffer, F_OK) == -1)
2329 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR,
2331 if (access (buffer, F_OK) == -1)
2332 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2333 if (access (buffer, F_OK) != -1) {
2334 if (source_rc (buffer, &err) != 0) {
2335 fputs (err.data, stderr);
2336 fputc ('\n', stderr);
2342 /* Read the user's initialization file. */
2343 if (access (Muttrc, F_OK) != -1) {
2344 if (!option (OPTNOCURSES))
2346 if (source_rc (Muttrc, &err) != 0) {
2347 fputs (err.data, stderr);
2348 fputc ('\n', stderr);
2352 else if (!default_rc) {
2353 /* file specified by -F does not exist */
2354 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2355 mutt_endwin (buffer);
2359 if (mutt_execute_commands (commands) != 0)
2362 if (need_pause && !option (OPTNOCURSES)) {
2363 if (mutt_any_key_to_continue (NULL) == -1)
2368 set_option (OPTWEED); /* turn weeding on by default */
2372 int mutt_get_hook_type (const char *name)
2374 struct command_t *c;
2376 for (c = Commands; c->name; c++)
2377 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)