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>
25 #include <lib-ui/curses.h>
33 #include <lib-crypt/crypt.h>
34 #include "mutt_idna.h"
36 #if defined(USE_SSL) || defined(USE_GNUTLS)
40 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
41 #include "mutt_libesmtp.h"
49 #include "lib/debug.h"
55 #include <sys/utsname.h>
62 static const struct mapping_t* get_sortmap (struct option_t* option);
63 static int parse_sort (struct option_t* dst, const char *s,
64 const struct mapping_t *map,
65 char* errbuf, size_t errlen);
67 static HASH *ConfigOptions = NULL;
69 /* for synonym warning reports: synonym found during parsing */
73 struct option_t* n; /* new */
74 struct option_t* o; /* old */
77 /* for synonym warning reports: list of synonyms found */
78 static list2_t* Synonyms;
79 /* for synonym warning reports: current rc file */
80 static const char* CurRCFile = NULL;
81 /* for synonym warning reports: current rc line */
82 static int CurRCLine = 0;
84 /* prototypes for checking for special vars */
85 static int check_dsn_return (const char* option, unsigned long val,
86 char* errbuf, size_t errlen);
87 static int check_dsn_notify (const char* option, unsigned long val,
88 char* errbuf, size_t errlen);
89 static int check_history (const char* option, unsigned long val,
90 char* errbuf, size_t errlen);
91 /* this checks that numbers are >= 0 */
92 static int check_num (const char* option, unsigned long val,
93 char* errbuf, size_t errlen);
95 static int check_debug (const char* option, unsigned long val,
96 char* errbuf, size_t errlen);
99 /* use this to check only */
100 static int check_special (const char* option, unsigned long val,
101 char* errbuf, size_t errlen);
103 /* variable <-> sanity check function mappings
104 * when changing these, make sure the proper _from_string handler
105 * does this checking!
109 int (*check) (const char* option, unsigned long val,
110 char* errbuf, size_t errlen);
112 { "dsn_notify", check_dsn_notify },
113 { "dsn_return", check_dsn_return },
114 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
115 { "smtp_use_tls", mutt_libesmtp_check_usetls },
117 { "history", check_history },
118 { "pager_index_lines", check_num },
120 { "debug_level", check_debug },
126 /* protos for config type handles: convert value to string */
127 static void bool_to_string (char* dst, size_t dstlen, struct option_t* option);
128 static void num_to_string (char* dst, size_t dstlen, struct option_t* option);
129 static void str_to_string (char* dst, size_t dstlen, struct option_t* option);
130 static void quad_to_string (char* dst, size_t dstlen, struct option_t* option);
131 static void sort_to_string (char* dst, size_t dstlen, struct option_t* option);
132 static void rx_to_string (char* dst, size_t dstlen, struct option_t* option);
133 static void magic_to_string (char* dst, size_t dstlen, struct option_t* option);
134 static void addr_to_string (char* dst, size_t dstlen, struct option_t* option);
135 static void user_to_string (char* dst, size_t dstlen, struct option_t* option);
136 static void sys_to_string (char* dst, size_t dstlen, struct option_t* option);
138 /* protos for config type handles: convert to value from string */
139 static int bool_from_string (struct option_t* dst, const char* val,
140 char* errbuf, size_t errlen);
141 static int num_from_string (struct option_t* dst, const char* val,
142 char* errbuf, size_t errlen);
143 static int str_from_string (struct option_t* dst, const char* val,
144 char* errbuf, size_t errlen);
145 static int path_from_string (struct option_t* dst, const char* val,
146 char* errbuf, size_t errlen);
147 static int quad_from_string (struct option_t* dst, const char* val,
148 char* errbuf, size_t errlen);
149 static int sort_from_string (struct option_t* dst, const char* val,
150 char* errbuf, size_t errlen);
151 static int rx_from_string (struct option_t* dst, const char* val,
152 char* errbuf, size_t errlen);
153 static int magic_from_string (struct option_t* dst, const char* val,
154 char* errbuf, size_t errlen);
155 static int addr_from_string (struct option_t* dst, const char* val,
156 char* errbuf, size_t errlen);
157 static int user_from_string (struct option_t* dst, const char* val,
158 char* errbuf, size_t errlen);
162 void (*opt_to_string) (char* dst, size_t dstlen, struct option_t* option);
163 int (*opt_from_string) (struct option_t* dst, const char* val,
164 char* errbuf, size_t errlen);
166 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
167 { DT_BOOL, bool_to_string, bool_from_string },
168 { DT_NUM, num_to_string, num_from_string },
169 { DT_STR, str_to_string, str_from_string },
170 { DT_PATH, str_to_string, path_from_string },
171 { DT_QUAD, quad_to_string, quad_from_string },
172 { DT_SORT, sort_to_string, sort_from_string },
173 { DT_RX, rx_to_string, rx_from_string },
174 { DT_MAGIC, magic_to_string, magic_from_string },
175 /* synonyms should be resolved already so we don't need this
176 * but must define it as DT_ is used for indexing */
177 { DT_SYN, NULL, NULL },
178 { DT_ADDR, addr_to_string, addr_from_string },
179 { DT_USER, user_to_string, user_from_string },
180 { DT_SYS, sys_to_string, NULL },
183 static void bool_to_string (char* dst, size_t dstlen,
184 struct option_t* option) {
185 snprintf (dst, dstlen, "%s=%s", option->option,
186 option (option->data) ? "yes" : "no");
189 static int bool_from_string (struct option_t* dst, const char* val,
190 char* errbuf __attribute__ ((unused)),
191 size_t errlen __attribute__ ((unused))) {
196 if (ascii_strncasecmp (val, "yes", 3) == 0)
198 else if (ascii_strncasecmp (val, "no", 2) == 0)
204 set_option (dst->data);
206 unset_option (dst->data);
210 static void num_to_string (char* dst, size_t dstlen,
211 struct option_t* option) {
213 const char* fmt = (m_strcmp(option->option, "umask") == 0) ?
215 snprintf (dst, dstlen, fmt, option->option,
216 *((short*) option->data));
219 static int num_from_string (struct option_t* dst, const char* val,
220 char* errbuf, size_t errlen) {
221 int num = 0, old = 0;
227 num = strtol (val, &t, 0);
229 if (!*val || *t || (short) num != num) {
231 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
237 /* just temporarily accept new val so that check_special for
238 * $history already has it when doing history's init() */
239 old = *((short*) dst->data);
240 *((short*) dst->data) = (short) num;
242 if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
243 *((short*) dst->data) = old;
250 static void str_to_string (char* dst, size_t dstlen,
251 struct option_t* option) {
252 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
253 NONULL (*((char**) option->data)));
256 static void user_to_string (char* dst, size_t dstlen,
257 struct option_t* option) {
258 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
259 NONULL (((char*) option->data)));
262 static void sys_to_string (char* dst, size_t dstlen,
263 struct option_t* option) {
264 char *val = NULL, *t = NULL;
267 /* get some $madmutt_ values dynamically */
268 if (ascii_strcmp ("madmutt_pwd", option->option) == 0) {
269 val = p_new(char, _POSIX_PATH_MAX);
270 val = getcwd (val, _POSIX_PATH_MAX-1);
272 } else if (ascii_strcmp ("madmutt_folder_path", option->option) == 0 &&
273 CurrentFolder && *CurrentFolder) {
275 } else if (ascii_strcmp ("madmutt_folder_name", option->option) == 0 &&
276 CurrentFolder && *CurrentFolder) {
278 ssize_t Maildirlength = m_strlen(Maildir);
281 * if name starts with $folder, just strip it to keep hierarchy
282 * $folder=imap://host, path=imap://host/inbox/b -> inbox/b
284 if (Maildirlength > 0 && m_strncmp(CurrentFolder, Maildir,
285 Maildirlength) == 0 &&
286 m_strlen(CurrentFolder) > Maildirlength) {
287 val = CurrentFolder + Maildirlength;
288 if (Maildir[strlen(Maildir)-1]!='/')
290 /* if not $folder, just use everything after last / */
291 } else if ((t = strrchr (CurrentFolder, '/')) != NULL)
293 /* default: use as-is */
295 val = (char *) CurrentFolder;
298 val = (char *) option->init;
300 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (val));
305 static int path_from_string (struct option_t* dst, const char* val,
306 char* errbuf __attribute__ ((unused)), size_t errlen __attribute__ ((unused))) {
307 char path[_POSIX_PATH_MAX];
313 p_delete((char**) dst->data);
318 m_strcpy(path, sizeof(path), val);
319 mutt_expand_path (path, sizeof(path));
320 m_strreplace((char **) dst->data, path);
324 static int str_from_string (struct option_t* dst, const char* val,
325 char* errbuf, size_t errlen) {
329 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
332 m_strreplace((char**) dst->data, val);
336 static int user_from_string (struct option_t* dst, const char* val,
337 char* errbuf __attribute__ ((unused)), size_t errlen __attribute__ ((unused))) {
338 /* if dst == NULL, we may get here in case the user did unset it,
339 * see parse_set() where item is free()'d before coming here; so
340 * just silently ignore it */
343 if (m_strlen((char*) dst->data) == 0)
344 dst->data = (unsigned long) m_strdup(val);
346 char* s = (char*) dst->data;
347 m_strreplace(&s, val);
349 if (m_strlen(dst->init) == 0)
350 dst->init = m_strdup((char*) dst->data);
354 static void quad_to_string (char* dst, size_t dstlen,
355 struct option_t* option) {
356 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
357 snprintf (dst, dstlen, "%s=%s", option->option,
358 vals[quadoption (option->data)]);
361 static int quad_from_string (struct option_t* dst, const char* val,
362 char* errbuf __attribute__ ((unused)), size_t errlen __attribute__ ((unused))) {
367 if (ascii_strncasecmp (val, "yes", 3) == 0)
369 else if (ascii_strncasecmp (val, "no", 2) == 0)
371 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
373 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
379 set_quadoption (dst->data, flag);
383 static void sort_to_string (char* dst, size_t dstlen,
384 struct option_t* option) {
385 const struct mapping_t *map = get_sortmap (option);
386 const char *p = NULL;
389 snprintf (dst, sizeof(dst), "%s=unknown", option->option);
393 p = mutt_getnamebyvalue(*((short *)option->data) & SORT_MASK, map);
395 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
396 (*((short *) option->data) & SORT_REVERSE) ?
398 (*((short *) option->data) & SORT_LAST) ? "last-" :
402 static int sort_from_string (struct option_t* dst, const char* val,
403 char* errbuf, size_t errlen) {
404 const struct mapping_t *map = NULL;
405 if (!(map = get_sortmap (dst))) {
407 snprintf (errbuf, errlen, _("%s: Unknown type."),
411 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
416 static void rx_to_string (char* dst, size_t dstlen,
417 struct option_t* option) {
418 rx_t* p = (rx_t*) option->data;
419 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
420 NONULL (p->pattern));
423 static int rx_from_string (struct option_t* dst, const char* val,
424 char* errbuf, size_t errlen) {
427 int flags = 0, e = 0, not = 0;
433 if (option (OPTATTACHMSG) && !m_strcmp(dst->option, "reply_regexp")) {
435 snprintf (errbuf, errlen,
436 "Operation not permitted when in attach-message mode.");
440 if (!((rx_t*) dst->data))
441 *((rx_t**) dst->data) = p_new(rx_t, 1);
443 p = (rx_t*) dst->data;
445 /* something to do? */
446 if (!val || !*val || (p->pattern && m_strcmp(p->pattern, val) == 0))
449 if (m_strcmp(dst->option, "mask") != 0)
450 flags |= mutt_which_case (val);
453 if (m_strcmp(dst->option, "mask") == 0 && *s == '!') {
458 rx = p_new(regex_t, 1);
460 if ((e = REGCOMP (rx, s, flags)) != 0) {
461 regerror (e, rx, errbuf, errlen);
472 m_strreplace(&p->pattern, val);
476 if (m_strcmp(dst->option, "reply_regexp") == 0)
477 mutt_adjust_all_subjects ();
482 static void magic_to_string (char* dst, size_t dstlen,
483 struct option_t* option) {
484 const char* s = NULL;
485 switch (option->data) {
486 case M_MBOX: s = "mbox"; break;
487 case M_MMDF: s = "MMDF"; break;
488 case M_MH: s = "MH"; break;
489 case M_MAILDIR: s = "Maildir"; break;
490 default: s = "unknown"; break;
492 snprintf (dst, dstlen, "%s=%s", option->option, s);
495 static int magic_from_string (struct option_t* dst, const char* val,
496 char* errbuf __attribute__ ((unused)), size_t errlen __attribute__ ((unused))) {
499 if (!dst || !val || !*val)
501 if (ascii_strncasecmp (val, "mbox", 4) == 0)
503 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
505 else if (ascii_strncasecmp (val, "mh", 2) == 0)
507 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
513 *((short*) dst->data) = flag;
518 static void addr_to_string (char* dst, size_t dstlen,
519 struct option_t* option) {
522 rfc822_write_address (s, sizeof(s), *((address_t**) option->data), 0);
523 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
526 static int addr_from_string (struct option_t* dst, const char* val,
527 char* errbuf __attribute__ ((unused)), size_t errlen __attribute__ ((unused))) {
530 address_delete ((address_t**) dst->data);
532 *((address_t**) dst->data) = rfc822_parse_adrlist (NULL, val);
536 int mutt_option_value (const char* val, char* dst, size_t dstlen) {
537 struct option_t* option = NULL;
538 char* tmp = NULL, *t = NULL;
541 if (!(option = hash_find (ConfigOptions, val))) {
542 debug_print (1, ("var '%s' not found\n", val));
546 tmp = p_new(char, dstlen+1);
547 FuncTable[DTYPE (option->type)].opt_to_string (tmp, dstlen, option);
549 /* as we get things of type $var=value and don't want to bloat the
550 * above "just" for expansion, we do the stripping here */
551 debug_print (1, ("orig == '%s'\n", tmp));
552 t = strchr (tmp, '=');
556 if (t[l-1] == '"' && *t == '"') {
561 memcpy (dst, t, l+1);
563 debug_print (1, ("stripped == '%s'\n", dst));
568 /* for synonym warning reports: adds synonym to end of list */
569 static void syn_add (struct option_t* n, struct option_t* o) {
570 syn_t* tmp = p_new(syn_t, 1);
571 tmp->f = m_strdup(CurRCFile);
575 list_push_back (&Synonyms, tmp);
578 /* for synonym warning reports: free single item (for list_del()) */
579 static void syn_del (void** p) {
580 p_delete(&(*(syn_t**) p)->f);
584 void toggle_quadoption (int opt)
587 int b = (opt % 4) * 2;
589 QuadOptions[n] ^= (1 << b);
592 void set_quadoption (int opt, int flag)
595 int b = (opt % 4) * 2;
597 QuadOptions[n] &= ~(0x3 << b);
598 QuadOptions[n] |= (flag & 0x3) << b;
601 int quadoption (int opt)
604 int b = (opt % 4) * 2;
606 return (QuadOptions[n] >> b) & 0x3;
609 int query_quadoption (int opt, const char *prompt)
611 int v = quadoption (opt);
619 v = mutt_yesorno (prompt, (v == M_ASKYES));
620 CLEARLINE (LINES - 1);
627 static void add_to_list (LIST ** list, const char *str)
629 LIST *t, *last = NULL;
631 /* don't add a NULL or empty string to the list */
632 if (!str || *str == '\0')
635 /* check to make sure the item is not already on this list */
636 for (last = *list; last; last = last->next) {
637 if (ascii_strcasecmp (str, last->data) == 0) {
638 /* already on the list, so just ignore it */
646 if (!*list || last) {
648 t->data = m_strdup(str);
658 static int add_to_rx_list (list2_t** list, const char *s, int flags,
667 if (!(rx = rx_compile (s, flags))) {
668 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
672 i = rx_lookup ((*list), rx->pattern);
676 list_push_back (list, rx);
680 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
681 const char *templ, BUFFER * err)
683 SPAM_LIST *t = NULL, *last = NULL;
688 if (!pat || !*pat || !templ)
691 if (!(rx = rx_compile (pat, REG_ICASE))) {
692 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
696 /* check to make sure the item is not already on this list */
697 for (last = *list; last; last = last->next) {
698 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
699 /* Already on the list. Formerly we just skipped this case, but
700 * now we're supporting removals, which means we're supporting
701 * re-adds conceptually. So we probably want this to imply a
702 * removal, then do an add. We can achieve the removal by freeing
703 * the template, and leaving t pointed at the current item.
706 p_delete(&t->template);
713 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
714 * update. Otherwise we want to make a new one to link at the list's end.
717 t = mutt_new_spam_list ();
725 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
726 t->template = m_strdup(templ);
728 /* Find highest match number in template string */
730 for (p = templ; *p;) {
735 while (*p && isdigit ((int) *p))
741 t->nmatch++; /* match 0 is always the whole expr */
746 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
748 SPAM_LIST *spam, *prev;
751 /* Being first is a special case. */
755 if (spam->rx && !m_strcmp(spam->rx->pattern, pat)) {
758 p_delete(&spam->template);
764 for (spam = prev->next; spam;) {
765 if (!m_strcmp(spam->rx->pattern, pat)) {
766 prev->next = spam->next;
768 p_delete(&spam->template);
781 static void remove_from_list (LIST ** l, const char *str)
783 LIST *p, *last = NULL;
785 if (m_strcmp("*", str) == 0)
786 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
791 if (ascii_strcasecmp (str, p->data) == 0) {
794 last->next = p->next;
807 static int remove_from_rx_list (list2_t** l, const char *str)
811 if (m_strcmp("*", str) == 0) {
812 list_del (l, (list_del_t*) rx_free);
816 i = rx_lookup ((*l), str);
818 rx_t* r = list_pop_idx ((*l), i);
826 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
830 unsigned long res = 0;
832 struct option_t* option = NULL;
835 mutt_extract_token (tmp, s, 0);
837 /* is the item defined as a variable or a function? */
838 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL)
841 for (i = 0; !res && i < MENU_MAX; i++) {
842 struct binding_t *b = km_get_table (Menus[i].value);
847 for (j = 0; b[j].name; j++)
848 if (!ascii_strncasecmp (tmp->data, b[j].name, m_strlen(tmp->data))
849 && (m_strlen(b[j].name) == m_strlen(tmp->data))) {
855 /* check for feature_* */
856 if (!res && ascii_strncasecmp (tmp->data, "feature_", 8) == 0 &&
857 (j = m_strlen(tmp->data)) > 8) {
859 while (Features[i]) {
860 if (m_strlen(Features[i]) == j-8 &&
861 ascii_strncasecmp (Features[i], tmp->data+8, j-8) == 0) {
871 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
873 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
877 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
880 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
881 mutt_error ("Error: %s", err->data);
882 p_delete(&token.data);
885 p_delete(&token.data);
890 static int parse_unignore (BUFFER * buf, BUFFER * s,
891 unsigned long data __attribute__ ((unused)),
892 BUFFER * err __attribute__ ((unused)))
895 mutt_extract_token (buf, s, 0);
897 /* don't add "*" to the unignore list */
898 if (strcmp (buf->data, "*"))
899 add_to_list (&UnIgnore, buf->data);
901 remove_from_list (&Ignore, buf->data);
903 while (MoreArgs (s));
908 static int parse_ignore (BUFFER * buf, BUFFER * s,
909 unsigned long data __attribute__ ((unused)),
910 BUFFER * err __attribute__ ((unused)))
913 mutt_extract_token (buf, s, 0);
914 remove_from_list (&UnIgnore, buf->data);
915 add_to_list (&Ignore, buf->data);
917 while (MoreArgs (s));
922 static int parse_list (BUFFER * buf, BUFFER * s,
923 unsigned long data __attribute__ ((unused)),
924 BUFFER * err __attribute__ ((unused)))
927 mutt_extract_token (buf, s, 0);
928 add_to_list ((LIST **) data, buf->data);
930 while (MoreArgs (s));
935 static void _alternates_clean (void)
939 if (Context && Context->msgcount) {
940 for (i = 0; i < Context->msgcount; i++)
941 Context->hdrs[i]->recip_valid = 0;
945 static int parse_alternates (BUFFER * buf, BUFFER * s,
946 unsigned long data __attribute__ ((unused)),
947 BUFFER * err __attribute__ ((unused)))
949 _alternates_clean ();
951 mutt_extract_token (buf, s, 0);
952 remove_from_rx_list (&UnAlternates, buf->data);
954 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
957 while (MoreArgs (s));
962 static int parse_unalternates (BUFFER * buf, BUFFER * s,
963 unsigned long data __attribute__ ((unused)),
964 BUFFER * err __attribute__ ((unused)))
966 _alternates_clean ();
968 mutt_extract_token (buf, s, 0);
969 remove_from_rx_list (&Alternates, buf->data);
971 if (m_strcmp(buf->data, "*") &&
972 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
976 while (MoreArgs (s));
981 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
988 /* Insist on at least one parameter */
991 m_strcpy(err->data, err->dsize, _("spam: no matching pattern"));
993 m_strcpy(err->data, err->dsize, _("nospam: no matching pattern"));
997 /* Extract the first token, a regexp */
998 mutt_extract_token (buf, s, 0);
1000 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
1001 if (data == M_SPAM) {
1002 /* If there's a second parameter, it's a template for the spam tag. */
1004 mutt_extract_token (&templ, s, 0);
1006 /* Add to the spam list. */
1007 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
1008 p_delete(&templ.data);
1011 p_delete(&templ.data);
1014 /* If not, try to remove from the nospam list. */
1016 remove_from_rx_list (&NoSpamList, buf->data);
1022 /* M_NOSPAM is for nospam commands. */
1023 else if (data == M_NOSPAM) {
1024 /* nospam only ever has one parameter. */
1026 /* "*" is a special case. */
1027 if (!m_strcmp(buf->data, "*")) {
1028 mutt_free_spam_list (&SpamList);
1029 list_del (&NoSpamList, (list_del_t*) rx_free);
1033 /* If it's on the spam list, just remove it. */
1034 if (remove_from_spam_list (&SpamList, buf->data) != 0)
1037 /* Otherwise, add it to the nospam list. */
1038 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
1044 /* This should not happen. */
1045 m_strcpy(err->data, err->dsize, "This is no good at all.");
1049 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
1050 BUFFER * err __attribute__ ((unused)))
1053 mutt_extract_token (buf, s, 0);
1055 * Check for deletion of entire list
1057 if (m_strcmp(buf->data, "*") == 0) {
1058 mutt_free_list ((LIST **) data);
1061 remove_from_list ((LIST **) data, buf->data);
1063 while (MoreArgs (s));
1068 static int parse_lists (BUFFER * buf, BUFFER * s,
1069 unsigned long data __attribute__ ((unused)),
1073 mutt_extract_token (buf, s, 0);
1074 remove_from_rx_list (&UnMailLists, buf->data);
1076 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1079 while (MoreArgs (s));
1084 /* always wise to do what someone else did before */
1085 static void _attachments_clean (void) {
1087 if (Context && Context->msgcount) {
1088 for (i = 0; i < Context->msgcount; i++)
1089 Context->hdrs[i]->attach_valid = 0;
1093 static int parse_attach_list (BUFFER *buf, BUFFER *s, LIST **ldata,
1094 BUFFER *err __attribute__ ((unused))) {
1096 LIST *listp, *lastp;
1101 /* Find the last item in the list that data points to. */
1103 debug_print (5, ("parse_attach_list: ldata = %p, *ldata = %p\n",
1105 for (listp = *ldata; listp; listp = listp->next) {
1106 a = (ATTACH_MATCH *)listp->data;
1107 debug_print (5, ("parse_attach_list: skipping %s/%s\n", a->major, a->minor));
1112 mutt_extract_token (buf, s, 0);
1114 if (!buf->data || *buf->data == '\0')
1117 a = p_new(ATTACH_MATCH, 1);
1119 /* some cheap hacks that I expect to remove */
1120 if (!m_strcasecmp(buf->data, "any"))
1121 a->major = m_strdup("*/.*");
1122 else if (!m_strcasecmp(buf->data, "none"))
1123 a->major = m_strdup("cheap_hack/this_should_never_match");
1125 a->major = m_strdup(buf->data);
1127 if ((p = strchr(a->major, '/'))) {
1132 a->minor = "unknown";
1135 len = m_strlen(a->minor);
1136 tmpminor = p_new(char, len + 3);
1137 strcpy(&tmpminor[1], a->minor); /* __STRCPY_CHECKED__ */
1139 tmpminor[len+1] = '$';
1140 tmpminor[len+2] = '\0';
1142 a->major_int = mutt_check_mime_type(a->major);
1143 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
1145 p_delete(&tmpminor);
1147 debug_print (5, ("parse_attach_list: added %s/%s [%d]\n",
1148 a->major, a->minor, a->major_int));
1150 listp = p_new(LIST, 1);
1151 listp->data = (char *)a;
1154 lastp->next = listp;
1160 while (MoreArgs (s));
1162 _attachments_clean();
1166 static int parse_unattach_list (BUFFER *buf, BUFFER *s, LIST **ldata,
1167 BUFFER *err __attribute__ ((unused))) {
1169 LIST *lp, *lastp, *newlp;
1175 mutt_extract_token (buf, s, 0);
1177 if (!m_strcasecmp(buf->data, "any"))
1178 tmp = m_strdup("*/.*");
1179 else if (!m_strcasecmp(buf->data, "none"))
1180 tmp = m_strdup("cheap_hack/this_should_never_match");
1182 tmp = m_strdup(buf->data);
1184 if ((minor = strchr(tmp, '/'))) {
1188 minor = m_strdup("unknown");
1190 major = mutt_check_mime_type(tmp);
1192 /* We must do our own walk here because remove_from_list() will only
1193 * remove the LIST->data, not anything pointed to by the LIST->data. */
1195 for(lp = *ldata; lp; ) {
1196 a = (ATTACH_MATCH *)lp->data;
1197 debug_print(5, ("parse_unattach_list: check %s/%s [%d] : %s/%s [%d]\n",
1198 a->major, a->minor, a->major_int, tmp, minor, major));
1199 if (a->major_int == major && !m_strcasecmp(minor, a->minor)) {
1200 debug_print(5, ("parse_unattach_list: removed %s/%s [%d]\n",
1201 a->major, a->minor, a->major_int));
1202 regfree(&a->minor_rx);
1203 p_delete(&a->major);
1205 /* Relink backward */
1207 lastp->next = lp->next;
1212 p_delete(&lp->data); /* same as a */
1222 while (MoreArgs (s));
1225 _attachments_clean();
1229 static int print_attach_list (LIST *lp, char op, const char *name) {
1231 printf("attachments %c%s %s/%s\n", op, name,
1232 ((ATTACH_MATCH *)lp->data)->major,
1233 ((ATTACH_MATCH *)lp->data)->minor);
1240 static int parse_attachments (BUFFER *buf, BUFFER *s,
1241 unsigned long data __attribute__ ((unused)),
1246 mutt_extract_token(buf, s, 0);
1247 if (!buf->data || *buf->data == '\0') {
1248 m_strcpy(err->data, err->dsize, _("attachments: no disposition"));
1252 category = buf->data;
1258 printf("\nCurrent attachments settings:\n\n");
1259 print_attach_list(AttachAllow, '+', "A");
1260 print_attach_list(AttachExclude, '-', "A");
1261 print_attach_list(InlineAllow, '+', "I");
1262 print_attach_list(InlineExclude, '-', "I");
1263 set_option (OPTFORCEREDRAWINDEX);
1264 set_option (OPTFORCEREDRAWPAGER);
1265 mutt_any_key_to_continue (NULL);
1269 if (op != '+' && op != '-') {
1273 if (!m_strncasecmp(category, "attachment", strlen(category))) {
1275 listp = &AttachAllow;
1277 listp = &AttachExclude;
1279 else if (!m_strncasecmp(category, "inline", strlen(category))) {
1281 listp = &InlineAllow;
1283 listp = &InlineExclude;
1285 m_strcpy(err->data, err->dsize, _("attachments: invalid disposition"));
1289 return parse_attach_list(buf, s, listp, err);
1292 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data __attribute__ ((unused)), BUFFER *err) {
1296 mutt_extract_token(buf, s, 0);
1297 if (!buf->data || *buf->data == '\0') {
1298 m_strcpy(err->data, err->dsize, _("unattachments: no disposition"));
1304 if (op != '+' && op != '-') {
1308 if (!m_strncasecmp(p, "attachment", strlen(p))) {
1310 listp = &AttachAllow;
1312 listp = &AttachExclude;
1314 else if (!m_strncasecmp(p, "inline", strlen(p))) {
1316 listp = &InlineAllow;
1318 listp = &InlineExclude;
1321 m_strcpy(err->data, err->dsize, _("unattachments: invalid disposition"));
1325 return parse_unattach_list(buf, s, listp, err);
1328 static int parse_unlists (BUFFER * buf, BUFFER * s,
1329 unsigned long data __attribute__ ((unused)),
1330 BUFFER * err __attribute__ ((unused)))
1333 mutt_extract_token (buf, s, 0);
1334 remove_from_rx_list (&SubscribedLists, buf->data);
1335 remove_from_rx_list (&MailLists, buf->data);
1337 if (m_strcmp(buf->data, "*") &&
1338 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1341 while (MoreArgs (s));
1346 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1350 mutt_extract_token (buf, s, 0);
1351 remove_from_rx_list (&UnMailLists, buf->data);
1352 remove_from_rx_list (&UnSubscribedLists, buf->data);
1354 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1356 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1359 while (MoreArgs (s));
1364 static int parse_unsubscribe (BUFFER * buf, BUFFER * s,
1365 unsigned long data __attribute__ ((unused)),
1366 BUFFER * err __attribute__ ((unused)))
1369 mutt_extract_token (buf, s, 0);
1370 remove_from_rx_list (&SubscribedLists, buf->data);
1372 if (m_strcmp(buf->data, "*") &&
1373 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1376 while (MoreArgs (s));
1381 static int parse_unalias (BUFFER * buf, BUFFER * s,
1382 unsigned long data __attribute__ ((unused)),
1383 BUFFER * err __attribute__ ((unused)))
1385 ALIAS *tmp, *last = NULL;
1388 mutt_extract_token (buf, s, 0);
1390 if (m_strcmp("*", buf->data) == 0) {
1391 if (CurrentMenu == MENU_ALIAS) {
1392 for (tmp = Aliases; tmp; tmp = tmp->next)
1394 set_option (OPTFORCEREDRAWINDEX);
1397 mutt_free_alias (&Aliases);
1401 for (tmp = Aliases; tmp; tmp = tmp->next) {
1402 if (m_strcasecmp(buf->data, tmp->name) == 0) {
1403 if (CurrentMenu == MENU_ALIAS) {
1405 set_option (OPTFORCEREDRAWINDEX);
1410 last->next = tmp->next;
1412 Aliases = tmp->next;
1414 mutt_free_alias (&tmp);
1420 while (MoreArgs (s));
1424 static int parse_alias (BUFFER * buf, BUFFER * s,
1425 unsigned long data __attribute__ ((unused)),
1428 ALIAS *tmp = Aliases;
1432 if (!MoreArgs (s)) {
1433 m_strcpy(err->data, err->dsize, _("alias: no address"));
1437 mutt_extract_token (buf, s, 0);
1439 debug_print (2, ("first token is '%s'.\n", buf->data));
1441 /* check to see if an alias with this name already exists */
1442 for (; tmp; tmp = tmp->next) {
1443 if (!m_strcasecmp(tmp->name, buf->data))
1449 /* create a new alias */
1450 tmp = p_new(ALIAS, 1);
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_delete (&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)))
1499 LIST *tmp = UserHeader;
1504 mutt_extract_token (buf, s, 0);
1505 if (m_strcmp("*", buf->data) == 0)
1506 mutt_free_list (&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 mutt_free_list (&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 = mutt_new_list ();
1569 tmp = mutt_new_list ();
1572 tmp->data = buf->data;
1578 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1579 char* errbuf, size_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, size_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, size_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, size_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, size_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, size_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, size_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_delete ((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 **)&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 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, size_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, size_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 size_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 (LIST * 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 (LIST * 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, LIST * 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 Realname = m_strdup(mutt_gecos_name (rnbuf, sizeof(rnbuf), pw));
2612 Shell = m_strdup(pw->pw_shell);
2618 fputs (_("unable to determine home directory"), stderr);
2621 if ((p = getenv ("USER")))
2622 Username = m_strdup(p);
2625 fputs (_("unable to determine username"), stderr);
2628 Shell = m_strdup((p = getenv ("SHELL")) ? p : "/bin/sh");
2631 debug_start(Homedir);
2633 /* And about the host... */
2635 /* some systems report the FQDN instead of just the hostname */
2636 if ((p = strchr (utsname.nodename, '.'))) {
2637 Hostname = p_dupstr(utsname.nodename, p - utsname.nodename);
2639 m_strcpy(buffer, sizeof(buffer), p); /* save the domain for below */
2642 Hostname = m_strdup(utsname.nodename);
2644 if (!p && getdnsdomainname (buffer, sizeof(buffer)) == -1)
2645 Fqdn = m_strdup("@");
2647 if (*buffer != '@') {
2648 Fqdn = p_new(char, m_strlen(buffer) + m_strlen(Hostname) + 2);
2649 sprintf (Fqdn, "%s.%s", NONULL(Hostname), buffer); /* __SPRINTF_CHECKED__ */
2652 Fqdn = m_strdup(NONULL (Hostname));
2659 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2661 fgets (buffer, sizeof(buffer), f);
2662 p = vskipspaces(buffer);
2664 while (*q && !isspace(*q))
2667 NewsServer = m_strdup(p);
2671 if ((p = getenv ("NNTPSERVER")))
2672 NewsServer = m_strdup(p);
2675 if ((p = getenv ("MAIL")))
2676 Spoolfile = m_strdup(p);
2677 else if ((p = getenv ("MAILDIR")))
2678 Spoolfile = m_strdup(p);
2681 mutt_concat_path(buffer, sizeof(buffer), NONULL(Homedir), MAILPATH);
2683 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(Username));
2685 Spoolfile = m_strdup(buffer);
2688 if ((p = getenv ("MAILCAPS")))
2689 MailcapPath = m_strdup(p);
2691 /* Default search path from RFC1524 */
2693 m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2694 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2697 Tempdir = m_strdup((p = getenv ("TMPDIR")) ? p : "/tmp");
2699 p = getenv ("VISUAL");
2701 p = getenv ("EDITOR");
2705 Editor = m_strdup(p);
2707 if ((p = getenv ("REPLYTO")) != NULL) {
2710 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2713 buf.data = buf.dptr = buffer;
2714 buf.dsize = m_strlen(buffer);
2717 parse_my_hdr (&token, &buf, 0, &err);
2718 p_delete(&token.data);
2721 if ((p = getenv ("EMAIL")) != NULL)
2722 From = rfc822_parse_adrlist (NULL, p);
2724 mutt_set_langinfo_charset ();
2725 mutt_set_charset (Charset);
2728 /* Set standard defaults */
2729 hash_map (ConfigOptions, mutt_set_default, 0);
2730 hash_map (ConfigOptions, mutt_restore_default, 0);
2732 CurrentMenu = MENU_MAIN;
2734 /* Do we have a locale definition? */
2735 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2736 ((p = getenv ("LANG")) != NULL && p[0]) ||
2737 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2738 set_option (OPTLOCALES);
2741 /* Unset suspend by default if we're the session leader */
2742 if (getsid (0) == getpid ())
2743 unset_option (OPTSUSPEND);
2746 mutt_init_history ();
2755 * When changing the code which looks for a configuration file,
2756 * please also change the corresponding code in muttbug.sh.in.
2766 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc-%s", NONULL (Homedir),
2768 if (access (buffer, F_OK) == -1)
2770 snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL (Homedir));
2771 if (access (buffer, F_OK) == -1)
2773 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc-%s",
2774 NONULL (Homedir), MUTT_VERSION);
2775 if (access (buffer, F_OK) == -1)
2777 snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
2781 Muttrc = m_strdup(buffer);
2784 m_strcpy(buffer, sizeof(buffer), Muttrc);
2786 mutt_expand_path (buffer, sizeof(buffer));
2787 Muttrc = m_strdup(buffer);
2789 p_delete(&AliasFile);
2790 AliasFile = m_strdup(NONULL (Muttrc));
2792 /* Process the global rc file if it exists and the user hasn't explicity
2793 requested not to via "-n". */
2795 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
2797 if (access (buffer, F_OK) == -1)
2798 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
2799 if (access (buffer, F_OK) == -1)
2800 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
2802 if (access (buffer, F_OK) == -1)
2803 snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
2804 if (access (buffer, F_OK) != -1) {
2805 if (source_rc (buffer, &err) != 0) {
2806 fputs (err.data, stderr);
2807 fputc ('\n', stderr);
2813 /* Read the user's initialization file. */
2814 if (access (Muttrc, F_OK) != -1) {
2815 if (!option (OPTNOCURSES))
2817 if (source_rc (Muttrc, &err) != 0) {
2818 fputs (err.data, stderr);
2819 fputc ('\n', stderr);
2823 else if (!default_rc) {
2824 /* file specified by -F does not exist */
2825 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2826 mutt_endwin (buffer);
2830 if (mutt_execute_commands (commands) != 0)
2833 /* warn about synonym variables */
2834 if (!list_empty(Synonyms)) {
2836 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2837 for (i = 0; i < Synonyms->length; i++) {
2838 struct option_t* newopt = NULL, *oldopt = NULL;
2839 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2840 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2841 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2842 oldopt ? NONULL (oldopt->option) : "",
2843 newopt ? NONULL (newopt->option) : "",
2844 NONULL(((syn_t*) Synonyms->data[i])->f),
2845 ((syn_t*) Synonyms->data[i])->l);
2847 fprintf (stderr, _("Warning: synonym variables are scheduled"
2848 " for removal.\n"));
2849 list_del (&Synonyms, syn_del);
2853 if (need_pause && !option (OPTNOCURSES)) {
2854 if (mutt_any_key_to_continue (NULL) == -1)
2859 set_option (OPTWEED); /* turn weeding on by default */
2863 int mutt_get_hook_type (const char *name)
2865 struct command_t *c;
2867 for (c = Commands; c->name; c++)
2868 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2873 /* compare two option_t*'s for sorting -t/-T output */
2874 static int opt_cmp (const void* a, const void* b) {
2875 return (m_strcmp((*(struct option_t**) a)->option,
2876 (*(struct option_t**) b)->option));
2879 /* callback for hash_map() to put all non-synonym vars into list */
2880 static void opt_sel_full (const char* key __attribute__ ((unused)),
2882 unsigned long more) {
2883 list2_t** l = (list2_t**) more;
2884 struct option_t* option = (struct option_t*) data;
2886 if (DTYPE (option->type) == DT_SYN)
2888 list_push_back (l, option);
2891 /* callback for hash_map() to put all changed non-synonym vars into list */
2892 static void opt_sel_diff (const char* key __attribute__ ((unused)),
2894 unsigned long more) {
2895 list2_t** l = (list2_t**) more;
2896 struct option_t* option = (struct option_t*) data;
2897 char buf[LONG_STRING];
2899 if (DTYPE (option->type) == DT_SYN)
2902 mutt_option_value (option->option, buf, sizeof(buf));
2903 if (m_strcmp(buf, option->init) != 0)
2904 list_push_back (l, option);
2907 /* dump out the value of all the variables we have */
2908 int mutt_dump_variables (int full) {
2910 char outbuf[STRING];
2911 list2_t* tmp = NULL;
2912 struct option_t* option = NULL;
2914 /* get all non-synonyms into list... */
2915 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2916 (unsigned long) &tmp);
2918 if (!list_empty(tmp)) {
2919 /* ...and dump list sorted */
2920 qsort (tmp->data, tmp->length, sizeof(void*), opt_cmp);
2921 for (i = 0; i < tmp->length; i++) {
2922 option = (struct option_t*) tmp->data[i];
2923 FuncTable[DTYPE (option->type)].opt_to_string
2924 (outbuf, sizeof(outbuf), option);
2925 printf ("%s\n", outbuf);
2928 list_del (&tmp, NULL);