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>
36 #include <lib-crypt/crypt.h>
37 #include "mutt_idna.h"
39 #if defined(USE_SSL) || defined(USE_GNUTLS)
40 #include <lib-sys/mutt_ssl.h>
43 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
44 #include "mutt_libesmtp.h"
56 #include <sys/utsname.h>
63 static const struct mapping_t* get_sortmap (struct option_t* option);
64 static int parse_sort (struct option_t* dst, const char *s,
65 const struct mapping_t *map,
66 char* errbuf, ssize_t errlen);
68 static HASH *ConfigOptions = NULL;
70 /* for synonym warning reports: synonym found during parsing */
74 struct option_t* n; /* new */
75 struct option_t* o; /* old */
78 /* for synonym warning reports: list of synonyms found */
79 static list2_t* Synonyms;
80 /* for synonym warning reports: current rc file */
81 static const char* CurRCFile = NULL;
82 /* for synonym warning reports: current rc line */
83 static int CurRCLine = 0;
85 /* prototypes for checking for special vars */
86 static int check_dsn_return (const char* option, unsigned long val,
87 char* errbuf, ssize_t errlen);
88 static int check_dsn_notify (const char* option, unsigned long val,
89 char* errbuf, ssize_t errlen);
90 static int check_history (const char* option, unsigned long val,
91 char* errbuf, ssize_t errlen);
92 /* this checks that numbers are >= 0 */
93 static int check_num (const char* option, unsigned long val,
94 char* errbuf, ssize_t errlen);
96 static int check_debug (const char* option, unsigned long val,
97 char* errbuf, ssize_t errlen);
100 /* use this to check only */
101 static int check_special (const char* option, unsigned long val,
102 char* errbuf, ssize_t errlen);
104 /* variable <-> sanity check function mappings
105 * when changing these, make sure the proper _from_string handler
106 * does this checking!
110 int (*check) (const char* option, unsigned long val,
111 char* errbuf, ssize_t errlen);
113 { "dsn_notify", check_dsn_notify },
114 { "dsn_return", check_dsn_return },
115 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
116 { "smtp_use_tls", mutt_libesmtp_check_usetls },
118 { "history", check_history },
119 { "pager_index_lines", check_num },
121 { "debug_level", check_debug },
127 /* protos for config type handles: convert value to string */
128 static void bool_to_string (char* dst, ssize_t dstlen, struct option_t* option);
129 static void num_to_string (char* dst, ssize_t dstlen, struct option_t* option);
130 static void str_to_string (char* dst, ssize_t dstlen, struct option_t* option);
131 static void quad_to_string (char* dst, ssize_t dstlen, struct option_t* option);
132 static void sort_to_string (char* dst, ssize_t dstlen, struct option_t* option);
133 static void rx_to_string (char* dst, ssize_t dstlen, struct option_t* option);
134 static void magic_to_string (char* dst, ssize_t dstlen, struct option_t* option);
135 static void addr_to_string (char* dst, ssize_t dstlen, struct option_t* option);
136 static void user_to_string (char* dst, ssize_t dstlen, struct option_t* option);
137 static void sys_to_string (char* dst, ssize_t dstlen, struct option_t* option);
139 /* protos for config type handles: convert to value from string */
140 static int bool_from_string (struct option_t* dst, const char* val,
141 char* errbuf, ssize_t errlen);
142 static int num_from_string (struct option_t* dst, const char* val,
143 char* errbuf, ssize_t errlen);
144 static int str_from_string (struct option_t* dst, const char* val,
145 char* errbuf, ssize_t errlen);
146 static int path_from_string (struct option_t* dst, const char* val,
147 char* errbuf, ssize_t errlen);
148 static int quad_from_string (struct option_t* dst, const char* val,
149 char* errbuf, ssize_t errlen);
150 static int sort_from_string (struct option_t* dst, const char* val,
151 char* errbuf, ssize_t errlen);
152 static int rx_from_string (struct option_t* dst, const char* val,
153 char* errbuf, ssize_t errlen);
154 static int magic_from_string (struct option_t* dst, const char* val,
155 char* errbuf, ssize_t errlen);
156 static int addr_from_string (struct option_t* dst, const char* val,
157 char* errbuf, ssize_t errlen);
158 static int user_from_string (struct option_t* dst, const char* val,
159 char* errbuf, ssize_t errlen);
163 void (*opt_to_string) (char* dst, ssize_t dstlen, struct option_t* option);
164 int (*opt_from_string) (struct option_t* dst, const char* val,
165 char* errbuf, ssize_t errlen);
167 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
168 { DT_BOOL, bool_to_string, bool_from_string },
169 { DT_NUM, num_to_string, num_from_string },
170 { DT_STR, str_to_string, str_from_string },
171 { DT_PATH, str_to_string, path_from_string },
172 { DT_QUAD, quad_to_string, quad_from_string },
173 { DT_SORT, sort_to_string, sort_from_string },
174 { DT_RX, rx_to_string, rx_from_string },
175 { DT_MAGIC, magic_to_string, magic_from_string },
176 /* synonyms should be resolved already so we don't need this
177 * but must define it as DT_ is used for indexing */
178 { DT_SYN, NULL, NULL },
179 { DT_ADDR, addr_to_string, addr_from_string },
180 { DT_USER, user_to_string, user_from_string },
181 { DT_SYS, sys_to_string, NULL },
184 static void bool_to_string (char* dst, ssize_t dstlen,
185 struct option_t* option) {
186 snprintf (dst, dstlen, "%s=%s", option->option,
187 option (option->data) ? "yes" : "no");
190 static int bool_from_string (struct option_t* dst, const char* val,
191 char* errbuf __attribute__ ((unused)),
192 ssize_t errlen __attribute__ ((unused))) {
197 if (ascii_strncasecmp (val, "yes", 3) == 0)
199 else if (ascii_strncasecmp (val, "no", 2) == 0)
205 set_option (dst->data);
207 unset_option (dst->data);
211 static void num_to_string (char* dst, ssize_t dstlen,
212 struct option_t* option) {
214 const char* fmt = (m_strcmp(option->option, "umask") == 0) ?
216 snprintf (dst, dstlen, fmt, option->option,
217 *((short*) option->data));
220 static int num_from_string (struct option_t* dst, const char* val,
221 char* errbuf, ssize_t errlen) {
222 int num = 0, old = 0;
228 num = strtol (val, &t, 0);
230 if (!*val || *t || (short) num != num) {
232 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
238 /* just temporarily accept new val so that check_special for
239 * $history already has it when doing history's init() */
240 old = *((short*) dst->data);
241 *((short*) dst->data) = (short) num;
243 if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
244 *((short*) dst->data) = old;
251 static void str_to_string (char* dst, ssize_t dstlen,
252 struct option_t* option) {
253 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
254 NONULL (*((char**) option->data)));
257 static void user_to_string (char* dst, ssize_t dstlen,
258 struct option_t* option) {
259 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
260 NONULL (((char*) option->data)));
263 static void sys_to_string (char* dst, ssize_t dstlen,
264 struct option_t* option) {
265 char *val = NULL, *t = NULL;
268 /* get some $madmutt_ values dynamically */
269 if (ascii_strcmp ("madmutt_pwd", option->option) == 0) {
270 val = p_new(char, _POSIX_PATH_MAX);
271 val = getcwd (val, _POSIX_PATH_MAX-1);
273 } else if (ascii_strcmp ("madmutt_folder_path", option->option) == 0 &&
274 CurrentFolder && *CurrentFolder) {
276 } else if (ascii_strcmp ("madmutt_folder_name", option->option) == 0 &&
277 CurrentFolder && *CurrentFolder) {
279 ssize_t Maildirlength = m_strlen(Maildir);
282 * if name starts with $folder, just strip it to keep hierarchy
283 * $folder=imap://host, path=imap://host/inbox/b -> inbox/b
285 if (Maildirlength > 0 && m_strncmp(CurrentFolder, Maildir,
286 Maildirlength) == 0 &&
287 m_strlen(CurrentFolder) > Maildirlength) {
288 val = CurrentFolder + Maildirlength;
289 if (Maildir[strlen(Maildir)-1]!='/')
291 /* if not $folder, just use everything after last / */
292 } else if ((t = strrchr (CurrentFolder, '/')) != NULL)
294 /* default: use as-is */
296 val = (char *) CurrentFolder;
299 val = (char *) option->init;
301 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (val));
306 static int path_from_string (struct option_t* dst, const char* val,
307 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
308 char path[_POSIX_PATH_MAX];
314 p_delete((char**) dst->data);
319 m_strcpy(path, sizeof(path), val);
320 mutt_expand_path (path, sizeof(path));
321 m_strreplace((char **) dst->data, path);
325 static int str_from_string (struct option_t* dst, const char* val,
326 char* errbuf, ssize_t errlen) {
330 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
333 m_strreplace((char**) dst->data, val);
337 static int user_from_string (struct option_t* dst, const char* val,
338 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
339 /* if dst == NULL, we may get here in case the user did unset it,
340 * see parse_set() where item is free()'d before coming here; so
341 * just silently ignore it */
344 if (m_strlen((char*) dst->data) == 0)
345 dst->data = (unsigned long) m_strdup(val);
347 char* s = (char*) dst->data;
348 m_strreplace(&s, val);
350 if (m_strlen(dst->init) == 0)
351 dst->init = m_strdup((char*) dst->data);
355 static void quad_to_string (char* dst, ssize_t dstlen,
356 struct option_t* option) {
357 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
358 snprintf (dst, dstlen, "%s=%s", option->option,
359 vals[quadoption (option->data)]);
362 static int quad_from_string (struct option_t* dst, const char* val,
363 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
368 if (ascii_strncasecmp (val, "yes", 3) == 0)
370 else if (ascii_strncasecmp (val, "no", 2) == 0)
372 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
374 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
380 set_quadoption (dst->data, flag);
384 static void sort_to_string (char* dst, ssize_t dstlen,
385 struct option_t* option) {
386 const struct mapping_t *map = get_sortmap (option);
387 const char *p = NULL;
390 snprintf (dst, sizeof(dst), "%s=unknown", option->option);
394 p = mutt_getnamebyvalue(*((short *)option->data) & SORT_MASK, map);
396 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
397 (*((short *) option->data) & SORT_REVERSE) ?
399 (*((short *) option->data) & SORT_LAST) ? "last-" :
403 static int sort_from_string (struct option_t* dst, const char* val,
404 char* errbuf, ssize_t errlen) {
405 const struct mapping_t *map = NULL;
406 if (!(map = get_sortmap (dst))) {
408 snprintf (errbuf, errlen, _("%s: Unknown type."),
412 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
417 static void rx_to_string (char* dst, ssize_t dstlen,
418 struct option_t* option) {
419 rx_t* p = (rx_t*) option->data;
420 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
421 NONULL (p->pattern));
424 static int rx_from_string (struct option_t* dst, const char* val,
425 char* errbuf, ssize_t errlen) {
428 int flags = 0, e = 0, not = 0;
434 if (option (OPTATTACHMSG) && !m_strcmp(dst->option, "reply_regexp")) {
436 snprintf (errbuf, errlen,
437 "Operation not permitted when in attach-message mode.");
441 if (!((rx_t*) dst->data))
442 *((rx_t**) dst->data) = p_new(rx_t, 1);
444 p = (rx_t*) dst->data;
446 /* something to do? */
447 if (!val || !*val || (p->pattern && m_strcmp(p->pattern, val) == 0))
450 if (m_strcmp(dst->option, "mask") != 0)
451 flags |= mutt_which_case (val);
454 if (m_strcmp(dst->option, "mask") == 0 && *s == '!') {
459 rx = p_new(regex_t, 1);
461 if ((e = REGCOMP (rx, s, flags)) != 0) {
462 regerror (e, rx, errbuf, errlen);
473 m_strreplace(&p->pattern, val);
477 if (m_strcmp(dst->option, "reply_regexp") == 0)
478 mutt_adjust_all_subjects ();
483 static void magic_to_string (char* dst, ssize_t dstlen,
484 struct option_t* option) {
485 const char* s = NULL;
486 switch (option->data) {
487 case M_MBOX: s = "mbox"; break;
488 case M_MMDF: s = "MMDF"; break;
489 case M_MH: s = "MH"; break;
490 case M_MAILDIR: s = "Maildir"; break;
491 default: s = "unknown"; break;
493 snprintf (dst, dstlen, "%s=%s", option->option, s);
496 static int magic_from_string (struct option_t* dst, const char* val,
497 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
500 if (!dst || !val || !*val)
502 if (ascii_strncasecmp (val, "mbox", 4) == 0)
504 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
506 else if (ascii_strncasecmp (val, "mh", 2) == 0)
508 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
514 *((short*) dst->data) = flag;
519 static void addr_to_string (char* dst, ssize_t dstlen,
520 struct option_t* option) {
523 rfc822_write_address (s, sizeof(s), *((address_t**) option->data), 0);
524 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
527 static int addr_from_string (struct option_t* dst, const char* val,
528 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
531 address_list_wipe((address_t**) dst->data);
533 *((address_t**) dst->data) = rfc822_parse_adrlist (NULL, val);
537 int mutt_option_value (const char* val, char* dst, ssize_t dstlen) {
538 struct option_t* option = NULL;
539 char* tmp = NULL, *t = NULL;
542 if (!(option = hash_find (ConfigOptions, val))) {
543 debug_print (1, ("var '%s' not found\n", val));
547 tmp = p_new(char, dstlen+1);
548 FuncTable[DTYPE (option->type)].opt_to_string (tmp, dstlen, option);
550 /* as we get things of type $var=value and don't want to bloat the
551 * above "just" for expansion, we do the stripping here */
552 debug_print (1, ("orig == '%s'\n", tmp));
553 t = strchr (tmp, '=');
557 if (t[l-1] == '"' && *t == '"') {
562 memcpy (dst, t, l+1);
564 debug_print (1, ("stripped == '%s'\n", dst));
569 /* for synonym warning reports: adds synonym to end of list */
570 static void syn_add (struct option_t* n, struct option_t* o) {
571 syn_t* tmp = p_new(syn_t, 1);
572 tmp->f = m_strdup(CurRCFile);
576 list_push_back (&Synonyms, tmp);
579 /* for synonym warning reports: free single item (for list_del()) */
580 static void syn_del (void** p) {
581 p_delete(&(*(syn_t**) p)->f);
585 void toggle_quadoption (int opt)
588 int b = (opt % 4) * 2;
590 QuadOptions[n] ^= (1 << b);
593 void set_quadoption (int opt, int flag)
596 int b = (opt % 4) * 2;
598 QuadOptions[n] &= ~(0x3 << b);
599 QuadOptions[n] |= (flag & 0x3) << b;
602 int quadoption (int opt)
605 int b = (opt % 4) * 2;
607 return (QuadOptions[n] >> b) & 0x3;
610 int query_quadoption (int opt, const char *prompt)
612 int v = quadoption (opt);
620 v = mutt_yesorno (prompt, (v == M_ASKYES));
621 CLEARLINE (LINES - 1);
628 static void add_to_list (string_list_t ** list, const char *str)
630 string_list_t *t, *last = NULL;
632 /* don't add a NULL or empty string to the list */
633 if (!str || *str == '\0')
636 /* check to make sure the item is not already on this list */
637 for (last = *list; last; last = last->next) {
638 if (ascii_strcasecmp (str, last->data) == 0) {
639 /* already on the list, so just ignore it */
647 if (!*list || last) {
648 t = p_new(string_list_t, 1);
649 t->data = m_strdup(str);
659 static int add_to_rx_list (list2_t** list, const char *s, int flags,
668 if (!(rx = rx_compile (s, flags))) {
669 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
673 i = rx_lookup ((*list), rx->pattern);
677 list_push_back (list, rx);
681 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
682 const char *templ, BUFFER * err)
684 SPAM_LIST *t = NULL, *last = NULL;
689 if (!pat || !*pat || !templ)
692 if (!(rx = rx_compile (pat, REG_ICASE))) {
693 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
697 /* check to make sure the item is not already on this list */
698 for (last = *list; last; last = last->next) {
699 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
700 /* Already on the list. Formerly we just skipped this case, but
701 * now we're supporting removals, which means we're supporting
702 * re-adds conceptually. So we probably want this to imply a
703 * removal, then do an add. We can achieve the removal by freeing
704 * the template, and leaving t pointed at the current item.
707 p_delete(&t->template);
714 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
715 * update. Otherwise we want to make a new one to link at the list's end.
718 t = mutt_new_spam_list ();
726 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
727 t->template = m_strdup(templ);
729 /* Find highest match number in template string */
731 for (p = templ; *p;) {
736 while (*p && isdigit ((int) *p))
742 t->nmatch++; /* match 0 is always the whole expr */
747 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
749 SPAM_LIST *spam, *prev;
752 /* Being first is a special case. */
756 if (spam->rx && !m_strcmp(spam->rx->pattern, pat)) {
758 rx_delete(&spam->rx);
759 p_delete(&spam->template);
765 for (spam = prev->next; spam;) {
766 if (!m_strcmp(spam->rx->pattern, pat)) {
767 prev->next = spam->next;
768 rx_delete(&spam->rx);
769 p_delete(&spam->template);
782 static void remove_from_list (string_list_t ** l, const char *str)
784 string_list_t *p, *last = NULL;
786 if (m_strcmp("*", str) == 0)
787 string_list_wipe(l); /* ``unCMD *'' means delete all current entries */
792 if (ascii_strcasecmp (str, p->data) == 0) {
795 last->next = p->next;
808 static int remove_from_rx_list (list2_t** l, const char *str)
812 if (m_strcmp("*", str) == 0) {
813 list_del (l, (list_del_t*) rx_delete);
817 i = rx_lookup ((*l), str);
819 rx_t* r = list_pop_idx ((*l), i);
827 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
831 unsigned long res = 0;
833 struct option_t* option = NULL;
836 mutt_extract_token (tmp, s, 0);
838 /* is the item defined as a variable or a function? */
839 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL)
842 for (i = 0; !res && i < MENU_MAX; i++) {
843 struct binding_t *b = km_get_table (Menus[i].value);
848 for (j = 0; b[j].name; j++)
849 if (!ascii_strncasecmp (tmp->data, b[j].name, m_strlen(tmp->data))
850 && (m_strlen(b[j].name) == m_strlen(tmp->data))) {
856 /* check for feature_* */
857 if (!res && ascii_strncasecmp (tmp->data, "feature_", 8) == 0 &&
858 (j = m_strlen(tmp->data)) > 8) {
860 while (Features[i]) {
861 if (m_strlen(Features[i]) == j-8 &&
862 ascii_strncasecmp (Features[i], tmp->data+8, j-8) == 0) {
872 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
874 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
878 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
881 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
882 mutt_error ("Error: %s", err->data);
883 p_delete(&token.data);
886 p_delete(&token.data);
891 static int parse_unignore (BUFFER * buf, BUFFER * s,
892 unsigned long data __attribute__ ((unused)),
893 BUFFER * err __attribute__ ((unused)))
896 mutt_extract_token (buf, s, 0);
898 /* don't add "*" to the unignore list */
899 if (strcmp (buf->data, "*"))
900 add_to_list (&UnIgnore, buf->data);
902 remove_from_list (&Ignore, buf->data);
904 while (MoreArgs (s));
909 static int parse_ignore (BUFFER * buf, BUFFER * s,
910 unsigned long data __attribute__ ((unused)),
911 BUFFER * err __attribute__ ((unused)))
914 mutt_extract_token (buf, s, 0);
915 remove_from_list (&UnIgnore, buf->data);
916 add_to_list (&Ignore, buf->data);
918 while (MoreArgs (s));
923 static int parse_list (BUFFER * buf, BUFFER * s,
924 unsigned long data __attribute__ ((unused)),
925 BUFFER * err __attribute__ ((unused)))
928 mutt_extract_token (buf, s, 0);
929 add_to_list ((string_list_t **) data, buf->data);
931 while (MoreArgs (s));
936 static void _alternates_clean (void)
940 if (Context && Context->msgcount) {
941 for (i = 0; i < Context->msgcount; i++)
942 Context->hdrs[i]->recip_valid = 0;
946 static int parse_alternates (BUFFER * buf, BUFFER * s,
947 unsigned long data __attribute__ ((unused)),
948 BUFFER * err __attribute__ ((unused)))
950 _alternates_clean ();
952 mutt_extract_token (buf, s, 0);
953 remove_from_rx_list (&UnAlternates, buf->data);
955 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
958 while (MoreArgs (s));
963 static int parse_unalternates (BUFFER * buf, BUFFER * s,
964 unsigned long data __attribute__ ((unused)),
965 BUFFER * err __attribute__ ((unused)))
967 _alternates_clean ();
969 mutt_extract_token (buf, s, 0);
970 remove_from_rx_list (&Alternates, buf->data);
972 if (m_strcmp(buf->data, "*") &&
973 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
977 while (MoreArgs (s));
982 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
989 /* Insist on at least one parameter */
992 m_strcpy(err->data, err->dsize, _("spam: no matching pattern"));
994 m_strcpy(err->data, err->dsize, _("nospam: no matching pattern"));
998 /* Extract the first token, a regexp */
999 mutt_extract_token (buf, s, 0);
1001 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
1002 if (data == M_SPAM) {
1003 /* If there's a second parameter, it's a template for the spam tag. */
1005 mutt_extract_token (&templ, s, 0);
1007 /* Add to the spam list. */
1008 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
1009 p_delete(&templ.data);
1012 p_delete(&templ.data);
1015 /* If not, try to remove from the nospam list. */
1017 remove_from_rx_list (&NoSpamList, buf->data);
1023 /* M_NOSPAM is for nospam commands. */
1024 else if (data == M_NOSPAM) {
1025 /* nospam only ever has one parameter. */
1027 /* "*" is a special case. */
1028 if (!m_strcmp(buf->data, "*")) {
1029 mutt_free_spam_list (&SpamList);
1030 list_del (&NoSpamList, (list_del_t*) rx_delete);
1034 /* If it's on the spam list, just remove it. */
1035 if (remove_from_spam_list (&SpamList, buf->data) != 0)
1038 /* Otherwise, add it to the nospam list. */
1039 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
1045 /* This should not happen. */
1046 m_strcpy(err->data, err->dsize, "This is no good at all.");
1050 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
1051 BUFFER * err __attribute__ ((unused)))
1054 mutt_extract_token (buf, s, 0);
1056 * Check for deletion of entire list
1058 if (m_strcmp(buf->data, "*") == 0) {
1059 string_list_wipe((string_list_t **) data);
1062 remove_from_list ((string_list_t **) data, buf->data);
1064 while (MoreArgs (s));
1069 static int parse_lists (BUFFER * buf, BUFFER * s,
1070 unsigned long data __attribute__ ((unused)),
1074 mutt_extract_token (buf, s, 0);
1075 remove_from_rx_list (&UnMailLists, buf->data);
1077 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1080 while (MoreArgs (s));
1085 /* always wise to do what someone else did before */
1086 static void _attachments_clean (void) {
1088 if (Context && Context->msgcount) {
1089 for (i = 0; i < Context->msgcount; i++)
1090 Context->hdrs[i]->attach_valid = 0;
1094 static int parse_attach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
1095 BUFFER *err __attribute__ ((unused))) {
1097 string_list_t *listp, *lastp;
1102 /* Find the last item in the list that data points to. */
1104 debug_print (5, ("parse_attach_list: ldata = %p, *ldata = %p\n",
1106 for (listp = *ldata; listp; listp = listp->next) {
1107 a = (ATTACH_MATCH *)listp->data;
1108 debug_print (5, ("parse_attach_list: skipping %s/%s\n", a->major, a->minor));
1113 mutt_extract_token (buf, s, 0);
1115 if (!buf->data || *buf->data == '\0')
1118 a = p_new(ATTACH_MATCH, 1);
1120 /* some cheap hacks that I expect to remove */
1121 if (!m_strcasecmp(buf->data, "any"))
1122 a->major = m_strdup("*/.*");
1123 else if (!m_strcasecmp(buf->data, "none"))
1124 a->major = m_strdup("cheap_hack/this_should_never_match");
1126 a->major = m_strdup(buf->data);
1128 if ((p = strchr(a->major, '/'))) {
1133 a->minor = "unknown";
1136 len = m_strlen(a->minor);
1137 tmpminor = p_new(char, len + 3);
1138 strcpy(&tmpminor[1], a->minor); /* __STRCPY_CHECKED__ */
1140 tmpminor[len+1] = '$';
1141 tmpminor[len+2] = '\0';
1143 a->major_int = mutt_check_mime_type(a->major);
1144 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
1146 p_delete(&tmpminor);
1148 debug_print (5, ("parse_attach_list: added %s/%s [%d]\n",
1149 a->major, a->minor, a->major_int));
1151 listp = p_new(string_list_t, 1);
1152 listp->data = (char *)a;
1155 lastp->next = listp;
1161 while (MoreArgs (s));
1163 _attachments_clean();
1167 static int parse_unattach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
1168 BUFFER *err __attribute__ ((unused))) {
1170 string_list_t *lp, *lastp, *newlp;
1176 mutt_extract_token (buf, s, 0);
1178 if (!m_strcasecmp(buf->data, "any"))
1179 tmp = m_strdup("*/.*");
1180 else if (!m_strcasecmp(buf->data, "none"))
1181 tmp = m_strdup("cheap_hack/this_should_never_match");
1183 tmp = m_strdup(buf->data);
1185 if ((minor = strchr(tmp, '/'))) {
1189 minor = m_strdup("unknown");
1191 major = mutt_check_mime_type(tmp);
1193 /* We must do our own walk here because remove_from_list() will only
1194 * remove the string_list_t->data, not anything pointed to by the string_list_t->data. */
1196 for(lp = *ldata; lp; ) {
1197 a = (ATTACH_MATCH *)lp->data;
1198 debug_print(5, ("parse_unattach_list: check %s/%s [%d] : %s/%s [%d]\n",
1199 a->major, a->minor, a->major_int, tmp, minor, major));
1200 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
1201 debug_print(5, ("parse_unattach_list: removed %s/%s [%d]\n",
1202 a->major, a->minor, a->major_int));
1203 regfree(&a->minor_rx);
1204 p_delete(&a->major);
1206 /* Relink backward */
1208 lastp->next = lp->next;
1213 p_delete(&lp->data); /* same as a */
1223 while (MoreArgs (s));
1226 _attachments_clean();
1230 static int print_attach_list (string_list_t *lp, char op, const char *name) {
1232 printf("attachments %c%s %s/%s\n", op, name,
1233 ((ATTACH_MATCH *)lp->data)->major,
1234 ((ATTACH_MATCH *)lp->data)->minor);
1241 static int parse_attachments (BUFFER *buf, BUFFER *s,
1242 unsigned long data __attribute__ ((unused)),
1245 string_list_t **listp;
1247 mutt_extract_token(buf, s, 0);
1248 if (!buf->data || *buf->data == '\0') {
1249 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
1253 category = buf->data;
1259 printf("\nCurrent attachments settings:\n\n");
1260 print_attach_list(AttachAllow, '+', "A");
1261 print_attach_list(AttachExclude, '-', "A");
1262 print_attach_list(InlineAllow, '+', "I");
1263 print_attach_list(InlineExclude, '-', "I");
1264 set_option (OPTFORCEREDRAWINDEX);
1265 set_option (OPTFORCEREDRAWPAGER);
1266 mutt_any_key_to_continue (NULL);
1270 if (op != '+' && op != '-') {
1274 if (!m_strncasecmp(category, "attachment", strlen(category))) {
1276 listp = &AttachAllow;
1278 listp = &AttachExclude;
1280 else if (!m_strncasecmp(category, "inline", strlen(category))) {
1282 listp = &InlineAllow;
1284 listp = &InlineExclude;
1286 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
1290 return parse_attach_list(buf, s, listp, err);
1293 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
1295 string_list_t **listp;
1297 mutt_extract_token(buf, s, 0);
1298 if (!buf->data || *buf->data == '\0') {
1299 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
1305 if (op != '+' && op != '-') {
1309 if (!m_strncasecmp(p, "attachment", strlen(p))) {
1311 listp = &AttachAllow;
1313 listp = &AttachExclude;
1315 else if (!m_strncasecmp(p, "inline", strlen(p))) {
1317 listp = &InlineAllow;
1319 listp = &InlineExclude;
1322 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
1326 return parse_unattach_list(buf, s, listp, err);
1329 static int parse_unlists (BUFFER * buf, BUFFER * s,
1330 unsigned long data __attribute__ ((unused)),
1331 BUFFER * err __attribute__ ((unused)))
1334 mutt_extract_token (buf, s, 0);
1335 remove_from_rx_list (&SubscribedLists, buf->data);
1336 remove_from_rx_list (&MailLists, buf->data);
1338 if (m_strcmp(buf->data, "*") &&
1339 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1342 while (MoreArgs (s));
1347 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1351 mutt_extract_token (buf, s, 0);
1352 remove_from_rx_list (&UnMailLists, buf->data);
1353 remove_from_rx_list (&UnSubscribedLists, buf->data);
1355 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1357 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1360 while (MoreArgs (s));
1365 static int parse_unsubscribe (BUFFER * buf, BUFFER * s,
1366 unsigned long data __attribute__ ((unused)),
1367 BUFFER * err __attribute__ ((unused)))
1370 mutt_extract_token (buf, s, 0);
1371 remove_from_rx_list (&SubscribedLists, buf->data);
1373 if (m_strcmp(buf->data, "*") &&
1374 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1377 while (MoreArgs (s));
1382 static int parse_unalias (BUFFER * buf, BUFFER * s,
1383 unsigned long data __attribute__ ((unused)),
1384 BUFFER * err __attribute__ ((unused)))
1386 alias_t *tmp, *last = NULL;
1389 mutt_extract_token (buf, s, 0);
1391 if (m_strcmp("*", buf->data) == 0) {
1392 if (CurrentMenu == MENU_ALIAS) {
1393 for (tmp = Aliases; tmp; tmp = tmp->next)
1395 set_option (OPTFORCEREDRAWINDEX);
1398 alias_list_wipe(&Aliases);
1402 for (tmp = Aliases; tmp; tmp = tmp->next) {
1403 if (m_strcasecmp(buf->data, tmp->name) == 0) {
1404 if (CurrentMenu == MENU_ALIAS) {
1406 set_option (OPTFORCEREDRAWINDEX);
1411 last->next = tmp->next;
1413 Aliases = tmp->next;
1415 alias_list_wipe(&tmp);
1421 while (MoreArgs (s));
1425 static int parse_alias (BUFFER * buf, BUFFER * s,
1426 unsigned long data __attribute__ ((unused)),
1429 alias_t *tmp = Aliases;
1430 alias_t *last = NULL;
1431 const char *estr = NULL;
1433 if (!MoreArgs (s)) {
1434 m_strcpy(err->data, err->dsize, _("alias: no address"));
1438 mutt_extract_token (buf, s, 0);
1440 debug_print (2, ("first token is '%s'.\n", buf->data));
1442 /* check to see if an alias with this name already exists */
1443 for (; tmp; tmp = tmp->next) {
1444 if (!m_strcasecmp(tmp->name, buf->data))
1450 /* create a new alias */
1452 tmp->name = m_strdup(buf->data);
1453 /* give the main addressbook code a chance */
1454 if (CurrentMenu == MENU_ALIAS)
1455 set_option (OPTMENUCALLER);
1458 /* override the previous value */
1459 address_list_wipe(&tmp->addr);
1460 if (CurrentMenu == MENU_ALIAS)
1461 set_option (OPTFORCEREDRAWINDEX);
1464 mutt_extract_token (buf, s,
1465 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1466 debug_print (2, ("second token is '%s'.\n", buf->data));
1467 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1472 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1473 snprintf (err->data, err->dsize,
1474 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1478 if (DebugLevel >= 2) {
1481 /* A group is terminated with an empty address, so check a->mailbox */
1482 for (a = tmp->addr; a && a->mailbox; a = a->next) {
1484 debug_print (2, ("%s\n", a->mailbox));
1486 debug_print (2, ("group %s\n", a->mailbox));
1494 parse_unmy_hdr (BUFFER * buf, BUFFER * s,
1495 unsigned long data __attribute__ ((unused)),
1496 BUFFER * err __attribute__ ((unused)))
1498 string_list_t *last = NULL;
1499 string_list_t *tmp = UserHeader;
1504 mutt_extract_token (buf, s, 0);
1505 if (m_strcmp("*", buf->data) == 0)
1506 string_list_wipe(&UserHeader);
1511 l = m_strlen(buf->data);
1512 if (buf->data[l - 1] == ':')
1516 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1517 && tmp->data[l] == ':') {
1520 last->next = tmp->next;
1522 UserHeader = tmp->next;
1525 string_list_wipe(&ptr);
1534 while (MoreArgs (s));
1538 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1545 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1546 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1547 m_strcpy(err->data, err->dsize, _("invalid header field"));
1550 keylen = p - buf->data + 1;
1553 for (tmp = UserHeader;; tmp = tmp->next) {
1554 /* see if there is already a field by this name */
1555 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1556 /* replace the old value */
1557 p_delete(&tmp->data);
1558 tmp->data = buf->data;
1565 tmp->next = string_item_new();
1569 tmp = string_item_new();
1572 tmp->data = buf->data;
1578 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1579 char* errbuf, ssize_t errlen) {
1582 if (m_strncmp("reverse-", s, 8) == 0) {
1584 flags = SORT_REVERSE;
1587 if (m_strncmp("last-", s, 5) == 0) {
1592 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1594 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1598 *((short*) dst->data) = i | flags;
1602 /* if additional data more == 1, we want to resolve synonyms */
1603 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
1605 char buf[LONG_STRING];
1606 struct option_t *ptr = p;
1608 if (DTYPE(ptr->type) == DT_SYN) {
1611 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1613 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_from_string)
1616 mutt_option_value(ptr->option, buf, sizeof(buf));
1617 if (m_strlen(ptr->init) == 0 && buf && *buf)
1618 ptr->init = m_strdup(buf);
1621 static struct option_t* add_option (const char* name, const char* init,
1622 short type, short dodup) {
1623 struct option_t* option = p_new(struct option_t, 1);
1625 debug_print (1, ("adding $%s\n", name));
1627 option->option = m_strdup(name);
1628 option->type = type;
1630 option->init = dodup ? m_strdup(init) : (char*) init;
1634 /* creates new option_t* of type DT_USER for $user_ var */
1635 static struct option_t* add_user_option (const char* name) {
1636 return (add_option (name, NULL, DT_USER, 1));
1639 /* free()'s option_t* */
1640 static void del_option (void* p) {
1641 struct option_t *ptr = (struct option_t*) p;
1642 char* s = (char*) ptr->data;
1643 debug_print (1, ("removing option '%s' from table\n", NONULL (ptr->option)));
1644 p_delete(&ptr->option);
1646 p_delete(&ptr->init);
1650 static int init_expand (char** dst, struct option_t* src) {
1656 if (DTYPE(src->type) == DT_STR ||
1657 DTYPE(src->type) == DT_PATH) {
1658 /* only expand for string as it's the only place where
1659 * we want to expand vars right now */
1660 if (src->init && *src->init) {
1663 len = m_strlen(src->init) + 2;
1664 in.data = p_new(char, len + 1);
1665 snprintf (in.data, len, "\"%s\"", src->init);
1668 mutt_extract_token (&token, &in, 0);
1669 if (token.data && *token.data)
1670 *dst = m_strdup(token.data);
1672 *dst = m_strdup("");
1674 p_delete(&token.data);
1676 *dst = m_strdup("");
1678 /* for non-string: take value as is */
1679 *dst = m_strdup(src->init);
1683 /* if additional data more == 1, we want to resolve synonyms */
1684 static void mutt_restore_default (const char* name __attribute__ ((unused)),
1685 void* p, unsigned long more) {
1686 char errbuf[STRING];
1687 struct option_t* ptr = (struct option_t*) p;
1690 if (DTYPE (ptr->type) == DT_SYN) {
1693 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1697 if (FuncTable[DTYPE (ptr->type)].opt_from_string) {
1698 init_expand (&init, ptr);
1699 if (!FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, init, errbuf,
1701 if (!option (OPTNOCURSES))
1703 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1704 "Please report this error: \"%s\"\n"),
1705 ptr->option, NONULL (init), errbuf);
1711 if (ptr->flags & R_INDEX)
1712 set_option (OPTFORCEREDRAWINDEX);
1713 if (ptr->flags & R_PAGER)
1714 set_option (OPTFORCEREDRAWPAGER);
1715 if (ptr->flags & R_RESORT_SUB)
1716 set_option (OPTSORTSUBTHREADS);
1717 if (ptr->flags & R_RESORT)
1718 set_option (OPTNEEDRESORT);
1719 if (ptr->flags & R_RESORT_INIT)
1720 set_option (OPTRESORTINIT);
1721 if (ptr->flags & R_TREE)
1722 set_option (OPTREDRAWTREE);
1725 /* check whether value for $dsn_return would be valid */
1726 static int check_dsn_return (const char* option __attribute__ ((unused)), unsigned long p,
1727 char* errbuf, ssize_t errlen) {
1728 char* val = (char*) p;
1729 if (val && *val && m_strncmp(val, "hdrs", 4) != 0 &&
1730 m_strncmp(val, "full", 4) != 0) {
1732 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1738 /* check whether value for $dsn_notify would be valid */
1739 static int check_dsn_notify (const char* option, unsigned long p,
1740 char* errbuf, ssize_t errlen) {
1741 list2_t* list = NULL;
1744 char* val = (char*) p;
1748 list = list_from_str (val, ",");
1749 if (list_empty (list))
1752 for (i = 0; i < list->length; i++)
1753 if (m_strncmp(list->data[i], "never", 5) != 0 &&
1754 m_strncmp(list->data[i], "failure", 7) != 0 &&
1755 m_strncmp(list->data[i], "delay", 5) != 0 &&
1756 m_strncmp(list->data[i], "success", 7) != 0) {
1758 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1759 (char*) list->data[i], "dsn_notify");
1763 list_del (&list, (list_del_t*)xmemfree);
1767 static int check_num (const char* option, unsigned long p,
1768 char* errbuf, ssize_t errlen) {
1771 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1778 static int check_debug (const char* option, unsigned long p,
1779 char* errbuf, ssize_t errlen) {
1780 if ((int) p <= DEBUG_MAX_LEVEL &&
1781 (int) p >= DEBUG_MIN_LEVEL)
1785 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1790 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1791 char* errbuf, ssize_t errlen) {
1792 if (!check_num ("history", p, errbuf, errlen))
1794 mutt_init_history ();
1798 static int check_special (const char* name, unsigned long val,
1799 char* errbuf, ssize_t errlen) {
1802 for (i = 0; SpecialVars[i].name; i++) {
1803 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1804 return (SpecialVars[i].check (SpecialVars[i].name,
1805 val, errbuf, errlen));
1811 static const struct mapping_t* get_sortmap (struct option_t* option) {
1812 const struct mapping_t* map = NULL;
1814 switch (option->type & DT_SUBTYPE_MASK) {
1816 map = SortAliasMethods;
1818 case DT_SORT_BROWSER:
1819 map = SortBrowserMethods;
1822 map = SortKeyMethods;
1825 map = SortAuxMethods;
1834 #define CHECK_PAGER \
1835 if ((CurrentMenu == MENU_PAGER) && \
1836 (!option || (option->flags & R_RESORT))) \
1838 snprintf (err->data, err->dsize, \
1839 _("Not available in this menu.")); \
1843 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1846 int query, unset, inv, reset, r = 0;
1847 struct option_t* option = NULL;
1849 while (MoreArgs (s)) {
1850 /* reset state variables */
1852 unset = data & M_SET_UNSET;
1853 inv = data & M_SET_INV;
1854 reset = data & M_SET_RESET;
1856 if (*s->dptr == '?') {
1860 else if (m_strncmp("no", s->dptr, 2) == 0) {
1864 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1868 else if (*s->dptr == '&') {
1873 /* get the variable name */
1874 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1876 /* resolve synonyms */
1877 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1878 DTYPE (option->type == DT_SYN)) {
1879 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1880 syn_add (newopt, option);
1884 /* see if we need to add $user_ var */
1885 if (!option && ascii_strncmp ("user_", tmp->data, 5) == 0) {
1886 /* there's no option named like this yet so only add one
1887 * if the action isn't any of: reset, unset, query */
1888 if (!(reset || unset || query || *s->dptr != '=')) {
1889 debug_print (1, ("adding user option '%s'\n", tmp->data));
1890 option = add_user_option (tmp->data);
1891 hash_insert (ConfigOptions, option->option, option, 0);
1895 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1896 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1899 s->dptr = vskipspaces(s->dptr);
1902 if (query || unset || inv) {
1903 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1907 if (s && *s->dptr == '=') {
1908 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1912 if (!m_strcmp("all", tmp->data)) {
1913 if (CurrentMenu == MENU_PAGER) {
1914 snprintf (err->data, err->dsize, _("Not available in this menu."));
1917 hash_map (ConfigOptions, mutt_restore_default, 1);
1918 set_option (OPTFORCEREDRAWINDEX);
1919 set_option (OPTFORCEREDRAWPAGER);
1920 set_option (OPTSORTSUBTHREADS);
1921 set_option (OPTNEEDRESORT);
1922 set_option (OPTRESORTINIT);
1923 set_option (OPTREDRAWTREE);
1926 else if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1927 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1932 mutt_restore_default (NULL, option, 1);
1935 else if (DTYPE (option->type) == DT_BOOL) {
1936 /* XXX this currently ignores the function table
1937 * as we don't get invert and stuff into it */
1938 if (s && *s->dptr == '=') {
1939 if (unset || inv || query) {
1940 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1945 mutt_extract_token (tmp, s, 0);
1946 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1948 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1951 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1957 bool_to_string (err->data, err->dsize, option);
1963 unset_option (option->data);
1965 toggle_option (option->data);
1967 set_option (option->data);
1969 else if (DTYPE (option->type) == DT_STR ||
1970 DTYPE (option->type) == DT_PATH ||
1971 DTYPE (option->type) == DT_ADDR ||
1972 DTYPE (option->type) == DT_MAGIC ||
1973 DTYPE (option->type) == DT_NUM ||
1974 DTYPE (option->type) == DT_SORT ||
1975 DTYPE (option->type) == DT_RX ||
1976 DTYPE (option->type) == DT_USER ||
1977 DTYPE (option->type) == DT_SYS) {
1979 /* XXX maybe we need to get unset into handlers? */
1980 if (DTYPE (option->type) == DT_STR ||
1981 DTYPE (option->type) == DT_PATH ||
1982 DTYPE (option->type) == DT_ADDR ||
1983 DTYPE (option->type) == DT_USER ||
1984 DTYPE (option->type) == DT_SYS) {
1987 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1988 snprintf (err->data, err->dsize, _("$%s is read-only"),
1992 } else if (DTYPE (option->type) == DT_ADDR)
1993 address_list_wipe((address_t **) option->data);
1994 else if (DTYPE (option->type) == DT_USER)
1995 /* to unset $user_ means remove */
1996 hash_delete (ConfigOptions, option->option,
1997 option, del_option);
1999 p_delete((void **)(void *)&option->data);
2004 if (query || *s->dptr != '=') {
2005 FuncTable[DTYPE (option->type)].opt_to_string
2006 (err->data, err->dsize, option);
2010 /* the $madmutt_ variables are read-only */
2011 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
2012 snprintf (err->data, err->dsize, _("$%s is read-only"),
2019 mutt_extract_token (tmp, s, 0);
2020 if (!FuncTable[DTYPE (option->type)].opt_from_string
2021 (option, tmp->data, err->data, err->dsize))
2025 else if (DTYPE (option->type) == DT_QUAD) {
2028 quad_to_string (err->data, err->dsize, option);
2032 if (*s->dptr == '=') {
2035 mutt_extract_token (tmp, s, 0);
2036 if (ascii_strcasecmp ("yes", tmp->data) == 0)
2037 set_quadoption (option->data, M_YES);
2038 else if (ascii_strcasecmp ("no", tmp->data) == 0)
2039 set_quadoption (option->data, M_NO);
2040 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
2041 set_quadoption (option->data, M_ASKYES);
2042 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
2043 set_quadoption (option->data, M_ASKNO);
2045 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
2046 tmp->data, option->option);
2053 toggle_quadoption (option->data);
2055 set_quadoption (option->data, M_NO);
2057 set_quadoption (option->data, M_YES);
2061 snprintf (err->data, err->dsize, _("%s: unknown type"),
2067 if (option->flags & R_INDEX)
2068 set_option (OPTFORCEREDRAWINDEX);
2069 if (option->flags & R_PAGER)
2070 set_option (OPTFORCEREDRAWPAGER);
2071 if (option->flags & R_RESORT_SUB)
2072 set_option (OPTSORTSUBTHREADS);
2073 if (option->flags & R_RESORT)
2074 set_option (OPTNEEDRESORT);
2075 if (option->flags & R_RESORT_INIT)
2076 set_option (OPTRESORTINIT);
2077 if (option->flags & R_TREE)
2078 set_option (OPTREDRAWTREE);
2085 /* reads the specified initialization file. returns -1 if errors were found
2086 so that we can pause to let the user know... */
2087 static int source_rc (const char *rcfile, BUFFER * err)
2090 int line = 0, rc = 0, conv = 0;
2092 char *linebuf = NULL;
2093 char *currentline = NULL;
2097 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
2099 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
2100 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
2105 while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
2106 conv = ConfigCharset && (*ConfigCharset) && Charset;
2108 currentline = m_strdup(linebuf);
2111 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
2114 currentline = linebuf;
2119 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
2120 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
2121 if (--rc < -MAXERRS) {
2123 p_delete(¤tline);
2132 p_delete(¤tline);
2134 p_delete(&token.data);
2138 mutt_wait_filter (pid);
2140 /* the muttrc source keyword */
2141 snprintf (err->data, err->dsize,
2142 rc >= -MAXERRS ? _("source: errors in %s")
2143 : _("source: reading aborted due too many errors in %s"),
2152 static int parse_source (BUFFER * tmp, BUFFER * s,
2153 unsigned long data __attribute__ ((unused)),
2156 char path[_POSIX_PATH_MAX];
2160 if (mutt_extract_token (tmp, s, 0) != 0) {
2161 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
2165 m_strcpy(path, sizeof(path), tmp->data);
2166 mutt_expand_path (path, sizeof(path));
2168 rc += source_rc (path, err);
2170 while (MoreArgs (s));
2172 return ((rc < 0) ? -1 : 0);
2175 /* line command to execute
2177 token scratch buffer to be used by parser. caller should free
2178 token->data when finished. the reason for this variable is
2179 to avoid having to allocate and deallocate a lot of memory
2180 if we are parsing many lines. the caller can pass in the
2181 memory to use, which avoids having to create new space for
2182 every call to this function.
2184 err where to write error messages */
2185 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
2191 expn.data = expn.dptr = line;
2192 expn.dsize = m_strlen(line);
2196 debug_print (1, ("expand '%s'\n", line));
2198 expn.dptr = vskipspaces(expn.dptr);
2199 while (*expn.dptr) {
2200 if (*expn.dptr == '#')
2201 break; /* rest of line is a comment */
2202 if (*expn.dptr == ';') {
2206 mutt_extract_token (token, &expn, 0);
2207 for (i = 0; Commands[i].name; i++) {
2208 if (!m_strcmp(token->data, Commands[i].name)) {
2209 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
2214 if (!Commands[i].name) {
2215 snprintf (err->data, err->dsize, _("%s: unknown command"),
2216 NONULL (token->data));
2223 p_delete(&expn.data);
2228 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
2229 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
2230 /* initial string that starts completion. No telling how much crap
2231 * the user has typed so far. Allocate LONG_STRING just to be sure! */
2232 char User_typed[LONG_STRING] = { 0 };
2234 int Num_matched = 0; /* Number of matches for completion */
2235 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
2236 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
2238 /* helper function for completion. Changes the dest buffer if
2239 necessary/possible to aid completion.
2240 dest == completion result gets here.
2241 src == candidate for completion.
2242 try == user entered data for completion.
2243 len == length of dest buffer.
2245 static void candidate (char *dest, char *try, const char *src, int len)
2249 if (strstr (src, try) == src) {
2250 Matches[Num_matched++] = src;
2252 m_strcpy(dest, len, src);
2254 for (l = 0; src[l] && src[l] == dest[l]; l++);
2260 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
2264 int spaces; /* keep track of the number of leading spaces on the line */
2266 buffer = vskipspaces(buffer);
2267 spaces = buffer - pt;
2269 pt = buffer + pos - spaces;
2270 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2273 if (pt == buffer) { /* complete cmd */
2274 /* first TAB. Collect all the matches */
2277 m_strcpy(User_typed, sizeof(User_typed), pt);
2278 p_clear(Matches, countof(Matches));
2279 p_clear(Completed, countof(Completed));
2280 for (num = 0; Commands[num].name; num++)
2281 candidate (Completed, User_typed, Commands[num].name,
2283 Matches[Num_matched++] = User_typed;
2285 /* All matches are stored. Longest non-ambiguous string is ""
2286 * i.e. dont change 'buffer'. Fake successful return this time */
2287 if (User_typed[0] == 0)
2291 if (Completed[0] == 0 && User_typed[0])
2294 /* Num_matched will _always_ be atleast 1 since the initial
2295 * user-typed string is always stored */
2296 if (numtabs == 1 && Num_matched == 2)
2297 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2298 else if (numtabs > 1 && Num_matched > 2)
2299 /* cycle thru all the matches */
2300 snprintf (Completed, sizeof(Completed), "%s",
2301 Matches[(numtabs - 2) % Num_matched]);
2303 /* return the completed command */
2304 m_strcpy(buffer, len - spaces, Completed);
2306 else if (!m_strncmp(buffer, "set", 3)
2307 || !m_strncmp(buffer, "unset", 5)
2308 || !m_strncmp(buffer, "reset", 5)
2309 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
2310 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
2313 /* loop through all the possible prefixes (no, inv, ...) */
2314 if (!m_strncmp(buffer, "set", 3)) {
2315 for (num = 0; prefixes[num]; num++) {
2316 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
2317 pt += m_strlen(prefixes[num]);
2323 /* first TAB. Collect all the matches */
2326 m_strcpy(User_typed, sizeof(User_typed), pt);
2327 p_clear(Matches, countof(Matches));
2328 p_clear(Completed, countof(Completed));
2329 for (num = 0; MuttVars[num].option; num++)
2330 candidate(Completed, User_typed, MuttVars[num].option,
2332 Matches[Num_matched++] = User_typed;
2334 /* All matches are stored. Longest non-ambiguous string is ""
2335 * i.e. dont change 'buffer'. Fake successful return this time */
2336 if (User_typed[0] == 0)
2340 if (Completed[0] == 0 && User_typed[0])
2343 /* Num_matched will _always_ be atleast 1 since the initial
2344 * user-typed string is always stored */
2345 if (numtabs == 1 && Num_matched == 2)
2346 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2347 else if (numtabs > 1 && Num_matched > 2)
2348 /* cycle thru all the matches */
2349 snprintf (Completed, sizeof(Completed), "%s",
2350 Matches[(numtabs - 2) % Num_matched]);
2352 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2354 else if (!m_strncmp(buffer, "exec", 4)) {
2355 struct binding_t *menu = km_get_table (CurrentMenu);
2357 if (!menu && CurrentMenu != MENU_PAGER)
2361 /* first TAB. Collect all the matches */
2364 m_strcpy(User_typed, sizeof(User_typed), pt);
2365 p_clear(Matches, countof(Matches));
2366 p_clear(Completed, countof(Completed));
2367 for (num = 0; menu[num].name; num++)
2368 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
2369 /* try the generic menu */
2370 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2372 for (num = 0; menu[num].name; num++)
2373 candidate (Completed, User_typed, menu[num].name,
2376 Matches[Num_matched++] = User_typed;
2378 /* All matches are stored. Longest non-ambiguous string is ""
2379 * i.e. dont change 'buffer'. Fake successful return this time */
2380 if (User_typed[0] == 0)
2384 if (Completed[0] == 0 && User_typed[0])
2387 /* Num_matched will _always_ be atleast 1 since the initial
2388 * user-typed string is always stored */
2389 if (numtabs == 1 && Num_matched == 2)
2390 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2391 else if (numtabs > 1 && Num_matched > 2)
2392 /* cycle thru all the matches */
2393 snprintf (Completed, sizeof(Completed), "%s",
2394 Matches[(numtabs - 2) % Num_matched]);
2396 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2404 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
2406 char var[STRING], *pt = buffer;
2408 struct option_t* option = NULL;
2413 buffer = vskipspaces(buffer);
2414 spaces = buffer - pt;
2416 pt = buffer + pos - spaces;
2417 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2419 pt++; /* move past the space */
2420 if (*pt == '=') /* abort if no var before the '=' */
2423 if (m_strncmp(buffer, "set", 3) == 0) {
2424 m_strcpy(var, sizeof(var), pt);
2425 /* ignore the trailing '=' when comparing */
2426 var[m_strlen(var) - 1] = 0;
2427 if (!(option = hash_find (ConfigOptions, var)))
2428 return 0; /* no such variable. */
2430 char tmp[LONG_STRING], tmp2[LONG_STRING];
2432 ssize_t dlen = buffer + len - pt - spaces;
2433 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2437 if ((DTYPE (option->type) == DT_STR) ||
2438 (DTYPE (option->type) == DT_PATH) ||
2439 (DTYPE (option->type) == DT_RX)) {
2440 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
2441 if (DTYPE (option->type) == DT_PATH)
2442 mutt_pretty_mailbox (tmp);
2444 else if (DTYPE (option->type) == DT_ADDR) {
2445 rfc822_write_address (tmp, sizeof(tmp),
2446 *((address_t **) option->data), 0);
2448 else if (DTYPE (option->type) == DT_QUAD)
2449 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
2450 else if (DTYPE (option->type) == DT_NUM)
2451 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
2452 else if (DTYPE (option->type) == DT_SORT) {
2453 const struct mapping_t *map;
2456 switch (option->type & DT_SUBTYPE_MASK) {
2458 map = SortAliasMethods;
2460 case DT_SORT_BROWSER:
2461 map = SortBrowserMethods;
2464 map = SortKeyMethods;
2470 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2471 snprintf(tmp, sizeof(tmp), "%s%s%s",
2472 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2473 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2475 else if (DTYPE (option->type) == DT_MAGIC) {
2477 switch (DefaultMagic) {
2493 m_strcpy(tmp, sizeof(tmp), p);
2495 else if (DTYPE (option->type) == DT_BOOL)
2496 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
2500 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
2501 if (*s == '\\' || *s == '"')
2507 m_strcpy(tmp, sizeof(tmp), pt);
2508 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2516 /* Implement the -Q command line flag */
2517 int mutt_query_variables (string_list_t * queries)
2521 char errbuff[STRING];
2522 char command[STRING];
2530 err.dsize = sizeof(errbuff);
2532 for (p = queries; p; p = p->next) {
2533 snprintf (command, sizeof(command), "set ?%s\n", p->data);
2534 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2535 fprintf (stderr, "%s\n", err.data);
2536 p_delete(&token.data);
2539 printf ("%s\n", err.data);
2542 p_delete(&token.data);
2546 static int mutt_execute_commands (string_list_t * p)
2549 char errstr[SHORT_STRING];
2553 err.dsize = sizeof(errstr);
2555 for (; p; p = p->next) {
2556 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2557 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2558 p_delete(&token.data);
2562 p_delete(&token.data);
2566 void mutt_init (int skip_sys_rc, string_list_t * commands)
2569 struct utsname utsname;
2571 char buffer[STRING], error[STRING];
2572 int default_rc = 0, need_pause = 0;
2578 err.dsize = sizeof(error);
2580 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2581 * to have some room for $user_ vars */
2582 ConfigOptions = hash_create (sizeof(MuttVars) * 3);
2583 for (i = 0; MuttVars[i].option; i++) {
2584 if (DTYPE (MuttVars[i].type) != DT_SYS)
2585 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2587 hash_insert (ConfigOptions, MuttVars[i].option,
2588 add_option (MuttVars[i].option, MuttVars[i].init,
2593 * XXX - use something even more difficult to predict?
2595 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2596 "\033]9;%ld\a", (long) time (NULL));
2598 /* on one of the systems I use, getcwd() does not return the same prefix
2599 as is listed in the passwd file */
2600 if ((p = getenv ("HOME")))
2601 Homedir = m_strdup(p);
2603 /* Get some information about the user */
2604 if ((pw = getpwuid (getuid ()))) {
2607 Username = m_strdup(pw->pw_name);
2609 Homedir = m_strdup(pw->pw_dir);
2611 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, GecosMask.rx);
2612 Realname = m_strdup(rnbuf);
2613 Shell = m_strdup(pw->pw_shell);
2619 fputs (_("unable to determine home directory"), stderr);
2622 if ((p = getenv ("USER")))
2623 Username = m_strdup(p);
2626 fputs (_("unable to determine username"), stderr);
2629 Shell = m_strdup((p = getenv ("SHELL")) ? p : "/bin/sh");
2632 debug_start(Homedir);
2634 /* And about the host... */
2636 /* some systems report the FQDN instead of just the hostname */
2637 if ((p = strchr (utsname.nodename, '.'))) {
2638 Hostname = p_dupstr(utsname.nodename, p - utsname.nodename);
2640 m_strcpy(buffer, sizeof(buffer), p); /* save the domain for below */
2643 Hostname = m_strdup(utsname.nodename);
2645 if (!p && getdnsdomainname(buffer, sizeof(buffer)) == -1)
2646 Fqdn = m_strdup("@");
2648 if (*buffer != '@') {
2649 Fqdn = p_new(char, m_strlen(buffer) + m_strlen(Hostname) + 2);
2650 sprintf (Fqdn, "%s.%s", NONULL(Hostname), buffer); /* __SPRINTF_CHECKED__ */
2653 Fqdn = m_strdup(NONULL (Hostname));
2660 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2662 fgets (buffer, sizeof(buffer), f);
2663 p = vskipspaces(buffer);
2665 while (*q && !isspace(*q))
2668 NewsServer = m_strdup(p);
2672 if ((p = getenv ("NNTPSERVER")))
2673 NewsServer = m_strdup(p);
2676 if ((p = getenv ("MAIL")))
2677 Spoolfile = m_strdup(p);
2678 else if ((p = getenv ("MAILDIR")))
2679 Spoolfile = m_strdup(p);
2682 mutt_concat_path(buffer, sizeof(buffer), NONULL(Homedir), MAILPATH);
2684 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(Username));
2686 Spoolfile = m_strdup(buffer);
2689 if ((p = getenv ("MAILCAPS")))
2690 MailcapPath = m_strdup(p);
2692 /* Default search path from RFC1524 */
2694 m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2695 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2698 Tempdir = m_strdup((p = getenv ("TMPDIR")) ? p : "/tmp");
2700 p = getenv ("VISUAL");
2702 p = getenv ("EDITOR");
2706 Editor = m_strdup(p);
2708 if ((p = getenv ("REPLYTO")) != NULL) {
2711 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2714 buf.data = buf.dptr = buffer;
2715 buf.dsize = m_strlen(buffer);
2718 parse_my_hdr (&token, &buf, 0, &err);
2719 p_delete(&token.data);
2722 if ((p = getenv ("EMAIL")) != NULL)
2723 From = rfc822_parse_adrlist (NULL, p);
2725 mutt_set_langinfo_charset ();
2726 mutt_set_charset (Charset);
2729 /* Set standard defaults */
2730 hash_map (ConfigOptions, mutt_set_default, 0);
2731 hash_map (ConfigOptions, mutt_restore_default, 0);
2733 CurrentMenu = MENU_MAIN;
2735 /* Do we have a locale definition? */
2736 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2737 ((p = getenv ("LANG")) != NULL && p[0]) ||
2738 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2739 set_option (OPTLOCALES);
2742 /* Unset suspend by default if we're the session leader */
2743 if (getsid (0) == getpid ())
2744 unset_option (OPTSUSPEND);
2747 mutt_init_history ();
2756 * When changing the code which looks for a configuration file,
2757 * please also change the corresponding code in muttbug.sh.in.
2767 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc-%s", NONULL (Homedir),
2769 if (access (buffer, F_OK) == -1)
2771 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL (Homedir));
2772 if (access (buffer, F_OK) == -1)
2774 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc-%s",
2775 NONULL (Homedir), MUTT_VERSION);
2776 if (access (buffer, F_OK) == -1)
2778 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
2782 Muttrc = m_strdup(buffer);
2785 m_strcpy(buffer, sizeof(buffer), Muttrc);
2787 mutt_expand_path (buffer, sizeof(buffer));
2788 Muttrc = m_strdup(buffer);
2790 p_delete(&AliasFile);
2791 AliasFile = m_strdup(NONULL (Muttrc));
2793 /* Process the global rc file if it exists and the user hasn't explicity
2794 requested not to via "-n". */
2796 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
2798 if (access (buffer, F_OK) == -1)
2799 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
2800 if (access (buffer, F_OK) == -1)
2801 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
2803 if (access (buffer, F_OK) == -1)
2804 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
2805 if (access (buffer, F_OK) != -1) {
2806 if (source_rc (buffer, &err) != 0) {
2807 fputs (err.data, stderr);
2808 fputc ('\n', stderr);
2814 /* Read the user's initialization file. */
2815 if (access (Muttrc, F_OK) != -1) {
2816 if (!option (OPTNOCURSES))
2818 if (source_rc (Muttrc, &err) != 0) {
2819 fputs (err.data, stderr);
2820 fputc ('\n', stderr);
2824 else if (!default_rc) {
2825 /* file specified by -F does not exist */
2826 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2827 mutt_endwin (buffer);
2831 if (mutt_execute_commands (commands) != 0)
2834 /* warn about synonym variables */
2835 if (!list_empty(Synonyms)) {
2837 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2838 for (i = 0; i < Synonyms->length; i++) {
2839 struct option_t* newopt = NULL, *oldopt = NULL;
2840 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2841 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2842 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2843 oldopt ? NONULL (oldopt->option) : "",
2844 newopt ? NONULL (newopt->option) : "",
2845 NONULL(((syn_t*) Synonyms->data[i])->f),
2846 ((syn_t*) Synonyms->data[i])->l);
2848 fprintf (stderr, _("Warning: synonym variables are scheduled"
2849 " for removal.\n"));
2850 list_del (&Synonyms, syn_del);
2854 if (need_pause && !option (OPTNOCURSES)) {
2855 if (mutt_any_key_to_continue (NULL) == -1)
2860 set_option (OPTWEED); /* turn weeding on by default */
2864 int mutt_get_hook_type (const char *name)
2866 struct command_t *c;
2868 for (c = Commands; c->name; c++)
2869 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2874 /* compare two option_t*'s for sorting -t/-T output */
2875 static int opt_cmp (const void* a, const void* b) {
2876 return (m_strcmp((*(struct option_t**) a)->option,
2877 (*(struct option_t**) b)->option));
2880 /* callback for hash_map() to put all non-synonym vars into list */
2881 static void opt_sel_full (const char* key __attribute__ ((unused)),
2883 unsigned long more) {
2884 list2_t** l = (list2_t**) more;
2885 struct option_t* option = (struct option_t*) data;
2887 if (DTYPE (option->type) == DT_SYN)
2889 list_push_back (l, option);
2892 /* callback for hash_map() to put all changed non-synonym vars into list */
2893 static void opt_sel_diff (const char* key __attribute__ ((unused)),
2895 unsigned long more) {
2896 list2_t** l = (list2_t**) more;
2897 struct option_t* option = (struct option_t*) data;
2898 char buf[LONG_STRING];
2900 if (DTYPE (option->type) == DT_SYN)
2903 mutt_option_value (option->option, buf, sizeof(buf));
2904 if (m_strcmp(buf, option->init) != 0)
2905 list_push_back (l, option);
2908 /* dump out the value of all the variables we have */
2909 int mutt_dump_variables (int full) {
2911 char outbuf[STRING];
2912 list2_t* tmp = NULL;
2913 struct option_t* option = NULL;
2915 /* get all non-synonyms into list... */
2916 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2917 (unsigned long) &tmp);
2919 if (!list_empty(tmp)) {
2920 /* ...and dump list sorted */
2921 qsort (tmp->data, tmp->length, sizeof(void*), opt_cmp);
2922 for (i = 0; i < tmp->length; i++) {
2923 option = (struct option_t*) tmp->data[i];
2924 FuncTable[DTYPE (option->type)].opt_to_string
2925 (outbuf, sizeof(outbuf), option);
2926 printf ("%s\n", outbuf);
2929 list_del (&tmp, NULL);