+static int CurRCLine = 0;
+
+/* prototypes for checking for special vars */
+static int check_dsn_return (const char* option, unsigned long val,
+ char* errbuf, ssize_t errlen);
+static int check_dsn_notify (const char* option, unsigned long val,
+ char* errbuf, ssize_t errlen);
+static int check_history (const char* option, unsigned long val,
+ char* errbuf, ssize_t errlen);
+/* this checks that numbers are >= 0 */
+static int check_num (const char* option, unsigned long val,
+ char* errbuf, ssize_t errlen);
+
+/* use this to check only */
+static int check_special (const char* option, unsigned long val,
+ char* errbuf, ssize_t errlen);
+
+/* variable <-> sanity check function mappings
+ * when changing these, make sure the proper _from_string handler
+ * does this checking!
+ */
+static struct {
+ const char* name;
+ int (*check) (const char* option, unsigned long val,
+ char* errbuf, ssize_t errlen);
+} SpecialVars[] = {
+ { "dsn_notify", check_dsn_notify },
+ { "dsn_return", check_dsn_return },
+#if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
+ { "smtp_use_tls", mutt_libesmtp_check_usetls },
+#endif
+ { "history", check_history },
+ { "pager_index_lines", check_num },
+ /* last */
+ { NULL, NULL }
+};
+
+/* protos for config type handles: convert value to string */
+static void bool_to_string (char* dst, ssize_t dstlen, struct option_t* option);
+static void num_to_string (char* dst, ssize_t dstlen, struct option_t* option);
+static void str_to_string (char* dst, ssize_t dstlen, struct option_t* option);
+static void quad_to_string (char* dst, ssize_t dstlen, struct option_t* option);
+static void sort_to_string (char* dst, ssize_t dstlen, struct option_t* option);
+static void rx_to_string (char* dst, ssize_t dstlen, struct option_t* option);
+static void magic_to_string (char* dst, ssize_t dstlen, struct option_t* option);
+static void addr_to_string (char* dst, ssize_t dstlen, struct option_t* option);
+static void user_to_string (char* dst, ssize_t dstlen, struct option_t* option);
+static void sys_to_string (char* dst, ssize_t dstlen, struct option_t* option);
+
+/* protos for config type handles: convert to value from string */
+static int bool_from_string (struct option_t* dst, const char* val,
+ char* errbuf, ssize_t errlen);
+static int num_from_string (struct option_t* dst, const char* val,
+ char* errbuf, ssize_t errlen);
+static int str_from_string (struct option_t* dst, const char* val,
+ char* errbuf, ssize_t errlen);
+static int path_from_string (struct option_t* dst, const char* val,
+ char* errbuf, ssize_t errlen);
+static int quad_from_string (struct option_t* dst, const char* val,
+ char* errbuf, ssize_t errlen);
+static int sort_from_string (struct option_t* dst, const char* val,
+ char* errbuf, ssize_t errlen);
+static int rx_from_string (struct option_t* dst, const char* val,
+ char* errbuf, ssize_t errlen);
+static int magic_from_string (struct option_t* dst, const char* val,
+ char* errbuf, ssize_t errlen);
+static int addr_from_string (struct option_t* dst, const char* val,
+ char* errbuf, ssize_t errlen);
+static int user_from_string (struct option_t* dst, const char* val,
+ char* errbuf, ssize_t errlen);
+
+static struct {
+ unsigned short type;
+ void (*opt_to_string) (char* dst, ssize_t dstlen, struct option_t* option);
+ int (*opt_from_string) (struct option_t* dst, const char* val,
+ char* errbuf, ssize_t errlen);
+} FuncTable[] = {
+ { 0, NULL, NULL }, /* there's no DT_ type with 0 */
+ { DT_BOOL, bool_to_string, bool_from_string },
+ { DT_NUM, num_to_string, num_from_string },
+ { DT_STR, str_to_string, str_from_string },
+ { DT_PATH, str_to_string, path_from_string },
+ { DT_QUAD, quad_to_string, quad_from_string },
+ { DT_SORT, sort_to_string, sort_from_string },
+ { DT_RX, rx_to_string, rx_from_string },
+ { DT_MAGIC, magic_to_string, magic_from_string },
+ /* synonyms should be resolved already so we don't need this
+ * but must define it as DT_ is used for indexing */
+ { DT_SYN, NULL, NULL },
+ { DT_ADDR, addr_to_string, addr_from_string },
+ { DT_USER, user_to_string, user_from_string },
+ { DT_SYS, sys_to_string, NULL },
+};
+
+static void bool_to_string (char* dst, ssize_t dstlen,
+ struct option_t* option) {
+ snprintf (dst, dstlen, "%s=%s", option->option,
+ option (option->data) ? "yes" : "no");
+}
+
+static int bool_from_string (struct option_t* dst, const char* val,
+ char* errbuf __attribute__ ((unused)),
+ ssize_t errlen __attribute__ ((unused))) {
+ int flag = -1;
+
+ if (!dst)
+ return (0);
+ if (ascii_strncasecmp (val, "yes", 3) == 0)
+ flag = 1;
+ else if (ascii_strncasecmp (val, "no", 2) == 0)
+ flag = 0;
+
+ if (flag < 0)
+ return (0);
+ if (flag)
+ set_option (dst->data);
+ else
+ unset_option (dst->data);
+ return (1);
+}
+
+static void num_to_string (char* dst, ssize_t dstlen,
+ struct option_t* option) {
+ /* XXX puke */
+ const char* fmt = (m_strcmp(option->option, "umask") == 0) ?
+ "%s=%04o" : "%s=%d";
+ snprintf (dst, dstlen, fmt, option->option,
+ *((short*) option->data));
+}
+
+static int num_from_string (struct option_t* dst, const char* val,
+ char* errbuf, ssize_t errlen) {
+ int num = 0, old = 0;
+ char* t = NULL;
+
+ if (!dst)
+ return (0);
+
+ num = strtol (val, &t, 0);
+
+ if (!*val || *t || (short) num != num) {
+ if (errbuf) {
+ snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
+ val, dst->option);
+ }
+ return (0);
+ }
+
+ /* just temporarily accept new val so that check_special for
+ * $history already has it when doing history's init() */
+ old = *((short*) dst->data);
+ *((short*) dst->data) = (short) num;
+
+ if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
+ *((short*) dst->data) = old;
+ return (0);
+ }
+
+ return (1);
+}
+
+static void str_to_string (char* dst, ssize_t dstlen,
+ struct option_t* option) {
+ snprintf (dst, dstlen, "%s=\"%s\"", option->option,
+ NONULL (*((char**) option->data)));
+}
+
+static void user_to_string (char* dst, ssize_t dstlen,
+ struct option_t* option) {
+ snprintf (dst, dstlen, "%s=\"%s\"", option->option,
+ NONULL (((char*) option->data)));
+}
+
+static void sys_to_string (char* dst, ssize_t dstlen,
+ struct option_t* option) {
+ char *val = NULL, *t = NULL;
+ int clean = 0;
+
+ /* get some $madmutt_ values dynamically */
+ if (m_strcmp("madmutt_pwd", option->option) == 0) {
+ val = p_new(char, _POSIX_PATH_MAX);
+ val = getcwd (val, _POSIX_PATH_MAX-1);
+ clean = 1;
+ } else if (m_strcmp("madmutt_folder_path", option->option) == 0 &&
+ CurrentFolder && *CurrentFolder) {
+ val = CurrentFolder;
+ } else if (m_strcmp("madmutt_folder_name", option->option) == 0 &&
+ CurrentFolder && *CurrentFolder) {
+
+ ssize_t Maildirlength = m_strlen(Maildir);
+
+ /*
+ * if name starts with $folder, just strip it to keep hierarchy
+ * $folder=imap://host, path=imap://host/inbox/b -> inbox/b
+ */
+ if (Maildirlength > 0 && m_strncmp(CurrentFolder, Maildir,
+ Maildirlength) == 0 &&
+ m_strlen(CurrentFolder) > Maildirlength) {
+ val = CurrentFolder + Maildirlength;
+ if (Maildir[Maildirlength]!='/')
+ val += 1;
+ /* if not $folder, just use everything after last / */
+ } else if ((t = strrchr (CurrentFolder, '/')) != NULL)
+ val = t+1;
+ /* default: use as-is */
+ else
+ val = (char *) CurrentFolder;
+
+ } else
+ val = (char *) option->init;
+
+ snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (val));
+ if (clean)
+ p_delete(&val);
+}
+
+static int path_from_string (struct option_t* dst, const char* val,
+ char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
+ char path[_POSIX_PATH_MAX];
+
+ if (!dst)
+ return (0);
+
+ if (!val || !*val) {
+ p_delete((char**) dst->data);
+ return (1);
+ }