2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
5 * Parts were written/modified by:
6 * Rocco Rutte <pdmef@cs.tu-berlin.de>
8 * This file is part of mutt-ng, see http://www.muttng.org/.
9 * It's licensed under the GNU General Public License,
10 * please see the file GPL in the top level source directory.
17 #include <lib-lib/mem.h>
18 #include <lib-lib/str.h>
19 #include <lib-lib/file.h>
20 #include <lib-lib/ascii.h>
21 #include <lib-lib/macros.h>
22 #include <lib-lib/buffer.h>
23 #include <lib-lib/mapping.h>
24 #include <lib-lib/debug.h>
25 #include <lib-lib/rx.h>
27 #include <lib-sys/unix.h>
29 #include <lib-ui/curses.h>
30 #include <lib-ui/history.h>
37 #include <lib-crypt/crypt.h>
38 #include "mutt_idna.h"
40 #if defined(USE_SSL) || defined(USE_GNUTLS)
41 #include <lib-sys/mutt_ssl.h>
44 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
45 #include "mutt_libesmtp.h"
57 #include <sys/utsname.h>
64 static const struct mapping_t* get_sortmap (struct option_t* option);
65 static int parse_sort (struct option_t* dst, const char *s,
66 const struct mapping_t *map,
67 char* errbuf, size_t errlen);
69 static HASH *ConfigOptions = NULL;
71 /* for synonym warning reports: synonym found during parsing */
75 struct option_t* n; /* new */
76 struct option_t* o; /* old */
79 /* for synonym warning reports: list of synonyms found */
80 static list2_t* Synonyms;
81 /* for synonym warning reports: current rc file */
82 static const char* CurRCFile = NULL;
83 /* for synonym warning reports: current rc line */
84 static int CurRCLine = 0;
86 /* prototypes for checking for special vars */
87 static int check_dsn_return (const char* option, unsigned long val,
88 char* errbuf, size_t errlen);
89 static int check_dsn_notify (const char* option, unsigned long val,
90 char* errbuf, size_t errlen);
91 static int check_history (const char* option, unsigned long val,
92 char* errbuf, size_t errlen);
93 /* this checks that numbers are >= 0 */
94 static int check_num (const char* option, unsigned long val,
95 char* errbuf, size_t errlen);
97 static int check_debug (const char* option, unsigned long val,
98 char* errbuf, size_t errlen);
101 /* use this to check only */
102 static int check_special (const char* option, unsigned long val,
103 char* errbuf, size_t errlen);
105 /* variable <-> sanity check function mappings
106 * when changing these, make sure the proper _from_string handler
107 * does this checking!
111 int (*check) (const char* option, unsigned long val,
112 char* errbuf, size_t errlen);
114 { "dsn_notify", check_dsn_notify },
115 { "dsn_return", check_dsn_return },
116 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
117 { "smtp_use_tls", mutt_libesmtp_check_usetls },
119 { "history", check_history },
120 { "pager_index_lines", check_num },
122 { "debug_level", check_debug },
128 /* protos for config type handles: convert value to string */
129 static void bool_to_string (char* dst, size_t dstlen, struct option_t* option);
130 static void num_to_string (char* dst, size_t dstlen, struct option_t* option);
131 static void str_to_string (char* dst, size_t dstlen, struct option_t* option);
132 static void quad_to_string (char* dst, size_t dstlen, struct option_t* option);
133 static void sort_to_string (char* dst, size_t dstlen, struct option_t* option);
134 static void rx_to_string (char* dst, size_t dstlen, struct option_t* option);
135 static void magic_to_string (char* dst, size_t dstlen, struct option_t* option);
136 static void addr_to_string (char* dst, size_t dstlen, struct option_t* option);
137 static void user_to_string (char* dst, size_t dstlen, struct option_t* option);
138 static void sys_to_string (char* dst, size_t dstlen, struct option_t* option);
140 /* protos for config type handles: convert to value from string */
141 static int bool_from_string (struct option_t* dst, const char* val,
142 char* errbuf, size_t errlen);
143 static int num_from_string (struct option_t* dst, const char* val,
144 char* errbuf, size_t errlen);
145 static int str_from_string (struct option_t* dst, const char* val,
146 char* errbuf, size_t errlen);
147 static int path_from_string (struct option_t* dst, const char* val,
148 char* errbuf, size_t errlen);
149 static int quad_from_string (struct option_t* dst, const char* val,
150 char* errbuf, size_t errlen);
151 static int sort_from_string (struct option_t* dst, const char* val,
152 char* errbuf, size_t errlen);
153 static int rx_from_string (struct option_t* dst, const char* val,
154 char* errbuf, size_t errlen);
155 static int magic_from_string (struct option_t* dst, const char* val,
156 char* errbuf, size_t errlen);
157 static int addr_from_string (struct option_t* dst, const char* val,
158 char* errbuf, size_t errlen);
159 static int user_from_string (struct option_t* dst, const char* val,
160 char* errbuf, size_t errlen);
164 void (*opt_to_string) (char* dst, size_t dstlen, struct option_t* option);
165 int (*opt_from_string) (struct option_t* dst, const char* val,
166 char* errbuf, size_t errlen);
168 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
169 { DT_BOOL, bool_to_string, bool_from_string },
170 { DT_NUM, num_to_string, num_from_string },
171 { DT_STR, str_to_string, str_from_string },
172 { DT_PATH, str_to_string, path_from_string },
173 { DT_QUAD, quad_to_string, quad_from_string },
174 { DT_SORT, sort_to_string, sort_from_string },
175 { DT_RX, rx_to_string, rx_from_string },
176 { DT_MAGIC, magic_to_string, magic_from_string },
177 /* synonyms should be resolved already so we don't need this
178 * but must define it as DT_ is used for indexing */
179 { DT_SYN, NULL, NULL },
180 { DT_ADDR, addr_to_string, addr_from_string },
181 { DT_USER, user_to_string, user_from_string },
182 { DT_SYS, sys_to_string, NULL },
185 static void bool_to_string (char* dst, size_t dstlen,
186 struct option_t* option) {
187 snprintf (dst, dstlen, "%s=%s", option->option,
188 option (option->data) ? "yes" : "no");
191 static int bool_from_string (struct option_t* dst, const char* val,
192 char* errbuf __attribute__ ((unused)),
193 size_t errlen __attribute__ ((unused))) {
198 if (ascii_strncasecmp (val, "yes", 3) == 0)
200 else if (ascii_strncasecmp (val, "no", 2) == 0)
206 set_option (dst->data);
208 unset_option (dst->data);
212 static void num_to_string (char* dst, size_t dstlen,
213 struct option_t* option) {
215 const char* fmt = (m_strcmp(option->option, "umask") == 0) ?
217 snprintf (dst, dstlen, fmt, option->option,
218 *((short*) option->data));
221 static int num_from_string (struct option_t* dst, const char* val,
222 char* errbuf, size_t errlen) {
223 int num = 0, old = 0;
229 num = strtol (val, &t, 0);
231 if (!*val || *t || (short) num != num) {
233 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
239 /* just temporarily accept new val so that check_special for
240 * $history already has it when doing history's init() */
241 old = *((short*) dst->data);
242 *((short*) dst->data) = (short) num;
244 if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
245 *((short*) dst->data) = old;
252 static void str_to_string (char* dst, size_t dstlen,
253 struct option_t* option) {
254 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
255 NONULL (*((char**) option->data)));
258 static void user_to_string (char* dst, size_t dstlen,
259 struct option_t* option) {
260 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
261 NONULL (((char*) option->data)));
264 static void sys_to_string (char* dst, size_t dstlen,
265 struct option_t* option) {
266 char *val = NULL, *t = NULL;
269 /* get some $madmutt_ values dynamically */
270 if (ascii_strcmp ("madmutt_pwd", option->option) == 0) {
271 val = p_new(char, _POSIX_PATH_MAX);
272 val = getcwd (val, _POSIX_PATH_MAX-1);
274 } else if (ascii_strcmp ("madmutt_folder_path", option->option) == 0 &&
275 CurrentFolder && *CurrentFolder) {
277 } else if (ascii_strcmp ("madmutt_folder_name", option->option) == 0 &&
278 CurrentFolder && *CurrentFolder) {
280 ssize_t Maildirlength = m_strlen(Maildir);
283 * if name starts with $folder, just strip it to keep hierarchy
284 * $folder=imap://host, path=imap://host/inbox/b -> inbox/b
286 if (Maildirlength > 0 && m_strncmp(CurrentFolder, Maildir,
287 Maildirlength) == 0 &&
288 m_strlen(CurrentFolder) > Maildirlength) {
289 val = CurrentFolder + Maildirlength;
290 if (Maildir[strlen(Maildir)-1]!='/')
292 /* if not $folder, just use everything after last / */
293 } else if ((t = strrchr (CurrentFolder, '/')) != NULL)
295 /* default: use as-is */
297 val = (char *) CurrentFolder;
300 val = (char *) option->init;
302 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (val));
307 static int path_from_string (struct option_t* dst, const char* val,
308 char* errbuf __attribute__ ((unused)), size_t errlen __attribute__ ((unused))) {
309 char path[_POSIX_PATH_MAX];
315 p_delete((char**) dst->data);
320 m_strcpy(path, sizeof(path), val);
321 mutt_expand_path (path, sizeof(path));
322 m_strreplace((char **) dst->data, path);
326 static int str_from_string (struct option_t* dst, const char* val,
327 char* errbuf, size_t errlen) {
331 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
334 m_strreplace((char**) dst->data, val);
338 static int user_from_string (struct option_t* dst, const char* val,
339 char* errbuf __attribute__ ((unused)), size_t errlen __attribute__ ((unused))) {
340 /* if dst == NULL, we may get here in case the user did unset it,
341 * see parse_set() where item is free()'d before coming here; so
342 * just silently ignore it */
345 if (m_strlen((char*) dst->data) == 0)
346 dst->data = (unsigned long) m_strdup(val);
348 char* s = (char*) dst->data;
349 m_strreplace(&s, val);
351 if (m_strlen(dst->init) == 0)
352 dst->init = m_strdup((char*) dst->data);
356 static void quad_to_string (char* dst, size_t dstlen,
357 struct option_t* option) {
358 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
359 snprintf (dst, dstlen, "%s=%s", option->option,
360 vals[quadoption (option->data)]);
363 static int quad_from_string (struct option_t* dst, const char* val,
364 char* errbuf __attribute__ ((unused)), size_t errlen __attribute__ ((unused))) {
369 if (ascii_strncasecmp (val, "yes", 3) == 0)
371 else if (ascii_strncasecmp (val, "no", 2) == 0)
373 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
375 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
381 set_quadoption (dst->data, flag);
385 static void sort_to_string (char* dst, size_t dstlen,
386 struct option_t* option) {
387 const struct mapping_t *map = get_sortmap (option);
388 const char *p = NULL;
391 snprintf (dst, sizeof(dst), "%s=unknown", option->option);
395 p = mutt_getnamebyvalue(*((short *)option->data) & SORT_MASK, map);
397 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
398 (*((short *) option->data) & SORT_REVERSE) ?
400 (*((short *) option->data) & SORT_LAST) ? "last-" :
404 static int sort_from_string (struct option_t* dst, const char* val,
405 char* errbuf, size_t errlen) {
406 const struct mapping_t *map = NULL;
407 if (!(map = get_sortmap (dst))) {
409 snprintf (errbuf, errlen, _("%s: Unknown type."),
413 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
418 static void rx_to_string (char* dst, size_t dstlen,
419 struct option_t* option) {
420 rx_t* p = (rx_t*) option->data;
421 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
422 NONULL (p->pattern));
425 static int rx_from_string (struct option_t* dst, const char* val,
426 char* errbuf, size_t errlen) {
429 int flags = 0, e = 0, not = 0;
435 if (option (OPTATTACHMSG) && !m_strcmp(dst->option, "reply_regexp")) {
437 snprintf (errbuf, errlen,
438 "Operation not permitted when in attach-message mode.");
442 if (!((rx_t*) dst->data))
443 *((rx_t**) dst->data) = p_new(rx_t, 1);
445 p = (rx_t*) dst->data;
447 /* something to do? */
448 if (!val || !*val || (p->pattern && m_strcmp(p->pattern, val) == 0))
451 if (m_strcmp(dst->option, "mask") != 0)
452 flags |= mutt_which_case (val);
455 if (m_strcmp(dst->option, "mask") == 0 && *s == '!') {
460 rx = p_new(regex_t, 1);
462 if ((e = REGCOMP (rx, s, flags)) != 0) {
463 regerror (e, rx, errbuf, errlen);
474 m_strreplace(&p->pattern, val);
478 if (m_strcmp(dst->option, "reply_regexp") == 0)
479 mutt_adjust_all_subjects ();
484 static void magic_to_string (char* dst, size_t dstlen,
485 struct option_t* option) {
486 const char* s = NULL;
487 switch (option->data) {
488 case M_MBOX: s = "mbox"; break;
489 case M_MMDF: s = "MMDF"; break;
490 case M_MH: s = "MH"; break;
491 case M_MAILDIR: s = "Maildir"; break;
492 default: s = "unknown"; break;
494 snprintf (dst, dstlen, "%s=%s", option->option, s);
497 static int magic_from_string (struct option_t* dst, const char* val,
498 char* errbuf __attribute__ ((unused)), size_t errlen __attribute__ ((unused))) {
501 if (!dst || !val || !*val)
503 if (ascii_strncasecmp (val, "mbox", 4) == 0)
505 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
507 else if (ascii_strncasecmp (val, "mh", 2) == 0)
509 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
515 *((short*) dst->data) = flag;
520 static void addr_to_string (char* dst, size_t dstlen,
521 struct option_t* option) {
524 rfc822_write_address (s, sizeof(s), *((address_t**) option->data), 0);
525 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
528 static int addr_from_string (struct option_t* dst, const char* val,
529 char* errbuf __attribute__ ((unused)), size_t errlen __attribute__ ((unused))) {
532 address_delete ((address_t**) dst->data);
534 *((address_t**) dst->data) = rfc822_parse_adrlist (NULL, val);
538 int mutt_option_value (const char* val, char* dst, size_t dstlen) {
539 struct option_t* option = NULL;
540 char* tmp = NULL, *t = NULL;
543 if (!(option = hash_find (ConfigOptions, val))) {
544 debug_print (1, ("var '%s' not found\n", val));
548 tmp = p_new(char, dstlen+1);
549 FuncTable[DTYPE (option->type)].opt_to_string (tmp, dstlen, option);
551 /* as we get things of type $var=value and don't want to bloat the
552 * above "just" for expansion, we do the stripping here */
553 debug_print (1, ("orig == '%s'\n", tmp));
554 t = strchr (tmp, '=');
558 if (t[l-1] == '"' && *t == '"') {
563 memcpy (dst, t, l+1);
565 debug_print (1, ("stripped == '%s'\n", dst));
570 /* for synonym warning reports: adds synonym to end of list */
571 static void syn_add (struct option_t* n, struct option_t* o) {
572 syn_t* tmp = p_new(syn_t, 1);
573 tmp->f = m_strdup(CurRCFile);
577 list_push_back (&Synonyms, tmp);
580 /* for synonym warning reports: free single item (for list_del()) */
581 static void syn_del (void** p) {
582 p_delete(&(*(syn_t**) p)->f);
586 void toggle_quadoption (int opt)
589 int b = (opt % 4) * 2;
591 QuadOptions[n] ^= (1 << b);
594 void set_quadoption (int opt, int flag)
597 int b = (opt % 4) * 2;
599 QuadOptions[n] &= ~(0x3 << b);
600 QuadOptions[n] |= (flag & 0x3) << b;
603 int quadoption (int opt)
606 int b = (opt % 4) * 2;
608 return (QuadOptions[n] >> b) & 0x3;
611 int query_quadoption (int opt, const char *prompt)
613 int v = quadoption (opt);
621 v = mutt_yesorno (prompt, (v == M_ASKYES));
622 CLEARLINE (LINES - 1);
629 static void add_to_list (LIST ** list, const char *str)
631 LIST *t, *last = NULL;
633 /* don't add a NULL or empty string to the list */
634 if (!str || *str == '\0')
637 /* check to make sure the item is not already on this list */
638 for (last = *list; last; last = last->next) {
639 if (ascii_strcasecmp (str, last->data) == 0) {
640 /* already on the list, so just ignore it */
648 if (!*list || last) {
650 t->data = m_strdup(str);
660 static int add_to_rx_list (list2_t** list, const char *s, int flags,
669 if (!(rx = rx_compile (s, flags))) {
670 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
674 i = rx_lookup ((*list), rx->pattern);
678 list_push_back (list, rx);
682 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
683 const char *templ, BUFFER * err)
685 SPAM_LIST *t = NULL, *last = NULL;
690 if (!pat || !*pat || !templ)
693 if (!(rx = rx_compile (pat, REG_ICASE))) {
694 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
698 /* check to make sure the item is not already on this list */
699 for (last = *list; last; last = last->next) {
700 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
701 /* Already on the list. Formerly we just skipped this case, but
702 * now we're supporting removals, which means we're supporting
703 * re-adds conceptually. So we probably want this to imply a
704 * removal, then do an add. We can achieve the removal by freeing
705 * the template, and leaving t pointed at the current item.
708 p_delete(&t->template);
715 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
716 * update. Otherwise we want to make a new one to link at the list's end.
719 t = mutt_new_spam_list ();
727 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
728 t->template = m_strdup(templ);
730 /* Find highest match number in template string */
732 for (p = templ; *p;) {
737 while (*p && isdigit ((int) *p))
743 t->nmatch++; /* match 0 is always the whole expr */
748 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
750 SPAM_LIST *spam, *prev;
753 /* Being first is a special case. */
757 if (spam->rx && !m_strcmp(spam->rx->pattern, pat)) {
759 rx_delete(&spam->rx);
760 p_delete(&spam->template);
766 for (spam = prev->next; spam;) {
767 if (!m_strcmp(spam->rx->pattern, pat)) {
768 prev->next = spam->next;
769 rx_delete(&spam->rx);
770 p_delete(&spam->template);
783 static void remove_from_list (LIST ** l, const char *str)
785 LIST *p, *last = NULL;
787 if (m_strcmp("*", str) == 0)
788 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
793 if (ascii_strcasecmp (str, p->data) == 0) {
796 last->next = p->next;
809 static int remove_from_rx_list (list2_t** l, const char *str)
813 if (m_strcmp("*", str) == 0) {
814 list_del (l, (list_del_t*) rx_delete);
818 i = rx_lookup ((*l), str);
820 rx_t* r = list_pop_idx ((*l), i);
828 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
832 unsigned long res = 0;
834 struct option_t* option = NULL;
837 mutt_extract_token (tmp, s, 0);
839 /* is the item defined as a variable or a function? */
840 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL)
843 for (i = 0; !res && i < MENU_MAX; i++) {
844 struct binding_t *b = km_get_table (Menus[i].value);
849 for (j = 0; b[j].name; j++)
850 if (!ascii_strncasecmp (tmp->data, b[j].name, m_strlen(tmp->data))
851 && (m_strlen(b[j].name) == m_strlen(tmp->data))) {
857 /* check for feature_* */
858 if (!res && ascii_strncasecmp (tmp->data, "feature_", 8) == 0 &&
859 (j = m_strlen(tmp->data)) > 8) {
861 while (Features[i]) {
862 if (m_strlen(Features[i]) == j-8 &&
863 ascii_strncasecmp (Features[i], tmp->data+8, j-8) == 0) {
873 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
875 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
879 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
882 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
883 mutt_error ("Error: %s", err->data);
884 p_delete(&token.data);
887 p_delete(&token.data);
892 static int parse_unignore (BUFFER * buf, BUFFER * s,
893 unsigned long data __attribute__ ((unused)),
894 BUFFER * err __attribute__ ((unused)))
897 mutt_extract_token (buf, s, 0);
899 /* don't add "*" to the unignore list */
900 if (strcmp (buf->data, "*"))
901 add_to_list (&UnIgnore, buf->data);
903 remove_from_list (&Ignore, buf->data);
905 while (MoreArgs (s));
910 static int parse_ignore (BUFFER * buf, BUFFER * s,
911 unsigned long data __attribute__ ((unused)),
912 BUFFER * err __attribute__ ((unused)))
915 mutt_extract_token (buf, s, 0);
916 remove_from_list (&UnIgnore, buf->data);
917 add_to_list (&Ignore, buf->data);
919 while (MoreArgs (s));
924 static int parse_list (BUFFER * buf, BUFFER * s,
925 unsigned long data __attribute__ ((unused)),
926 BUFFER * err __attribute__ ((unused)))
929 mutt_extract_token (buf, s, 0);
930 add_to_list ((LIST **) data, buf->data);
932 while (MoreArgs (s));
937 static void _alternates_clean (void)
941 if (Context && Context->msgcount) {
942 for (i = 0; i < Context->msgcount; i++)
943 Context->hdrs[i]->recip_valid = 0;
947 static int parse_alternates (BUFFER * buf, BUFFER * s,
948 unsigned long data __attribute__ ((unused)),
949 BUFFER * err __attribute__ ((unused)))
951 _alternates_clean ();
953 mutt_extract_token (buf, s, 0);
954 remove_from_rx_list (&UnAlternates, buf->data);
956 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
959 while (MoreArgs (s));
964 static int parse_unalternates (BUFFER * buf, BUFFER * s,
965 unsigned long data __attribute__ ((unused)),
966 BUFFER * err __attribute__ ((unused)))
968 _alternates_clean ();
970 mutt_extract_token (buf, s, 0);
971 remove_from_rx_list (&Alternates, buf->data);
973 if (m_strcmp(buf->data, "*") &&
974 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
978 while (MoreArgs (s));
983 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
990 /* Insist on at least one parameter */
993 m_strcpy(err->data, err->dsize, _("spam: no matching pattern"));
995 m_strcpy(err->data, err->dsize, _("nospam: no matching pattern"));
999 /* Extract the first token, a regexp */
1000 mutt_extract_token (buf, s, 0);
1002 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
1003 if (data == M_SPAM) {
1004 /* If there's a second parameter, it's a template for the spam tag. */
1006 mutt_extract_token (&templ, s, 0);
1008 /* Add to the spam list. */
1009 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
1010 p_delete(&templ.data);
1013 p_delete(&templ.data);
1016 /* If not, try to remove from the nospam list. */
1018 remove_from_rx_list (&NoSpamList, buf->data);
1024 /* M_NOSPAM is for nospam commands. */
1025 else if (data == M_NOSPAM) {
1026 /* nospam only ever has one parameter. */
1028 /* "*" is a special case. */
1029 if (!m_strcmp(buf->data, "*")) {
1030 mutt_free_spam_list (&SpamList);
1031 list_del (&NoSpamList, (list_del_t*) rx_delete);
1035 /* If it's on the spam list, just remove it. */
1036 if (remove_from_spam_list (&SpamList, buf->data) != 0)
1039 /* Otherwise, add it to the nospam list. */
1040 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
1046 /* This should not happen. */
1047 m_strcpy(err->data, err->dsize, "This is no good at all.");
1051 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
1052 BUFFER * err __attribute__ ((unused)))
1055 mutt_extract_token (buf, s, 0);
1057 * Check for deletion of entire list
1059 if (m_strcmp(buf->data, "*") == 0) {
1060 mutt_free_list ((LIST **) data);
1063 remove_from_list ((LIST **) data, buf->data);
1065 while (MoreArgs (s));
1070 static int parse_lists (BUFFER * buf, BUFFER * s,
1071 unsigned long data __attribute__ ((unused)),
1075 mutt_extract_token (buf, s, 0);
1076 remove_from_rx_list (&UnMailLists, buf->data);
1078 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1081 while (MoreArgs (s));
1086 /* always wise to do what someone else did before */
1087 static void _attachments_clean (void) {
1089 if (Context && Context->msgcount) {
1090 for (i = 0; i < Context->msgcount; i++)
1091 Context->hdrs[i]->attach_valid = 0;
1095 static int parse_attach_list (BUFFER *buf, BUFFER *s, LIST **ldata,
1096 BUFFER *err __attribute__ ((unused))) {
1098 LIST *listp, *lastp;
1103 /* Find the last item in the list that data points to. */
1105 debug_print (5, ("parse_attach_list: ldata = %p, *ldata = %p\n",
1107 for (listp = *ldata; listp; listp = listp->next) {
1108 a = (ATTACH_MATCH *)listp->data;
1109 debug_print (5, ("parse_attach_list: skipping %s/%s\n", a->major, a->minor));
1114 mutt_extract_token (buf, s, 0);
1116 if (!buf->data || *buf->data == '\0')
1119 a = p_new(ATTACH_MATCH, 1);
1121 /* some cheap hacks that I expect to remove */
1122 if (!m_strcasecmp(buf->data, "any"))
1123 a->major = m_strdup("*/.*");
1124 else if (!m_strcasecmp(buf->data, "none"))
1125 a->major = m_strdup("cheap_hack/this_should_never_match");
1127 a->major = m_strdup(buf->data);
1129 if ((p = strchr(a->major, '/'))) {
1134 a->minor = "unknown";
1137 len = m_strlen(a->minor);
1138 tmpminor = p_new(char, len + 3);
1139 strcpy(&tmpminor[1], a->minor); /* __STRCPY_CHECKED__ */
1141 tmpminor[len+1] = '$';
1142 tmpminor[len+2] = '\0';
1144 a->major_int = mutt_check_mime_type(a->major);
1145 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
1147 p_delete(&tmpminor);
1149 debug_print (5, ("parse_attach_list: added %s/%s [%d]\n",
1150 a->major, a->minor, a->major_int));
1152 listp = p_new(LIST, 1);
1153 listp->data = (char *)a;
1156 lastp->next = listp;
1162 while (MoreArgs (s));
1164 _attachments_clean();
1168 static int parse_unattach_list (BUFFER *buf, BUFFER *s, LIST **ldata,
1169 BUFFER *err __attribute__ ((unused))) {
1171 LIST *lp, *lastp, *newlp;
1177 mutt_extract_token (buf, s, 0);
1179 if (!m_strcasecmp(buf->data, "any"))
1180 tmp = m_strdup("*/.*");
1181 else if (!m_strcasecmp(buf->data, "none"))
1182 tmp = m_strdup("cheap_hack/this_should_never_match");
1184 tmp = m_strdup(buf->data);
1186 if ((minor = strchr(tmp, '/'))) {
1190 minor = m_strdup("unknown");
1192 major = mutt_check_mime_type(tmp);
1194 /* We must do our own walk here because remove_from_list() will only
1195 * remove the LIST->data, not anything pointed to by the LIST->data. */
1197 for(lp = *ldata; lp; ) {
1198 a = (ATTACH_MATCH *)lp->data;
1199 debug_print(5, ("parse_unattach_list: check %s/%s [%d] : %s/%s [%d]\n",
1200 a->major, a->minor, a->major_int, tmp, minor, major));
1201 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
1202 debug_print(5, ("parse_unattach_list: removed %s/%s [%d]\n",
1203 a->major, a->minor, a->major_int));
1204 regfree(&a->minor_rx);
1205 p_delete(&a->major);
1207 /* Relink backward */
1209 lastp->next = lp->next;
1214 p_delete(&lp->data); /* same as a */
1224 while (MoreArgs (s));
1227 _attachments_clean();
1231 static int print_attach_list (LIST *lp, char op, const char *name) {
1233 printf("attachments %c%s %s/%s\n", op, name,
1234 ((ATTACH_MATCH *)lp->data)->major,
1235 ((ATTACH_MATCH *)lp->data)->minor);
1242 static int parse_attachments (BUFFER *buf, BUFFER *s,
1243 unsigned long data __attribute__ ((unused)),
1248 mutt_extract_token(buf, s, 0);
1249 if (!buf->data || *buf->data == '\0') {
1250 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
1254 category = buf->data;
1260 printf("\nCurrent attachments settings:\n\n");
1261 print_attach_list(AttachAllow, '+', "A");
1262 print_attach_list(AttachExclude, '-', "A");
1263 print_attach_list(InlineAllow, '+', "I");
1264 print_attach_list(InlineExclude, '-', "I");
1265 set_option (OPTFORCEREDRAWINDEX);
1266 set_option (OPTFORCEREDRAWPAGER);
1267 mutt_any_key_to_continue (NULL);
1271 if (op != '+' && op != '-') {
1275 if (!m_strncasecmp(category, "attachment", strlen(category))) {
1277 listp = &AttachAllow;
1279 listp = &AttachExclude;
1281 else if (!m_strncasecmp(category, "inline", strlen(category))) {
1283 listp = &InlineAllow;
1285 listp = &InlineExclude;
1287 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
1291 return parse_attach_list(buf, s, listp, err);
1294 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
1298 mutt_extract_token(buf, s, 0);
1299 if (!buf->data || *buf->data == '\0') {
1300 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
1306 if (op != '+' && op != '-') {
1310 if (!m_strncasecmp(p, "attachment", strlen(p))) {
1312 listp = &AttachAllow;
1314 listp = &AttachExclude;
1316 else if (!m_strncasecmp(p, "inline", strlen(p))) {
1318 listp = &InlineAllow;
1320 listp = &InlineExclude;
1323 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
1327 return parse_unattach_list(buf, s, listp, err);
1330 static int parse_unlists (BUFFER * buf, BUFFER * s,
1331 unsigned long data __attribute__ ((unused)),
1332 BUFFER * err __attribute__ ((unused)))
1335 mutt_extract_token (buf, s, 0);
1336 remove_from_rx_list (&SubscribedLists, buf->data);
1337 remove_from_rx_list (&MailLists, buf->data);
1339 if (m_strcmp(buf->data, "*") &&
1340 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1343 while (MoreArgs (s));
1348 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1352 mutt_extract_token (buf, s, 0);
1353 remove_from_rx_list (&UnMailLists, buf->data);
1354 remove_from_rx_list (&UnSubscribedLists, buf->data);
1356 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1358 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1361 while (MoreArgs (s));
1366 static int parse_unsubscribe (BUFFER * buf, BUFFER * s,
1367 unsigned long data __attribute__ ((unused)),
1368 BUFFER * err __attribute__ ((unused)))
1371 mutt_extract_token (buf, s, 0);
1372 remove_from_rx_list (&SubscribedLists, buf->data);
1374 if (m_strcmp(buf->data, "*") &&
1375 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1378 while (MoreArgs (s));
1383 static int parse_unalias (BUFFER * buf, BUFFER * s,
1384 unsigned long data __attribute__ ((unused)),
1385 BUFFER * err __attribute__ ((unused)))
1387 ALIAS *tmp, *last = NULL;
1390 mutt_extract_token (buf, s, 0);
1392 if (m_strcmp("*", buf->data) == 0) {
1393 if (CurrentMenu == MENU_ALIAS) {
1394 for (tmp = Aliases; tmp; tmp = tmp->next)
1396 set_option (OPTFORCEREDRAWINDEX);
1399 mutt_free_alias (&Aliases);
1403 for (tmp = Aliases; tmp; tmp = tmp->next) {
1404 if (m_strcasecmp(buf->data, tmp->name) == 0) {
1405 if (CurrentMenu == MENU_ALIAS) {
1407 set_option (OPTFORCEREDRAWINDEX);
1412 last->next = tmp->next;
1414 Aliases = tmp->next;
1416 mutt_free_alias (&tmp);
1422 while (MoreArgs (s));
1426 static int parse_alias (BUFFER * buf, BUFFER * s,
1427 unsigned long data __attribute__ ((unused)),
1430 ALIAS *tmp = Aliases;
1434 if (!MoreArgs (s)) {
1435 m_strcpy(err->data, err->dsize, _("alias: no address"));
1439 mutt_extract_token (buf, s, 0);
1441 debug_print (2, ("first token is '%s'.\n", buf->data));
1443 /* check to see if an alias with this name already exists */
1444 for (; tmp; tmp = tmp->next) {
1445 if (!m_strcasecmp(tmp->name, buf->data))
1451 /* create a new alias */
1452 tmp = p_new(ALIAS, 1);
1454 tmp->name = m_strdup(buf->data);
1455 /* give the main addressbook code a chance */
1456 if (CurrentMenu == MENU_ALIAS)
1457 set_option (OPTMENUCALLER);
1460 /* override the previous value */
1461 address_delete (&tmp->addr);
1462 if (CurrentMenu == MENU_ALIAS)
1463 set_option (OPTFORCEREDRAWINDEX);
1466 mutt_extract_token (buf, s,
1467 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1468 debug_print (2, ("second token is '%s'.\n", buf->data));
1469 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1474 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1475 snprintf (err->data, err->dsize,
1476 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1480 if (DebugLevel >= 2) {
1483 /* A group is terminated with an empty address, so check a->mailbox */
1484 for (a = tmp->addr; a && a->mailbox; a = a->next) {
1486 debug_print (2, ("%s\n", a->mailbox));
1488 debug_print (2, ("group %s\n", a->mailbox));
1496 parse_unmy_hdr (BUFFER * buf, BUFFER * s,
1497 unsigned long data __attribute__ ((unused)),
1498 BUFFER * err __attribute__ ((unused)))
1501 LIST *tmp = UserHeader;
1506 mutt_extract_token (buf, s, 0);
1507 if (m_strcmp("*", buf->data) == 0)
1508 mutt_free_list (&UserHeader);
1513 l = m_strlen(buf->data);
1514 if (buf->data[l - 1] == ':')
1518 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1519 && tmp->data[l] == ':') {
1522 last->next = tmp->next;
1524 UserHeader = tmp->next;
1527 mutt_free_list (&ptr);
1536 while (MoreArgs (s));
1540 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1547 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1548 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1549 m_strcpy(err->data, err->dsize, _("invalid header field"));
1552 keylen = p - buf->data + 1;
1555 for (tmp = UserHeader;; tmp = tmp->next) {
1556 /* see if there is already a field by this name */
1557 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1558 /* replace the old value */
1559 p_delete(&tmp->data);
1560 tmp->data = buf->data;
1567 tmp->next = mutt_new_list ();
1571 tmp = mutt_new_list ();
1574 tmp->data = buf->data;
1580 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1581 char* errbuf, size_t errlen) {
1584 if (m_strncmp("reverse-", s, 8) == 0) {
1586 flags = SORT_REVERSE;
1589 if (m_strncmp("last-", s, 5) == 0) {
1594 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1596 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1600 *((short*) dst->data) = i | flags;
1604 /* if additional data more == 1, we want to resolve synonyms */
1605 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
1607 char buf[LONG_STRING];
1608 struct option_t *ptr = p;
1610 if (DTYPE(ptr->type) == DT_SYN) {
1613 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1615 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_from_string)
1618 mutt_option_value(ptr->option, buf, sizeof(buf));
1619 if (m_strlen(ptr->init) == 0 && buf && *buf)
1620 ptr->init = m_strdup(buf);
1623 static struct option_t* add_option (const char* name, const char* init,
1624 short type, short dodup) {
1625 struct option_t* option = p_new(struct option_t, 1);
1627 debug_print (1, ("adding $%s\n", name));
1629 option->option = m_strdup(name);
1630 option->type = type;
1632 option->init = dodup ? m_strdup(init) : (char*) init;
1636 /* creates new option_t* of type DT_USER for $user_ var */
1637 static struct option_t* add_user_option (const char* name) {
1638 return (add_option (name, NULL, DT_USER, 1));
1641 /* free()'s option_t* */
1642 static void del_option (void* p) {
1643 struct option_t *ptr = (struct option_t*) p;
1644 char* s = (char*) ptr->data;
1645 debug_print (1, ("removing option '%s' from table\n", NONULL (ptr->option)));
1646 p_delete(&ptr->option);
1648 p_delete(&ptr->init);
1652 static int init_expand (char** dst, struct option_t* src) {
1658 if (DTYPE(src->type) == DT_STR ||
1659 DTYPE(src->type) == DT_PATH) {
1660 /* only expand for string as it's the only place where
1661 * we want to expand vars right now */
1662 if (src->init && *src->init) {
1665 len = m_strlen(src->init) + 2;
1666 in.data = p_new(char, len + 1);
1667 snprintf (in.data, len, "\"%s\"", src->init);
1670 mutt_extract_token (&token, &in, 0);
1671 if (token.data && *token.data)
1672 *dst = m_strdup(token.data);
1674 *dst = m_strdup("");
1676 p_delete(&token.data);
1678 *dst = m_strdup("");
1680 /* for non-string: take value as is */
1681 *dst = m_strdup(src->init);
1685 /* if additional data more == 1, we want to resolve synonyms */
1686 static void mutt_restore_default (const char* name __attribute__ ((unused)),
1687 void* p, unsigned long more) {
1688 char errbuf[STRING];
1689 struct option_t* ptr = (struct option_t*) p;
1692 if (DTYPE (ptr->type) == DT_SYN) {
1695 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1699 if (FuncTable[DTYPE (ptr->type)].opt_from_string) {
1700 init_expand (&init, ptr);
1701 if (!FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, init, errbuf,
1703 if (!option (OPTNOCURSES))
1705 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1706 "Please report this error: \"%s\"\n"),
1707 ptr->option, NONULL (init), errbuf);
1713 if (ptr->flags & R_INDEX)
1714 set_option (OPTFORCEREDRAWINDEX);
1715 if (ptr->flags & R_PAGER)
1716 set_option (OPTFORCEREDRAWPAGER);
1717 if (ptr->flags & R_RESORT_SUB)
1718 set_option (OPTSORTSUBTHREADS);
1719 if (ptr->flags & R_RESORT)
1720 set_option (OPTNEEDRESORT);
1721 if (ptr->flags & R_RESORT_INIT)
1722 set_option (OPTRESORTINIT);
1723 if (ptr->flags & R_TREE)
1724 set_option (OPTREDRAWTREE);
1727 /* check whether value for $dsn_return would be valid */
1728 static int check_dsn_return (const char* option __attribute__ ((unused)), unsigned long p,
1729 char* errbuf, size_t errlen) {
1730 char* val = (char*) p;
1731 if (val && *val && m_strncmp(val, "hdrs", 4) != 0 &&
1732 m_strncmp(val, "full", 4) != 0) {
1734 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1740 /* check whether value for $dsn_notify would be valid */
1741 static int check_dsn_notify (const char* option, unsigned long p,
1742 char* errbuf, size_t errlen) {
1743 list2_t* list = NULL;
1746 char* val = (char*) p;
1750 list = list_from_str (val, ",");
1751 if (list_empty (list))
1754 for (i = 0; i < list->length; i++)
1755 if (m_strncmp(list->data[i], "never", 5) != 0 &&
1756 m_strncmp(list->data[i], "failure", 7) != 0 &&
1757 m_strncmp(list->data[i], "delay", 5) != 0 &&
1758 m_strncmp(list->data[i], "success", 7) != 0) {
1760 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1761 (char*) list->data[i], "dsn_notify");
1765 list_del (&list, (list_del_t*)xmemfree);
1769 static int check_num (const char* option, unsigned long p,
1770 char* errbuf, size_t errlen) {
1773 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1780 static int check_debug (const char* option, unsigned long p,
1781 char* errbuf, size_t errlen) {
1782 if ((int) p <= DEBUG_MAX_LEVEL &&
1783 (int) p >= DEBUG_MIN_LEVEL)
1787 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1792 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1793 char* errbuf, size_t errlen) {
1794 if (!check_num ("history", p, errbuf, errlen))
1796 mutt_init_history ();
1800 static int check_special (const char* name, unsigned long val,
1801 char* errbuf, size_t errlen) {
1804 for (i = 0; SpecialVars[i].name; i++) {
1805 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1806 return (SpecialVars[i].check (SpecialVars[i].name,
1807 val, errbuf, errlen));
1813 static const struct mapping_t* get_sortmap (struct option_t* option) {
1814 const struct mapping_t* map = NULL;
1816 switch (option->type & DT_SUBTYPE_MASK) {
1818 map = SortAliasMethods;
1820 case DT_SORT_BROWSER:
1821 map = SortBrowserMethods;
1824 map = SortKeyMethods;
1827 map = SortAuxMethods;
1836 #define CHECK_PAGER \
1837 if ((CurrentMenu == MENU_PAGER) && \
1838 (!option || (option->flags & R_RESORT))) \
1840 snprintf (err->data, err->dsize, \
1841 _("Not available in this menu.")); \
1845 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1848 int query, unset, inv, reset, r = 0;
1849 struct option_t* option = NULL;
1851 while (MoreArgs (s)) {
1852 /* reset state variables */
1854 unset = data & M_SET_UNSET;
1855 inv = data & M_SET_INV;
1856 reset = data & M_SET_RESET;
1858 if (*s->dptr == '?') {
1862 else if (m_strncmp("no", s->dptr, 2) == 0) {
1866 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1870 else if (*s->dptr == '&') {
1875 /* get the variable name */
1876 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1878 /* resolve synonyms */
1879 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1880 DTYPE (option->type == DT_SYN)) {
1881 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1882 syn_add (newopt, option);
1886 /* see if we need to add $user_ var */
1887 if (!option && ascii_strncmp ("user_", tmp->data, 5) == 0) {
1888 /* there's no option named like this yet so only add one
1889 * if the action isn't any of: reset, unset, query */
1890 if (!(reset || unset || query || *s->dptr != '=')) {
1891 debug_print (1, ("adding user option '%s'\n", tmp->data));
1892 option = add_user_option (tmp->data);
1893 hash_insert (ConfigOptions, option->option, option, 0);
1897 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1898 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1901 s->dptr = vskipspaces(s->dptr);
1904 if (query || unset || inv) {
1905 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1909 if (s && *s->dptr == '=') {
1910 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1914 if (!m_strcmp("all", tmp->data)) {
1915 if (CurrentMenu == MENU_PAGER) {
1916 snprintf (err->data, err->dsize, _("Not available in this menu."));
1919 hash_map (ConfigOptions, mutt_restore_default, 1);
1920 set_option (OPTFORCEREDRAWINDEX);
1921 set_option (OPTFORCEREDRAWPAGER);
1922 set_option (OPTSORTSUBTHREADS);
1923 set_option (OPTNEEDRESORT);
1924 set_option (OPTRESORTINIT);
1925 set_option (OPTREDRAWTREE);
1928 else if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1929 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1934 mutt_restore_default (NULL, option, 1);
1937 else if (DTYPE (option->type) == DT_BOOL) {
1938 /* XXX this currently ignores the function table
1939 * as we don't get invert and stuff into it */
1940 if (s && *s->dptr == '=') {
1941 if (unset || inv || query) {
1942 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1947 mutt_extract_token (tmp, s, 0);
1948 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1950 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1953 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1959 bool_to_string (err->data, err->dsize, option);
1965 unset_option (option->data);
1967 toggle_option (option->data);
1969 set_option (option->data);
1971 else if (DTYPE (option->type) == DT_STR ||
1972 DTYPE (option->type) == DT_PATH ||
1973 DTYPE (option->type) == DT_ADDR ||
1974 DTYPE (option->type) == DT_MAGIC ||
1975 DTYPE (option->type) == DT_NUM ||
1976 DTYPE (option->type) == DT_SORT ||
1977 DTYPE (option->type) == DT_RX ||
1978 DTYPE (option->type) == DT_USER ||
1979 DTYPE (option->type) == DT_SYS) {
1981 /* XXX maybe we need to get unset into handlers? */
1982 if (DTYPE (option->type) == DT_STR ||
1983 DTYPE (option->type) == DT_PATH ||
1984 DTYPE (option->type) == DT_ADDR ||
1985 DTYPE (option->type) == DT_USER ||
1986 DTYPE (option->type) == DT_SYS) {
1989 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1990 snprintf (err->data, err->dsize, _("$%s is read-only"),
1994 } else if (DTYPE (option->type) == DT_ADDR)
1995 address_delete ((address_t **) option->data);
1996 else if (DTYPE (option->type) == DT_USER)
1997 /* to unset $user_ means remove */
1998 hash_delete (ConfigOptions, option->option,
1999 option, del_option);
2001 p_delete((void **)&option->data);
2006 if (query || *s->dptr != '=') {
2007 FuncTable[DTYPE (option->type)].opt_to_string
2008 (err->data, err->dsize, option);
2012 /* the $madmutt_ variables are read-only */
2013 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
2014 snprintf (err->data, err->dsize, _("$%s is read-only"),
2021 mutt_extract_token (tmp, s, 0);
2022 if (!FuncTable[DTYPE (option->type)].opt_from_string
2023 (option, tmp->data, err->data, err->dsize))
2027 else if (DTYPE (option->type) == DT_QUAD) {
2030 quad_to_string (err->data, err->dsize, option);
2034 if (*s->dptr == '=') {
2037 mutt_extract_token (tmp, s, 0);
2038 if (ascii_strcasecmp ("yes", tmp->data) == 0)
2039 set_quadoption (option->data, M_YES);
2040 else if (ascii_strcasecmp ("no", tmp->data) == 0)
2041 set_quadoption (option->data, M_NO);
2042 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
2043 set_quadoption (option->data, M_ASKYES);
2044 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
2045 set_quadoption (option->data, M_ASKNO);
2047 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
2048 tmp->data, option->option);
2055 toggle_quadoption (option->data);
2057 set_quadoption (option->data, M_NO);
2059 set_quadoption (option->data, M_YES);
2063 snprintf (err->data, err->dsize, _("%s: unknown type"),
2069 if (option->flags & R_INDEX)
2070 set_option (OPTFORCEREDRAWINDEX);
2071 if (option->flags & R_PAGER)
2072 set_option (OPTFORCEREDRAWPAGER);
2073 if (option->flags & R_RESORT_SUB)
2074 set_option (OPTSORTSUBTHREADS);
2075 if (option->flags & R_RESORT)
2076 set_option (OPTNEEDRESORT);
2077 if (option->flags & R_RESORT_INIT)
2078 set_option (OPTRESORTINIT);
2079 if (option->flags & R_TREE)
2080 set_option (OPTREDRAWTREE);
2087 /* reads the specified initialization file. returns -1 if errors were found
2088 so that we can pause to let the user know... */
2089 static int source_rc (const char *rcfile, BUFFER * err)
2092 int line = 0, rc = 0, conv = 0;
2094 char *linebuf = NULL;
2095 char *currentline = NULL;
2099 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
2101 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
2102 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
2107 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
2108 conv = ConfigCharset && (*ConfigCharset) && Charset;
2110 currentline = m_strdup(linebuf);
2113 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
2116 currentline = linebuf;
2121 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
2122 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
2123 if (--rc < -MAXERRS) {
2125 p_delete(¤tline);
2134 p_delete(¤tline);
2136 p_delete(&token.data);
2140 mutt_wait_filter (pid);
2142 /* the muttrc source keyword */
2143 snprintf (err->data, err->dsize,
2144 rc >= -MAXERRS ? _("source: errors in %s")
2145 : _("source: reading aborted due too many errors in %s"),
2154 static int parse_source (BUFFER * tmp, BUFFER * s,
2155 unsigned long data __attribute__ ((unused)),
2158 char path[_POSIX_PATH_MAX];
2162 if (mutt_extract_token (tmp, s, 0) != 0) {
2163 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
2167 m_strcpy(path, sizeof(path), tmp->data);
2168 mutt_expand_path (path, sizeof(path));
2170 rc += source_rc (path, err);
2172 while (MoreArgs (s));
2174 return ((rc < 0) ? -1 : 0);
2177 /* line command to execute
2179 token scratch buffer to be used by parser. caller should free
2180 token->data when finished. the reason for this variable is
2181 to avoid having to allocate and deallocate a lot of memory
2182 if we are parsing many lines. the caller can pass in the
2183 memory to use, which avoids having to create new space for
2184 every call to this function.
2186 err where to write error messages */
2187 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
2193 expn.data = expn.dptr = line;
2194 expn.dsize = m_strlen(line);
2198 debug_print (1, ("expand '%s'\n", line));
2200 expn.dptr = vskipspaces(expn.dptr);
2201 while (*expn.dptr) {
2202 if (*expn.dptr == '#')
2203 break; /* rest of line is a comment */
2204 if (*expn.dptr == ';') {
2208 mutt_extract_token (token, &expn, 0);
2209 for (i = 0; Commands[i].name; i++) {
2210 if (!m_strcmp(token->data, Commands[i].name)) {
2211 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
2216 if (!Commands[i].name) {
2217 snprintf (err->data, err->dsize, _("%s: unknown command"),
2218 NONULL (token->data));
2225 p_delete(&expn.data);
2230 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
2231 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
2232 /* initial string that starts completion. No telling how much crap
2233 * the user has typed so far. Allocate LONG_STRING just to be sure! */
2234 char User_typed[LONG_STRING] = { 0 };
2236 int Num_matched = 0; /* Number of matches for completion */
2237 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
2238 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
2240 /* helper function for completion. Changes the dest buffer if
2241 necessary/possible to aid completion.
2242 dest == completion result gets here.
2243 src == candidate for completion.
2244 try == user entered data for completion.
2245 len == length of dest buffer.
2247 static void candidate (char *dest, char *try, const char *src, int len)
2251 if (strstr (src, try) == src) {
2252 Matches[Num_matched++] = src;
2254 m_strcpy(dest, len, src);
2256 for (l = 0; src[l] && src[l] == dest[l]; l++);
2262 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
2266 int spaces; /* keep track of the number of leading spaces on the line */
2268 buffer = vskipspaces(buffer);
2269 spaces = buffer - pt;
2271 pt = buffer + pos - spaces;
2272 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2275 if (pt == buffer) { /* complete cmd */
2276 /* first TAB. Collect all the matches */
2279 m_strcpy(User_typed, sizeof(User_typed), pt);
2280 p_clear(Matches, countof(Matches));
2281 p_clear(Completed, countof(Completed));
2282 for (num = 0; Commands[num].name; num++)
2283 candidate (Completed, User_typed, Commands[num].name,
2285 Matches[Num_matched++] = User_typed;
2287 /* All matches are stored. Longest non-ambiguous string is ""
2288 * i.e. dont change 'buffer'. Fake successful return this time */
2289 if (User_typed[0] == 0)
2293 if (Completed[0] == 0 && User_typed[0])
2296 /* Num_matched will _always_ be atleast 1 since the initial
2297 * user-typed string is always stored */
2298 if (numtabs == 1 && Num_matched == 2)
2299 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2300 else if (numtabs > 1 && Num_matched > 2)
2301 /* cycle thru all the matches */
2302 snprintf (Completed, sizeof(Completed), "%s",
2303 Matches[(numtabs - 2) % Num_matched]);
2305 /* return the completed command */
2306 m_strcpy(buffer, len - spaces, Completed);
2308 else if (!m_strncmp(buffer, "set", 3)
2309 || !m_strncmp(buffer, "unset", 5)
2310 || !m_strncmp(buffer, "reset", 5)
2311 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
2312 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
2315 /* loop through all the possible prefixes (no, inv, ...) */
2316 if (!m_strncmp(buffer, "set", 3)) {
2317 for (num = 0; prefixes[num]; num++) {
2318 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
2319 pt += m_strlen(prefixes[num]);
2325 /* first TAB. Collect all the matches */
2328 m_strcpy(User_typed, sizeof(User_typed), pt);
2329 p_clear(Matches, countof(Matches));
2330 p_clear(Completed, countof(Completed));
2331 for (num = 0; MuttVars[num].option; num++)
2332 candidate(Completed, User_typed, MuttVars[num].option,
2334 Matches[Num_matched++] = User_typed;
2336 /* All matches are stored. Longest non-ambiguous string is ""
2337 * i.e. dont change 'buffer'. Fake successful return this time */
2338 if (User_typed[0] == 0)
2342 if (Completed[0] == 0 && User_typed[0])
2345 /* Num_matched will _always_ be atleast 1 since the initial
2346 * user-typed string is always stored */
2347 if (numtabs == 1 && Num_matched == 2)
2348 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2349 else if (numtabs > 1 && Num_matched > 2)
2350 /* cycle thru all the matches */
2351 snprintf (Completed, sizeof(Completed), "%s",
2352 Matches[(numtabs - 2) % Num_matched]);
2354 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2356 else if (!m_strncmp(buffer, "exec", 4)) {
2357 struct binding_t *menu = km_get_table (CurrentMenu);
2359 if (!menu && CurrentMenu != MENU_PAGER)
2363 /* first TAB. Collect all the matches */
2366 m_strcpy(User_typed, sizeof(User_typed), pt);
2367 p_clear(Matches, countof(Matches));
2368 p_clear(Completed, countof(Completed));
2369 for (num = 0; menu[num].name; num++)
2370 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
2371 /* try the generic menu */
2372 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2374 for (num = 0; menu[num].name; num++)
2375 candidate (Completed, User_typed, menu[num].name,
2378 Matches[Num_matched++] = User_typed;
2380 /* All matches are stored. Longest non-ambiguous string is ""
2381 * i.e. dont change 'buffer'. Fake successful return this time */
2382 if (User_typed[0] == 0)
2386 if (Completed[0] == 0 && User_typed[0])
2389 /* Num_matched will _always_ be atleast 1 since the initial
2390 * user-typed string is always stored */
2391 if (numtabs == 1 && Num_matched == 2)
2392 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2393 else if (numtabs > 1 && Num_matched > 2)
2394 /* cycle thru all the matches */
2395 snprintf (Completed, sizeof(Completed), "%s",
2396 Matches[(numtabs - 2) % Num_matched]);
2398 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2406 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2408 char var[STRING], *pt = buffer;
2410 struct option_t* option = NULL;
2415 buffer = vskipspaces(buffer);
2416 spaces = buffer - pt;
2418 pt = buffer + pos - spaces;
2419 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2421 pt++; /* move past the space */
2422 if (*pt == '=') /* abort if no var before the '=' */
2425 if (m_strncmp(buffer, "set", 3) == 0) {
2426 m_strcpy(var, sizeof(var), pt);
2427 /* ignore the trailing '=' when comparing */
2428 var[m_strlen(var) - 1] = 0;
2429 if (!(option = hash_find (ConfigOptions, var)))
2430 return 0; /* no such variable. */
2432 char tmp[LONG_STRING], tmp2[LONG_STRING];
2434 size_t dlen = buffer + len - pt - spaces;
2435 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2439 if ((DTYPE (option->type) == DT_STR) ||
2440 (DTYPE (option->type) == DT_PATH) ||
2441 (DTYPE (option->type) == DT_RX)) {
2442 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
2443 if (DTYPE (option->type) == DT_PATH)
2444 mutt_pretty_mailbox (tmp);
2446 else if (DTYPE (option->type) == DT_ADDR) {
2447 rfc822_write_address (tmp, sizeof(tmp),
2448 *((address_t **) option->data), 0);
2450 else if (DTYPE (option->type) == DT_QUAD)
2451 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
2452 else if (DTYPE (option->type) == DT_NUM)
2453 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
2454 else if (DTYPE (option->type) == DT_SORT) {
2455 const struct mapping_t *map;
2458 switch (option->type & DT_SUBTYPE_MASK) {
2460 map = SortAliasMethods;
2462 case DT_SORT_BROWSER:
2463 map = SortBrowserMethods;
2466 map = SortKeyMethods;
2472 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2473 snprintf(tmp, sizeof(tmp), "%s%s%s",
2474 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2475 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2477 else if (DTYPE (option->type) == DT_MAGIC) {
2479 switch (DefaultMagic) {
2495 m_strcpy(tmp, sizeof(tmp), p);
2497 else if (DTYPE (option->type) == DT_BOOL)
2498 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
2502 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
2503 if (*s == '\\' || *s == '"')
2509 m_strcpy(tmp, sizeof(tmp), pt);
2510 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2518 /* Implement the -Q command line flag */
2519 int mutt_query_variables (LIST * queries)
2523 char errbuff[STRING];
2524 char command[STRING];
2532 err.dsize = sizeof(errbuff);
2534 for (p = queries; p; p = p->next) {
2535 snprintf (command, sizeof(command), "set ?%s\n", p->data);
2536 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2537 fprintf (stderr, "%s\n", err.data);
2538 p_delete(&token.data);
2541 printf ("%s\n", err.data);
2544 p_delete(&token.data);
2548 static int mutt_execute_commands (LIST * p)
2551 char errstr[SHORT_STRING];
2555 err.dsize = sizeof(errstr);
2557 for (; p; p = p->next) {
2558 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2559 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2560 p_delete(&token.data);
2564 p_delete(&token.data);
2568 void mutt_init (int skip_sys_rc, LIST * commands)
2571 struct utsname utsname;
2573 char buffer[STRING], error[STRING];
2574 int default_rc = 0, need_pause = 0;
2580 err.dsize = sizeof(error);
2582 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2583 * to have some room for $user_ vars */
2584 ConfigOptions = hash_create (sizeof(MuttVars) * 3);
2585 for (i = 0; MuttVars[i].option; i++) {
2586 if (DTYPE (MuttVars[i].type) != DT_SYS)
2587 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2589 hash_insert (ConfigOptions, MuttVars[i].option,
2590 add_option (MuttVars[i].option, MuttVars[i].init,
2595 * XXX - use something even more difficult to predict?
2597 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2598 "\033]9;%ld\a", (long) time (NULL));
2600 /* on one of the systems I use, getcwd() does not return the same prefix
2601 as is listed in the passwd file */
2602 if ((p = getenv ("HOME")))
2603 Homedir = m_strdup(p);
2605 /* Get some information about the user */
2606 if ((pw = getpwuid (getuid ()))) {
2609 Username = m_strdup(pw->pw_name);
2611 Homedir = m_strdup(pw->pw_dir);
2613 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, GecosMask.rx);
2614 Realname = m_strdup(rnbuf);
2615 Shell = m_strdup(pw->pw_shell);
2621 fputs (_("unable to determine home directory"), stderr);
2624 if ((p = getenv ("USER")))
2625 Username = m_strdup(p);
2628 fputs (_("unable to determine username"), stderr);
2631 Shell = m_strdup((p = getenv ("SHELL")) ? p : "/bin/sh");
2634 debug_start(Homedir);
2636 /* And about the host... */
2638 /* some systems report the FQDN instead of just the hostname */
2639 if ((p = strchr (utsname.nodename, '.'))) {
2640 Hostname = p_dupstr(utsname.nodename, p - utsname.nodename);
2642 m_strcpy(buffer, sizeof(buffer), p); /* save the domain for below */
2645 Hostname = m_strdup(utsname.nodename);
2647 if (!p && getdnsdomainname(buffer, sizeof(buffer)) == -1)
2648 Fqdn = m_strdup("@");
2650 if (*buffer != '@') {
2651 Fqdn = p_new(char, m_strlen(buffer) + m_strlen(Hostname) + 2);
2652 sprintf (Fqdn, "%s.%s", NONULL(Hostname), buffer); /* __SPRINTF_CHECKED__ */
2655 Fqdn = m_strdup(NONULL (Hostname));
2662 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2664 fgets (buffer, sizeof(buffer), f);
2665 p = vskipspaces(buffer);
2667 while (*q && !isspace(*q))
2670 NewsServer = m_strdup(p);
2674 if ((p = getenv ("NNTPSERVER")))
2675 NewsServer = m_strdup(p);
2678 if ((p = getenv ("MAIL")))
2679 Spoolfile = m_strdup(p);
2680 else if ((p = getenv ("MAILDIR")))
2681 Spoolfile = m_strdup(p);
2684 mutt_concat_path(buffer, sizeof(buffer), NONULL(Homedir), MAILPATH);
2686 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(Username));
2688 Spoolfile = m_strdup(buffer);
2691 if ((p = getenv ("MAILCAPS")))
2692 MailcapPath = m_strdup(p);
2694 /* Default search path from RFC1524 */
2696 m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2697 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2700 Tempdir = m_strdup((p = getenv ("TMPDIR")) ? p : "/tmp");
2702 p = getenv ("VISUAL");
2704 p = getenv ("EDITOR");
2708 Editor = m_strdup(p);
2710 if ((p = getenv ("REPLYTO")) != NULL) {
2713 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2716 buf.data = buf.dptr = buffer;
2717 buf.dsize = m_strlen(buffer);
2720 parse_my_hdr (&token, &buf, 0, &err);
2721 p_delete(&token.data);
2724 if ((p = getenv ("EMAIL")) != NULL)
2725 From = rfc822_parse_adrlist (NULL, p);
2727 mutt_set_langinfo_charset ();
2728 mutt_set_charset (Charset);
2731 /* Set standard defaults */
2732 hash_map (ConfigOptions, mutt_set_default, 0);
2733 hash_map (ConfigOptions, mutt_restore_default, 0);
2735 CurrentMenu = MENU_MAIN;
2737 /* Do we have a locale definition? */
2738 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2739 ((p = getenv ("LANG")) != NULL && p[0]) ||
2740 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2741 set_option (OPTLOCALES);
2744 /* Unset suspend by default if we're the session leader */
2745 if (getsid (0) == getpid ())
2746 unset_option (OPTSUSPEND);
2749 mutt_init_history ();
2758 * When changing the code which looks for a configuration file,
2759 * please also change the corresponding code in muttbug.sh.in.
2769 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc-%s", NONULL (Homedir),
2771 if (access (buffer, F_OK) == -1)
2773 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL (Homedir));
2774 if (access (buffer, F_OK) == -1)
2776 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc-%s",
2777 NONULL (Homedir), MUTT_VERSION);
2778 if (access (buffer, F_OK) == -1)
2780 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
2784 Muttrc = m_strdup(buffer);
2787 m_strcpy(buffer, sizeof(buffer), Muttrc);
2789 mutt_expand_path (buffer, sizeof(buffer));
2790 Muttrc = m_strdup(buffer);
2792 p_delete(&AliasFile);
2793 AliasFile = m_strdup(NONULL (Muttrc));
2795 /* Process the global rc file if it exists and the user hasn't explicity
2796 requested not to via "-n". */
2798 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
2800 if (access (buffer, F_OK) == -1)
2801 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
2802 if (access (buffer, F_OK) == -1)
2803 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
2805 if (access (buffer, F_OK) == -1)
2806 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
2807 if (access (buffer, F_OK) != -1) {
2808 if (source_rc (buffer, &err) != 0) {
2809 fputs (err.data, stderr);
2810 fputc ('\n', stderr);
2816 /* Read the user's initialization file. */
2817 if (access (Muttrc, F_OK) != -1) {
2818 if (!option (OPTNOCURSES))
2820 if (source_rc (Muttrc, &err) != 0) {
2821 fputs (err.data, stderr);
2822 fputc ('\n', stderr);
2826 else if (!default_rc) {
2827 /* file specified by -F does not exist */
2828 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2829 mutt_endwin (buffer);
2833 if (mutt_execute_commands (commands) != 0)
2836 /* warn about synonym variables */
2837 if (!list_empty(Synonyms)) {
2839 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2840 for (i = 0; i < Synonyms->length; i++) {
2841 struct option_t* newopt = NULL, *oldopt = NULL;
2842 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2843 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2844 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2845 oldopt ? NONULL (oldopt->option) : "",
2846 newopt ? NONULL (newopt->option) : "",
2847 NONULL(((syn_t*) Synonyms->data[i])->f),
2848 ((syn_t*) Synonyms->data[i])->l);
2850 fprintf (stderr, _("Warning: synonym variables are scheduled"
2851 " for removal.\n"));
2852 list_del (&Synonyms, syn_del);
2856 if (need_pause && !option (OPTNOCURSES)) {
2857 if (mutt_any_key_to_continue (NULL) == -1)
2862 set_option (OPTWEED); /* turn weeding on by default */
2866 int mutt_get_hook_type (const char *name)
2868 struct command_t *c;
2870 for (c = Commands; c->name; c++)
2871 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2876 /* compare two option_t*'s for sorting -t/-T output */
2877 static int opt_cmp (const void* a, const void* b) {
2878 return (m_strcmp((*(struct option_t**) a)->option,
2879 (*(struct option_t**) b)->option));
2882 /* callback for hash_map() to put all non-synonym vars into list */
2883 static void opt_sel_full (const char* key __attribute__ ((unused)),
2885 unsigned long more) {
2886 list2_t** l = (list2_t**) more;
2887 struct option_t* option = (struct option_t*) data;
2889 if (DTYPE (option->type) == DT_SYN)
2891 list_push_back (l, option);
2894 /* callback for hash_map() to put all changed non-synonym vars into list */
2895 static void opt_sel_diff (const char* key __attribute__ ((unused)),
2897 unsigned long more) {
2898 list2_t** l = (list2_t**) more;
2899 struct option_t* option = (struct option_t*) data;
2900 char buf[LONG_STRING];
2902 if (DTYPE (option->type) == DT_SYN)
2905 mutt_option_value (option->option, buf, sizeof(buf));
2906 if (m_strcmp(buf, option->init) != 0)
2907 list_push_back (l, option);
2910 /* dump out the value of all the variables we have */
2911 int mutt_dump_variables (int full) {
2913 char outbuf[STRING];
2914 list2_t* tmp = NULL;
2915 struct option_t* option = NULL;
2917 /* get all non-synonyms into list... */
2918 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2919 (unsigned long) &tmp);
2921 if (!list_empty(tmp)) {
2922 /* ...and dump list sorted */
2923 qsort (tmp->data, tmp->length, sizeof(void*), opt_cmp);
2924 for (i = 0; i < tmp->length; i++) {
2925 option = (struct option_t*) tmp->data[i];
2926 FuncTable[DTYPE (option->type)].opt_to_string
2927 (outbuf, sizeof(outbuf), option);
2928 printf ("%s\n", outbuf);
2931 list_del (&tmp, NULL);