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_list_wipe((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 (string_list_t ** list, const char *str)
631 string_list_t *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) {
649 t = p_new(string_list_t, 1);
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 (string_list_t ** l, const char *str)
785 string_list_t *p, *last = NULL;
787 if (m_strcmp("*", str) == 0)
788 string_list_wipe(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 ((string_list_t **) 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 string_list_wipe((string_list_t **) data);
1063 remove_from_list ((string_list_t **) 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, string_list_t **ldata,
1096 BUFFER *err __attribute__ ((unused))) {
1098 string_list_t *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(string_list_t, 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, string_list_t **ldata,
1169 BUFFER *err __attribute__ ((unused))) {
1171 string_list_t *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 string_list_t->data, not anything pointed to by the string_list_t->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 (string_list_t *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)),
1246 string_list_t **listp;
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) {
1296 string_list_t **listp;
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_t *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 alias_list_wipe(&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 alias_list_wipe(&tmp);
1422 while (MoreArgs (s));
1426 static int parse_alias (BUFFER * buf, BUFFER * s,
1427 unsigned long data __attribute__ ((unused)),
1430 alias_t *tmp = Aliases;
1431 alias_t *last = NULL;
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 */
1453 tmp->name = m_strdup(buf->data);
1454 /* give the main addressbook code a chance */
1455 if (CurrentMenu == MENU_ALIAS)
1456 set_option (OPTMENUCALLER);
1459 /* override the previous value */
1460 address_list_wipe(&tmp->addr);
1461 if (CurrentMenu == MENU_ALIAS)
1462 set_option (OPTFORCEREDRAWINDEX);
1465 mutt_extract_token (buf, s,
1466 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1467 debug_print (2, ("second token is '%s'.\n", buf->data));
1468 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1473 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1474 snprintf (err->data, err->dsize,
1475 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1479 if (DebugLevel >= 2) {
1482 /* A group is terminated with an empty address, so check a->mailbox */
1483 for (a = tmp->addr; a && a->mailbox; a = a->next) {
1485 debug_print (2, ("%s\n", a->mailbox));
1487 debug_print (2, ("group %s\n", a->mailbox));
1495 parse_unmy_hdr (BUFFER * buf, BUFFER * s,
1496 unsigned long data __attribute__ ((unused)),
1497 BUFFER * err __attribute__ ((unused)))
1499 string_list_t *last = NULL;
1500 string_list_t *tmp = UserHeader;
1505 mutt_extract_token (buf, s, 0);
1506 if (m_strcmp("*", buf->data) == 0)
1507 string_list_wipe(&UserHeader);
1512 l = m_strlen(buf->data);
1513 if (buf->data[l - 1] == ':')
1517 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1518 && tmp->data[l] == ':') {
1521 last->next = tmp->next;
1523 UserHeader = tmp->next;
1526 string_list_wipe(&ptr);
1535 while (MoreArgs (s));
1539 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1546 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1547 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1548 m_strcpy(err->data, err->dsize, _("invalid header field"));
1551 keylen = p - buf->data + 1;
1554 for (tmp = UserHeader;; tmp = tmp->next) {
1555 /* see if there is already a field by this name */
1556 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1557 /* replace the old value */
1558 p_delete(&tmp->data);
1559 tmp->data = buf->data;
1566 tmp->next = string_item_new();
1570 tmp = string_item_new();
1573 tmp->data = buf->data;
1579 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1580 char* errbuf, size_t errlen) {
1583 if (m_strncmp("reverse-", s, 8) == 0) {
1585 flags = SORT_REVERSE;
1588 if (m_strncmp("last-", s, 5) == 0) {
1593 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1595 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1599 *((short*) dst->data) = i | flags;
1603 /* if additional data more == 1, we want to resolve synonyms */
1604 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
1606 char buf[LONG_STRING];
1607 struct option_t *ptr = p;
1609 if (DTYPE(ptr->type) == DT_SYN) {
1612 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1614 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_from_string)
1617 mutt_option_value(ptr->option, buf, sizeof(buf));
1618 if (m_strlen(ptr->init) == 0 && buf && *buf)
1619 ptr->init = m_strdup(buf);
1622 static struct option_t* add_option (const char* name, const char* init,
1623 short type, short dodup) {
1624 struct option_t* option = p_new(struct option_t, 1);
1626 debug_print (1, ("adding $%s\n", name));
1628 option->option = m_strdup(name);
1629 option->type = type;
1631 option->init = dodup ? m_strdup(init) : (char*) init;
1635 /* creates new option_t* of type DT_USER for $user_ var */
1636 static struct option_t* add_user_option (const char* name) {
1637 return (add_option (name, NULL, DT_USER, 1));
1640 /* free()'s option_t* */
1641 static void del_option (void* p) {
1642 struct option_t *ptr = (struct option_t*) p;
1643 char* s = (char*) ptr->data;
1644 debug_print (1, ("removing option '%s' from table\n", NONULL (ptr->option)));
1645 p_delete(&ptr->option);
1647 p_delete(&ptr->init);
1651 static int init_expand (char** dst, struct option_t* src) {
1657 if (DTYPE(src->type) == DT_STR ||
1658 DTYPE(src->type) == DT_PATH) {
1659 /* only expand for string as it's the only place where
1660 * we want to expand vars right now */
1661 if (src->init && *src->init) {
1664 len = m_strlen(src->init) + 2;
1665 in.data = p_new(char, len + 1);
1666 snprintf (in.data, len, "\"%s\"", src->init);
1669 mutt_extract_token (&token, &in, 0);
1670 if (token.data && *token.data)
1671 *dst = m_strdup(token.data);
1673 *dst = m_strdup("");
1675 p_delete(&token.data);
1677 *dst = m_strdup("");
1679 /* for non-string: take value as is */
1680 *dst = m_strdup(src->init);
1684 /* if additional data more == 1, we want to resolve synonyms */
1685 static void mutt_restore_default (const char* name __attribute__ ((unused)),
1686 void* p, unsigned long more) {
1687 char errbuf[STRING];
1688 struct option_t* ptr = (struct option_t*) p;
1691 if (DTYPE (ptr->type) == DT_SYN) {
1694 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1698 if (FuncTable[DTYPE (ptr->type)].opt_from_string) {
1699 init_expand (&init, ptr);
1700 if (!FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, init, errbuf,
1702 if (!option (OPTNOCURSES))
1704 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1705 "Please report this error: \"%s\"\n"),
1706 ptr->option, NONULL (init), errbuf);
1712 if (ptr->flags & R_INDEX)
1713 set_option (OPTFORCEREDRAWINDEX);
1714 if (ptr->flags & R_PAGER)
1715 set_option (OPTFORCEREDRAWPAGER);
1716 if (ptr->flags & R_RESORT_SUB)
1717 set_option (OPTSORTSUBTHREADS);
1718 if (ptr->flags & R_RESORT)
1719 set_option (OPTNEEDRESORT);
1720 if (ptr->flags & R_RESORT_INIT)
1721 set_option (OPTRESORTINIT);
1722 if (ptr->flags & R_TREE)
1723 set_option (OPTREDRAWTREE);
1726 /* check whether value for $dsn_return would be valid */
1727 static int check_dsn_return (const char* option __attribute__ ((unused)), unsigned long p,
1728 char* errbuf, size_t errlen) {
1729 char* val = (char*) p;
1730 if (val && *val && m_strncmp(val, "hdrs", 4) != 0 &&
1731 m_strncmp(val, "full", 4) != 0) {
1733 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1739 /* check whether value for $dsn_notify would be valid */
1740 static int check_dsn_notify (const char* option, unsigned long p,
1741 char* errbuf, size_t errlen) {
1742 list2_t* list = NULL;
1745 char* val = (char*) p;
1749 list = list_from_str (val, ",");
1750 if (list_empty (list))
1753 for (i = 0; i < list->length; i++)
1754 if (m_strncmp(list->data[i], "never", 5) != 0 &&
1755 m_strncmp(list->data[i], "failure", 7) != 0 &&
1756 m_strncmp(list->data[i], "delay", 5) != 0 &&
1757 m_strncmp(list->data[i], "success", 7) != 0) {
1759 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1760 (char*) list->data[i], "dsn_notify");
1764 list_del (&list, (list_del_t*)xmemfree);
1768 static int check_num (const char* option, unsigned long p,
1769 char* errbuf, size_t errlen) {
1772 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1779 static int check_debug (const char* option, unsigned long p,
1780 char* errbuf, size_t errlen) {
1781 if ((int) p <= DEBUG_MAX_LEVEL &&
1782 (int) p >= DEBUG_MIN_LEVEL)
1786 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1791 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1792 char* errbuf, size_t errlen) {
1793 if (!check_num ("history", p, errbuf, errlen))
1795 mutt_init_history ();
1799 static int check_special (const char* name, unsigned long val,
1800 char* errbuf, size_t errlen) {
1803 for (i = 0; SpecialVars[i].name; i++) {
1804 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1805 return (SpecialVars[i].check (SpecialVars[i].name,
1806 val, errbuf, errlen));
1812 static const struct mapping_t* get_sortmap (struct option_t* option) {
1813 const struct mapping_t* map = NULL;
1815 switch (option->type & DT_SUBTYPE_MASK) {
1817 map = SortAliasMethods;
1819 case DT_SORT_BROWSER:
1820 map = SortBrowserMethods;
1823 map = SortKeyMethods;
1826 map = SortAuxMethods;
1835 #define CHECK_PAGER \
1836 if ((CurrentMenu == MENU_PAGER) && \
1837 (!option || (option->flags & R_RESORT))) \
1839 snprintf (err->data, err->dsize, \
1840 _("Not available in this menu.")); \
1844 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1847 int query, unset, inv, reset, r = 0;
1848 struct option_t* option = NULL;
1850 while (MoreArgs (s)) {
1851 /* reset state variables */
1853 unset = data & M_SET_UNSET;
1854 inv = data & M_SET_INV;
1855 reset = data & M_SET_RESET;
1857 if (*s->dptr == '?') {
1861 else if (m_strncmp("no", s->dptr, 2) == 0) {
1865 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1869 else if (*s->dptr == '&') {
1874 /* get the variable name */
1875 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1877 /* resolve synonyms */
1878 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1879 DTYPE (option->type == DT_SYN)) {
1880 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1881 syn_add (newopt, option);
1885 /* see if we need to add $user_ var */
1886 if (!option && ascii_strncmp ("user_", tmp->data, 5) == 0) {
1887 /* there's no option named like this yet so only add one
1888 * if the action isn't any of: reset, unset, query */
1889 if (!(reset || unset || query || *s->dptr != '=')) {
1890 debug_print (1, ("adding user option '%s'\n", tmp->data));
1891 option = add_user_option (tmp->data);
1892 hash_insert (ConfigOptions, option->option, option, 0);
1896 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1897 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1900 s->dptr = vskipspaces(s->dptr);
1903 if (query || unset || inv) {
1904 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1908 if (s && *s->dptr == '=') {
1909 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1913 if (!m_strcmp("all", tmp->data)) {
1914 if (CurrentMenu == MENU_PAGER) {
1915 snprintf (err->data, err->dsize, _("Not available in this menu."));
1918 hash_map (ConfigOptions, mutt_restore_default, 1);
1919 set_option (OPTFORCEREDRAWINDEX);
1920 set_option (OPTFORCEREDRAWPAGER);
1921 set_option (OPTSORTSUBTHREADS);
1922 set_option (OPTNEEDRESORT);
1923 set_option (OPTRESORTINIT);
1924 set_option (OPTREDRAWTREE);
1927 else if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1928 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1933 mutt_restore_default (NULL, option, 1);
1936 else if (DTYPE (option->type) == DT_BOOL) {
1937 /* XXX this currently ignores the function table
1938 * as we don't get invert and stuff into it */
1939 if (s && *s->dptr == '=') {
1940 if (unset || inv || query) {
1941 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1946 mutt_extract_token (tmp, s, 0);
1947 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1949 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1952 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1958 bool_to_string (err->data, err->dsize, option);
1964 unset_option (option->data);
1966 toggle_option (option->data);
1968 set_option (option->data);
1970 else if (DTYPE (option->type) == DT_STR ||
1971 DTYPE (option->type) == DT_PATH ||
1972 DTYPE (option->type) == DT_ADDR ||
1973 DTYPE (option->type) == DT_MAGIC ||
1974 DTYPE (option->type) == DT_NUM ||
1975 DTYPE (option->type) == DT_SORT ||
1976 DTYPE (option->type) == DT_RX ||
1977 DTYPE (option->type) == DT_USER ||
1978 DTYPE (option->type) == DT_SYS) {
1980 /* XXX maybe we need to get unset into handlers? */
1981 if (DTYPE (option->type) == DT_STR ||
1982 DTYPE (option->type) == DT_PATH ||
1983 DTYPE (option->type) == DT_ADDR ||
1984 DTYPE (option->type) == DT_USER ||
1985 DTYPE (option->type) == DT_SYS) {
1988 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1989 snprintf (err->data, err->dsize, _("$%s is read-only"),
1993 } else if (DTYPE (option->type) == DT_ADDR)
1994 address_list_wipe((address_t **) option->data);
1995 else if (DTYPE (option->type) == DT_USER)
1996 /* to unset $user_ means remove */
1997 hash_delete (ConfigOptions, option->option,
1998 option, del_option);
2000 p_delete((void **)&option->data);
2005 if (query || *s->dptr != '=') {
2006 FuncTable[DTYPE (option->type)].opt_to_string
2007 (err->data, err->dsize, option);
2011 /* the $madmutt_ variables are read-only */
2012 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
2013 snprintf (err->data, err->dsize, _("$%s is read-only"),
2020 mutt_extract_token (tmp, s, 0);
2021 if (!FuncTable[DTYPE (option->type)].opt_from_string
2022 (option, tmp->data, err->data, err->dsize))
2026 else if (DTYPE (option->type) == DT_QUAD) {
2029 quad_to_string (err->data, err->dsize, option);
2033 if (*s->dptr == '=') {
2036 mutt_extract_token (tmp, s, 0);
2037 if (ascii_strcasecmp ("yes", tmp->data) == 0)
2038 set_quadoption (option->data, M_YES);
2039 else if (ascii_strcasecmp ("no", tmp->data) == 0)
2040 set_quadoption (option->data, M_NO);
2041 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
2042 set_quadoption (option->data, M_ASKYES);
2043 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
2044 set_quadoption (option->data, M_ASKNO);
2046 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
2047 tmp->data, option->option);
2054 toggle_quadoption (option->data);
2056 set_quadoption (option->data, M_NO);
2058 set_quadoption (option->data, M_YES);
2062 snprintf (err->data, err->dsize, _("%s: unknown type"),
2068 if (option->flags & R_INDEX)
2069 set_option (OPTFORCEREDRAWINDEX);
2070 if (option->flags & R_PAGER)
2071 set_option (OPTFORCEREDRAWPAGER);
2072 if (option->flags & R_RESORT_SUB)
2073 set_option (OPTSORTSUBTHREADS);
2074 if (option->flags & R_RESORT)
2075 set_option (OPTNEEDRESORT);
2076 if (option->flags & R_RESORT_INIT)
2077 set_option (OPTRESORTINIT);
2078 if (option->flags & R_TREE)
2079 set_option (OPTREDRAWTREE);
2086 /* reads the specified initialization file. returns -1 if errors were found
2087 so that we can pause to let the user know... */
2088 static int source_rc (const char *rcfile, BUFFER * err)
2091 int line = 0, rc = 0, conv = 0;
2093 char *linebuf = NULL;
2094 char *currentline = NULL;
2098 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
2100 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
2101 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
2106 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
2107 conv = ConfigCharset && (*ConfigCharset) && Charset;
2109 currentline = m_strdup(linebuf);
2112 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
2115 currentline = linebuf;
2120 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
2121 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
2122 if (--rc < -MAXERRS) {
2124 p_delete(¤tline);
2133 p_delete(¤tline);
2135 p_delete(&token.data);
2139 mutt_wait_filter (pid);
2141 /* the muttrc source keyword */
2142 snprintf (err->data, err->dsize,
2143 rc >= -MAXERRS ? _("source: errors in %s")
2144 : _("source: reading aborted due too many errors in %s"),
2153 static int parse_source (BUFFER * tmp, BUFFER * s,
2154 unsigned long data __attribute__ ((unused)),
2157 char path[_POSIX_PATH_MAX];
2161 if (mutt_extract_token (tmp, s, 0) != 0) {
2162 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
2166 m_strcpy(path, sizeof(path), tmp->data);
2167 mutt_expand_path (path, sizeof(path));
2169 rc += source_rc (path, err);
2171 while (MoreArgs (s));
2173 return ((rc < 0) ? -1 : 0);
2176 /* line command to execute
2178 token scratch buffer to be used by parser. caller should free
2179 token->data when finished. the reason for this variable is
2180 to avoid having to allocate and deallocate a lot of memory
2181 if we are parsing many lines. the caller can pass in the
2182 memory to use, which avoids having to create new space for
2183 every call to this function.
2185 err where to write error messages */
2186 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
2192 expn.data = expn.dptr = line;
2193 expn.dsize = m_strlen(line);
2197 debug_print (1, ("expand '%s'\n", line));
2199 expn.dptr = vskipspaces(expn.dptr);
2200 while (*expn.dptr) {
2201 if (*expn.dptr == '#')
2202 break; /* rest of line is a comment */
2203 if (*expn.dptr == ';') {
2207 mutt_extract_token (token, &expn, 0);
2208 for (i = 0; Commands[i].name; i++) {
2209 if (!m_strcmp(token->data, Commands[i].name)) {
2210 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
2215 if (!Commands[i].name) {
2216 snprintf (err->data, err->dsize, _("%s: unknown command"),
2217 NONULL (token->data));
2224 p_delete(&expn.data);
2229 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
2230 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
2231 /* initial string that starts completion. No telling how much crap
2232 * the user has typed so far. Allocate LONG_STRING just to be sure! */
2233 char User_typed[LONG_STRING] = { 0 };
2235 int Num_matched = 0; /* Number of matches for completion */
2236 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
2237 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
2239 /* helper function for completion. Changes the dest buffer if
2240 necessary/possible to aid completion.
2241 dest == completion result gets here.
2242 src == candidate for completion.
2243 try == user entered data for completion.
2244 len == length of dest buffer.
2246 static void candidate (char *dest, char *try, const char *src, int len)
2250 if (strstr (src, try) == src) {
2251 Matches[Num_matched++] = src;
2253 m_strcpy(dest, len, src);
2255 for (l = 0; src[l] && src[l] == dest[l]; l++);
2261 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
2265 int spaces; /* keep track of the number of leading spaces on the line */
2267 buffer = vskipspaces(buffer);
2268 spaces = buffer - pt;
2270 pt = buffer + pos - spaces;
2271 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2274 if (pt == buffer) { /* complete cmd */
2275 /* first TAB. Collect all the matches */
2278 m_strcpy(User_typed, sizeof(User_typed), pt);
2279 p_clear(Matches, countof(Matches));
2280 p_clear(Completed, countof(Completed));
2281 for (num = 0; Commands[num].name; num++)
2282 candidate (Completed, User_typed, Commands[num].name,
2284 Matches[Num_matched++] = User_typed;
2286 /* All matches are stored. Longest non-ambiguous string is ""
2287 * i.e. dont change 'buffer'. Fake successful return this time */
2288 if (User_typed[0] == 0)
2292 if (Completed[0] == 0 && User_typed[0])
2295 /* Num_matched will _always_ be atleast 1 since the initial
2296 * user-typed string is always stored */
2297 if (numtabs == 1 && Num_matched == 2)
2298 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2299 else if (numtabs > 1 && Num_matched > 2)
2300 /* cycle thru all the matches */
2301 snprintf (Completed, sizeof(Completed), "%s",
2302 Matches[(numtabs - 2) % Num_matched]);
2304 /* return the completed command */
2305 m_strcpy(buffer, len - spaces, Completed);
2307 else if (!m_strncmp(buffer, "set", 3)
2308 || !m_strncmp(buffer, "unset", 5)
2309 || !m_strncmp(buffer, "reset", 5)
2310 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
2311 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
2314 /* loop through all the possible prefixes (no, inv, ...) */
2315 if (!m_strncmp(buffer, "set", 3)) {
2316 for (num = 0; prefixes[num]; num++) {
2317 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
2318 pt += m_strlen(prefixes[num]);
2324 /* first TAB. Collect all the matches */
2327 m_strcpy(User_typed, sizeof(User_typed), pt);
2328 p_clear(Matches, countof(Matches));
2329 p_clear(Completed, countof(Completed));
2330 for (num = 0; MuttVars[num].option; num++)
2331 candidate(Completed, User_typed, MuttVars[num].option,
2333 Matches[Num_matched++] = User_typed;
2335 /* All matches are stored. Longest non-ambiguous string is ""
2336 * i.e. dont change 'buffer'. Fake successful return this time */
2337 if (User_typed[0] == 0)
2341 if (Completed[0] == 0 && User_typed[0])
2344 /* Num_matched will _always_ be atleast 1 since the initial
2345 * user-typed string is always stored */
2346 if (numtabs == 1 && Num_matched == 2)
2347 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2348 else if (numtabs > 1 && Num_matched > 2)
2349 /* cycle thru all the matches */
2350 snprintf (Completed, sizeof(Completed), "%s",
2351 Matches[(numtabs - 2) % Num_matched]);
2353 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2355 else if (!m_strncmp(buffer, "exec", 4)) {
2356 struct binding_t *menu = km_get_table (CurrentMenu);
2358 if (!menu && CurrentMenu != MENU_PAGER)
2362 /* first TAB. Collect all the matches */
2365 m_strcpy(User_typed, sizeof(User_typed), pt);
2366 p_clear(Matches, countof(Matches));
2367 p_clear(Completed, countof(Completed));
2368 for (num = 0; menu[num].name; num++)
2369 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
2370 /* try the generic menu */
2371 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2373 for (num = 0; menu[num].name; num++)
2374 candidate (Completed, User_typed, menu[num].name,
2377 Matches[Num_matched++] = User_typed;
2379 /* All matches are stored. Longest non-ambiguous string is ""
2380 * i.e. dont change 'buffer'. Fake successful return this time */
2381 if (User_typed[0] == 0)
2385 if (Completed[0] == 0 && User_typed[0])
2388 /* Num_matched will _always_ be atleast 1 since the initial
2389 * user-typed string is always stored */
2390 if (numtabs == 1 && Num_matched == 2)
2391 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2392 else if (numtabs > 1 && Num_matched > 2)
2393 /* cycle thru all the matches */
2394 snprintf (Completed, sizeof(Completed), "%s",
2395 Matches[(numtabs - 2) % Num_matched]);
2397 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2405 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2407 char var[STRING], *pt = buffer;
2409 struct option_t* option = NULL;
2414 buffer = vskipspaces(buffer);
2415 spaces = buffer - pt;
2417 pt = buffer + pos - spaces;
2418 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2420 pt++; /* move past the space */
2421 if (*pt == '=') /* abort if no var before the '=' */
2424 if (m_strncmp(buffer, "set", 3) == 0) {
2425 m_strcpy(var, sizeof(var), pt);
2426 /* ignore the trailing '=' when comparing */
2427 var[m_strlen(var) - 1] = 0;
2428 if (!(option = hash_find (ConfigOptions, var)))
2429 return 0; /* no such variable. */
2431 char tmp[LONG_STRING], tmp2[LONG_STRING];
2433 size_t dlen = buffer + len - pt - spaces;
2434 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2438 if ((DTYPE (option->type) == DT_STR) ||
2439 (DTYPE (option->type) == DT_PATH) ||
2440 (DTYPE (option->type) == DT_RX)) {
2441 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
2442 if (DTYPE (option->type) == DT_PATH)
2443 mutt_pretty_mailbox (tmp);
2445 else if (DTYPE (option->type) == DT_ADDR) {
2446 rfc822_write_address (tmp, sizeof(tmp),
2447 *((address_t **) option->data), 0);
2449 else if (DTYPE (option->type) == DT_QUAD)
2450 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
2451 else if (DTYPE (option->type) == DT_NUM)
2452 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
2453 else if (DTYPE (option->type) == DT_SORT) {
2454 const struct mapping_t *map;
2457 switch (option->type & DT_SUBTYPE_MASK) {
2459 map = SortAliasMethods;
2461 case DT_SORT_BROWSER:
2462 map = SortBrowserMethods;
2465 map = SortKeyMethods;
2471 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2472 snprintf(tmp, sizeof(tmp), "%s%s%s",
2473 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2474 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2476 else if (DTYPE (option->type) == DT_MAGIC) {
2478 switch (DefaultMagic) {
2494 m_strcpy(tmp, sizeof(tmp), p);
2496 else if (DTYPE (option->type) == DT_BOOL)
2497 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
2501 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
2502 if (*s == '\\' || *s == '"')
2508 m_strcpy(tmp, sizeof(tmp), pt);
2509 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2517 /* Implement the -Q command line flag */
2518 int mutt_query_variables (string_list_t * queries)
2522 char errbuff[STRING];
2523 char command[STRING];
2531 err.dsize = sizeof(errbuff);
2533 for (p = queries; p; p = p->next) {
2534 snprintf (command, sizeof(command), "set ?%s\n", p->data);
2535 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2536 fprintf (stderr, "%s\n", err.data);
2537 p_delete(&token.data);
2540 printf ("%s\n", err.data);
2543 p_delete(&token.data);
2547 static int mutt_execute_commands (string_list_t * p)
2550 char errstr[SHORT_STRING];
2554 err.dsize = sizeof(errstr);
2556 for (; p; p = p->next) {
2557 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2558 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2559 p_delete(&token.data);
2563 p_delete(&token.data);
2567 void mutt_init (int skip_sys_rc, string_list_t * commands)
2570 struct utsname utsname;
2572 char buffer[STRING], error[STRING];
2573 int default_rc = 0, need_pause = 0;
2579 err.dsize = sizeof(error);
2581 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2582 * to have some room for $user_ vars */
2583 ConfigOptions = hash_create (sizeof(MuttVars) * 3);
2584 for (i = 0; MuttVars[i].option; i++) {
2585 if (DTYPE (MuttVars[i].type) != DT_SYS)
2586 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2588 hash_insert (ConfigOptions, MuttVars[i].option,
2589 add_option (MuttVars[i].option, MuttVars[i].init,
2594 * XXX - use something even more difficult to predict?
2596 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2597 "\033]9;%ld\a", (long) time (NULL));
2599 /* on one of the systems I use, getcwd() does not return the same prefix
2600 as is listed in the passwd file */
2601 if ((p = getenv ("HOME")))
2602 Homedir = m_strdup(p);
2604 /* Get some information about the user */
2605 if ((pw = getpwuid (getuid ()))) {
2608 Username = m_strdup(pw->pw_name);
2610 Homedir = m_strdup(pw->pw_dir);
2612 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, GecosMask.rx);
2613 Realname = m_strdup(rnbuf);
2614 Shell = m_strdup(pw->pw_shell);
2620 fputs (_("unable to determine home directory"), stderr);
2623 if ((p = getenv ("USER")))
2624 Username = m_strdup(p);
2627 fputs (_("unable to determine username"), stderr);
2630 Shell = m_strdup((p = getenv ("SHELL")) ? p : "/bin/sh");
2633 debug_start(Homedir);
2635 /* And about the host... */
2637 /* some systems report the FQDN instead of just the hostname */
2638 if ((p = strchr (utsname.nodename, '.'))) {
2639 Hostname = p_dupstr(utsname.nodename, p - utsname.nodename);
2641 m_strcpy(buffer, sizeof(buffer), p); /* save the domain for below */
2644 Hostname = m_strdup(utsname.nodename);
2646 if (!p && getdnsdomainname(buffer, sizeof(buffer)) == -1)
2647 Fqdn = m_strdup("@");
2649 if (*buffer != '@') {
2650 Fqdn = p_new(char, m_strlen(buffer) + m_strlen(Hostname) + 2);
2651 sprintf (Fqdn, "%s.%s", NONULL(Hostname), buffer); /* __SPRINTF_CHECKED__ */
2654 Fqdn = m_strdup(NONULL (Hostname));
2661 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2663 fgets (buffer, sizeof(buffer), f);
2664 p = vskipspaces(buffer);
2666 while (*q && !isspace(*q))
2669 NewsServer = m_strdup(p);
2673 if ((p = getenv ("NNTPSERVER")))
2674 NewsServer = m_strdup(p);
2677 if ((p = getenv ("MAIL")))
2678 Spoolfile = m_strdup(p);
2679 else if ((p = getenv ("MAILDIR")))
2680 Spoolfile = m_strdup(p);
2683 mutt_concat_path(buffer, sizeof(buffer), NONULL(Homedir), MAILPATH);
2685 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(Username));
2687 Spoolfile = m_strdup(buffer);
2690 if ((p = getenv ("MAILCAPS")))
2691 MailcapPath = m_strdup(p);
2693 /* Default search path from RFC1524 */
2695 m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2696 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2699 Tempdir = m_strdup((p = getenv ("TMPDIR")) ? p : "/tmp");
2701 p = getenv ("VISUAL");
2703 p = getenv ("EDITOR");
2707 Editor = m_strdup(p);
2709 if ((p = getenv ("REPLYTO")) != NULL) {
2712 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2715 buf.data = buf.dptr = buffer;
2716 buf.dsize = m_strlen(buffer);
2719 parse_my_hdr (&token, &buf, 0, &err);
2720 p_delete(&token.data);
2723 if ((p = getenv ("EMAIL")) != NULL)
2724 From = rfc822_parse_adrlist (NULL, p);
2726 mutt_set_langinfo_charset ();
2727 mutt_set_charset (Charset);
2730 /* Set standard defaults */
2731 hash_map (ConfigOptions, mutt_set_default, 0);
2732 hash_map (ConfigOptions, mutt_restore_default, 0);
2734 CurrentMenu = MENU_MAIN;
2736 /* Do we have a locale definition? */
2737 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2738 ((p = getenv ("LANG")) != NULL && p[0]) ||
2739 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2740 set_option (OPTLOCALES);
2743 /* Unset suspend by default if we're the session leader */
2744 if (getsid (0) == getpid ())
2745 unset_option (OPTSUSPEND);
2748 mutt_init_history ();
2757 * When changing the code which looks for a configuration file,
2758 * please also change the corresponding code in muttbug.sh.in.
2768 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc-%s", NONULL (Homedir),
2770 if (access (buffer, F_OK) == -1)
2772 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL (Homedir));
2773 if (access (buffer, F_OK) == -1)
2775 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc-%s",
2776 NONULL (Homedir), MUTT_VERSION);
2777 if (access (buffer, F_OK) == -1)
2779 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
2783 Muttrc = m_strdup(buffer);
2786 m_strcpy(buffer, sizeof(buffer), Muttrc);
2788 mutt_expand_path (buffer, sizeof(buffer));
2789 Muttrc = m_strdup(buffer);
2791 p_delete(&AliasFile);
2792 AliasFile = m_strdup(NONULL (Muttrc));
2794 /* Process the global rc file if it exists and the user hasn't explicity
2795 requested not to via "-n". */
2797 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
2799 if (access (buffer, F_OK) == -1)
2800 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
2801 if (access (buffer, F_OK) == -1)
2802 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
2804 if (access (buffer, F_OK) == -1)
2805 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
2806 if (access (buffer, F_OK) != -1) {
2807 if (source_rc (buffer, &err) != 0) {
2808 fputs (err.data, stderr);
2809 fputc ('\n', stderr);
2815 /* Read the user's initialization file. */
2816 if (access (Muttrc, F_OK) != -1) {
2817 if (!option (OPTNOCURSES))
2819 if (source_rc (Muttrc, &err) != 0) {
2820 fputs (err.data, stderr);
2821 fputc ('\n', stderr);
2825 else if (!default_rc) {
2826 /* file specified by -F does not exist */
2827 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2828 mutt_endwin (buffer);
2832 if (mutt_execute_commands (commands) != 0)
2835 /* warn about synonym variables */
2836 if (!list_empty(Synonyms)) {
2838 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2839 for (i = 0; i < Synonyms->length; i++) {
2840 struct option_t* newopt = NULL, *oldopt = NULL;
2841 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2842 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2843 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2844 oldopt ? NONULL (oldopt->option) : "",
2845 newopt ? NONULL (newopt->option) : "",
2846 NONULL(((syn_t*) Synonyms->data[i])->f),
2847 ((syn_t*) Synonyms->data[i])->l);
2849 fprintf (stderr, _("Warning: synonym variables are scheduled"
2850 " for removal.\n"));
2851 list_del (&Synonyms, syn_del);
2855 if (need_pause && !option (OPTNOCURSES)) {
2856 if (mutt_any_key_to_continue (NULL) == -1)
2861 set_option (OPTWEED); /* turn weeding on by default */
2865 int mutt_get_hook_type (const char *name)
2867 struct command_t *c;
2869 for (c = Commands; c->name; c++)
2870 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2875 /* compare two option_t*'s for sorting -t/-T output */
2876 static int opt_cmp (const void* a, const void* b) {
2877 return (m_strcmp((*(struct option_t**) a)->option,
2878 (*(struct option_t**) b)->option));
2881 /* callback for hash_map() to put all non-synonym vars into list */
2882 static void opt_sel_full (const char* key __attribute__ ((unused)),
2884 unsigned long more) {
2885 list2_t** l = (list2_t**) more;
2886 struct option_t* option = (struct option_t*) data;
2888 if (DTYPE (option->type) == DT_SYN)
2890 list_push_back (l, option);
2893 /* callback for hash_map() to put all changed non-synonym vars into list */
2894 static void opt_sel_diff (const char* key __attribute__ ((unused)),
2896 unsigned long more) {
2897 list2_t** l = (list2_t**) more;
2898 struct option_t* option = (struct option_t*) data;
2899 char buf[LONG_STRING];
2901 if (DTYPE (option->type) == DT_SYN)
2904 mutt_option_value (option->option, buf, sizeof(buf));
2905 if (m_strcmp(buf, option->init) != 0)
2906 list_push_back (l, option);
2909 /* dump out the value of all the variables we have */
2910 int mutt_dump_variables (int full) {
2912 char outbuf[STRING];
2913 list2_t* tmp = NULL;
2914 struct option_t* option = NULL;
2916 /* get all non-synonyms into list... */
2917 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2918 (unsigned long) &tmp);
2920 if (!list_empty(tmp)) {
2921 /* ...and dump list sorted */
2922 qsort (tmp->data, tmp->length, sizeof(void*), opt_cmp);
2923 for (i = 0; i < tmp->length; i++) {
2924 option = (struct option_t*) tmp->data[i];
2925 FuncTable[DTYPE (option->type)].opt_to_string
2926 (outbuf, sizeof(outbuf), option);
2927 printf ("%s\n", outbuf);
2930 list_del (&tmp, NULL);