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.
21 #include "mutt_curses.h"
22 #include "mutt_regex.h"
27 #include "mutt_crypt.h"
28 #include "mutt_idna.h"
30 #if defined(USE_SSL) || defined(USE_NSS)
44 #include <sys/utsname.h>
48 void toggle_quadoption (int opt)
51 int b = (opt % 4) * 2;
53 QuadOptions[n] ^= (1 << b);
56 void set_quadoption (int opt, int flag)
59 int b = (opt % 4) * 2;
61 QuadOptions[n] &= ~(0x3 << b);
62 QuadOptions[n] |= (flag & 0x3) << b;
65 int quadoption (int opt)
68 int b = (opt % 4) * 2;
70 return (QuadOptions[n] >> b) & 0x3;
73 int query_quadoption (int opt, const char *prompt)
75 int v = quadoption (opt);
84 v = mutt_yesorno (prompt, (v == M_ASKYES));
85 CLEARLINE (LINES - 1);
92 /* given the variable ``s'', return the index into the rc_vars array which
93 matches, or -1 if the variable is not found. */
94 int mutt_option_index (char *s)
98 for (i = 0; MuttVars[i].option; i++)
99 if (mutt_strcmp (s, MuttVars[i].option) == 0)
100 return (MuttVars[i].type == DT_SYN ? mutt_option_index ((char *) MuttVars[i].data) : i);
104 int mutt_extract_token (BUFFER *dest, BUFFER *tok, int flags)
107 char qc = 0; /* quote char */
110 /* reset the destination pointer to the beginning of the buffer */
111 dest->dptr = dest->data;
114 while ((ch = *tok->dptr))
118 if ((ISSPACE (ch) && !(flags & M_TOKEN_SPACE)) ||
119 (ch == '#' && !(flags & M_TOKEN_COMMENT)) ||
120 (ch == '=' && (flags & M_TOKEN_EQUAL)) ||
121 (ch == ';' && !(flags & M_TOKEN_SEMICOLON)) ||
122 ((flags & M_TOKEN_PATTERN) && strchr ("~!|", ch)))
129 qc = 0; /* end of quote */
130 else if (!qc && (ch == '\'' || ch == '"') && !(flags & M_TOKEN_QUOTE))
132 else if (ch == '\\' && qc != '\'')
135 return -1; /* premature end of token */
136 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)))
167 mutt_buffer_addch (dest, (ch << 6) + (*tok->dptr << 3) + *(tok->dptr + 1) - 3504);
171 mutt_buffer_addch (dest, ch);
174 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) - '@');
187 mutt_buffer_addch (dest, '^');
188 mutt_buffer_addch (dest, ch);
191 else if (ch == '`' && (!qc || qc == '"'))
202 if ((pc = strpbrk (pc, "\\`")))
204 /* skip any quoted chars */
208 } while (pc && *pc != '`');
211 dprint (1, (debugfile, "mutt_get_token: mismatched backtics\n"));
214 cmd = mutt_substrdup (tok->dptr, pc);
215 if ((pid = mutt_create_filter (cmd, NULL, &fp, NULL)) < 0)
217 dprint (1, (debugfile, "mutt_get_token: unable to fork command: %s", cmd));
226 memset (&expn, 0, sizeof (expn));
227 expn.data = mutt_read_line (NULL, &expn.dsize, fp, &line);
229 mutt_wait_filter (pid);
231 /* if we got output, make a new string consiting of the shell ouptput
232 plus whatever else was left on the original line */
233 /* BUT: If this is inside a quoted string, directly add output to
237 mutt_buffer_addstr (dest, expn.data);
242 expnlen = mutt_strlen (expn.data);
243 tok->dsize = expnlen + mutt_strlen (tok->dptr) + 1;
244 ptr = safe_malloc (tok->dsize);
245 memcpy (ptr, expn.data, expnlen);
246 strcpy (ptr + expnlen, tok->dptr); /* __STRCPY_CHECKED__ */
251 tok->destroy = 1; /* mark that the caller should destroy this data */
256 else if (ch == '$' && (!qc || qc == '"') && (*tok->dptr == '{' || isalpha ((unsigned char) *tok->dptr)))
258 char *env = NULL, *var = NULL;
260 if (*tok->dptr == '{')
263 if ((pc = strchr (tok->dptr, '}')))
265 var = mutt_substrdup (tok->dptr, pc);
271 for (pc = tok->dptr; isalpha ((unsigned char) *pc) || *pc == '_'; pc++)
273 var = mutt_substrdup (tok->dptr, pc);
276 if (var && (env = getenv (var)))
277 mutt_buffer_addstr (dest, env);
281 mutt_buffer_addch (dest, ch);
283 mutt_buffer_addch (dest, 0); /* terminate the string */
288 static void add_to_list (LIST **list, const char *str)
290 LIST *t, *last = NULL;
292 /* don't add a NULL or empty string to the list */
293 if (!str || *str == '\0')
296 /* check to make sure the item is not already on this list */
297 for (last = *list; last; last = last->next)
299 if (ascii_strcasecmp (str, last->data) == 0)
301 /* already on the list, so just ignore it */
311 t = (LIST *) safe_calloc (1, sizeof (LIST));
312 t->data = safe_strdup (str);
323 static int add_to_rx_list (RX_LIST **list, const char *s, int flags, BUFFER *err)
325 RX_LIST *t, *last = NULL;
331 if (!(rx = mutt_compile_regexp (s, flags)))
333 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
337 /* check to make sure the item is not already on this list */
338 for (last = *list; last; last = last->next)
340 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0)
342 /* already on the list, so just ignore it */
352 t = mutt_new_rx_list();
363 mutt_free_regexp (&rx);
369 static void remove_from_list (LIST **l, const char *str)
371 LIST *p, *last = NULL;
373 if (mutt_strcmp ("*", str) == 0)
374 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
381 if (ascii_strcasecmp (str, p->data) == 0)
385 last->next = p->next;
399 static void remove_from_rx_list (RX_LIST **l, const char *str)
401 RX_LIST *p, *last = NULL;
403 if (mutt_strcmp ("*", str) == 0)
404 mutt_free_rx_list (l); /* ``unCMD *'' means delete all current entries */
411 if (ascii_strcasecmp (str, p->rx->pattern) == 0)
413 mutt_free_regexp (&p->rx);
415 last->next = p->next;
429 static int parse_unignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
433 mutt_extract_token (buf, s, 0);
435 /* don't add "*" to the unignore list */
436 if (strcmp (buf->data, "*"))
437 add_to_list (&UnIgnore, buf->data);
439 remove_from_list (&Ignore, buf->data);
441 while (MoreArgs (s));
446 static int parse_ignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
450 mutt_extract_token (buf, s, 0);
451 remove_from_list (&UnIgnore, buf->data);
452 add_to_list (&Ignore, buf->data);
454 while (MoreArgs (s));
459 static int parse_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
463 mutt_extract_token (buf, s, 0);
464 add_to_list ((LIST **) data, buf->data);
466 while (MoreArgs (s));
471 static int _parse_rx_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err, int flags)
475 mutt_extract_token (buf, s, 0);
476 if (add_to_rx_list ((RX_LIST **) data, buf->data, flags, err) != 0)
480 while (MoreArgs (s));
485 static int parse_rx_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
487 return _parse_rx_list (buf, s, data, err, REG_ICASE);
490 static int parse_rx_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
494 mutt_extract_token (buf, s, 0);
495 if (mutt_strcmp (buf->data, "*") == 0)
497 mutt_free_rx_list ((RX_LIST **) data);
500 remove_from_rx_list ((RX_LIST **) data, buf->data);
502 while (MoreArgs (s));
507 static int parse_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
511 mutt_extract_token (buf, s, 0);
513 * Check for deletion of entire list
515 if (mutt_strcmp (buf->data, "*") == 0)
517 mutt_free_list ((LIST **) data);
520 remove_from_list ((LIST **) data, buf->data);
522 while (MoreArgs (s));
528 static int parse_unlists (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
532 mutt_extract_token (buf, s, 0);
533 remove_from_rx_list (&MailLists, buf->data);
534 remove_from_rx_list (&SubscribedLists, buf->data);
536 while (MoreArgs (s));
541 static int parse_subscribe (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
545 mutt_extract_token (buf, s, 0);
546 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
548 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
551 while (MoreArgs (s));
556 static int parse_unalias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
558 ALIAS *tmp, *last = NULL;
562 mutt_extract_token (buf, s, 0);
564 if (mutt_strcmp ("*", buf->data) == 0)
566 if (CurrentMenu == MENU_ALIAS)
568 for (tmp = Aliases; tmp ; tmp = tmp->next)
570 set_option (OPTFORCEREDRAWINDEX);
573 mutt_free_alias (&Aliases);
577 for (tmp = Aliases; tmp; tmp = tmp->next)
579 if (mutt_strcasecmp (buf->data, tmp->name) == 0)
581 if (CurrentMenu == MENU_ALIAS)
584 set_option (OPTFORCEREDRAWINDEX);
589 last->next = tmp->next;
593 mutt_free_alias (&tmp);
599 while (MoreArgs (s));
603 static int parse_alias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
605 ALIAS *tmp = Aliases;
611 strfcpy (err->data, _("alias: no address"), err->dsize);
615 mutt_extract_token (buf, s, 0);
617 /* check to see if an alias with this name already exists */
618 for (; tmp; tmp = tmp->next)
620 if (!mutt_strcasecmp (tmp->name, buf->data))
627 /* create a new alias */
628 tmp = (ALIAS *) safe_calloc (1, sizeof (ALIAS));
630 tmp->name = safe_strdup (buf->data);
631 /* give the main addressbook code a chance */
632 if (CurrentMenu == MENU_ALIAS)
633 set_option (OPTMENUCALLER);
637 /* override the previous value */
638 rfc822_free_address (&tmp->addr);
639 if (CurrentMenu == MENU_ALIAS)
640 set_option (OPTFORCEREDRAWINDEX);
643 mutt_extract_token (buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
644 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
649 if (mutt_addrlist_to_idna (tmp->addr, &estr))
651 snprintf (err->data, err->dsize, _("Warning: Bad IDN '%s' in alias '%s'.\n"),
659 parse_unmy_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
662 LIST *tmp = UserHeader;
668 mutt_extract_token (buf, s, 0);
669 if (mutt_strcmp ("*", buf->data) == 0)
670 mutt_free_list (&UserHeader);
676 l = mutt_strlen (buf->data);
677 if (buf->data[l - 1] == ':')
682 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0 && tmp->data[l] == ':')
686 last->next = tmp->next;
688 UserHeader = tmp->next;
691 mutt_free_list (&ptr);
701 while (MoreArgs (s));
705 static int parse_my_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
711 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
712 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':')
714 strfcpy (err->data, _("invalid header field"), err->dsize);
717 keylen = p - buf->data + 1;
721 for (tmp = UserHeader; ; tmp = tmp->next)
723 /* see if there is already a field by this name */
724 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0)
726 /* replace the old value */
728 tmp->data = buf->data;
729 memset (buf, 0, sizeof (BUFFER));
735 tmp->next = mutt_new_list ();
740 tmp = mutt_new_list ();
743 tmp->data = buf->data;
744 memset (buf, 0, sizeof (BUFFER));
749 parse_sort (short *val, const char *s, const struct mapping_t *map, BUFFER *err)
753 if (mutt_strncmp ("reverse-", s, 8) == 0)
756 flags = SORT_REVERSE;
759 if (mutt_strncmp ("last-", s, 5) == 0)
765 if ((i = mutt_getvaluebyname (s, map)) == -1)
767 snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s);
776 static void mutt_set_default (struct option_t *p)
778 switch (p->type & DT_MASK)
781 if (!p->init && *((char **) p->data))
782 p->init = (unsigned long) safe_strdup (* ((char **) p->data));
785 if (!p->init && *((char **) p->data))
787 char *cp = safe_strdup (*((char **) p->data));
788 /* mutt_pretty_mailbox (cp); */
789 p->init = (unsigned long) cp;
793 if (!p->init && *((ADDRESS **) p->data))
795 char tmp[HUGE_STRING];
797 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data), 0);
798 p->init = (unsigned long) safe_strdup (tmp);
803 REGEXP *pp = (REGEXP *) p->data;
804 if (!p->init && pp->pattern)
805 p->init = (unsigned long) safe_strdup (pp->pattern);
811 static void mutt_restore_default (struct option_t *p)
813 switch (p->type & DT_MASK)
817 mutt_str_replace ((char **) p->data, (char *) p->init);
822 char path[_POSIX_PATH_MAX];
824 strfcpy (path, (char *) p->init, sizeof (path));
825 mutt_expand_path (path, sizeof (path));
826 mutt_str_replace ((char **) p->data, path);
832 rfc822_free_address ((ADDRESS **) p->data);
833 *((ADDRESS **) p->data) = rfc822_parse_adrlist (NULL, (char *) p->init);
838 set_option (p->data);
840 unset_option (p->data);
843 set_quadoption (p->data, p->init);
848 *((short *) p->data) = p->init;
852 REGEXP *pp = (REGEXP *) p->data;
864 char *s = (char *) p->init;
866 pp->rx = safe_calloc (1, sizeof (regex_t));
867 pp->pattern = safe_strdup ((char *) p->init);
868 if (mutt_strcmp (p->option, "alternates") == 0)
870 else if (mutt_strcmp (p->option, "mask") != 0)
871 flags |= mutt_which_case ((const char *) p->init);
872 if (mutt_strcmp (p->option, "mask") == 0 && *s == '!')
877 if (REGCOMP (pp->rx, s, flags) != 0)
879 fprintf (stderr, _("mutt_restore_default(%s): error in regexp: %s\n"),
880 p->option, pp->pattern);
890 if (p->flags & R_INDEX)
891 set_option (OPTFORCEREDRAWINDEX);
892 if (p->flags & R_PAGER)
893 set_option (OPTFORCEREDRAWPAGER);
894 if (p->flags & R_RESORT_SUB)
895 set_option (OPTSORTSUBTHREADS);
896 if (p->flags & R_RESORT)
897 set_option (OPTNEEDRESORT);
898 if (p->flags & R_RESORT_INIT)
899 set_option (OPTRESORTINIT);
900 if (p->flags & R_TREE)
901 set_option (OPTREDRAWTREE);
904 static int parse_set (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
906 int idx, query, unset, inv, reset, r = 0;
907 char *p, scratch[_POSIX_PATH_MAX];
911 /* reset state variables */
913 unset = data & M_SET_UNSET;
914 inv = data & M_SET_INV;
915 reset = data & M_SET_RESET;
922 else if (mutt_strncmp ("no", s->dptr, 2) == 0)
927 else if (mutt_strncmp ("inv", s->dptr, 3) == 0)
932 else if (*s->dptr == '&')
938 /* get the variable name */
939 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
941 if ((idx = mutt_option_index (tmp->data)) == -1 &&
942 !(reset && !mutt_strcmp ("all", tmp->data)))
944 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
951 if (query || unset || inv)
953 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
957 if (s && *s->dptr == '=')
959 snprintf (err->data, err->dsize, _("value is illegal with reset"));
963 if (!mutt_strcmp ("all", tmp->data))
965 for (idx = 0; MuttVars[idx].option; idx++)
966 mutt_restore_default (&MuttVars[idx]);
970 mutt_restore_default (&MuttVars[idx]);
972 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
974 if (s && *s->dptr == '=')
976 if (unset || inv || query)
978 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
983 mutt_extract_token (tmp, s, 0);
984 if (ascii_strcasecmp ("yes", tmp->data) == 0)
986 else if (ascii_strcasecmp ("no", tmp->data) == 0)
990 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
997 snprintf (err->data, err->dsize, option (MuttVars[idx].data)
998 ? _("%s is set") : _("%s is unset"), tmp->data);
1003 unset_option (MuttVars[idx].data);
1005 toggle_option (MuttVars[idx].data);
1007 set_option (MuttVars[idx].data);
1009 else if (DTYPE (MuttVars[idx].type) == DT_STR ||
1010 DTYPE (MuttVars[idx].type) == DT_PATH ||
1011 DTYPE (MuttVars[idx].type) == DT_ADDR)
1015 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1016 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1018 FREE (MuttVars[idx].data);
1020 else if (query || *s->dptr != '=')
1025 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1028 rfc822_write_address (_tmp, sizeof (_tmp), *((ADDRESS **) MuttVars[idx].data), 0);
1032 val = *((char **) MuttVars[idx].data);
1034 /* user requested the value of this variable */
1035 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1043 /* copy the value of the string */
1044 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1045 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1047 FREE (MuttVars[idx].data);
1049 mutt_extract_token (tmp, s, 0);
1050 if (DTYPE (MuttVars[idx].type) == DT_PATH)
1052 strfcpy (scratch, tmp->data, sizeof (scratch));
1053 mutt_expand_path (scratch, sizeof (scratch));
1054 *((char **) MuttVars[idx].data) = safe_strdup (scratch);
1056 else if (DTYPE (MuttVars[idx].type) == DT_STR)
1058 *((char **) MuttVars[idx].data) = safe_strdup (tmp->data);
1059 if (mutt_strcmp (MuttVars[idx].option, "charset") == 0)
1060 mutt_set_charset (Charset);
1064 *((ADDRESS **) MuttVars[idx].data) = rfc822_parse_adrlist (NULL, tmp->data);
1068 else if (DTYPE(MuttVars[idx].type) == DT_RX)
1070 REGEXP *ptr = (REGEXP *) MuttVars[idx].data;
1074 if (query || *s->dptr != '=')
1076 /* user requested the value of this variable */
1077 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1078 NONULL (ptr->pattern));
1082 if (option(OPTATTACHMSG) && (!mutt_strcmp(MuttVars[idx].option, "alternates")
1083 || !mutt_strcmp(MuttVars[idx].option, "reply_regexp")))
1085 snprintf (err->data, err->dsize, "Operation not permitted when in attach-message mode.");
1092 /* copy the value of the string */
1093 mutt_extract_token (tmp, s, 0);
1095 if (!ptr->pattern || mutt_strcmp (ptr->pattern, tmp->data) != 0)
1099 /* $alternates is case-insensitive,
1100 $mask is case-sensitive */
1101 if (mutt_strcmp (MuttVars[idx].option, "alternates") == 0)
1103 else if (mutt_strcmp (MuttVars[idx].option, "mask") != 0)
1104 flags |= mutt_which_case (tmp->data);
1107 if (mutt_strcmp (MuttVars[idx].option, "mask") == 0)
1116 rx = (regex_t *) safe_malloc (sizeof (regex_t));
1117 if ((e = REGCOMP (rx, p, flags)) != 0)
1119 regerror (e, rx, err->data, err->dsize);
1125 /* get here only if everything went smootly */
1128 FREE (&ptr->pattern);
1129 regfree ((regex_t *) ptr->rx);
1133 ptr->pattern = safe_strdup (tmp->data);
1137 /* $reply_regexp and $alterantes require special treatment */
1139 if (Context && Context->msgcount &&
1140 mutt_strcmp (MuttVars[idx].option, "reply_regexp") == 0)
1142 regmatch_t pmatch[1];
1145 #define CUR_ENV Context->hdrs[i]->env
1146 for (i = 0; i < Context->msgcount; i++)
1148 if (CUR_ENV && CUR_ENV->subject)
1150 CUR_ENV->real_subj = (regexec (ReplyRegexp.rx,
1151 CUR_ENV->subject, 1, pmatch, 0)) ?
1153 CUR_ENV->subject + pmatch[0].rm_eo;
1159 if(Context && Context->msgcount &&
1160 mutt_strcmp(MuttVars[idx].option, "alternates") == 0)
1164 for(i = 0; i < Context->msgcount; i++)
1165 Context->hdrs[i]->recip_valid = 0;
1169 else if (DTYPE(MuttVars[idx].type) == DT_MAGIC)
1171 if (query || *s->dptr != '=')
1173 switch (DefaultMagic)
1191 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p);
1197 /* copy the value of the string */
1198 mutt_extract_token (tmp, s, 0);
1199 if (mx_set_magic (tmp->data))
1201 snprintf (err->data, err->dsize, _("%s: invalid mailbox type"), tmp->data);
1206 else if (DTYPE(MuttVars[idx].type) == DT_NUM)
1208 short *ptr = (short *) MuttVars[idx].data;
1212 if (query || *s->dptr != '=')
1214 /* user requested the value of this variable */
1215 snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, *ptr);
1221 mutt_extract_token (tmp, s, 0);
1222 val = strtol (tmp->data, &t, 0);
1224 if (!*tmp->data || *t || (short) val != val)
1226 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1233 /* these ones need a sanity check */
1234 if (mutt_strcmp (MuttVars[idx].option, "history") == 0)
1238 mutt_init_history ();
1240 else if (mutt_strcmp (MuttVars[idx].option, "pager_index_lines") == 0)
1246 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
1250 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1252 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
1253 vals [ quadoption (MuttVars[idx].data) ]);
1257 if (*s->dptr == '=')
1260 mutt_extract_token (tmp, s, 0);
1261 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1262 set_quadoption (MuttVars[idx].data, M_YES);
1263 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1264 set_quadoption (MuttVars[idx].data, M_NO);
1265 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1266 set_quadoption (MuttVars[idx].data, M_ASKYES);
1267 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1268 set_quadoption (MuttVars[idx].data, M_ASKNO);
1271 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1279 toggle_quadoption (MuttVars[idx].data);
1281 set_quadoption (MuttVars[idx].data, M_NO);
1283 set_quadoption (MuttVars[idx].data, M_YES);
1286 else if (DTYPE (MuttVars[idx].type) == DT_SORT)
1288 const struct mapping_t *map = NULL;
1290 switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
1293 map = SortAliasMethods;
1295 case DT_SORT_BROWSER:
1296 map = SortBrowserMethods;
1299 if ((WithCrypto & APPLICATION_PGP))
1300 map = SortKeyMethods;
1303 map = SortAuxMethods;
1312 snprintf (err->data, err->dsize, _("%s: Unknown type."), MuttVars[idx].option);
1317 if (query || *s->dptr != '=')
1319 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
1321 snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
1322 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
1323 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
1328 mutt_extract_token (tmp, s , 0);
1330 if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) == -1)
1338 snprintf (err->data, err->dsize, _("%s: unknown type"), MuttVars[idx].option);
1343 if (MuttVars[idx].flags & R_INDEX)
1344 set_option (OPTFORCEREDRAWINDEX);
1345 if (MuttVars[idx].flags & R_PAGER)
1346 set_option (OPTFORCEREDRAWPAGER);
1347 if (MuttVars[idx].flags & R_RESORT_SUB)
1348 set_option (OPTSORTSUBTHREADS);
1349 if (MuttVars[idx].flags & R_RESORT)
1350 set_option (OPTNEEDRESORT);
1351 if (MuttVars[idx].flags & R_RESORT_INIT)
1352 set_option (OPTRESORTINIT);
1353 if (MuttVars[idx].flags & R_TREE)
1354 set_option (OPTREDRAWTREE);
1361 /* reads the specified initialization file. returns -1 if errors were found
1362 so that we can pause to let the user know... */
1363 static int source_rc (const char *rcfile, BUFFER *err)
1366 int line = 0, rc = 0;
1368 char *linebuf = NULL;
1373 if (stat (rcfile, &s) < 0)
1375 snprintf (err->data, err->dsize, _("%s: stat: %s"), rcfile, strerror (errno));
1378 if (!S_ISREG (s.st_mode))
1380 snprintf (err->data, err->dsize, _("%s: not a regular file"), rcfile);
1384 if ((f = mutt_open_read (rcfile, &pid)) == NULL)
1386 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1390 memset (&token, 0, sizeof (token));
1391 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL)
1393 if (mutt_parse_rc_line (linebuf, &token, err) == -1)
1395 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1396 if (--rc < -MAXERRS)
1407 mutt_wait_filter (pid);
1410 /* the muttrc source keyword */
1411 snprintf (err->data, err->dsize, rc >= -MAXERRS ? _("source: errors in %s")
1412 : _("source: reading aborted due too many errors in %s"), rcfile);
1420 static int parse_source (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
1422 char path[_POSIX_PATH_MAX];
1427 if (mutt_extract_token (tmp, s, 0) != 0)
1429 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1433 strfcpy (path, tmp->data, sizeof (path));
1434 mutt_expand_path (path, sizeof (path));
1436 rc += source_rc (path, err);
1438 while (MoreArgs (s));
1440 return ((rc < 0) ? -1 : 0);
1443 /* line command to execute
1445 token scratch buffer to be used by parser. caller should free
1446 token->data when finished. the reason for this variable is
1447 to avoid having to allocate and deallocate a lot of memory
1448 if we are parsing many lines. the caller can pass in the
1449 memory to use, which avoids having to create new space for
1450 every call to this function.
1452 err where to write error messages */
1453 int mutt_parse_rc_line (/* const */ char *line, BUFFER *token, BUFFER *err)
1458 memset (&expn, 0, sizeof (expn));
1459 expn.data = expn.dptr = line;
1460 expn.dsize = mutt_strlen (line);
1467 if (*expn.dptr == '#')
1468 break; /* rest of line is a comment */
1469 if (*expn.dptr == ';')
1474 mutt_extract_token (token, &expn, 0);
1475 for (i = 0; Commands[i].name; i++)
1477 if (!mutt_strcmp (token->data, Commands[i].name))
1479 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1484 if (!Commands[i].name)
1486 snprintf (err->data, err->dsize, _("%s: unknown command"), NONULL (token->data));
1498 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1499 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1500 /* initial string that starts completion. No telling how much crap
1501 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1502 char User_typed [LONG_STRING] = {0};
1504 int Num_matched = 0; /* Number of matches for completion */
1505 char Completed [STRING] = {0}; /* completed string (command or variable) */
1506 char *Matches[MAX(NUMVARS,NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1508 /* helper function for completion. Changes the dest buffer if
1509 necessary/possible to aid completion.
1510 dest == completion result gets here.
1511 src == candidate for completion.
1512 try == user entered data for completion.
1513 len == length of dest buffer.
1515 static void candidate (char *dest, char *try, char *src, int len)
1519 if (strstr (src, try) == src)
1521 Matches[Num_matched++] = src;
1523 strfcpy (dest, src, len);
1526 for (l = 0; src[l] && src[l] == dest[l]; l++);
1532 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1536 int spaces; /* keep track of the number of leading spaces on the line */
1539 spaces = buffer - pt;
1541 pt = buffer + pos - spaces;
1542 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1545 if (pt == buffer) /* complete cmd */
1547 /* first TAB. Collect all the matches */
1551 strfcpy (User_typed, pt, sizeof (User_typed));
1552 memset (Matches, 0, sizeof (Matches));
1553 memset (Completed, 0, sizeof (Completed));
1554 for (num = 0; Commands[num].name; num++)
1555 candidate (Completed, User_typed, Commands[num].name, sizeof (Completed));
1556 Matches[Num_matched++] = User_typed;
1558 /* All matches are stored. Longest non-ambiguous string is ""
1559 * i.e. dont change 'buffer'. Fake successful return this time */
1560 if (User_typed[0] == 0)
1564 if (Completed[0] == 0 && User_typed[0])
1567 /* Num_matched will _always_ be atleast 1 since the initial
1568 * user-typed string is always stored */
1569 if (numtabs == 1 && Num_matched == 2)
1570 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
1571 else if (numtabs > 1 && Num_matched > 2)
1572 /* cycle thru all the matches */
1573 snprintf(Completed, sizeof(Completed), "%s",
1574 Matches[(numtabs - 2) % Num_matched]);
1576 /* return the completed command */
1577 strncpy (buffer, Completed, len - spaces);
1579 else if (!mutt_strncmp (buffer, "set", 3)
1580 || !mutt_strncmp (buffer, "unset", 5)
1581 || !mutt_strncmp (buffer, "reset", 5)
1582 || !mutt_strncmp (buffer, "toggle", 6))
1583 { /* complete variables */
1584 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1587 /* loop through all the possible prefixes (no, inv, ...) */
1588 if (!mutt_strncmp (buffer, "set", 3))
1590 for (num = 0; prefixes[num]; num++)
1592 if (!mutt_strncmp (pt, prefixes[num], mutt_strlen (prefixes[num])))
1594 pt += mutt_strlen (prefixes[num]);
1600 /* first TAB. Collect all the matches */
1604 strfcpy (User_typed, pt, sizeof (User_typed));
1605 memset (Matches, 0, sizeof (Matches));
1606 memset (Completed, 0, sizeof (Completed));
1607 for (num = 0; MuttVars[num].option; num++)
1608 candidate (Completed, User_typed, MuttVars[num].option, sizeof (Completed));
1609 Matches[Num_matched++] = User_typed;
1611 /* All matches are stored. Longest non-ambiguous string is ""
1612 * i.e. dont change 'buffer'. Fake successful return this time */
1613 if (User_typed[0] == 0)
1617 if (Completed[0] == 0 && User_typed[0])
1620 /* Num_matched will _always_ be atleast 1 since the initial
1621 * user-typed string is always stored */
1622 if (numtabs == 1 && Num_matched == 2)
1623 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
1624 else if (numtabs > 1 && Num_matched > 2)
1625 /* cycle thru all the matches */
1626 snprintf(Completed, sizeof(Completed), "%s",
1627 Matches[(numtabs - 2) % Num_matched]);
1629 strncpy (pt, Completed, buffer + len - pt - spaces);
1631 else if (!mutt_strncmp (buffer, "exec", 4))
1633 struct binding_t *menu = km_get_table (CurrentMenu);
1635 if (!menu && CurrentMenu != MENU_PAGER)
1639 /* first TAB. Collect all the matches */
1643 strfcpy (User_typed, pt, sizeof (User_typed));
1644 memset (Matches, 0, sizeof (Matches));
1645 memset (Completed, 0, sizeof (Completed));
1646 for (num = 0; menu[num].name; num++)
1647 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1648 /* try the generic menu */
1649 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER)
1652 for (num = 0; menu[num].name; num++)
1653 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1655 Matches[Num_matched++] = User_typed;
1657 /* All matches are stored. Longest non-ambiguous string is ""
1658 * i.e. dont change 'buffer'. Fake successful return this time */
1659 if (User_typed[0] == 0)
1663 if (Completed[0] == 0 && User_typed[0])
1666 /* Num_matched will _always_ be atleast 1 since the initial
1667 * user-typed string is always stored */
1668 if (numtabs == 1 && Num_matched == 2)
1669 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
1670 else if (numtabs > 1 && Num_matched > 2)
1671 /* cycle thru all the matches */
1672 snprintf(Completed, sizeof(Completed), "%s",
1673 Matches[(numtabs - 2) % Num_matched]);
1675 strncpy (pt, Completed, buffer + len - pt - spaces);
1683 int mutt_var_value_complete (char *buffer, size_t len, int pos)
1685 char var[STRING], *pt = buffer;
1692 spaces = buffer - pt;
1694 pt = buffer + pos - spaces;
1695 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1697 pt++; /* move past the space */
1698 if (*pt == '=') /* abort if no var before the '=' */
1701 if (mutt_strncmp (buffer, "set", 3) == 0)
1704 strfcpy (var, pt, sizeof (var));
1705 /* ignore the trailing '=' when comparing */
1706 var[mutt_strlen (var) - 1] = 0;
1707 if ((idx = mutt_option_index (var)) == -1)
1708 return 0; /* no such variable. */
1711 char tmp [LONG_STRING], tmp2[LONG_STRING];
1713 size_t dlen = buffer + len - pt - spaces;
1714 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1718 if ((DTYPE(MuttVars[idx].type) == DT_STR) ||
1719 (DTYPE(MuttVars[idx].type) == DT_PATH) ||
1720 (DTYPE(MuttVars[idx].type) == DT_RX))
1722 strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data)), sizeof (tmp));
1723 if (DTYPE (MuttVars[idx].type) == DT_PATH)
1724 mutt_pretty_mailbox (tmp);
1726 else if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1728 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) MuttVars[idx].data), 0);
1730 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
1731 strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp));
1732 else if (DTYPE (MuttVars[idx].type) == DT_NUM)
1733 snprintf (tmp, sizeof (tmp), "%d", (*((short *) MuttVars[idx].data)));
1734 else if (DTYPE (MuttVars[idx].type) == DT_SORT)
1736 const struct mapping_t *map;
1739 switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
1742 map = SortAliasMethods;
1744 case DT_SORT_BROWSER:
1745 map = SortBrowserMethods;
1748 if ((WithCrypto & APPLICATION_PGP))
1749 map = SortKeyMethods;
1757 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
1758 snprintf (tmp, sizeof (tmp), "%s%s%s",
1759 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
1760 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
1763 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
1764 strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no", sizeof (tmp));
1768 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;)
1770 if (*s == '\\' || *s == '"')
1776 strfcpy (tmp, pt, sizeof (tmp));
1777 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
1785 /* Implement the -Q command line flag */
1786 int mutt_query_variables (LIST *queries)
1790 char errbuff[STRING];
1791 char command[STRING];
1795 memset (&err, 0, sizeof (err));
1796 memset (&token, 0, sizeof (token));
1799 err.dsize = sizeof (errbuff);
1801 for (p = queries; p; p = p->next)
1803 snprintf (command, sizeof (command), "set ?%s\n", p->data);
1804 if (mutt_parse_rc_line (command, &token, &err) == -1)
1806 fprintf (stderr, "%s\n", err.data);
1810 printf ("%s\n", err.data);
1817 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
1821 for (i=0; map[i].name; i++)
1822 if (map[i].value == val)
1823 return (map[i].name);
1827 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
1831 for (i = 0; map[i].name; i++)
1832 if (ascii_strcasecmp (map[i].name, name) == 0)
1833 return (map[i].value);
1838 static void start_debug (void)
1842 char buf[_POSIX_PATH_MAX];
1843 char buf2[_POSIX_PATH_MAX];
1845 /* rotate the old debug logs */
1846 for (i=3; i>=0; i--)
1848 snprintf (buf, sizeof(buf), "%s/.muttdebug%d", NONULL(Homedir), i);
1849 snprintf (buf2, sizeof(buf2), "%s/.muttdebug%d", NONULL(Homedir), i+1);
1852 if ((debugfile = safe_fopen(buf, "w")) != NULL)
1855 setbuf (debugfile, NULL); /* don't buffer the debugging output! */
1856 fprintf (debugfile, "Mutt %s started at %s.\nDebugging at level %d.\n\n",
1857 MUTT_VERSION, asctime (localtime (&t)), debuglevel);
1862 static int mutt_execute_commands (LIST *p)
1865 char errstr[SHORT_STRING];
1867 memset (&err, 0, sizeof (err));
1869 err.dsize = sizeof (errstr);
1870 memset (&token, 0, sizeof (token));
1871 for (; p; p = p->next)
1873 if (mutt_parse_rc_line (p->data, &token, &err) != 0)
1875 fprintf (stderr, _("Error in command line: %s\n"), err.data);
1884 void mutt_init (int skip_sys_rc, LIST *commands)
1887 struct utsname utsname;
1888 char *p, buffer[STRING], error[STRING];
1889 int i, default_rc = 0, need_pause = 0;
1892 memset (&err, 0, sizeof (err));
1894 err.dsize = sizeof (error);
1897 * XXX - use something even more difficult to predict?
1899 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
1900 "\033]9;%ld\a", (long) time (NULL));
1902 /* on one of the systems I use, getcwd() does not return the same prefix
1903 as is listed in the passwd file */
1904 if ((p = getenv ("HOME")))
1905 Homedir = safe_strdup (p);
1907 /* Get some information about the user */
1908 if ((pw = getpwuid (getuid ())))
1912 Username = safe_strdup (pw->pw_name);
1914 Homedir = safe_strdup (pw->pw_dir);
1916 Realname = safe_strdup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
1917 Shell = safe_strdup (pw->pw_shell);
1924 fputs (_("unable to determine home directory"), stderr);
1927 if ((p = getenv ("USER")))
1928 Username = safe_strdup (p);
1932 fputs (_("unable to determine username"), stderr);
1935 Shell = safe_strdup ((p = getenv ("SHELL")) ? p : "/bin/sh");
1939 /* Start up debugging mode if requested */
1944 /* And about the host... */
1946 /* some systems report the FQDN instead of just the hostname */
1947 if ((p = strchr (utsname.nodename, '.')))
1949 Hostname = mutt_substrdup (utsname.nodename, p);
1951 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
1954 Hostname = safe_strdup (utsname.nodename);
1957 #define DOMAIN buffer
1958 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
1959 Fqdn = safe_strdup ("@");
1964 Fqdn = safe_malloc (mutt_strlen (DOMAIN) + mutt_strlen (Hostname) + 2);
1965 sprintf (Fqdn, "%s.%s", NONULL(Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
1968 Fqdn = safe_strdup(NONULL(Hostname));
1975 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r")))
1978 fgets (buffer, sizeof (buffer), f);
1982 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r') && (*i != '\n')) i++;
1984 NewsServer = safe_strdup (p);
1988 if ((p = getenv ("NNTPSERVER")))
1989 NewsServer = safe_strdup (p);
1992 if ((p = getenv ("MAIL")))
1993 Spoolfile = safe_strdup (p);
1994 else if ((p = getenv ("MAILDIR")))
1995 Spoolfile = safe_strdup (p);
1999 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2001 mutt_concat_path (buffer, MAILPATH, NONULL(Username), sizeof (buffer));
2003 Spoolfile = safe_strdup (buffer);
2006 if ((p = getenv ("MAILCAPS")))
2007 MailcapPath = safe_strdup (p);
2010 /* Default search path from RFC1524 */
2011 MailcapPath = safe_strdup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2014 Tempdir = safe_strdup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2016 p = getenv ("VISUAL");
2019 p = getenv ("EDITOR");
2023 Editor = safe_strdup (p);
2024 Visual = safe_strdup (p);
2026 if ((p = getenv ("REPLYTO")) != NULL)
2030 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2032 memset (&buf, 0, sizeof (buf));
2033 buf.data = buf.dptr = buffer;
2034 buf.dsize = mutt_strlen (buffer);
2036 memset (&token, 0, sizeof (token));
2037 parse_my_hdr (&token, &buf, 0, &err);
2041 if ((p = getenv ("EMAIL")) != NULL)
2042 From = rfc822_parse_adrlist (NULL, p);
2044 mutt_set_langinfo_charset ();
2045 mutt_set_charset (Charset);
2048 /* Set standard defaults */
2049 for (i = 0; MuttVars[i].option; i++)
2051 mutt_set_default (&MuttVars[i]);
2052 mutt_restore_default (&MuttVars[i]);
2055 CurrentMenu = MENU_MAIN;
2058 #ifndef LOCALES_HACK
2059 /* Do we have a locale definition? */
2060 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2061 ((p = getenv ("LANG")) != NULL && p[0]) ||
2062 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2063 set_option (OPTLOCALES);
2067 /* Unset suspend by default if we're the session leader */
2068 if (getsid(0) == getpid())
2069 unset_option (OPTSUSPEND);
2072 mutt_init_history ();
2081 * When changing the code which looks for a configuration file,
2082 * please also change the corresponding code in muttbug.sh.in.
2092 snprintf (buffer, sizeof(buffer), "%s/.muttrc-%s", NONULL(Homedir), MUTT_VERSION);
2093 if (access(buffer, F_OK) == -1)
2094 snprintf (buffer, sizeof(buffer), "%s/.muttrc", NONULL(Homedir));
2095 if (access(buffer, F_OK) == -1)
2096 snprintf (buffer, sizeof (buffer), "%s/.mutt/muttrc-%s", NONULL(Homedir), MUTT_VERSION);
2097 if (access(buffer, F_OK) == -1)
2098 snprintf (buffer, sizeof (buffer), "%s/.mutt/muttrc", NONULL(Homedir));
2101 Muttrc = safe_strdup (buffer);
2105 strfcpy (buffer, Muttrc, sizeof (buffer));
2107 mutt_expand_path (buffer, sizeof (buffer));
2108 Muttrc = safe_strdup (buffer);
2111 AliasFile = safe_strdup (NONULL(Muttrc));
2113 /* Process the global rc file if it exists and the user hasn't explicity
2114 requested not to via "-n". */
2117 snprintf (buffer, sizeof(buffer), "%s/Muttrc-%s", SYSCONFDIR, MUTT_VERSION);
2118 if (access (buffer, F_OK) == -1)
2119 snprintf (buffer, sizeof(buffer), "%s/Muttrc", SYSCONFDIR);
2120 if (access (buffer, F_OK) == -1)
2121 snprintf (buffer, sizeof (buffer), "%s/Muttrc-%s", PKGDATADIR, MUTT_VERSION);
2122 if (access (buffer, F_OK) == -1)
2123 snprintf (buffer, sizeof (buffer), "%s/Muttrc", PKGDATADIR);
2124 if (access (buffer, F_OK) != -1)
2126 if (source_rc (buffer, &err) != 0)
2128 fputs (err.data, stderr);
2129 fputc ('\n', stderr);
2135 /* Read the user's initialization file. */
2136 if (access (Muttrc, F_OK) != -1)
2138 if (!option (OPTNOCURSES))
2140 if (source_rc (Muttrc, &err) != 0)
2142 fputs (err.data, stderr);
2143 fputc ('\n', stderr);
2147 else if (!default_rc)
2149 /* file specified by -F does not exist */
2150 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2151 mutt_endwin (buffer);
2155 if (mutt_execute_commands (commands) != 0)
2158 if (need_pause && !option (OPTNOCURSES))
2160 if (mutt_any_key_to_continue (NULL) == -1)
2165 set_option (OPTWEED); /* turn weeding on by default */
2169 int mutt_get_hook_type (const char *name)
2171 struct command_t *c;
2173 for (c = Commands ; c->name ; c++)
2174 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)