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.
21 #include <sys/utsname.h>
25 #include <lib-lib/mem.h>
26 #include <lib-lib/str.h>
27 #include <lib-lib/file.h>
28 #include <lib-lib/ascii.h>
29 #include <lib-lib/macros.h>
30 #include <lib-lib/buffer.h>
31 #include <lib-lib/mapping.h>
32 #include <lib-lib/debug.h>
33 #include <lib-lib/rx.h>
35 #include <lib-sys/unix.h>
36 #include <lib-sys/mutt_ssl.h>
38 #include <lib-ui/curses.h>
39 #include <lib-ui/history.h>
45 #include <lib-crypt/crypt.h>
46 #include "mutt_idna.h"
48 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
49 #include "mutt_libesmtp.h"
61 static const struct mapping_t* get_sortmap (struct option_t* option);
62 static int parse_sort (struct option_t* dst, const char *s,
63 const struct mapping_t *map,
64 char* errbuf, ssize_t errlen);
66 static HASH *ConfigOptions = NULL;
68 /* for synonym warning reports: synonym found during parsing */
72 struct option_t* n; /* new */
73 struct option_t* o; /* old */
76 /* for synonym warning reports: list of synonyms found */
77 static list2_t* Synonyms;
78 /* for synonym warning reports: current rc file */
79 static const char* CurRCFile = NULL;
80 /* for synonym warning reports: current rc line */
81 static int CurRCLine = 0;
83 /* prototypes for checking for special vars */
84 static int check_dsn_return (const char* option, unsigned long val,
85 char* errbuf, ssize_t errlen);
86 static int check_dsn_notify (const char* option, unsigned long val,
87 char* errbuf, ssize_t errlen);
88 static int check_history (const char* option, unsigned long val,
89 char* errbuf, ssize_t errlen);
90 /* this checks that numbers are >= 0 */
91 static int check_num (const char* option, unsigned long val,
92 char* errbuf, ssize_t errlen);
94 static int check_debug (const char* option, unsigned long val,
95 char* errbuf, ssize_t errlen);
98 /* use this to check only */
99 static int check_special (const char* option, unsigned long val,
100 char* errbuf, ssize_t errlen);
102 /* variable <-> sanity check function mappings
103 * when changing these, make sure the proper _from_string handler
104 * does this checking!
108 int (*check) (const char* option, unsigned long val,
109 char* errbuf, ssize_t errlen);
111 { "dsn_notify", check_dsn_notify },
112 { "dsn_return", check_dsn_return },
113 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
114 { "smtp_use_tls", mutt_libesmtp_check_usetls },
116 { "history", check_history },
117 { "pager_index_lines", check_num },
119 { "debug_level", check_debug },
125 /* protos for config type handles: convert value to string */
126 static void bool_to_string (char* dst, ssize_t dstlen, struct option_t* option);
127 static void num_to_string (char* dst, ssize_t dstlen, struct option_t* option);
128 static void str_to_string (char* dst, ssize_t dstlen, struct option_t* option);
129 static void quad_to_string (char* dst, ssize_t dstlen, struct option_t* option);
130 static void sort_to_string (char* dst, ssize_t dstlen, struct option_t* option);
131 static void rx_to_string (char* dst, ssize_t dstlen, struct option_t* option);
132 static void magic_to_string (char* dst, ssize_t dstlen, struct option_t* option);
133 static void addr_to_string (char* dst, ssize_t dstlen, struct option_t* option);
134 static void user_to_string (char* dst, ssize_t dstlen, struct option_t* option);
135 static void sys_to_string (char* dst, ssize_t dstlen, struct option_t* option);
137 /* protos for config type handles: convert to value from string */
138 static int bool_from_string (struct option_t* dst, const char* val,
139 char* errbuf, ssize_t errlen);
140 static int num_from_string (struct option_t* dst, const char* val,
141 char* errbuf, ssize_t errlen);
142 static int str_from_string (struct option_t* dst, const char* val,
143 char* errbuf, ssize_t errlen);
144 static int path_from_string (struct option_t* dst, const char* val,
145 char* errbuf, ssize_t errlen);
146 static int quad_from_string (struct option_t* dst, const char* val,
147 char* errbuf, ssize_t errlen);
148 static int sort_from_string (struct option_t* dst, const char* val,
149 char* errbuf, ssize_t errlen);
150 static int rx_from_string (struct option_t* dst, const char* val,
151 char* errbuf, ssize_t errlen);
152 static int magic_from_string (struct option_t* dst, const char* val,
153 char* errbuf, ssize_t errlen);
154 static int addr_from_string (struct option_t* dst, const char* val,
155 char* errbuf, ssize_t errlen);
156 static int user_from_string (struct option_t* dst, const char* val,
157 char* errbuf, ssize_t errlen);
161 void (*opt_to_string) (char* dst, ssize_t dstlen, struct option_t* option);
162 int (*opt_from_string) (struct option_t* dst, const char* val,
163 char* errbuf, ssize_t errlen);
165 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
166 { DT_BOOL, bool_to_string, bool_from_string },
167 { DT_NUM, num_to_string, num_from_string },
168 { DT_STR, str_to_string, str_from_string },
169 { DT_PATH, str_to_string, path_from_string },
170 { DT_QUAD, quad_to_string, quad_from_string },
171 { DT_SORT, sort_to_string, sort_from_string },
172 { DT_RX, rx_to_string, rx_from_string },
173 { DT_MAGIC, magic_to_string, magic_from_string },
174 /* synonyms should be resolved already so we don't need this
175 * but must define it as DT_ is used for indexing */
176 { DT_SYN, NULL, NULL },
177 { DT_ADDR, addr_to_string, addr_from_string },
178 { DT_USER, user_to_string, user_from_string },
179 { DT_SYS, sys_to_string, NULL },
182 static void bool_to_string (char* dst, ssize_t dstlen,
183 struct option_t* option) {
184 snprintf (dst, dstlen, "%s=%s", option->option,
185 option (option->data) ? "yes" : "no");
188 static int bool_from_string (struct option_t* dst, const char* val,
189 char* errbuf __attribute__ ((unused)),
190 ssize_t errlen __attribute__ ((unused))) {
195 if (ascii_strncasecmp (val, "yes", 3) == 0)
197 else if (ascii_strncasecmp (val, "no", 2) == 0)
203 set_option (dst->data);
205 unset_option (dst->data);
209 static void num_to_string (char* dst, ssize_t dstlen,
210 struct option_t* option) {
212 const char* fmt = (m_strcmp(option->option, "umask") == 0) ?
214 snprintf (dst, dstlen, fmt, option->option,
215 *((short*) option->data));
218 static int num_from_string (struct option_t* dst, const char* val,
219 char* errbuf, ssize_t errlen) {
220 int num = 0, old = 0;
226 num = strtol (val, &t, 0);
228 if (!*val || *t || (short) num != num) {
230 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
236 /* just temporarily accept new val so that check_special for
237 * $history already has it when doing history's init() */
238 old = *((short*) dst->data);
239 *((short*) dst->data) = (short) num;
241 if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
242 *((short*) dst->data) = old;
249 static void str_to_string (char* dst, ssize_t dstlen,
250 struct option_t* option) {
251 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
252 NONULL (*((char**) option->data)));
255 static void user_to_string (char* dst, ssize_t dstlen,
256 struct option_t* option) {
257 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
258 NONULL (((char*) option->data)));
261 static void sys_to_string (char* dst, ssize_t dstlen,
262 struct option_t* option) {
263 char *val = NULL, *t = NULL;
266 /* get some $madmutt_ values dynamically */
267 if (ascii_strcmp ("madmutt_pwd", option->option) == 0) {
268 val = p_new(char, _POSIX_PATH_MAX);
269 val = getcwd (val, _POSIX_PATH_MAX-1);
271 } else if (ascii_strcmp ("madmutt_folder_path", option->option) == 0 &&
272 CurrentFolder && *CurrentFolder) {
274 } else if (ascii_strcmp ("madmutt_folder_name", option->option) == 0 &&
275 CurrentFolder && *CurrentFolder) {
277 ssize_t Maildirlength = m_strlen(Maildir);
280 * if name starts with $folder, just strip it to keep hierarchy
281 * $folder=imap://host, path=imap://host/inbox/b -> inbox/b
283 if (Maildirlength > 0 && m_strncmp(CurrentFolder, Maildir,
284 Maildirlength) == 0 &&
285 m_strlen(CurrentFolder) > Maildirlength) {
286 val = CurrentFolder + Maildirlength;
287 if (Maildir[strlen(Maildir)-1]!='/')
289 /* if not $folder, just use everything after last / */
290 } else if ((t = strrchr (CurrentFolder, '/')) != NULL)
292 /* default: use as-is */
294 val = (char *) CurrentFolder;
297 val = (char *) option->init;
299 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (val));
304 static int path_from_string (struct option_t* dst, const char* val,
305 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
306 char path[_POSIX_PATH_MAX];
312 p_delete((char**) dst->data);
317 m_strcpy(path, sizeof(path), val);
318 mutt_expand_path (path, sizeof(path));
319 m_strreplace((char **) dst->data, path);
323 static int str_from_string (struct option_t* dst, const char* val,
324 char* errbuf, ssize_t errlen) {
328 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
331 m_strreplace((char**) dst->data, val);
335 static int user_from_string (struct option_t* dst, const char* val,
336 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
337 /* if dst == NULL, we may get here in case the user did unset it,
338 * see parse_set() where item is free()'d before coming here; so
339 * just silently ignore it */
342 if (m_strlen((char*) dst->data) == 0)
343 dst->data = (unsigned long) m_strdup(val);
345 char* s = (char*) dst->data;
346 m_strreplace(&s, val);
348 if (m_strlen(dst->init) == 0)
349 dst->init = m_strdup((char*) dst->data);
353 static void quad_to_string (char* dst, ssize_t dstlen,
354 struct option_t* option) {
355 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
356 snprintf (dst, dstlen, "%s=%s", option->option,
357 vals[quadoption (option->data)]);
360 static int quad_from_string (struct option_t* dst, const char* val,
361 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
366 if (ascii_strncasecmp (val, "yes", 3) == 0)
368 else if (ascii_strncasecmp (val, "no", 2) == 0)
370 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
372 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
378 set_quadoption (dst->data, flag);
382 static void sort_to_string (char* dst, ssize_t dstlen,
383 struct option_t* option) {
384 const struct mapping_t *map = get_sortmap (option);
385 const char *p = NULL;
388 snprintf (dst, sizeof(dst), "%s=unknown", option->option);
392 p = mutt_getnamebyvalue(*((short *)option->data) & SORT_MASK, map);
394 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
395 (*((short *) option->data) & SORT_REVERSE) ?
397 (*((short *) option->data) & SORT_LAST) ? "last-" :
401 static int sort_from_string (struct option_t* dst, const char* val,
402 char* errbuf, ssize_t errlen) {
403 const struct mapping_t *map = NULL;
404 if (!(map = get_sortmap (dst))) {
406 snprintf (errbuf, errlen, _("%s: Unknown type."),
410 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
415 static void rx_to_string (char* dst, ssize_t dstlen,
416 struct option_t* option) {
417 rx_t* p = (rx_t*) option->data;
418 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
419 NONULL (p->pattern));
422 static int rx_from_string (struct option_t* dst, const char* val,
423 char* errbuf, ssize_t errlen) {
426 int flags = 0, e = 0, not = 0;
432 if (option (OPTATTACHMSG) && !m_strcmp(dst->option, "reply_regexp")) {
434 snprintf (errbuf, errlen,
435 "Operation not permitted when in attach-message mode.");
439 if (!((rx_t*) dst->data))
440 *((rx_t**) dst->data) = p_new(rx_t, 1);
442 p = (rx_t*) dst->data;
444 /* something to do? */
445 if (!val || !*val || (p->pattern && m_strcmp(p->pattern, val) == 0))
448 if (m_strcmp(dst->option, "mask") != 0)
449 flags |= mutt_which_case (val);
452 if (m_strcmp(dst->option, "mask") == 0 && *s == '!') {
457 rx = p_new(regex_t, 1);
459 if ((e = REGCOMP (rx, s, flags)) != 0) {
460 regerror (e, rx, errbuf, errlen);
471 m_strreplace(&p->pattern, val);
475 if (m_strcmp(dst->option, "reply_regexp") == 0)
476 mutt_adjust_all_subjects ();
481 static void magic_to_string (char* dst, ssize_t dstlen,
482 struct option_t* option) {
483 const char* s = NULL;
484 switch (option->data) {
485 case M_MBOX: s = "mbox"; break;
486 case M_MMDF: s = "MMDF"; break;
487 case M_MH: s = "MH"; break;
488 case M_MAILDIR: s = "Maildir"; break;
489 default: s = "unknown"; break;
491 snprintf (dst, dstlen, "%s=%s", option->option, s);
494 static int magic_from_string (struct option_t* dst, const char* val,
495 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
498 if (!dst || !val || !*val)
500 if (ascii_strncasecmp (val, "mbox", 4) == 0)
502 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
504 else if (ascii_strncasecmp (val, "mh", 2) == 0)
506 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
512 *((short*) dst->data) = flag;
517 static void addr_to_string (char* dst, ssize_t dstlen,
518 struct option_t* option) {
521 rfc822_write_address (s, sizeof(s), *((address_t**) option->data), 0);
522 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
525 static int addr_from_string (struct option_t* dst, const char* val,
526 char* errbuf __attribute__ ((unused)), ssize_t errlen __attribute__ ((unused))) {
529 address_list_wipe((address_t**) dst->data);
531 *((address_t**) dst->data) = rfc822_parse_adrlist (NULL, val);
535 int mutt_option_value (const char* val, char* dst, ssize_t dstlen) {
536 struct option_t* option = NULL;
537 char* tmp = NULL, *t = NULL;
540 if (!(option = hash_find (ConfigOptions, val))) {
541 debug_print (1, ("var '%s' not found\n", val));
545 tmp = p_new(char, dstlen+1);
546 FuncTable[DTYPE (option->type)].opt_to_string (tmp, dstlen, option);
548 /* as we get things of type $var=value and don't want to bloat the
549 * above "just" for expansion, we do the stripping here */
550 debug_print (1, ("orig == '%s'\n", tmp));
551 t = strchr (tmp, '=');
555 if (t[l-1] == '"' && *t == '"') {
560 memcpy (dst, t, l+1);
562 debug_print (1, ("stripped == '%s'\n", dst));
567 /* for synonym warning reports: adds synonym to end of list */
568 static void syn_add (struct option_t* n, struct option_t* o) {
569 syn_t* tmp = p_new(syn_t, 1);
570 tmp->f = m_strdup(CurRCFile);
574 list_push_back (&Synonyms, tmp);
577 /* for synonym warning reports: free single item (for list_del()) */
578 static void syn_del (void** p) {
579 p_delete(&(*(syn_t**) p)->f);
583 void toggle_quadoption (int opt)
586 int b = (opt % 4) * 2;
588 QuadOptions[n] ^= (1 << b);
591 void set_quadoption (int opt, int flag)
594 int b = (opt % 4) * 2;
596 QuadOptions[n] &= ~(0x3 << b);
597 QuadOptions[n] |= (flag & 0x3) << b;
600 int quadoption (int opt)
603 int b = (opt % 4) * 2;
605 return (QuadOptions[n] >> b) & 0x3;
608 int query_quadoption (int opt, const char *prompt)
610 int v = quadoption (opt);
618 v = mutt_yesorno (prompt, (v == M_ASKYES));
619 CLEARLINE (LINES - 1);
626 static void add_to_list (string_list_t ** list, const char *str)
628 string_list_t *t, *last = NULL;
630 /* don't add a NULL or empty string to the list */
631 if (!str || *str == '\0')
634 /* check to make sure the item is not already on this list */
635 for (last = *list; last; last = last->next) {
636 if (ascii_strcasecmp (str, last->data) == 0) {
637 /* already on the list, so just ignore it */
645 if (!*list || last) {
646 t = p_new(string_list_t, 1);
647 t->data = m_strdup(str);
657 static int add_to_rx_list (list2_t** list, const char *s, int flags,
666 if (!(rx = rx_compile (s, flags))) {
667 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
671 i = rx_lookup ((*list), rx->pattern);
675 list_push_back (list, rx);
679 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
680 const char *templ, BUFFER * err)
682 SPAM_LIST *t = NULL, *last = NULL;
687 if (!pat || !*pat || !templ)
690 if (!(rx = rx_compile (pat, REG_ICASE))) {
691 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
695 /* check to make sure the item is not already on this list */
696 for (last = *list; last; last = last->next) {
697 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
698 /* Already on the list. Formerly we just skipped this case, but
699 * now we're supporting removals, which means we're supporting
700 * re-adds conceptually. So we probably want this to imply a
701 * removal, then do an add. We can achieve the removal by freeing
702 * the template, and leaving t pointed at the current item.
705 p_delete(&t->template);
712 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
713 * update. Otherwise we want to make a new one to link at the list's end.
716 t = mutt_new_spam_list ();
724 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
725 t->template = m_strdup(templ);
727 /* Find highest match number in template string */
729 for (p = templ; *p;) {
734 while (*p && isdigit ((int) *p))
740 t->nmatch++; /* match 0 is always the whole expr */
745 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
747 SPAM_LIST *spam, *prev;
750 /* Being first is a special case. */
754 if (spam->rx && !m_strcmp(spam->rx->pattern, pat)) {
756 rx_delete(&spam->rx);
757 p_delete(&spam->template);
763 for (spam = prev->next; spam;) {
764 if (!m_strcmp(spam->rx->pattern, pat)) {
765 prev->next = spam->next;
766 rx_delete(&spam->rx);
767 p_delete(&spam->template);
780 static void remove_from_list (string_list_t ** l, const char *str)
782 string_list_t *p, *last = NULL;
784 if (m_strcmp("*", str) == 0)
785 string_list_wipe(l); /* ``unCMD *'' means delete all current entries */
790 if (ascii_strcasecmp (str, p->data) == 0) {
793 last->next = p->next;
806 static int remove_from_rx_list (list2_t** l, const char *str)
810 if (m_strcmp("*", str) == 0) {
811 list_del (l, (list_del_t*) rx_delete);
815 i = rx_lookup ((*l), str);
817 rx_t* r = list_pop_idx ((*l), i);
825 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
829 unsigned long res = 0;
831 struct option_t* option = NULL;
834 mutt_extract_token (tmp, s, 0);
836 /* is the item defined as a variable or a function? */
837 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL)
840 for (i = 0; !res && i < MENU_MAX; i++) {
841 struct binding_t *b = km_get_table (Menus[i].value);
846 for (j = 0; b[j].name; j++)
847 if (!ascii_strncasecmp (tmp->data, b[j].name, m_strlen(tmp->data))
848 && (m_strlen(b[j].name) == m_strlen(tmp->data))) {
854 /* check for feature_* */
855 if (!res && ascii_strncasecmp (tmp->data, "feature_", 8) == 0 &&
856 (j = m_strlen(tmp->data)) > 8) {
858 while (Features[i]) {
859 if (m_strlen(Features[i]) == j-8 &&
860 ascii_strncasecmp (Features[i], tmp->data+8, j-8) == 0) {
870 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
872 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
876 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
879 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
880 mutt_error ("Error: %s", err->data);
881 p_delete(&token.data);
884 p_delete(&token.data);
889 static int parse_unignore (BUFFER * buf, BUFFER * s,
890 unsigned long data __attribute__ ((unused)),
891 BUFFER * err __attribute__ ((unused)))
894 mutt_extract_token (buf, s, 0);
896 /* don't add "*" to the unignore list */
897 if (strcmp (buf->data, "*"))
898 add_to_list (&UnIgnore, buf->data);
900 remove_from_list (&Ignore, buf->data);
902 while (MoreArgs (s));
907 static int parse_ignore (BUFFER * buf, BUFFER * s,
908 unsigned long data __attribute__ ((unused)),
909 BUFFER * err __attribute__ ((unused)))
912 mutt_extract_token (buf, s, 0);
913 remove_from_list (&UnIgnore, buf->data);
914 add_to_list (&Ignore, buf->data);
916 while (MoreArgs (s));
921 static int parse_list (BUFFER * buf, BUFFER * s,
922 unsigned long data __attribute__ ((unused)),
923 BUFFER * err __attribute__ ((unused)))
926 mutt_extract_token (buf, s, 0);
927 add_to_list ((string_list_t **) data, buf->data);
929 while (MoreArgs (s));
934 static void _alternates_clean (void)
938 if (Context && Context->msgcount) {
939 for (i = 0; i < Context->msgcount; i++)
940 Context->hdrs[i]->recip_valid = 0;
944 static int parse_alternates (BUFFER * buf, BUFFER * s,
945 unsigned long data __attribute__ ((unused)),
946 BUFFER * err __attribute__ ((unused)))
948 _alternates_clean ();
950 mutt_extract_token (buf, s, 0);
951 remove_from_rx_list (&UnAlternates, buf->data);
953 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
956 while (MoreArgs (s));
961 static int parse_unalternates (BUFFER * buf, BUFFER * s,
962 unsigned long data __attribute__ ((unused)),
963 BUFFER * err __attribute__ ((unused)))
965 _alternates_clean ();
967 mutt_extract_token (buf, s, 0);
968 remove_from_rx_list (&Alternates, buf->data);
970 if (m_strcmp(buf->data, "*") &&
971 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
975 while (MoreArgs (s));
980 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
987 /* Insist on at least one parameter */
990 m_strcpy(err->data, err->dsize, _("spam: no matching pattern"));
992 m_strcpy(err->data, err->dsize, _("nospam: no matching pattern"));
996 /* Extract the first token, a regexp */
997 mutt_extract_token (buf, s, 0);
999 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
1000 if (data == M_SPAM) {
1001 /* If there's a second parameter, it's a template for the spam tag. */
1003 mutt_extract_token (&templ, s, 0);
1005 /* Add to the spam list. */
1006 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
1007 p_delete(&templ.data);
1010 p_delete(&templ.data);
1013 /* If not, try to remove from the nospam list. */
1015 remove_from_rx_list (&NoSpamList, buf->data);
1021 /* M_NOSPAM is for nospam commands. */
1022 else if (data == M_NOSPAM) {
1023 /* nospam only ever has one parameter. */
1025 /* "*" is a special case. */
1026 if (!m_strcmp(buf->data, "*")) {
1027 mutt_free_spam_list (&SpamList);
1028 list_del (&NoSpamList, (list_del_t*) rx_delete);
1032 /* If it's on the spam list, just remove it. */
1033 if (remove_from_spam_list (&SpamList, buf->data) != 0)
1036 /* Otherwise, add it to the nospam list. */
1037 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
1043 /* This should not happen. */
1044 m_strcpy(err->data, err->dsize, "This is no good at all.");
1048 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
1049 BUFFER * err __attribute__ ((unused)))
1052 mutt_extract_token (buf, s, 0);
1054 * Check for deletion of entire list
1056 if (m_strcmp(buf->data, "*") == 0) {
1057 string_list_wipe((string_list_t **) data);
1060 remove_from_list ((string_list_t **) data, buf->data);
1062 while (MoreArgs (s));
1067 static int parse_lists (BUFFER * buf, BUFFER * s,
1068 unsigned long data __attribute__ ((unused)),
1072 mutt_extract_token (buf, s, 0);
1073 remove_from_rx_list (&UnMailLists, buf->data);
1075 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1078 while (MoreArgs (s));
1083 /* always wise to do what someone else did before */
1084 static void _attachments_clean (void) {
1086 if (Context && Context->msgcount) {
1087 for (i = 0; i < Context->msgcount; i++)
1088 Context->hdrs[i]->attach_valid = 0;
1092 static int parse_attach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
1093 BUFFER *err __attribute__ ((unused))) {
1095 string_list_t *listp, *lastp;
1100 /* Find the last item in the list that data points to. */
1102 debug_print (5, ("parse_attach_list: ldata = %p, *ldata = %p\n",
1104 for (listp = *ldata; listp; listp = listp->next) {
1105 a = (ATTACH_MATCH *)listp->data;
1106 debug_print (5, ("parse_attach_list: skipping %s/%s\n", a->major, a->minor));
1111 mutt_extract_token (buf, s, 0);
1113 if (!buf->data || *buf->data == '\0')
1116 a = p_new(ATTACH_MATCH, 1);
1118 /* some cheap hacks that I expect to remove */
1119 if (!m_strcasecmp(buf->data, "any"))
1120 a->major = m_strdup("*/.*");
1121 else if (!m_strcasecmp(buf->data, "none"))
1122 a->major = m_strdup("cheap_hack/this_should_never_match");
1124 a->major = m_strdup(buf->data);
1126 if ((p = strchr(a->major, '/'))) {
1131 a->minor = "unknown";
1134 len = m_strlen(a->minor);
1135 tmpminor = p_new(char, len + 3);
1136 strcpy(&tmpminor[1], a->minor); /* __STRCPY_CHECKED__ */
1138 tmpminor[len+1] = '$';
1139 tmpminor[len+2] = '\0';
1141 a->major_int = mutt_check_mime_type(a->major);
1142 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
1144 p_delete(&tmpminor);
1146 debug_print (5, ("parse_attach_list: added %s/%s [%d]\n",
1147 a->major, a->minor, a->major_int));
1149 listp = p_new(string_list_t, 1);
1150 listp->data = (char *)a;
1153 lastp->next = listp;
1159 while (MoreArgs (s));
1161 _attachments_clean();
1165 static int parse_unattach_list (BUFFER *buf, BUFFER *s, string_list_t **ldata,
1166 BUFFER *err __attribute__ ((unused))) {
1168 string_list_t *lp, *lastp, *newlp;
1174 mutt_extract_token (buf, s, 0);
1176 if (!m_strcasecmp(buf->data, "any"))
1177 tmp = m_strdup("*/.*");
1178 else if (!m_strcasecmp(buf->data, "none"))
1179 tmp = m_strdup("cheap_hack/this_should_never_match");
1181 tmp = m_strdup(buf->data);
1183 if ((minor = strchr(tmp, '/'))) {
1187 minor = m_strdup("unknown");
1189 major = mutt_check_mime_type(tmp);
1191 /* We must do our own walk here because remove_from_list() will only
1192 * remove the string_list_t->data, not anything pointed to by the string_list_t->data. */
1194 for(lp = *ldata; lp; ) {
1195 a = (ATTACH_MATCH *)lp->data;
1196 debug_print(5, ("parse_unattach_list: check %s/%s [%d] : %s/%s [%d]\n",
1197 a->major, a->minor, a->major_int, tmp, minor, major));
1198 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
1199 debug_print(5, ("parse_unattach_list: removed %s/%s [%d]\n",
1200 a->major, a->minor, a->major_int));
1201 regfree(&a->minor_rx);
1202 p_delete(&a->major);
1204 /* Relink backward */
1206 lastp->next = lp->next;
1211 p_delete(&lp->data); /* same as a */
1221 while (MoreArgs (s));
1224 _attachments_clean();
1228 static int print_attach_list (string_list_t *lp, char op, const char *name) {
1230 printf("attachments %c%s %s/%s\n", op, name,
1231 ((ATTACH_MATCH *)lp->data)->major,
1232 ((ATTACH_MATCH *)lp->data)->minor);
1239 static int parse_attachments (BUFFER *buf, BUFFER *s,
1240 unsigned long data __attribute__ ((unused)),
1243 string_list_t **listp;
1245 mutt_extract_token(buf, s, 0);
1246 if (!buf->data || *buf->data == '\0') {
1247 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
1251 category = buf->data;
1257 printf("\nCurrent attachments settings:\n\n");
1258 print_attach_list(AttachAllow, '+', "A");
1259 print_attach_list(AttachExclude, '-', "A");
1260 print_attach_list(InlineAllow, '+', "I");
1261 print_attach_list(InlineExclude, '-', "I");
1262 set_option (OPTFORCEREDRAWINDEX);
1263 set_option (OPTFORCEREDRAWPAGER);
1264 mutt_any_key_to_continue (NULL);
1268 if (op != '+' && op != '-') {
1272 if (!m_strncasecmp(category, "attachment", strlen(category))) {
1274 listp = &AttachAllow;
1276 listp = &AttachExclude;
1278 else if (!m_strncasecmp(category, "inline", strlen(category))) {
1280 listp = &InlineAllow;
1282 listp = &InlineExclude;
1284 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
1288 return parse_attach_list(buf, s, listp, err);
1291 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
1293 string_list_t **listp;
1295 mutt_extract_token(buf, s, 0);
1296 if (!buf->data || *buf->data == '\0') {
1297 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
1303 if (op != '+' && op != '-') {
1307 if (!m_strncasecmp(p, "attachment", strlen(p))) {
1309 listp = &AttachAllow;
1311 listp = &AttachExclude;
1313 else if (!m_strncasecmp(p, "inline", strlen(p))) {
1315 listp = &InlineAllow;
1317 listp = &InlineExclude;
1320 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
1324 return parse_unattach_list(buf, s, listp, err);
1327 static int parse_unlists (BUFFER * buf, BUFFER * s,
1328 unsigned long data __attribute__ ((unused)),
1329 BUFFER * err __attribute__ ((unused)))
1332 mutt_extract_token (buf, s, 0);
1333 remove_from_rx_list (&SubscribedLists, buf->data);
1334 remove_from_rx_list (&MailLists, buf->data);
1336 if (m_strcmp(buf->data, "*") &&
1337 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1340 while (MoreArgs (s));
1345 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1349 mutt_extract_token (buf, s, 0);
1350 remove_from_rx_list (&UnMailLists, buf->data);
1351 remove_from_rx_list (&UnSubscribedLists, buf->data);
1353 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1355 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1358 while (MoreArgs (s));
1363 static int parse_unsubscribe (BUFFER * buf, BUFFER * s,
1364 unsigned long data __attribute__ ((unused)),
1365 BUFFER * err __attribute__ ((unused)))
1368 mutt_extract_token (buf, s, 0);
1369 remove_from_rx_list (&SubscribedLists, buf->data);
1371 if (m_strcmp(buf->data, "*") &&
1372 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1375 while (MoreArgs (s));
1380 static int parse_unalias (BUFFER * buf, BUFFER * s,
1381 unsigned long data __attribute__ ((unused)),
1382 BUFFER * err __attribute__ ((unused)))
1384 alias_t *tmp, *last = NULL;
1387 mutt_extract_token (buf, s, 0);
1389 if (m_strcmp("*", buf->data) == 0) {
1390 if (CurrentMenu == MENU_ALIAS) {
1391 for (tmp = Aliases; tmp; tmp = tmp->next)
1393 set_option (OPTFORCEREDRAWINDEX);
1396 alias_list_wipe(&Aliases);
1400 for (tmp = Aliases; tmp; tmp = tmp->next) {
1401 if (m_strcasecmp(buf->data, tmp->name) == 0) {
1402 if (CurrentMenu == MENU_ALIAS) {
1404 set_option (OPTFORCEREDRAWINDEX);
1409 last->next = tmp->next;
1411 Aliases = tmp->next;
1413 alias_list_wipe(&tmp);
1419 while (MoreArgs (s));
1423 static int parse_alias (BUFFER * buf, BUFFER * s,
1424 unsigned long data __attribute__ ((unused)),
1427 alias_t *tmp = Aliases;
1428 alias_t *last = NULL;
1429 const char *estr = NULL;
1431 if (!MoreArgs (s)) {
1432 m_strcpy(err->data, err->dsize, _("alias: no address"));
1436 mutt_extract_token (buf, s, 0);
1438 debug_print (2, ("first token is '%s'.\n", buf->data));
1440 /* check to see if an alias with this name already exists */
1441 for (; tmp; tmp = tmp->next) {
1442 if (!m_strcasecmp(tmp->name, buf->data))
1448 /* create a new alias */
1450 tmp->name = m_strdup(buf->data);
1451 /* give the main addressbook code a chance */
1452 if (CurrentMenu == MENU_ALIAS)
1453 set_option (OPTMENUCALLER);
1456 /* override the previous value */
1457 address_list_wipe(&tmp->addr);
1458 if (CurrentMenu == MENU_ALIAS)
1459 set_option (OPTFORCEREDRAWINDEX);
1462 mutt_extract_token (buf, s,
1463 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1464 debug_print (2, ("second token is '%s'.\n", buf->data));
1465 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1470 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1471 snprintf (err->data, err->dsize,
1472 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1476 if (DebugLevel >= 2) {
1479 /* A group is terminated with an empty address, so check a->mailbox */
1480 for (a = tmp->addr; a && a->mailbox; a = a->next) {
1482 debug_print (2, ("%s\n", a->mailbox));
1484 debug_print (2, ("group %s\n", a->mailbox));
1492 parse_unmy_hdr (BUFFER * buf, BUFFER * s,
1493 unsigned long data __attribute__ ((unused)),
1494 BUFFER * err __attribute__ ((unused)))
1496 string_list_t *last = NULL;
1497 string_list_t *tmp = UserHeader;
1502 mutt_extract_token (buf, s, 0);
1503 if (m_strcmp("*", buf->data) == 0)
1504 string_list_wipe(&UserHeader);
1509 l = m_strlen(buf->data);
1510 if (buf->data[l - 1] == ':')
1514 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1515 && tmp->data[l] == ':') {
1518 last->next = tmp->next;
1520 UserHeader = tmp->next;
1523 string_list_wipe(&ptr);
1532 while (MoreArgs (s));
1536 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1543 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1544 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1545 m_strcpy(err->data, err->dsize, _("invalid header field"));
1548 keylen = p - buf->data + 1;
1551 for (tmp = UserHeader;; tmp = tmp->next) {
1552 /* see if there is already a field by this name */
1553 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1554 /* replace the old value */
1555 p_delete(&tmp->data);
1556 tmp->data = buf->data;
1563 tmp->next = string_item_new();
1567 tmp = string_item_new();
1570 tmp->data = buf->data;
1576 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1577 char* errbuf, ssize_t errlen) {
1580 if (m_strncmp("reverse-", s, 8) == 0) {
1582 flags = SORT_REVERSE;
1585 if (m_strncmp("last-", s, 5) == 0) {
1590 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1592 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1596 *((short*) dst->data) = i | flags;
1600 /* if additional data more == 1, we want to resolve synonyms */
1601 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
1603 char buf[LONG_STRING];
1604 struct option_t *ptr = p;
1606 if (DTYPE(ptr->type) == DT_SYN) {
1609 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1611 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_from_string)
1614 mutt_option_value(ptr->option, buf, sizeof(buf));
1615 if (m_strlen(ptr->init) == 0 && buf && *buf)
1616 ptr->init = m_strdup(buf);
1619 static struct option_t* add_option (const char* name, const char* init,
1620 short type, short dodup) {
1621 struct option_t* option = p_new(struct option_t, 1);
1623 debug_print (1, ("adding $%s\n", name));
1625 option->option = m_strdup(name);
1626 option->type = type;
1628 option->init = dodup ? m_strdup(init) : (char*) init;
1632 /* creates new option_t* of type DT_USER for $user_ var */
1633 static struct option_t* add_user_option (const char* name) {
1634 return (add_option (name, NULL, DT_USER, 1));
1637 /* free()'s option_t* */
1638 static void del_option (void* p) {
1639 struct option_t *ptr = (struct option_t*) p;
1640 char* s = (char*) ptr->data;
1641 debug_print (1, ("removing option '%s' from table\n", NONULL (ptr->option)));
1642 p_delete(&ptr->option);
1644 p_delete(&ptr->init);
1648 static int init_expand (char** dst, struct option_t* src) {
1654 if (DTYPE(src->type) == DT_STR ||
1655 DTYPE(src->type) == DT_PATH) {
1656 /* only expand for string as it's the only place where
1657 * we want to expand vars right now */
1658 if (src->init && *src->init) {
1661 len = m_strlen(src->init) + 2;
1662 in.data = p_new(char, len + 1);
1663 snprintf (in.data, len, "\"%s\"", src->init);
1666 mutt_extract_token (&token, &in, 0);
1667 if (token.data && *token.data)
1668 *dst = m_strdup(token.data);
1670 *dst = m_strdup("");
1672 p_delete(&token.data);
1674 *dst = m_strdup("");
1676 /* for non-string: take value as is */
1677 *dst = m_strdup(src->init);
1681 /* if additional data more == 1, we want to resolve synonyms */
1682 static void mutt_restore_default (const char* name __attribute__ ((unused)),
1683 void* p, unsigned long more) {
1684 char errbuf[STRING];
1685 struct option_t* ptr = (struct option_t*) p;
1688 if (DTYPE (ptr->type) == DT_SYN) {
1691 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1695 if (FuncTable[DTYPE (ptr->type)].opt_from_string) {
1696 init_expand (&init, ptr);
1697 if (!FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, init, errbuf,
1699 if (!option (OPTNOCURSES))
1701 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1702 "Please report this error: \"%s\"\n"),
1703 ptr->option, NONULL (init), errbuf);
1709 if (ptr->flags & R_INDEX)
1710 set_option (OPTFORCEREDRAWINDEX);
1711 if (ptr->flags & R_PAGER)
1712 set_option (OPTFORCEREDRAWPAGER);
1713 if (ptr->flags & R_RESORT_SUB)
1714 set_option (OPTSORTSUBTHREADS);
1715 if (ptr->flags & R_RESORT)
1716 set_option (OPTNEEDRESORT);
1717 if (ptr->flags & R_RESORT_INIT)
1718 set_option (OPTRESORTINIT);
1719 if (ptr->flags & R_TREE)
1720 set_option (OPTREDRAWTREE);
1723 /* check whether value for $dsn_return would be valid */
1724 static int check_dsn_return (const char* option __attribute__ ((unused)), unsigned long p,
1725 char* errbuf, ssize_t errlen) {
1726 char* val = (char*) p;
1727 if (val && *val && m_strncmp(val, "hdrs", 4) != 0 &&
1728 m_strncmp(val, "full", 4) != 0) {
1730 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1736 /* check whether value for $dsn_notify would be valid */
1737 static int check_dsn_notify (const char* option, unsigned long p,
1738 char* errbuf, ssize_t errlen) {
1739 list2_t* list = NULL;
1742 char* val = (char*) p;
1746 list = list_from_str (val, ",");
1747 if (list_empty (list))
1750 for (i = 0; i < list->length; i++)
1751 if (m_strncmp(list->data[i], "never", 5) != 0 &&
1752 m_strncmp(list->data[i], "failure", 7) != 0 &&
1753 m_strncmp(list->data[i], "delay", 5) != 0 &&
1754 m_strncmp(list->data[i], "success", 7) != 0) {
1756 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1757 (char*) list->data[i], "dsn_notify");
1761 list_del (&list, (list_del_t*)xmemfree);
1765 static int check_num (const char* option, unsigned long p,
1766 char* errbuf, ssize_t errlen) {
1769 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1776 static int check_debug (const char* option, unsigned long p,
1777 char* errbuf, ssize_t errlen) {
1778 if ((int) p <= DEBUG_MAX_LEVEL &&
1779 (int) p >= DEBUG_MIN_LEVEL)
1783 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1788 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1789 char* errbuf, ssize_t errlen) {
1790 if (!check_num ("history", p, errbuf, errlen))
1792 mutt_init_history ();
1796 static int check_special (const char* name, unsigned long val,
1797 char* errbuf, ssize_t errlen) {
1800 for (i = 0; SpecialVars[i].name; i++) {
1801 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1802 return (SpecialVars[i].check (SpecialVars[i].name,
1803 val, errbuf, errlen));
1809 static const struct mapping_t* get_sortmap (struct option_t* option) {
1810 const struct mapping_t* map = NULL;
1812 switch (option->type & DT_SUBTYPE_MASK) {
1814 map = SortAliasMethods;
1816 case DT_SORT_BROWSER:
1817 map = SortBrowserMethods;
1820 map = SortKeyMethods;
1823 map = SortAuxMethods;
1832 #define CHECK_PAGER \
1833 if ((CurrentMenu == MENU_PAGER) && \
1834 (!option || (option->flags & R_RESORT))) \
1836 snprintf (err->data, err->dsize, \
1837 _("Not available in this menu.")); \
1841 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1844 int query, unset, inv, reset, r = 0;
1845 struct option_t* option = NULL;
1847 while (MoreArgs (s)) {
1848 /* reset state variables */
1850 unset = data & M_SET_UNSET;
1851 inv = data & M_SET_INV;
1852 reset = data & M_SET_RESET;
1854 if (*s->dptr == '?') {
1858 else if (m_strncmp("no", s->dptr, 2) == 0) {
1862 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1866 else if (*s->dptr == '&') {
1871 /* get the variable name */
1872 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1874 /* resolve synonyms */
1875 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1876 DTYPE (option->type == DT_SYN)) {
1877 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1878 syn_add (newopt, option);
1882 /* see if we need to add $user_ var */
1883 if (!option && ascii_strncmp ("user_", tmp->data, 5) == 0) {
1884 /* there's no option named like this yet so only add one
1885 * if the action isn't any of: reset, unset, query */
1886 if (!(reset || unset || query || *s->dptr != '=')) {
1887 debug_print (1, ("adding user option '%s'\n", tmp->data));
1888 option = add_user_option (tmp->data);
1889 hash_insert (ConfigOptions, option->option, option, 0);
1893 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1894 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1897 s->dptr = vskipspaces(s->dptr);
1900 if (query || unset || inv) {
1901 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1905 if (s && *s->dptr == '=') {
1906 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1910 if (!m_strcmp("all", tmp->data)) {
1911 if (CurrentMenu == MENU_PAGER) {
1912 snprintf (err->data, err->dsize, _("Not available in this menu."));
1915 hash_map (ConfigOptions, mutt_restore_default, 1);
1916 set_option (OPTFORCEREDRAWINDEX);
1917 set_option (OPTFORCEREDRAWPAGER);
1918 set_option (OPTSORTSUBTHREADS);
1919 set_option (OPTNEEDRESORT);
1920 set_option (OPTRESORTINIT);
1921 set_option (OPTREDRAWTREE);
1924 else if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1925 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1930 mutt_restore_default (NULL, option, 1);
1933 else if (DTYPE (option->type) == DT_BOOL) {
1934 /* XXX this currently ignores the function table
1935 * as we don't get invert and stuff into it */
1936 if (s && *s->dptr == '=') {
1937 if (unset || inv || query) {
1938 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1943 mutt_extract_token (tmp, s, 0);
1944 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1946 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1949 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1955 bool_to_string (err->data, err->dsize, option);
1961 unset_option (option->data);
1963 toggle_option (option->data);
1965 set_option (option->data);
1967 else if (DTYPE (option->type) == DT_STR ||
1968 DTYPE (option->type) == DT_PATH ||
1969 DTYPE (option->type) == DT_ADDR ||
1970 DTYPE (option->type) == DT_MAGIC ||
1971 DTYPE (option->type) == DT_NUM ||
1972 DTYPE (option->type) == DT_SORT ||
1973 DTYPE (option->type) == DT_RX ||
1974 DTYPE (option->type) == DT_USER ||
1975 DTYPE (option->type) == DT_SYS) {
1977 /* XXX maybe we need to get unset into handlers? */
1978 if (DTYPE (option->type) == DT_STR ||
1979 DTYPE (option->type) == DT_PATH ||
1980 DTYPE (option->type) == DT_ADDR ||
1981 DTYPE (option->type) == DT_USER ||
1982 DTYPE (option->type) == DT_SYS) {
1985 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1986 snprintf (err->data, err->dsize, _("$%s is read-only"),
1990 } else if (DTYPE (option->type) == DT_ADDR)
1991 address_list_wipe((address_t **) option->data);
1992 else if (DTYPE (option->type) == DT_USER)
1993 /* to unset $user_ means remove */
1994 hash_delete (ConfigOptions, option->option,
1995 option, del_option);
1997 p_delete((void **)(void *)&option->data);
2002 if (query || *s->dptr != '=') {
2003 FuncTable[DTYPE (option->type)].opt_to_string
2004 (err->data, err->dsize, option);
2008 /* the $madmutt_ variables are read-only */
2009 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
2010 snprintf (err->data, err->dsize, _("$%s is read-only"),
2017 mutt_extract_token (tmp, s, 0);
2018 if (!FuncTable[DTYPE (option->type)].opt_from_string
2019 (option, tmp->data, err->data, err->dsize))
2023 else if (DTYPE (option->type) == DT_QUAD) {
2026 quad_to_string (err->data, err->dsize, option);
2030 if (*s->dptr == '=') {
2033 mutt_extract_token (tmp, s, 0);
2034 if (ascii_strcasecmp ("yes", tmp->data) == 0)
2035 set_quadoption (option->data, M_YES);
2036 else if (ascii_strcasecmp ("no", tmp->data) == 0)
2037 set_quadoption (option->data, M_NO);
2038 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
2039 set_quadoption (option->data, M_ASKYES);
2040 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
2041 set_quadoption (option->data, M_ASKNO);
2043 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
2044 tmp->data, option->option);
2051 toggle_quadoption (option->data);
2053 set_quadoption (option->data, M_NO);
2055 set_quadoption (option->data, M_YES);
2059 snprintf (err->data, err->dsize, _("%s: unknown type"),
2065 if (option->flags & R_INDEX)
2066 set_option (OPTFORCEREDRAWINDEX);
2067 if (option->flags & R_PAGER)
2068 set_option (OPTFORCEREDRAWPAGER);
2069 if (option->flags & R_RESORT_SUB)
2070 set_option (OPTSORTSUBTHREADS);
2071 if (option->flags & R_RESORT)
2072 set_option (OPTNEEDRESORT);
2073 if (option->flags & R_RESORT_INIT)
2074 set_option (OPTRESORTINIT);
2075 if (option->flags & R_TREE)
2076 set_option (OPTREDRAWTREE);
2083 /* reads the specified initialization file. returns -1 if errors were found
2084 so that we can pause to let the user know... */
2085 static int source_rc (const char *rcfile, BUFFER * err)
2088 int line = 0, rc = 0, conv = 0;
2090 char *linebuf = NULL;
2091 char *currentline = NULL;
2095 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
2097 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
2098 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
2103 while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
2104 conv = ConfigCharset && (*ConfigCharset) && Charset;
2106 currentline = m_strdup(linebuf);
2109 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
2112 currentline = linebuf;
2117 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
2118 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
2119 if (--rc < -MAXERRS) {
2121 p_delete(¤tline);
2130 p_delete(¤tline);
2132 p_delete(&token.data);
2136 mutt_wait_filter (pid);
2138 /* the muttrc source keyword */
2139 snprintf (err->data, err->dsize,
2140 rc >= -MAXERRS ? _("source: errors in %s")
2141 : _("source: reading aborted due too many errors in %s"),
2150 static int parse_source (BUFFER * tmp, BUFFER * s,
2151 unsigned long data __attribute__ ((unused)),
2154 char path[_POSIX_PATH_MAX];
2158 if (mutt_extract_token (tmp, s, 0) != 0) {
2159 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
2163 m_strcpy(path, sizeof(path), tmp->data);
2164 mutt_expand_path (path, sizeof(path));
2166 rc += source_rc (path, err);
2168 while (MoreArgs (s));
2170 return ((rc < 0) ? -1 : 0);
2173 /* line command to execute
2175 token scratch buffer to be used by parser. caller should free
2176 token->data when finished. the reason for this variable is
2177 to avoid having to allocate and deallocate a lot of memory
2178 if we are parsing many lines. the caller can pass in the
2179 memory to use, which avoids having to create new space for
2180 every call to this function.
2182 err where to write error messages */
2183 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
2189 expn.data = expn.dptr = line;
2190 expn.dsize = m_strlen(line);
2194 debug_print (1, ("expand '%s'\n", line));
2196 expn.dptr = vskipspaces(expn.dptr);
2197 while (*expn.dptr) {
2198 if (*expn.dptr == '#')
2199 break; /* rest of line is a comment */
2200 if (*expn.dptr == ';') {
2204 mutt_extract_token (token, &expn, 0);
2205 for (i = 0; Commands[i].name; i++) {
2206 if (!m_strcmp(token->data, Commands[i].name)) {
2207 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
2212 if (!Commands[i].name) {
2213 snprintf (err->data, err->dsize, _("%s: unknown command"),
2214 NONULL (token->data));
2221 p_delete(&expn.data);
2226 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
2227 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
2228 /* initial string that starts completion. No telling how much crap
2229 * the user has typed so far. Allocate LONG_STRING just to be sure! */
2230 char User_typed[LONG_STRING] = { 0 };
2232 int Num_matched = 0; /* Number of matches for completion */
2233 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
2234 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
2236 /* helper function for completion. Changes the dest buffer if
2237 necessary/possible to aid completion.
2238 dest == completion result gets here.
2239 src == candidate for completion.
2240 try == user entered data for completion.
2241 len == length of dest buffer.
2243 static void candidate (char *dest, char *try, const char *src, int len)
2247 if (strstr (src, try) == src) {
2248 Matches[Num_matched++] = src;
2250 m_strcpy(dest, len, src);
2252 for (l = 0; src[l] && src[l] == dest[l]; l++);
2258 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
2262 int spaces; /* keep track of the number of leading spaces on the line */
2264 buffer = vskipspaces(buffer);
2265 spaces = buffer - pt;
2267 pt = buffer + pos - spaces;
2268 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2271 if (pt == buffer) { /* complete cmd */
2272 /* first TAB. Collect all the matches */
2275 m_strcpy(User_typed, sizeof(User_typed), pt);
2276 p_clear(Matches, countof(Matches));
2277 p_clear(Completed, countof(Completed));
2278 for (num = 0; Commands[num].name; num++)
2279 candidate (Completed, User_typed, Commands[num].name,
2281 Matches[Num_matched++] = User_typed;
2283 /* All matches are stored. Longest non-ambiguous string is ""
2284 * i.e. dont change 'buffer'. Fake successful return this time */
2285 if (User_typed[0] == 0)
2289 if (Completed[0] == 0 && User_typed[0])
2292 /* Num_matched will _always_ be atleast 1 since the initial
2293 * user-typed string is always stored */
2294 if (numtabs == 1 && Num_matched == 2)
2295 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2296 else if (numtabs > 1 && Num_matched > 2)
2297 /* cycle thru all the matches */
2298 snprintf (Completed, sizeof(Completed), "%s",
2299 Matches[(numtabs - 2) % Num_matched]);
2301 /* return the completed command */
2302 m_strcpy(buffer, len - spaces, Completed);
2304 else if (!m_strncmp(buffer, "set", 3)
2305 || !m_strncmp(buffer, "unset", 5)
2306 || !m_strncmp(buffer, "reset", 5)
2307 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
2308 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
2311 /* loop through all the possible prefixes (no, inv, ...) */
2312 if (!m_strncmp(buffer, "set", 3)) {
2313 for (num = 0; prefixes[num]; num++) {
2314 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
2315 pt += m_strlen(prefixes[num]);
2321 /* first TAB. Collect all the matches */
2324 m_strcpy(User_typed, sizeof(User_typed), pt);
2325 p_clear(Matches, countof(Matches));
2326 p_clear(Completed, countof(Completed));
2327 for (num = 0; MuttVars[num].option; num++)
2328 candidate(Completed, User_typed, MuttVars[num].option,
2330 Matches[Num_matched++] = User_typed;
2332 /* All matches are stored. Longest non-ambiguous string is ""
2333 * i.e. dont change 'buffer'. Fake successful return this time */
2334 if (User_typed[0] == 0)
2338 if (Completed[0] == 0 && User_typed[0])
2341 /* Num_matched will _always_ be atleast 1 since the initial
2342 * user-typed string is always stored */
2343 if (numtabs == 1 && Num_matched == 2)
2344 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2345 else if (numtabs > 1 && Num_matched > 2)
2346 /* cycle thru all the matches */
2347 snprintf (Completed, sizeof(Completed), "%s",
2348 Matches[(numtabs - 2) % Num_matched]);
2350 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2352 else if (!m_strncmp(buffer, "exec", 4)) {
2353 struct binding_t *menu = km_get_table (CurrentMenu);
2355 if (!menu && CurrentMenu != MENU_PAGER)
2359 /* first TAB. Collect all the matches */
2362 m_strcpy(User_typed, sizeof(User_typed), pt);
2363 p_clear(Matches, countof(Matches));
2364 p_clear(Completed, countof(Completed));
2365 for (num = 0; menu[num].name; num++)
2366 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
2367 /* try the generic menu */
2368 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2370 for (num = 0; menu[num].name; num++)
2371 candidate (Completed, User_typed, menu[num].name,
2374 Matches[Num_matched++] = User_typed;
2376 /* All matches are stored. Longest non-ambiguous string is ""
2377 * i.e. dont change 'buffer'. Fake successful return this time */
2378 if (User_typed[0] == 0)
2382 if (Completed[0] == 0 && User_typed[0])
2385 /* Num_matched will _always_ be atleast 1 since the initial
2386 * user-typed string is always stored */
2387 if (numtabs == 1 && Num_matched == 2)
2388 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2389 else if (numtabs > 1 && Num_matched > 2)
2390 /* cycle thru all the matches */
2391 snprintf (Completed, sizeof(Completed), "%s",
2392 Matches[(numtabs - 2) % Num_matched]);
2394 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2402 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
2404 char var[STRING], *pt = buffer;
2406 struct option_t* option = NULL;
2411 buffer = vskipspaces(buffer);
2412 spaces = buffer - pt;
2414 pt = buffer + pos - spaces;
2415 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2417 pt++; /* move past the space */
2418 if (*pt == '=') /* abort if no var before the '=' */
2421 if (m_strncmp(buffer, "set", 3) == 0) {
2422 m_strcpy(var, sizeof(var), pt);
2423 /* ignore the trailing '=' when comparing */
2424 var[m_strlen(var) - 1] = 0;
2425 if (!(option = hash_find (ConfigOptions, var)))
2426 return 0; /* no such variable. */
2428 char tmp[LONG_STRING], tmp2[LONG_STRING];
2430 ssize_t dlen = buffer + len - pt - spaces;
2431 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2435 if ((DTYPE (option->type) == DT_STR) ||
2436 (DTYPE (option->type) == DT_PATH) ||
2437 (DTYPE (option->type) == DT_RX)) {
2438 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
2439 if (DTYPE (option->type) == DT_PATH)
2440 mutt_pretty_mailbox (tmp);
2442 else if (DTYPE (option->type) == DT_ADDR) {
2443 rfc822_write_address (tmp, sizeof(tmp),
2444 *((address_t **) option->data), 0);
2446 else if (DTYPE (option->type) == DT_QUAD)
2447 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
2448 else if (DTYPE (option->type) == DT_NUM)
2449 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
2450 else if (DTYPE (option->type) == DT_SORT) {
2451 const struct mapping_t *map;
2454 switch (option->type & DT_SUBTYPE_MASK) {
2456 map = SortAliasMethods;
2458 case DT_SORT_BROWSER:
2459 map = SortBrowserMethods;
2462 map = SortKeyMethods;
2468 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2469 snprintf(tmp, sizeof(tmp), "%s%s%s",
2470 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2471 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2473 else if (DTYPE (option->type) == DT_MAGIC) {
2475 switch (DefaultMagic) {
2491 m_strcpy(tmp, sizeof(tmp), p);
2493 else if (DTYPE (option->type) == DT_BOOL)
2494 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
2498 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
2499 if (*s == '\\' || *s == '"')
2505 m_strcpy(tmp, sizeof(tmp), pt);
2506 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2514 /* Implement the -Q command line flag */
2515 int mutt_query_variables (string_list_t * queries)
2519 char errbuff[STRING];
2520 char command[STRING];
2528 err.dsize = sizeof(errbuff);
2530 for (p = queries; p; p = p->next) {
2531 snprintf (command, sizeof(command), "set ?%s\n", p->data);
2532 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2533 fprintf (stderr, "%s\n", err.data);
2534 p_delete(&token.data);
2537 printf ("%s\n", err.data);
2540 p_delete(&token.data);
2544 static int mutt_execute_commands (string_list_t * p)
2547 char errstr[SHORT_STRING];
2551 err.dsize = sizeof(errstr);
2553 for (; p; p = p->next) {
2554 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2555 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2556 p_delete(&token.data);
2560 p_delete(&token.data);
2564 void mutt_init (int skip_sys_rc, string_list_t * commands)
2567 struct utsname utsname;
2569 char buffer[STRING], error[STRING];
2570 int default_rc = 0, need_pause = 0;
2576 err.dsize = sizeof(error);
2578 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2579 * to have some room for $user_ vars */
2580 ConfigOptions = hash_create (sizeof(MuttVars) * 3);
2581 for (i = 0; MuttVars[i].option; i++) {
2582 if (DTYPE (MuttVars[i].type) != DT_SYS)
2583 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2585 hash_insert (ConfigOptions, MuttVars[i].option,
2586 add_option (MuttVars[i].option, MuttVars[i].init,
2591 * XXX - use something even more difficult to predict?
2593 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2594 "\033]9;%ld\a", (long) time (NULL));
2596 /* on one of the systems I use, getcwd() does not return the same prefix
2597 as is listed in the passwd file */
2598 if ((p = getenv ("HOME")))
2599 Homedir = m_strdup(p);
2601 /* Get some information about the user */
2602 if ((pw = getpwuid (getuid ()))) {
2605 Username = m_strdup(pw->pw_name);
2607 Homedir = m_strdup(pw->pw_dir);
2609 mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, GecosMask.rx);
2610 Realname = m_strdup(rnbuf);
2611 Shell = m_strdup(pw->pw_shell);
2617 fputs (_("unable to determine home directory"), stderr);
2620 if ((p = getenv ("USER")))
2621 Username = m_strdup(p);
2624 fputs (_("unable to determine username"), stderr);
2627 Shell = m_strdup((p = getenv ("SHELL")) ? p : "/bin/sh");
2630 debug_start(Homedir);
2632 /* And about the host... */
2634 /* some systems report the FQDN instead of just the hostname */
2635 if ((p = strchr (utsname.nodename, '.'))) {
2636 Hostname = p_dupstr(utsname.nodename, p - utsname.nodename);
2638 m_strcpy(buffer, sizeof(buffer), p); /* save the domain for below */
2641 Hostname = m_strdup(utsname.nodename);
2643 if (!p && getdnsdomainname(buffer, sizeof(buffer)) == -1)
2644 Fqdn = m_strdup("@");
2646 if (*buffer != '@') {
2647 Fqdn = p_new(char, m_strlen(buffer) + m_strlen(Hostname) + 2);
2648 sprintf (Fqdn, "%s.%s", NONULL(Hostname), buffer); /* __SPRINTF_CHECKED__ */
2651 Fqdn = m_strdup(NONULL (Hostname));
2658 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2660 fgets (buffer, sizeof(buffer), f);
2661 p = vskipspaces(buffer);
2663 while (*q && !isspace(*q))
2666 NewsServer = m_strdup(p);
2670 if ((p = getenv ("NNTPSERVER")))
2671 NewsServer = m_strdup(p);
2674 if ((p = getenv ("MAIL")))
2675 Spoolfile = m_strdup(p);
2676 else if ((p = getenv ("MAILDIR")))
2677 Spoolfile = m_strdup(p);
2680 mutt_concat_path(buffer, sizeof(buffer), NONULL(Homedir), MAILPATH);
2682 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(Username));
2684 Spoolfile = m_strdup(buffer);
2687 if ((p = getenv ("MAILCAPS")))
2688 MailcapPath = m_strdup(p);
2690 /* Default search path from RFC1524 */
2692 m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2693 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2696 Tempdir = m_strdup((p = getenv ("TMPDIR")) ? p : "/tmp");
2698 p = getenv ("VISUAL");
2700 p = getenv ("EDITOR");
2704 Editor = m_strdup(p);
2706 if ((p = getenv ("REPLYTO")) != NULL) {
2709 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2712 buf.data = buf.dptr = buffer;
2713 buf.dsize = m_strlen(buffer);
2716 parse_my_hdr (&token, &buf, 0, &err);
2717 p_delete(&token.data);
2720 if ((p = getenv ("EMAIL")) != NULL)
2721 From = rfc822_parse_adrlist (NULL, p);
2723 mutt_set_langinfo_charset ();
2724 mutt_set_charset (Charset);
2727 /* Set standard defaults */
2728 hash_map (ConfigOptions, mutt_set_default, 0);
2729 hash_map (ConfigOptions, mutt_restore_default, 0);
2731 CurrentMenu = MENU_MAIN;
2733 /* Do we have a locale definition? */
2734 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2735 ((p = getenv ("LANG")) != NULL && p[0]) ||
2736 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2737 set_option (OPTLOCALES);
2740 /* Unset suspend by default if we're the session leader */
2741 if (getsid (0) == getpid ())
2742 unset_option (OPTSUSPEND);
2745 mutt_init_history ();
2754 * When changing the code which looks for a configuration file,
2755 * please also change the corresponding code in muttbug.sh.in.
2765 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc-%s", NONULL (Homedir),
2767 if (access (buffer, F_OK) == -1)
2769 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL (Homedir));
2770 if (access (buffer, F_OK) == -1)
2772 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc-%s",
2773 NONULL (Homedir), MUTT_VERSION);
2774 if (access (buffer, F_OK) == -1)
2776 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
2780 Muttrc = m_strdup(buffer);
2783 m_strcpy(buffer, sizeof(buffer), Muttrc);
2785 mutt_expand_path (buffer, sizeof(buffer));
2786 Muttrc = m_strdup(buffer);
2788 p_delete(&AliasFile);
2789 AliasFile = m_strdup(NONULL (Muttrc));
2791 /* Process the global rc file if it exists and the user hasn't explicity
2792 requested not to via "-n". */
2794 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
2796 if (access (buffer, F_OK) == -1)
2797 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
2798 if (access (buffer, F_OK) == -1)
2799 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
2801 if (access (buffer, F_OK) == -1)
2802 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
2803 if (access (buffer, F_OK) != -1) {
2804 if (source_rc (buffer, &err) != 0) {
2805 fputs (err.data, stderr);
2806 fputc ('\n', stderr);
2812 /* Read the user's initialization file. */
2813 if (access (Muttrc, F_OK) != -1) {
2814 if (!option (OPTNOCURSES))
2816 if (source_rc (Muttrc, &err) != 0) {
2817 fputs (err.data, stderr);
2818 fputc ('\n', stderr);
2822 else if (!default_rc) {
2823 /* file specified by -F does not exist */
2824 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2825 mutt_endwin (buffer);
2829 if (mutt_execute_commands (commands) != 0)
2832 /* warn about synonym variables */
2833 if (!list_empty(Synonyms)) {
2835 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2836 for (i = 0; i < Synonyms->length; i++) {
2837 struct option_t* newopt = NULL, *oldopt = NULL;
2838 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2839 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2840 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2841 oldopt ? NONULL (oldopt->option) : "",
2842 newopt ? NONULL (newopt->option) : "",
2843 NONULL(((syn_t*) Synonyms->data[i])->f),
2844 ((syn_t*) Synonyms->data[i])->l);
2846 fprintf (stderr, _("Warning: synonym variables are scheduled"
2847 " for removal.\n"));
2848 list_del (&Synonyms, syn_del);
2852 if (need_pause && !option (OPTNOCURSES)) {
2853 if (mutt_any_key_to_continue (NULL) == -1)
2858 set_option (OPTWEED); /* turn weeding on by default */
2862 int mutt_get_hook_type (const char *name)
2864 struct command_t *c;
2866 for (c = Commands; c->name; c++)
2867 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2872 /* compare two option_t*'s for sorting -t/-T output */
2873 static int opt_cmp (const void* a, const void* b) {
2874 return (m_strcmp((*(struct option_t**) a)->option,
2875 (*(struct option_t**) b)->option));
2878 /* callback for hash_map() to put all non-synonym vars into list */
2879 static void opt_sel_full (const char* key __attribute__ ((unused)),
2881 unsigned long more) {
2882 list2_t** l = (list2_t**) more;
2883 struct option_t* option = (struct option_t*) data;
2885 if (DTYPE (option->type) == DT_SYN)
2887 list_push_back (l, option);
2890 /* callback for hash_map() to put all changed non-synonym vars into list */
2891 static void opt_sel_diff (const char* key __attribute__ ((unused)),
2893 unsigned long more) {
2894 list2_t** l = (list2_t**) more;
2895 struct option_t* option = (struct option_t*) data;
2896 char buf[LONG_STRING];
2898 if (DTYPE (option->type) == DT_SYN)
2901 mutt_option_value (option->option, buf, sizeof(buf));
2902 if (m_strcmp(buf, option->init) != 0)
2903 list_push_back (l, option);
2906 /* dump out the value of all the variables we have */
2907 int mutt_dump_variables (int full) {
2909 char outbuf[STRING];
2910 list2_t* tmp = NULL;
2911 struct option_t* option = NULL;
2913 /* get all non-synonyms into list... */
2914 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2915 (unsigned long) &tmp);
2917 if (!list_empty(tmp)) {
2918 /* ...and dump list sorted */
2919 qsort (tmp->data, tmp->length, sizeof(void*), opt_cmp);
2920 for (i = 0; i < tmp->length; i++) {
2921 option = (struct option_t*) tmp->data[i];
2922 FuncTable[DTYPE (option->type)].opt_to_string
2923 (outbuf, sizeof(outbuf), option);
2924 printf ("%s\n", outbuf);
2927 list_del (&tmp, NULL);