summaryrefslogtreecommitdiffstats
path: root/init.c
diff options
context:
space:
mode:
authorThomas Roessler <roessler@does-not-exist.org>1998-06-08 09:16:03 +0000
committerThomas Roessler <roessler@does-not-exist.org>1998-06-08 09:16:03 +0000
commit1a5381e07e97fe482c2b3a7c75f99938f0b105d4 (patch)
treeb4fa4088bbbf5fc9217ee6f87ab60034175e6899 /init.c
Initial revision
Diffstat (limited to 'init.c')
-rw-r--r--init.c1437
1 files changed, 1437 insertions, 0 deletions
diff --git a/init.c b/init.c
new file mode 100644
index 00000000..ddcb531e
--- /dev/null
+++ b/init.c
@@ -0,0 +1,1437 @@
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_curses.h"
+#include "mutt_regex.h"
+
+
+
+#ifdef _PGPPATH
+#include "pgp.h"
+#endif
+
+
+
+#include "mx.h"
+#include "init.h"
+#include "mailbox.h"
+
+#include <pwd.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/utsname.h>
+#include <errno.h>
+#include <sys/wait.h>
+
+#define toggle_quadoption(opt) QuadOptions ^= (1 << (2 * opt))
+
+void set_quadoption (int opt, int flag)
+{
+ QuadOptions &= ~(0x3 << (2 * opt)); /* first clear the bits */
+ QuadOptions |= (flag & 0x3) << (2 * opt); /* now set them */
+}
+
+int quadoption (int opt)
+{
+ return ((QuadOptions >> (opt * 2)) & 0x3);
+}
+
+int query_quadoption (int opt, const char *prompt)
+{
+ int v = quadoption (opt);
+
+ switch (v)
+ {
+ case M_YES:
+ case M_NO:
+ return (v);
+
+ default:
+ v = mutt_yesorno (prompt, (v == M_ASKYES));
+ CLEARLINE (LINES - 1);
+ return (v);
+ }
+
+ /* not reached */
+}
+
+/* given the variable ``s'', return the index into the rc_vars array which
+ matches, or -1 if the variable is not found. */
+int mutt_option_index (char *s)
+{
+ int i;
+
+ for (i = 0; MuttVars[i].option; i++)
+ if (strcmp (s, MuttVars[i].option) == 0)
+ return (MuttVars[i].type == DT_SYN ? mutt_option_index ((char *) MuttVars[i].data) : i);
+ return (-1);
+}
+
+static void add_char (BUFFER *buf, char ch)
+{
+ size_t offset;
+
+ if (buf->dptr >= buf->data + buf->dsize)
+ {
+ offset = buf->dptr - buf->data;
+ buf->dsize += 4;
+ safe_realloc ((void **) &buf->data, buf->dsize);
+ buf->dptr = buf->data + offset;
+ }
+ *buf->dptr++ = ch;
+}
+
+static void add_str (BUFFER *buf, const char *s)
+{
+ size_t slen = strlen (s);
+ size_t offset;
+
+ if (buf->dptr + slen > buf->data + buf->dsize)
+ {
+ offset = buf->dptr - buf->data;
+ buf->dsize += slen;
+ safe_realloc ((void **) &buf->data, buf->dsize);
+ buf->dptr = buf->data + offset;
+ }
+ memcpy (buf->dptr, s, slen);
+ buf->dptr += slen;
+}
+
+int mutt_extract_token (BUFFER *dest, BUFFER *tok, int flags)
+{
+ char ch;
+ char qc = 0; /* quote char */
+ char *pc;
+
+ /* reset the destination pointer to the beginning of the buffer */
+ dest->dptr = dest->data;
+
+ SKIPWS (tok->dptr);
+ while ((ch = *tok->dptr))
+ {
+ if (!qc)
+ {
+ if ((ISSPACE (ch) && !(flags & M_TOKEN_SPACE)) ||
+ (ch == '#' && !(flags & M_TOKEN_COMMENT)) ||
+ (ch == '=' && (flags & M_TOKEN_EQUAL)) ||
+ (ch == ';' && !(flags & M_TOKEN_SEMICOLON)) ||
+ ((flags & M_TOKEN_PATTERN) && strchr ("~!|", ch)))
+ break;
+ }
+
+ tok->dptr++;
+
+ if (ch == qc)
+ qc = 0; /* end of quote */
+ else if (!qc && (ch == '\'' || ch == '"') && !(flags & M_TOKEN_QUOTE))
+ qc = ch;
+ else if (ch == '\\' && qc != '\'')
+ {
+ switch (ch = *tok->dptr++)
+ {
+ case 'c':
+ case 'C':
+ add_char (dest, (toupper (*tok->dptr) - '@') & 0x7f);
+ tok->dptr++;
+ break;
+ case 'r':
+ add_char (dest, '\r');
+ break;
+ case 'n':
+ add_char (dest, '\n');
+ break;
+ case 't':
+ add_char (dest, '\t');
+ break;
+ case 'f':
+ add_char (dest, '\f');
+ break;
+ case 'e':
+ add_char (dest, '\033');
+ break;
+ default:
+ if (isdigit (ch) &&
+ isdigit (*tok->dptr) &&
+ isdigit (*(tok->dptr + 1)))
+ {
+
+ add_char (dest, (ch << 6) + (*tok->dptr << 3) + *(tok->dptr + 1) - 3504);
+ tok->dptr += 2;
+ }
+ else
+ add_char (dest, ch);
+ }
+ }
+ else if (ch == '^' && (flags & M_TOKEN_CONDENSE))
+ {
+ ch = *tok->dptr++;
+ if (ch == '^')
+ add_char (dest, ch);
+ else if (ch == '[')
+ add_char (dest, '\033');
+ else if (isalpha (ch))
+ add_char (dest, toupper (ch) - '@');
+ else
+ {
+ add_char (dest, '^');
+ add_char (dest, ch);
+ }
+ }
+ else if (ch == '`' && (!qc || qc == '"'))
+ {
+ FILE *fp;
+ pid_t pid;
+ char *cmd, *ptr;
+ size_t expnlen;
+ BUFFER expn;
+ int line = 0;
+
+ pc = tok->dptr;
+ do {
+ if ((pc = strpbrk (pc, "\\`")))
+ {
+ /* skip any quoted chars */
+ if (*pc == '\\')
+ pc += 2;
+ }
+ } while (pc && *pc != '`');
+ if (!pc)
+ {
+ dprint (1, (debugfile, "mutt_get_token: mismatched backtics\n"));
+ return (-1);
+ }
+ cmd = mutt_substrdup (tok->dptr, pc);
+ if ((pid = mutt_create_filter (cmd, NULL, &fp, NULL)) < 0)
+ {
+ dprint (1, (debugfile, "mutt_get_token: unable to fork command: %s", cmd));
+ return (-1);
+ }
+ FREE (&cmd);
+
+ tok->dptr = pc + 1;
+
+ /* read line */
+ memset (&expn, 0, sizeof (expn));
+ expn.data = mutt_read_line (NULL, &expn.dsize, fp, &line);
+ fclose (fp);
+ mutt_wait_filter (pid);
+
+ /* if we got output, make a new string consiting of the shell ouptput
+ plus whatever else was left on the original line */
+ if (expn.data)
+ {
+ expnlen = strlen (expn.data);
+ tok->dsize = expnlen + strlen (tok->dptr) + 1;
+ ptr = safe_malloc (tok->dsize);
+ memcpy (ptr, expn.data, expnlen);
+ strcpy (ptr + expnlen, tok->dptr);
+ if (tok->destroy)
+ FREE (&tok->data);
+ tok->data = ptr;
+ tok->dptr = ptr;
+ tok->destroy = 1; /* mark that the caller should destroy this data */
+ ptr = NULL;
+ FREE (&expn.data);
+ }
+ }
+ else if (ch == '$' && (!qc || qc == '"') && (*tok->dptr == '{' || isalpha (*tok->dptr)))
+ {
+ char *env, *var;
+
+ if (*tok->dptr == '{')
+ {
+ tok->dptr++;
+ if ((pc = strchr (tok->dptr, '}')))
+ {
+ var = mutt_substrdup (tok->dptr, pc);
+ tok->dptr = pc + 1;
+ }
+ }
+ else
+ {
+ for (pc = tok->dptr; isalpha (*pc) || *pc == '_'; pc++)
+ ;
+ var = mutt_substrdup (tok->dptr, pc);
+ tok->dptr = pc;
+ }
+ if ((env = getenv (var)))
+ add_str (dest, env);
+ FREE (&var);
+ }
+ else
+ add_char (dest, ch);
+ }
+ add_char (dest, 0); /* terminate the string */
+ SKIPWS (tok->dptr);
+ return 0;
+}
+
+void mutt_add_to_list (LIST **list, BUFFER *inp)
+{
+ LIST *t, *last = NULL;
+ BUFFER buf;
+
+ memset (&buf, 0, sizeof (buf));
+ do
+ {
+ mutt_extract_token (&buf, inp, 0);
+
+ /* check to make sure the item is not already on this list */
+ for (last = *list; last; last = last->next)
+ {
+ if (strcasecmp (buf.data, last->data) == 0)
+ {
+ /* already on the list, so just ignore it */
+ last = NULL;
+ break;
+ }
+ if (!last->next)
+ break;
+ }
+
+ if (!*list || last)
+ {
+ t = (LIST *) safe_calloc (1, sizeof (LIST));
+ t->data = buf.data;
+ memset (&buf, 0, sizeof (buf));
+ if (last)
+ {
+ last->next = t;
+ last = last->next;
+ }
+ else
+ *list = last = t;
+ }
+ }
+ while (MoreArgs (inp));
+ FREE (&buf.data);
+}
+
+static void remove_from_list (LIST **l, BUFFER *inp)
+{
+ LIST *p, *last = NULL;
+ BUFFER buf;
+
+ memset (&buf, 0, sizeof (buf));
+ do
+ {
+ mutt_extract_token (&buf, inp, 0);
+
+ if (strcmp ("*", buf.data) == 0)
+ mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
+ else
+ {
+ p = *l;
+ last = NULL;
+ while (p)
+ {
+ if (strcasecmp (buf.data, p->data) == 0)
+ {
+ safe_free ((void **) &p->data);
+ if (last)
+ last->next = p->next;
+ else
+ (*l) = p->next;
+ safe_free ((void **) &p);
+ }
+ else
+ {
+ last = p;
+ p = p->next;
+ }
+ }
+ }
+ }
+ while (MoreArgs (inp));
+ FREE (&buf.data);
+}
+
+static int parse_unignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ mutt_add_to_list (&UnIgnore, s);
+ remove_from_list (&Ignore, s);
+ return 0;
+}
+
+static int parse_ignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ mutt_add_to_list (&Ignore, s);
+ remove_from_list (&UnIgnore, s);
+ return 0;
+}
+
+static int parse_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ mutt_add_to_list ((LIST **) data, s);
+ return 0;
+}
+
+static int parse_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ remove_from_list ((LIST **) data, s);
+ return 0;
+}
+
+static int parse_unalias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ ALIAS *tmp, *last = NULL;
+
+ do
+ {
+ mutt_extract_token (buf, s, 0);
+
+ tmp = Aliases;
+ for (tmp = Aliases; tmp; tmp = tmp->next)
+ {
+ if (strcasecmp (buf->data, tmp->name) == 0)
+ {
+ if (last)
+ last->next = tmp->next;
+ else
+ Aliases = tmp->next;
+ tmp->next = NULL;
+ mutt_free_alias (&tmp);
+ break;
+ }
+ last = tmp;
+ }
+ }
+ while (MoreArgs (s));
+ return 0;
+}
+
+static int parse_alias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ ALIAS *tmp = Aliases;
+ ALIAS *last = NULL;
+ char *p;
+ size_t len;
+
+ if ((p = strpbrk (s->dptr, " \t")) == NULL)
+ {
+ strfcpy (err->data, "alias: no address", err->dsize);
+ return (-1);
+ }
+
+ len = p - s->dptr;
+
+ /* check to see if an alias with this name already exists */
+ for (; tmp; tmp = tmp->next)
+ {
+ if (!strncasecmp (tmp->name, s->dptr, len) && *(tmp->name + len) == 0)
+ break;
+ last = tmp;
+ }
+
+ if (!tmp)
+ {
+ /* create a new alias */
+ tmp = (ALIAS *) safe_calloc (1, sizeof (ALIAS));
+ tmp->name = safe_malloc (len + 1);
+ memcpy (tmp->name, s->dptr, len);
+ tmp->name[len] = 0;
+ }
+ else
+ {
+ /* override the previous value */
+ rfc822_free_address (&tmp->addr);
+ }
+ s->dptr = p;
+
+ mutt_extract_token (buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE);
+ tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
+ if (last)
+ last->next = tmp;
+ else
+ Aliases = tmp;
+ return 0;
+}
+
+static int
+parse_unmy_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ LIST *last = NULL;
+ LIST *tmp = UserHeader;
+ LIST *ptr;
+ size_t l;
+
+ do
+ {
+ mutt_extract_token (buf, s, 0);
+ if (strcmp ("*", buf->data) == 0)
+ mutt_free_list (&UserHeader);
+ else
+ {
+ tmp = UserHeader;
+ last = NULL;
+
+ l = strlen (buf->data);
+ if (buf->data[l - 1] == ':')
+ l--;
+
+ while (tmp)
+ {
+ if (strncasecmp (buf->data, tmp->data, l) == 0 && tmp->data[l] == ':')
+ {
+ ptr = tmp;
+ if (last)
+ last->next = tmp->next;
+ else
+ UserHeader = tmp->next;
+ tmp = tmp->next;
+ ptr->next = NULL;
+ mutt_free_list (&ptr);
+ }
+ else
+ {
+ last = tmp;
+ tmp = tmp->next;
+ }
+ }
+ }
+ }
+ while (MoreArgs (s));
+ return 0;
+}
+
+static int parse_my_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ LIST *tmp;
+ size_t keylen;
+ char *p;
+
+ mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
+ if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':')
+ {
+ strfcpy (err->data, "invalid header field", err->dsize);
+ return (-1);
+ }
+ keylen = p - buf->data + 1;
+ p++;
+ SKIPWS (p);
+ if (!*p)
+ {
+ snprintf (err->data, err->dsize, "ignoring empty header field: %s", buf->data);
+ return (-1);
+ }
+
+ if (UserHeader)
+ {
+ for (tmp = UserHeader; ; tmp = tmp->next)
+ {
+ /* see if there is already a field by this name */
+ if (strncasecmp (buf->data, tmp->data, keylen) == 0)
+ {
+ /* replace the old value */
+ safe_free ((void **) &tmp->data);
+ tmp->data = buf->data;
+ memset (buf, 0, sizeof (BUFFER));
+ return 0;
+ }
+ if (!tmp->next)
+ break;
+ }
+ tmp->next = mutt_new_list ();
+ tmp = tmp->next;
+ }
+ else
+ {
+ tmp = mutt_new_list ();
+ UserHeader = tmp;
+ }
+ tmp->data = buf->data;
+ memset (buf, 0, sizeof (BUFFER));
+ return 0;
+}
+
+static int
+parse_sort (short *val, const char *s, const struct mapping_t *map, BUFFER *err)
+{
+ int i, flags = 0;
+
+ if (strncmp ("reverse-", s, 8) == 0)
+ {
+ s += 8;
+ flags = SORT_REVERSE;
+ }
+
+ if (strncmp ("last-", s, 5) == 0)
+ {
+ s += 5;
+ flags |= SORT_LAST;
+ }
+
+ if ((i = mutt_getvaluebyname (s, map)) == -1)
+ {
+ snprintf (err->data, err->dsize, "%s: unknown sorting method", s);
+ return (-1);
+ }
+
+ *val = i | flags;
+
+ return 0;
+}
+
+static void mutt_restore_default (struct option_t *p)
+{
+ switch (p->type & DT_MASK)
+ {
+ case DT_STR:
+ if (p->init)
+ {
+ FREE (p->data);
+ *((char **) p->data) = safe_strdup ((char *) p->init);
+ }
+ break;
+ case DT_PATH:
+ if (p->init)
+ {
+ char path[_POSIX_PATH_MAX];
+
+ FREE (p->data);
+ strfcpy (path, (char *) p->init, sizeof (path));
+ mutt_expand_path (path, sizeof (path));
+ *((char **) p->data) = safe_strdup (path);
+ }
+ break;
+ case DT_BOOL:
+ if (p->init)
+ set_option (p->data);
+ else
+ unset_option (p->data);
+ break;
+ case DT_QUAD:
+ set_quadoption (p->data, p->init);
+ break;
+ case DT_NUM:
+ case DT_SORT:
+ case DT_MAGIC:
+ *((short *) p->data) = p->init;
+ break;
+ case DT_RX:
+ {
+ REGEXP *pp = (REGEXP *) p->data;
+ FREE (&pp->pattern);
+ if (p->init)
+ {
+ if (pp->rx)
+ regfree (pp->rx);
+ else
+ pp->rx = safe_calloc (1, sizeof (regex_t));
+ pp->pattern = safe_strdup ((char *) p->init);
+ if (REGCOMP (pp->rx, pp->pattern, mutt_which_case (pp->pattern)) != 0)
+ {
+ fprintf (stderr, "mutt_restore_default: error in regexp: %s\n",
+ pp->pattern);
+ }
+ }
+ }
+ break;
+ }
+ if (p->flags & R_INDEX)
+ set_option (OPTFORCEREDRAWINDEX);
+ if (p->flags & R_PAGER)
+ set_option (OPTFORCEREDRAWPAGER);
+ if (p->flags & R_RESORT_SUB)
+ set_option (OPTSORTSUBTHREADS);
+ if (p->flags & R_RESORT)
+ set_option (OPTNEEDRESORT);
+}
+
+static int parse_set (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ int idx, query, unset, inv, reset, r = 0;
+ char *p, scratch[_POSIX_PATH_MAX];
+
+ while (MoreArgs (s))
+ {
+ /* reset state variables */
+ query = 0;
+ unset = data & M_SET_UNSET;
+ inv = data & M_SET_INV;
+ reset = data & M_SET_RESET;
+
+ if (*s->dptr == '?')
+ {
+ query = 1;
+ s->dptr++;
+ }
+ else if (strncmp ("no", s->dptr, 2) == 0)
+ {
+ s->dptr += 2;
+ unset = !unset;
+ }
+ else if (strncmp ("inv", s->dptr, 3) == 0)
+ {
+ s->dptr += 3;
+ inv = !inv;
+ }
+ else if (*s->dptr == '&')
+ {
+ reset = 1;
+ s->dptr++;
+ }
+
+ /* get the variable name */
+ mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
+
+ if ((idx = mutt_option_index (tmp->data)) == -1 &&
+ !(reset && !strcmp ("all", tmp->data)))
+ {
+ snprintf (err->data, err->dsize, "%s: unknown variable", tmp->data);
+ return (-1);
+ }
+ SKIPWS (s->dptr);
+
+ if (reset)
+ {
+ if (query || unset || inv)
+ {
+ snprintf (err->data, err->dsize, "prefix is illegal with reset");
+ return (-1);
+ }
+
+ if (s && *s->dptr == '=')
+ {
+ snprintf (err->data, err->dsize, "value is illegal with reset");
+ return (-1);
+ }
+
+ if (!strcmp ("all", tmp->data))
+ {
+ for (idx = 0; MuttVars[idx].option; idx++)
+ mutt_restore_default (&MuttVars[idx]);
+ return 0;
+ }
+ else
+ mutt_restore_default (&MuttVars[idx]);
+ }
+ else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
+ {
+ if (s && *s->dptr == '=')
+ {
+ snprintf (err->data, err->dsize, "%s is a boolean var!", tmp->data);
+ return (-1);
+ }
+
+ if (query)
+ {
+ snprintf (err->data, err->dsize, "%s is %sset", tmp->data,
+ option (MuttVars[idx].data) ? "" : "un");
+ return 0;
+ }
+
+ if (unset)
+ unset_option (MuttVars[idx].data);
+ else if (inv)
+ toggle_option (MuttVars[idx].data);
+ else
+ set_option (MuttVars[idx].data);
+ }
+ else if (DTYPE (MuttVars[idx].type) == DT_STR ||
+ DTYPE (MuttVars[idx].type) == DT_PATH)
+ {
+ if (query || *s->dptr != '=')
+ {
+ /* user requested the value of this variable */
+ snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
+ NONULL (*((char **) MuttVars[idx].data)));
+ break;
+ }
+
+ s->dptr++;
+
+ /* copy the value of the string */
+ FREE (MuttVars[idx].data);
+ mutt_extract_token (tmp, s, 0);
+ if (MuttVars[idx].type == DT_PATH)
+ {
+ strfcpy (scratch, tmp->data, sizeof (scratch));
+ mutt_expand_path (scratch, sizeof (scratch));
+ *((char **) MuttVars[idx].data) = safe_strdup (scratch);
+ }
+ else
+ {
+ *((char **) MuttVars[idx].data) = safe_strdup (tmp->data);
+ }
+ }
+ else if (DTYPE(MuttVars[idx].type) == DT_RX)
+ {
+ REGEXP *ptr = (REGEXP *) MuttVars[idx].data;
+ regex_t *rx;
+ int e, flags = 0;
+
+ if (query || *s->dptr != '=')
+ {
+ /* user requested the value of this variable */
+ snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
+ NONULL (ptr->pattern));
+ break;
+ }
+
+ s->dptr++;
+
+ /* copy the value of the string */
+ mutt_extract_token (tmp, s, 0);
+
+ if (!ptr->pattern || strcmp (ptr->pattern, tmp->data) != 0)
+ {
+ /* $alternates is case-insensitive,
+ $mask is case-sensitive */
+ if (strcmp (MuttVars[idx].option, "alternates") == 0)
+ flags |= REG_ICASE;
+ else if (strcmp (MuttVars[idx].option, "mask") != 0)
+ flags |= mutt_which_case (tmp->data);
+
+ rx = (regex_t *) safe_malloc (sizeof (regex_t));
+ if ((e = REGCOMP (rx, tmp->data, flags)) != 0)
+ {
+ regerror (e, rx, err->data, err->dsize);
+ regfree (rx);
+ FREE (&rx);
+ break;
+ }
+
+ /* get here only if everything went smootly */
+ if (ptr->pattern)
+ {
+ FREE (&ptr->pattern);
+ regfree ((regex_t *) ptr->rx);
+ FREE (&ptr->rx);
+ }
+
+ ptr->pattern = safe_strdup (tmp->data);
+ ptr->rx = rx;
+
+ /* $reply_regexp requires special treatment */
+ if (Context && Context->msgcount &&
+ strcmp (MuttVars[idx].option, "reply_regexp") == 0)
+ {
+ regmatch_t pmatch[1];
+ int i;
+
+#define CUR_ENV Context->hdrs[i]->env
+ for (i = 0; i < Context->msgcount; i++)
+ {
+ if (CUR_ENV && CUR_ENV->subject)
+ {
+ CUR_ENV->real_subj = (regexec (ReplyRegexp.rx,
+ CUR_ENV->subject, 1, pmatch, 0)) ?
+ CUR_ENV->subject :
+ CUR_ENV->subject + pmatch[0].rm_eo;
+ }
+ }
+#undef CUR_ENV
+ }
+ }
+ }
+ else if (DTYPE(MuttVars[idx].type) == DT_MAGIC)
+ {
+ if (query || *s->dptr != '=')
+ {
+ switch (DefaultMagic)
+ {
+ case M_MBOX:
+ p = "mbox";
+ break;
+ case M_MMDF:
+ p = "MMDF";
+ break;
+ case M_MH:
+ p = "MH";
+ break;
+ case M_MAILDIR:
+ p = "Maildir";
+ break;
+ default:
+ p = "unknown";
+ break;
+ }
+ snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p);
+ break;
+ }
+
+ s->dptr++;
+
+ /* copy the value of the string */
+ mutt_extract_token (tmp, s, 0);
+ if (mx_set_magic (tmp->data))
+ {
+ snprintf (err->data, err->dsize, "%s: invalid mailbox type", tmp->data);
+ r = -1;
+ break;
+ }
+ }
+ else if (DTYPE(MuttVars[idx].type) == DT_NUM)
+ {
+ short *ptr = (short *) MuttVars[idx].data;
+
+ if (query || *s->dptr != '=')
+ {
+ /* user requested the value of this variable */
+ snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, *ptr);
+ break;
+ }
+
+ s->dptr++;
+
+ mutt_extract_token (tmp, s, 0);
+ *ptr = (short) atoi (tmp->data);
+
+ /* these ones need a sanity check */
+ if (strcmp (MuttVars[idx].option, "history") == 0)
+ {
+ if (*ptr < 0)
+ *ptr = 0;
+ mutt_init_history ();
+ }
+ else if (strcmp (MuttVars[idx].option, "pager_index_lines") == 0)
+ {
+ if (*ptr < 0)
+ *ptr = 0;
+ }
+ }
+ else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
+ {
+ if (query)
+ {
+ char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
+
+ snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
+ vals [ quadoption (MuttVars[idx].data) ]);
+ break;
+ }
+
+ if (*s->dptr == '=')
+ {
+ s->dptr++;
+ mutt_extract_token (tmp, s, 0);
+ if (strcasecmp ("yes", tmp->data) == 0)
+ set_quadoption (MuttVars[idx].data, M_YES);
+ else if (strcasecmp ("no", tmp->data) == 0)
+ set_quadoption (MuttVars[idx].data, M_NO);
+ else if (strcasecmp ("ask-yes", tmp->data) == 0)
+ set_quadoption (MuttVars[idx].data, M_ASKYES);
+ else if (strcasecmp ("ask-no", tmp->data) == 0)
+ set_quadoption (MuttVars[idx].data, M_ASKNO);
+ else
+ {
+ snprintf (err->data, err->dsize, "%s: invalid value.", tmp->data);
+ r = -1;
+ break;
+ }
+ }
+ else
+ {
+ if (inv)
+ toggle_quadoption (MuttVars[idx].data);
+ else if (unset)
+ set_quadoption (MuttVars[idx].data, M_NO);
+ else
+ set_quadoption (MuttVars[idx].data, M_YES);
+ }
+ }
+ else if (DTYPE (MuttVars[idx].type) == DT_SORT)
+ {
+ const struct mapping_t *map;
+
+ switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
+ {
+ case DT_SORT_ALIAS:
+ map = SortAliasMethods;
+ break;
+ case DT_SORT_BROWSER:
+ map = SortBrowserMethods;
+ break;
+ default:
+ map = SortMethods;
+ break;
+ }
+
+ if (query || *s->dptr != '=')
+ {
+ p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
+
+ snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
+ (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
+ (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
+ p);
+ return 0;
+ }
+ s->dptr++;
+ mutt_extract_token (tmp, s , 0);
+
+ if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) == -1)
+ break;
+ }
+ else
+ {
+ snprintf (err->data, err->dsize, "%s: unknown type", MuttVars[idx].option);
+ r = -1;
+ break;
+ }
+
+ if (MuttVars[idx].flags & R_INDEX)
+ set_option (OPTFORCEREDRAWINDEX);
+ if (MuttVars[idx].flags & R_PAGER)
+ set_option (OPTFORCEREDRAWPAGER);
+ if (MuttVars[idx].flags & R_RESORT_SUB)
+ set_option (OPTSORTSUBTHREADS);
+ if (MuttVars[idx].flags & R_RESORT)
+ set_option (OPTNEEDRESORT);
+ }
+ return (r);
+}
+
+void mutt_nocurses_error (const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ vfprintf (stderr, fmt, ap);
+ va_end (ap);
+ fputc ('\n', stderr);
+}
+
+/* reads the specified initialization file. returns -1 if errors were found
+ so that we can pause to let the user know... */
+static int source_rc (const char *rcfile, BUFFER *err)
+{
+ FILE *f;
+ int line = 0, rc = 0;
+ BUFFER token;
+ char *linebuf = NULL;
+ size_t buflen;
+ pid_t pid;
+
+ if ((f = mutt_open_read (rcfile, &pid)) == NULL)
+ {
+ snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
+ return (-1);
+ }
+
+ memset (&token, 0, sizeof (token));
+ while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL)
+ {
+ if (mutt_parse_rc_line (linebuf, &token, err) == -1)
+ {
+ mutt_error ("Error in %s, line %d: %s", rcfile, line, err->data);
+ rc = -1;
+ }
+ }
+ FREE (&token.data);
+ safe_free ((void **) &linebuf);
+ fclose (f);
+ if (pid != -1)
+ mutt_wait_filter (pid);
+ if (rc)
+ snprintf (err->data, err->dsize, "source: errors in %s", rcfile);
+ return (rc);
+}
+
+static int parse_source (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ char path[_POSIX_PATH_MAX];
+
+ if (mutt_extract_token (tmp, s, 0) != 0)
+ {
+ snprintf (err->data, err->dsize, "source: error at %s", s->dptr);
+ return (-1);
+ }
+ if (MoreArgs (s))
+ {
+ strfcpy (err->data, "source: too many arguments", err->dsize);
+ return (-1);
+ }
+ strfcpy (path, tmp->data, sizeof (path));
+ mutt_expand_path (path, sizeof (path));
+ return (source_rc (path, err));
+}
+
+/* line command to execute
+
+ token scratch buffer to be used by parser. caller should free
+ token->data when finished. the reason for this variable is
+ to avoid having to allocate and deallocate a lot of memory
+ if we are parsing many lines. the caller can pass in the
+ memory to use, which avoids having to create new space for
+ every call to this function.
+
+ err where to write error messages */
+int mutt_parse_rc_line (/* const */ char *line, BUFFER *token, BUFFER *err)
+{
+ int i, r = -1;
+ BUFFER expn;
+
+ memset (&expn, 0, sizeof (expn));
+ expn.data = expn.dptr = line;
+ expn.dsize = strlen (line);
+
+ *err->data = 0;
+
+ SKIPWS (expn.dptr);
+ while (*expn.dptr)
+ {
+ if (*expn.dptr == '#')
+ break; /* rest of line is a comment */
+ if (*expn.dptr == ';')
+ {
+ expn.dptr++;
+ continue;
+ }
+ mutt_extract_token (token, &expn, 0);
+ for (i = 0; Commands[i].name; i++)
+ {
+ if (!strcmp (token->data, Commands[i].name))
+ {
+ if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
+ goto finish;
+ break;
+ }
+ }
+ if (!Commands[i].name)
+ {
+ snprintf (err->data, err->dsize, "%s: unknown command", token->data);
+ goto finish;
+ }
+ }
+ r = 0;
+finish:
+ if (expn.destroy)
+ FREE (&expn.data);
+ return (r);
+}
+
+char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
+{
+ int i;
+
+ for (i=0; map[i].name; i++)
+ if (map[i].value == val)
+ return (map[i].name);
+ return NULL;
+}
+
+int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
+{
+ int i;
+
+ for (i = 0; map[i].name; i++)
+ if (strcasecmp (map[i].name, name) == 0)
+ return (map[i].value);
+ return (-1);
+}
+
+#ifdef DEBUG
+static void start_debug (void)
+{
+ time_t t;
+ int i;
+ char buf[_POSIX_PATH_MAX];
+ char buf2[_POSIX_PATH_MAX];
+
+ /* rotate the old debug logs */
+ for (i=3; i>=0; i--)
+ {
+ snprintf (buf, sizeof(buf), "%s/.muttdebug%d", Homedir, i);
+ snprintf (buf2, sizeof(buf2), "%s/.muttdebug%d", Homedir, i+1);
+ rename (buf, buf2);
+ }
+ if ((debugfile = safe_fopen(buf, "w")) != NULL)
+ {
+ t = time (0);
+ fprintf (debugfile, "Mutt %s started at %s.\nDebugging at level %d.\n\n",
+ VERSION, asctime (localtime (&t)), debuglevel);
+ }
+}
+#endif
+
+static int mutt_execute_commands (LIST *p)
+{
+ BUFFER err, token;
+ char errstr[SHORT_STRING];
+
+ memset (&err, 0, sizeof (err));
+ err.data = errstr;
+ err.dsize = sizeof (errstr);
+ memset (&token, 0, sizeof (token));
+ for (; p; p = p->next)
+ {
+ if (mutt_parse_rc_line (p->data, &token, &err) != 0)
+ {
+ fprintf (stderr, "Error in command line: %s\n", err.data);
+ FREE (&token.data);
+ return (-1);
+ }
+ }
+ FREE (&token.data);
+ return 0;
+}
+
+void mutt_init (int skip_sys_rc, LIST *commands)
+{
+ struct passwd *pw;
+ struct utsname utsname;
+ char *p, buffer[STRING], error[STRING];
+ int i, default_rc = 0, need_pause = 0;
+ BUFFER err;
+
+ memset (&err, 0, sizeof (err));
+ err.data = error;
+ err.dsize = sizeof (error);
+
+ /* on one of the systems I use, getcwd() does not return the same prefix
+ as is listed in the passwd file */
+ if ((p = getenv ("HOME")))
+ Homedir = safe_strdup (p);
+
+ /* Get some information about the user */
+ if ((pw = getpwuid (getuid ())))
+ {
+ Username = safe_strdup (pw->pw_name);
+ if (!Homedir)
+ Homedir = safe_strdup (pw->pw_dir);
+ if ((p = strchr (pw->pw_gecos, ',')))
+ Realname = mutt_substrdup (pw->pw_gecos, p);
+ else
+ Realname = safe_strdup (pw->pw_gecos);
+ Shell = safe_strdup (pw->pw_shell);
+ }
+ else
+ {
+ if (!Homedir)
+ {
+ mutt_endwin (NULL);
+ fputs ("unable to determine home directory", stderr);
+ exit (1);
+ }
+ if ((p = getenv ("USER")))
+ Username = safe_strdup (p);
+ else
+ {
+ mutt_endwin (NULL);
+ fputs ("unable to determine user", stderr);
+ exit (1);
+ }
+ Shell = safe_strdup ((p = getenv ("SHELL")) ? p : "/bin/sh");
+ }
+
+#ifdef DEBUG
+ /* Start up debugging mode if requested */
+ if (debuglevel > 0)
+ start_debug ();
+#endif
+
+ /* And about the host... */
+ uname (&utsname);
+ /* some systems report the FQDN instead of just the hostname */
+ if ((p = strchr (utsname.nodename, '.')))
+ {
+ Hostname = mutt_substrdup (utsname.nodename, p);
+ p++;
+ strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
+ }
+ else
+ Hostname = safe_strdup (utsname.nodename);
+
+#ifndef DOMAIN
+#define DOMAIN buffer
+ if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
+ Fqdn = safe_strdup ("@");
+ else
+#endif /* DOMAIN */
+ {
+# ifdef HIDDEN_HOST
+ Fqdn = safe_strdup (DOMAIN);
+# else
+ Fqdn = safe_malloc (strlen (DOMAIN) + strlen (Hostname) + 2);
+ sprintf (Fqdn, "%s.%s", Hostname, DOMAIN);
+# endif /* HIDDEN_HOST */
+ }
+
+ if ((p = getenv ("MAIL")))
+ Spoolfile = safe_strdup (p);
+ else
+ {
+#ifdef HOMESPOOL
+