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>
26 #include "mutt_curses.h"
32 #include "mutt_crypt.h"
33 #include "mutt_idna.h"
35 #if defined(USE_SSL) || defined(USE_GNUTLS)
39 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
40 #include "mutt_libesmtp.h"
48 #include "lib/debug.h"
54 #include <sys/utsname.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, size_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, size_t errlen);
86 static int check_dsn_notify (const char* option, unsigned long val,
87 char* errbuf, size_t errlen);
88 static int check_history (const char* option, unsigned long val,
89 char* errbuf, size_t errlen);
90 /* this checks that numbers are >= 0 */
91 static int check_num (const char* option, unsigned long val,
92 char* errbuf, size_t errlen);
94 static int check_debug (const char* option, unsigned long val,
95 char* errbuf, size_t errlen);
98 /* use this to check only */
99 static int check_special (const char* option, unsigned long val,
100 char* errbuf, size_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, size_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, size_t dstlen, struct option_t* option);
127 static void num_to_string (char* dst, size_t dstlen, struct option_t* option);
128 static void str_to_string (char* dst, size_t dstlen, struct option_t* option);
129 static void quad_to_string (char* dst, size_t dstlen, struct option_t* option);
130 static void sort_to_string (char* dst, size_t dstlen, struct option_t* option);
131 static void rx_to_string (char* dst, size_t dstlen, struct option_t* option);
132 static void magic_to_string (char* dst, size_t dstlen, struct option_t* option);
133 static void addr_to_string (char* dst, size_t dstlen, struct option_t* option);
134 static void user_to_string (char* dst, size_t dstlen, struct option_t* option);
135 static void sys_to_string (char* dst, size_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, size_t errlen);
140 static int num_from_string (struct option_t* dst, const char* val,
141 char* errbuf, size_t errlen);
142 static int str_from_string (struct option_t* dst, const char* val,
143 char* errbuf, size_t errlen);
144 static int path_from_string (struct option_t* dst, const char* val,
145 char* errbuf, size_t errlen);
146 static int quad_from_string (struct option_t* dst, const char* val,
147 char* errbuf, size_t errlen);
148 static int sort_from_string (struct option_t* dst, const char* val,
149 char* errbuf, size_t errlen);
150 static int rx_from_string (struct option_t* dst, const char* val,
151 char* errbuf, size_t errlen);
152 static int magic_from_string (struct option_t* dst, const char* val,
153 char* errbuf, size_t errlen);
154 static int addr_from_string (struct option_t* dst, const char* val,
155 char* errbuf, size_t errlen);
156 static int user_from_string (struct option_t* dst, const char* val,
157 char* errbuf, size_t errlen);
161 void (*opt_to_string) (char* dst, size_t dstlen, struct option_t* option);
162 int (*opt_from_string) (struct option_t* dst, const char* val,
163 char* errbuf, size_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, size_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 size_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, size_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, size_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, size_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, size_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, size_t dstlen,
262 struct option_t* option) {
263 char *val = NULL, *t = NULL;
266 /* get some $muttng_ values dynamically */
267 if (ascii_strcmp ("muttng_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 ("muttng_folder_path", option->option) == 0 &&
272 CurrentFolder && *CurrentFolder) {
274 } else if (ascii_strcmp ("muttng_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)), size_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, size_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)), size_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, size_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)), size_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, size_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, size_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, size_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, size_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, size_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)), size_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, size_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)), size_t errlen __attribute__ ((unused))) {
529 address_delete ((address_t**) dst->data);
531 *((address_t**) dst->data) = rfc822_parse_adrlist (NULL, val);
535 int mutt_option_value (const char* val, char* dst, size_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 (LIST ** list, const char *str)
628 LIST *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) {
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)) {
757 p_delete(&spam->template);
763 for (spam = prev->next; spam;) {
764 if (!m_strcmp(spam->rx->pattern, pat)) {
765 prev->next = spam->next;
767 p_delete(&spam->template);
780 static void remove_from_list (LIST ** l, const char *str)
782 LIST *p, *last = NULL;
784 if (m_strcmp("*", str) == 0)
785 mutt_free_list (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_free);
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 ((LIST **) 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_free);
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 mutt_free_list ((LIST **) data);
1060 remove_from_list ((LIST **) 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, LIST **ldata,
1093 BUFFER *err __attribute__ ((unused))) {
1095 LIST *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(LIST, 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, LIST **ldata,
1166 BUFFER *err __attribute__ ((unused))) {
1168 LIST *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 LIST->data, not anything pointed to by the LIST->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 (LIST *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)),
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) {
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 *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 mutt_free_alias (&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 mutt_free_alias (&tmp);
1419 while (MoreArgs (s));
1423 static int parse_alias (BUFFER * buf, BUFFER * s,
1424 unsigned long data __attribute__ ((unused)),
1427 ALIAS *tmp = Aliases;
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 */
1449 tmp = p_new(ALIAS, 1);
1451 tmp->name = m_strdup(buf->data);
1452 /* give the main addressbook code a chance */
1453 if (CurrentMenu == MENU_ALIAS)
1454 set_option (OPTMENUCALLER);
1457 /* override the previous value */
1458 address_delete (&tmp->addr);
1459 if (CurrentMenu == MENU_ALIAS)
1460 set_option (OPTFORCEREDRAWINDEX);
1463 mutt_extract_token (buf, s,
1464 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1465 debug_print (2, ("second token is '%s'.\n", buf->data));
1466 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1471 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1472 snprintf (err->data, err->dsize,
1473 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1477 if (DebugLevel >= 2) {
1480 /* A group is terminated with an empty address, so check a->mailbox */
1481 for (a = tmp->addr; a && a->mailbox; a = a->next) {
1483 debug_print (2, ("%s\n", a->mailbox));
1485 debug_print (2, ("group %s\n", a->mailbox));
1493 parse_unmy_hdr (BUFFER * buf, BUFFER * s,
1494 unsigned long data __attribute__ ((unused)),
1495 BUFFER * err __attribute__ ((unused)))
1498 LIST *tmp = UserHeader;
1503 mutt_extract_token (buf, s, 0);
1504 if (m_strcmp("*", buf->data) == 0)
1505 mutt_free_list (&UserHeader);
1510 l = m_strlen(buf->data);
1511 if (buf->data[l - 1] == ':')
1515 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1516 && tmp->data[l] == ':') {
1519 last->next = tmp->next;
1521 UserHeader = tmp->next;
1524 mutt_free_list (&ptr);
1533 while (MoreArgs (s));
1537 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
1544 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1545 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1546 m_strcpy(err->data, err->dsize, _("invalid header field"));
1549 keylen = p - buf->data + 1;
1552 for (tmp = UserHeader;; tmp = tmp->next) {
1553 /* see if there is already a field by this name */
1554 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1555 /* replace the old value */
1556 p_delete(&tmp->data);
1557 tmp->data = buf->data;
1564 tmp->next = mutt_new_list ();
1568 tmp = mutt_new_list ();
1571 tmp->data = buf->data;
1577 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1578 char* errbuf, size_t errlen) {
1581 if (m_strncmp("reverse-", s, 8) == 0) {
1583 flags = SORT_REVERSE;
1586 if (m_strncmp("last-", s, 5) == 0) {
1591 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1593 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1597 *((short*) dst->data) = i | flags;
1601 /* if additional data more == 1, we want to resolve synonyms */
1602 static void mutt_set_default(const char *name __attribute__ ((unused)), void* p, unsigned long more)
1604 char buf[LONG_STRING];
1605 struct option_t *ptr = p;
1607 if (DTYPE(ptr->type) == DT_SYN) {
1610 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1612 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_from_string)
1615 mutt_option_value(ptr->option, buf, sizeof(buf));
1616 if (m_strlen(ptr->init) == 0 && buf && *buf)
1617 ptr->init = m_strdup(buf);
1620 static struct option_t* add_option (const char* name, const char* init,
1621 short type, short dodup) {
1622 struct option_t* option = p_new(struct option_t, 1);
1624 debug_print (1, ("adding $%s\n", name));
1626 option->option = m_strdup(name);
1627 option->type = type;
1629 option->init = dodup ? m_strdup(init) : (char*) init;
1633 /* creates new option_t* of type DT_USER for $user_ var */
1634 static struct option_t* add_user_option (const char* name) {
1635 return (add_option (name, NULL, DT_USER, 1));
1638 /* free()'s option_t* */
1639 static void del_option (void* p) {
1640 struct option_t *ptr = (struct option_t*) p;
1641 char* s = (char*) ptr->data;
1642 debug_print (1, ("removing option '%s' from table\n", NONULL (ptr->option)));
1643 p_delete(&ptr->option);
1645 p_delete(&ptr->init);
1649 static int init_expand (char** dst, struct option_t* src) {
1655 if (DTYPE(src->type) == DT_STR ||
1656 DTYPE(src->type) == DT_PATH) {
1657 /* only expand for string as it's the only place where
1658 * we want to expand vars right now */
1659 if (src->init && *src->init) {
1662 len = m_strlen(src->init) + 2;
1663 in.data = p_new(char, len + 1);
1664 snprintf (in.data, len, "\"%s\"", src->init);
1667 mutt_extract_token (&token, &in, 0);
1668 if (token.data && *token.data)
1669 *dst = m_strdup(token.data);
1671 *dst = m_strdup("");
1673 p_delete(&token.data);
1675 *dst = m_strdup("");
1677 /* for non-string: take value as is */
1678 *dst = m_strdup(src->init);
1682 /* if additional data more == 1, we want to resolve synonyms */
1683 static void mutt_restore_default (const char* name __attribute__ ((unused)),
1684 void* p, unsigned long more) {
1685 char errbuf[STRING];
1686 struct option_t* ptr = (struct option_t*) p;
1689 if (DTYPE (ptr->type) == DT_SYN) {
1692 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1696 if (FuncTable[DTYPE (ptr->type)].opt_from_string) {
1697 init_expand (&init, ptr);
1698 if (!FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, init, errbuf,
1700 if (!option (OPTNOCURSES))
1702 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1703 "Please report this error: \"%s\"\n"),
1704 ptr->option, NONULL (init), errbuf);
1710 if (ptr->flags & R_INDEX)
1711 set_option (OPTFORCEREDRAWINDEX);
1712 if (ptr->flags & R_PAGER)
1713 set_option (OPTFORCEREDRAWPAGER);
1714 if (ptr->flags & R_RESORT_SUB)
1715 set_option (OPTSORTSUBTHREADS);
1716 if (ptr->flags & R_RESORT)
1717 set_option (OPTNEEDRESORT);
1718 if (ptr->flags & R_RESORT_INIT)
1719 set_option (OPTRESORTINIT);
1720 if (ptr->flags & R_TREE)
1721 set_option (OPTREDRAWTREE);
1724 /* check whether value for $dsn_return would be valid */
1725 static int check_dsn_return (const char* option __attribute__ ((unused)), unsigned long p,
1726 char* errbuf, size_t errlen) {
1727 char* val = (char*) p;
1728 if (val && *val && m_strncmp(val, "hdrs", 4) != 0 &&
1729 m_strncmp(val, "full", 4) != 0) {
1731 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1737 /* check whether value for $dsn_notify would be valid */
1738 static int check_dsn_notify (const char* option, unsigned long p,
1739 char* errbuf, size_t errlen) {
1740 list2_t* list = NULL;
1743 char* val = (char*) p;
1747 list = list_from_str (val, ",");
1748 if (list_empty (list))
1751 for (i = 0; i < list->length; i++)
1752 if (m_strncmp(list->data[i], "never", 5) != 0 &&
1753 m_strncmp(list->data[i], "failure", 7) != 0 &&
1754 m_strncmp(list->data[i], "delay", 5) != 0 &&
1755 m_strncmp(list->data[i], "success", 7) != 0) {
1757 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1758 (char*) list->data[i], "dsn_notify");
1762 list_del (&list, (list_del_t*)xmemfree);
1766 static int check_num (const char* option, unsigned long p,
1767 char* errbuf, size_t errlen) {
1770 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1777 static int check_debug (const char* option, unsigned long p,
1778 char* errbuf, size_t errlen) {
1779 if ((int) p <= DEBUG_MAX_LEVEL &&
1780 (int) p >= DEBUG_MIN_LEVEL)
1784 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1789 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1790 char* errbuf, size_t errlen) {
1791 if (!check_num ("history", p, errbuf, errlen))
1793 mutt_init_history ();
1797 static int check_special (const char* name, unsigned long val,
1798 char* errbuf, size_t errlen) {
1801 for (i = 0; SpecialVars[i].name; i++) {
1802 if (m_strcmp(SpecialVars[i].name, name) == 0) {
1803 return (SpecialVars[i].check (SpecialVars[i].name,
1804 val, errbuf, errlen));
1810 static const struct mapping_t* get_sortmap (struct option_t* option) {
1811 const struct mapping_t* map = NULL;
1813 switch (option->type & DT_SUBTYPE_MASK) {
1815 map = SortAliasMethods;
1817 case DT_SORT_BROWSER:
1818 map = SortBrowserMethods;
1821 map = SortKeyMethods;
1824 map = SortAuxMethods;
1833 #define CHECK_PAGER \
1834 if ((CurrentMenu == MENU_PAGER) && \
1835 (!option || (option->flags & R_RESORT))) \
1837 snprintf (err->data, err->dsize, \
1838 _("Not available in this menu.")); \
1842 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1845 int query, unset, inv, reset, r = 0;
1846 struct option_t* option = NULL;
1848 while (MoreArgs (s)) {
1849 /* reset state variables */
1851 unset = data & M_SET_UNSET;
1852 inv = data & M_SET_INV;
1853 reset = data & M_SET_RESET;
1855 if (*s->dptr == '?') {
1859 else if (m_strncmp("no", s->dptr, 2) == 0) {
1863 else if (m_strncmp("inv", s->dptr, 3) == 0) {
1867 else if (*s->dptr == '&') {
1872 /* get the variable name */
1873 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1875 /* resolve synonyms */
1876 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1877 DTYPE (option->type == DT_SYN)) {
1878 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1879 syn_add (newopt, option);
1883 /* see if we need to add $user_ var */
1884 if (!option && ascii_strncmp ("user_", tmp->data, 5) == 0) {
1885 /* there's no option named like this yet so only add one
1886 * if the action isn't any of: reset, unset, query */
1887 if (!(reset || unset || query || *s->dptr != '=')) {
1888 debug_print (1, ("adding user option '%s'\n", tmp->data));
1889 option = add_user_option (tmp->data);
1890 hash_insert (ConfigOptions, option->option, option, 0);
1894 if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1895 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1898 s->dptr = vskipspaces(s->dptr);
1901 if (query || unset || inv) {
1902 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1906 if (s && *s->dptr == '=') {
1907 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1911 if (!m_strcmp("all", tmp->data)) {
1912 if (CurrentMenu == MENU_PAGER) {
1913 snprintf (err->data, err->dsize, _("Not available in this menu."));
1916 hash_map (ConfigOptions, mutt_restore_default, 1);
1917 set_option (OPTFORCEREDRAWINDEX);
1918 set_option (OPTFORCEREDRAWPAGER);
1919 set_option (OPTSORTSUBTHREADS);
1920 set_option (OPTNEEDRESORT);
1921 set_option (OPTRESORTINIT);
1922 set_option (OPTREDRAWTREE);
1925 else if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1926 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1931 mutt_restore_default (NULL, option, 1);
1934 else if (DTYPE (option->type) == DT_BOOL) {
1935 /* XXX this currently ignores the function table
1936 * as we don't get invert and stuff into it */
1937 if (s && *s->dptr == '=') {
1938 if (unset || inv || query) {
1939 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1944 mutt_extract_token (tmp, s, 0);
1945 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1947 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1950 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1956 bool_to_string (err->data, err->dsize, option);
1962 unset_option (option->data);
1964 toggle_option (option->data);
1966 set_option (option->data);
1968 else if (DTYPE (option->type) == DT_STR ||
1969 DTYPE (option->type) == DT_PATH ||
1970 DTYPE (option->type) == DT_ADDR ||
1971 DTYPE (option->type) == DT_MAGIC ||
1972 DTYPE (option->type) == DT_NUM ||
1973 DTYPE (option->type) == DT_SORT ||
1974 DTYPE (option->type) == DT_RX ||
1975 DTYPE (option->type) == DT_USER ||
1976 DTYPE (option->type) == DT_SYS) {
1978 /* XXX maybe we need to get unset into handlers? */
1979 if (DTYPE (option->type) == DT_STR ||
1980 DTYPE (option->type) == DT_PATH ||
1981 DTYPE (option->type) == DT_ADDR ||
1982 DTYPE (option->type) == DT_USER ||
1983 DTYPE (option->type) == DT_SYS) {
1986 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1987 snprintf (err->data, err->dsize, _("$%s is read-only"),
1991 } else if (DTYPE (option->type) == DT_ADDR)
1992 address_delete ((address_t **) option->data);
1993 else if (DTYPE (option->type) == DT_USER)
1994 /* to unset $user_ means remove */
1995 hash_delete (ConfigOptions, option->option,
1996 option, del_option);
1998 p_delete((void **)&option->data);
2003 if (query || *s->dptr != '=') {
2004 FuncTable[DTYPE (option->type)].opt_to_string
2005 (err->data, err->dsize, option);
2009 /* the $muttng_ variables are read-only */
2010 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
2011 snprintf (err->data, err->dsize, _("$%s is read-only"),
2018 mutt_extract_token (tmp, s, 0);
2019 if (!FuncTable[DTYPE (option->type)].opt_from_string
2020 (option, tmp->data, err->data, err->dsize))
2024 else if (DTYPE (option->type) == DT_QUAD) {
2027 quad_to_string (err->data, err->dsize, option);
2031 if (*s->dptr == '=') {
2034 mutt_extract_token (tmp, s, 0);
2035 if (ascii_strcasecmp ("yes", tmp->data) == 0)
2036 set_quadoption (option->data, M_YES);
2037 else if (ascii_strcasecmp ("no", tmp->data) == 0)
2038 set_quadoption (option->data, M_NO);
2039 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
2040 set_quadoption (option->data, M_ASKYES);
2041 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
2042 set_quadoption (option->data, M_ASKNO);
2044 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
2045 tmp->data, option->option);
2052 toggle_quadoption (option->data);
2054 set_quadoption (option->data, M_NO);
2056 set_quadoption (option->data, M_YES);
2060 snprintf (err->data, err->dsize, _("%s: unknown type"),
2066 if (option->flags & R_INDEX)
2067 set_option (OPTFORCEREDRAWINDEX);
2068 if (option->flags & R_PAGER)
2069 set_option (OPTFORCEREDRAWPAGER);
2070 if (option->flags & R_RESORT_SUB)
2071 set_option (OPTSORTSUBTHREADS);
2072 if (option->flags & R_RESORT)
2073 set_option (OPTNEEDRESORT);
2074 if (option->flags & R_RESORT_INIT)
2075 set_option (OPTRESORTINIT);
2076 if (option->flags & R_TREE)
2077 set_option (OPTREDRAWTREE);
2084 /* reads the specified initialization file. returns -1 if errors were found
2085 so that we can pause to let the user know... */
2086 static int source_rc (const char *rcfile, BUFFER * err)
2089 int line = 0, rc = 0, conv = 0;
2091 char *linebuf = NULL;
2092 char *currentline = NULL;
2096 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
2098 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
2099 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
2104 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
2105 conv = ConfigCharset && (*ConfigCharset) && Charset;
2107 currentline = m_strdup(linebuf);
2110 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
2113 currentline = linebuf;
2118 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
2119 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
2120 if (--rc < -MAXERRS) {
2122 p_delete(¤tline);
2131 p_delete(¤tline);
2133 p_delete(&token.data);
2137 mutt_wait_filter (pid);
2139 /* the muttrc source keyword */
2140 snprintf (err->data, err->dsize,
2141 rc >= -MAXERRS ? _("source: errors in %s")
2142 : _("source: reading aborted due too many errors in %s"),
2151 static int parse_source (BUFFER * tmp, BUFFER * s,
2152 unsigned long data __attribute__ ((unused)),
2155 char path[_POSIX_PATH_MAX];
2159 if (mutt_extract_token (tmp, s, 0) != 0) {
2160 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
2164 m_strcpy(path, sizeof(path), tmp->data);
2165 mutt_expand_path (path, sizeof(path));
2167 rc += source_rc (path, err);
2169 while (MoreArgs (s));
2171 return ((rc < 0) ? -1 : 0);
2174 /* line command to execute
2176 token scratch buffer to be used by parser. caller should free
2177 token->data when finished. the reason for this variable is
2178 to avoid having to allocate and deallocate a lot of memory
2179 if we are parsing many lines. the caller can pass in the
2180 memory to use, which avoids having to create new space for
2181 every call to this function.
2183 err where to write error messages */
2184 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
2190 expn.data = expn.dptr = line;
2191 expn.dsize = m_strlen(line);
2195 debug_print (1, ("expand '%s'\n", line));
2197 expn.dptr = vskipspaces(expn.dptr);
2198 while (*expn.dptr) {
2199 if (*expn.dptr == '#')
2200 break; /* rest of line is a comment */
2201 if (*expn.dptr == ';') {
2205 mutt_extract_token (token, &expn, 0);
2206 for (i = 0; Commands[i].name; i++) {
2207 if (!m_strcmp(token->data, Commands[i].name)) {
2208 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
2213 if (!Commands[i].name) {
2214 snprintf (err->data, err->dsize, _("%s: unknown command"),
2215 NONULL (token->data));
2222 p_delete(&expn.data);
2227 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
2228 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
2229 /* initial string that starts completion. No telling how much crap
2230 * the user has typed so far. Allocate LONG_STRING just to be sure! */
2231 char User_typed[LONG_STRING] = { 0 };
2233 int Num_matched = 0; /* Number of matches for completion */
2234 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
2235 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
2237 /* helper function for completion. Changes the dest buffer if
2238 necessary/possible to aid completion.
2239 dest == completion result gets here.
2240 src == candidate for completion.
2241 try == user entered data for completion.
2242 len == length of dest buffer.
2244 static void candidate (char *dest, char *try, const char *src, int len)
2248 if (strstr (src, try) == src) {
2249 Matches[Num_matched++] = src;
2251 m_strcpy(dest, len, src);
2253 for (l = 0; src[l] && src[l] == dest[l]; l++);
2259 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
2263 int spaces; /* keep track of the number of leading spaces on the line */
2265 buffer = vskipspaces(buffer);
2266 spaces = buffer - pt;
2268 pt = buffer + pos - spaces;
2269 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2272 if (pt == buffer) { /* complete cmd */
2273 /* first TAB. Collect all the matches */
2276 m_strcpy(User_typed, sizeof(User_typed), pt);
2277 p_clear(Matches, sizeof(Matches));
2278 p_clear(Completed, sizeof(Completed));
2279 for (num = 0; Commands[num].name; num++)
2280 candidate (Completed, User_typed, Commands[num].name,
2282 Matches[Num_matched++] = User_typed;
2284 /* All matches are stored. Longest non-ambiguous string is ""
2285 * i.e. dont change 'buffer'. Fake successful return this time */
2286 if (User_typed[0] == 0)
2290 if (Completed[0] == 0 && User_typed[0])
2293 /* Num_matched will _always_ be atleast 1 since the initial
2294 * user-typed string is always stored */
2295 if (numtabs == 1 && Num_matched == 2)
2296 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2297 else if (numtabs > 1 && Num_matched > 2)
2298 /* cycle thru all the matches */
2299 snprintf (Completed, sizeof(Completed), "%s",
2300 Matches[(numtabs - 2) % Num_matched]);
2302 /* return the completed command */
2303 m_strcpy(buffer, len - spaces, Completed);
2305 else if (!m_strncmp(buffer, "set", 3)
2306 || !m_strncmp(buffer, "unset", 5)
2307 || !m_strncmp(buffer, "reset", 5)
2308 || !m_strncmp(buffer, "toggle", 6)) { /* complete variables */
2309 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
2312 /* loop through all the possible prefixes (no, inv, ...) */
2313 if (!m_strncmp(buffer, "set", 3)) {
2314 for (num = 0; prefixes[num]; num++) {
2315 if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
2316 pt += m_strlen(prefixes[num]);
2322 /* first TAB. Collect all the matches */
2325 m_strcpy(User_typed, sizeof(User_typed), pt);
2326 p_clear(Matches, sizeof(Matches));
2327 p_clear(Completed, sizeof(Completed));
2328 for (num = 0; MuttVars[num].option; num++)
2329 candidate(Completed, User_typed, MuttVars[num].option,
2331 Matches[Num_matched++] = User_typed;
2333 /* All matches are stored. Longest non-ambiguous string is ""
2334 * i.e. dont change 'buffer'. Fake successful return this time */
2335 if (User_typed[0] == 0)
2339 if (Completed[0] == 0 && User_typed[0])
2342 /* Num_matched will _always_ be atleast 1 since the initial
2343 * user-typed string is always stored */
2344 if (numtabs == 1 && Num_matched == 2)
2345 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2346 else if (numtabs > 1 && Num_matched > 2)
2347 /* cycle thru all the matches */
2348 snprintf (Completed, sizeof(Completed), "%s",
2349 Matches[(numtabs - 2) % Num_matched]);
2351 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2353 else if (!m_strncmp(buffer, "exec", 4)) {
2354 struct binding_t *menu = km_get_table (CurrentMenu);
2356 if (!menu && CurrentMenu != MENU_PAGER)
2360 /* first TAB. Collect all the matches */
2363 m_strcpy(User_typed, sizeof(User_typed), pt);
2364 p_clear(Matches, sizeof(Matches));
2365 p_clear(Completed, sizeof(Completed));
2366 for (num = 0; menu[num].name; num++)
2367 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
2368 /* try the generic menu */
2369 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2371 for (num = 0; menu[num].name; num++)
2372 candidate (Completed, User_typed, menu[num].name,
2375 Matches[Num_matched++] = User_typed;
2377 /* All matches are stored. Longest non-ambiguous string is ""
2378 * i.e. dont change 'buffer'. Fake successful return this time */
2379 if (User_typed[0] == 0)
2383 if (Completed[0] == 0 && User_typed[0])
2386 /* Num_matched will _always_ be atleast 1 since the initial
2387 * user-typed string is always stored */
2388 if (numtabs == 1 && Num_matched == 2)
2389 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2390 else if (numtabs > 1 && Num_matched > 2)
2391 /* cycle thru all the matches */
2392 snprintf (Completed, sizeof(Completed), "%s",
2393 Matches[(numtabs - 2) % Num_matched]);
2395 m_strcpy(pt, buffer + len - pt - spaces, Completed);
2403 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2405 char var[STRING], *pt = buffer;
2407 struct option_t* option = NULL;
2412 buffer = vskipspaces(buffer);
2413 spaces = buffer - pt;
2415 pt = buffer + pos - spaces;
2416 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2418 pt++; /* move past the space */
2419 if (*pt == '=') /* abort if no var before the '=' */
2422 if (m_strncmp(buffer, "set", 3) == 0) {
2423 m_strcpy(var, sizeof(var), pt);
2424 /* ignore the trailing '=' when comparing */
2425 var[m_strlen(var) - 1] = 0;
2426 if (!(option = hash_find (ConfigOptions, var)))
2427 return 0; /* no such variable. */
2429 char tmp[LONG_STRING], tmp2[LONG_STRING];
2431 size_t dlen = buffer + len - pt - spaces;
2432 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2436 if ((DTYPE (option->type) == DT_STR) ||
2437 (DTYPE (option->type) == DT_PATH) ||
2438 (DTYPE (option->type) == DT_RX)) {
2439 m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
2440 if (DTYPE (option->type) == DT_PATH)
2441 mutt_pretty_mailbox (tmp);
2443 else if (DTYPE (option->type) == DT_ADDR) {
2444 rfc822_write_address (tmp, sizeof(tmp),
2445 *((address_t **) option->data), 0);
2447 else if (DTYPE (option->type) == DT_QUAD)
2448 m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
2449 else if (DTYPE (option->type) == DT_NUM)
2450 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
2451 else if (DTYPE (option->type) == DT_SORT) {
2452 const struct mapping_t *map;
2455 switch (option->type & DT_SUBTYPE_MASK) {
2457 map = SortAliasMethods;
2459 case DT_SORT_BROWSER:
2460 map = SortBrowserMethods;
2463 map = SortKeyMethods;
2469 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2470 snprintf(tmp, sizeof(tmp), "%s%s%s",
2471 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2472 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2474 else if (DTYPE (option->type) == DT_MAGIC) {
2476 switch (DefaultMagic) {
2492 m_strcpy(tmp, sizeof(tmp), p);
2494 else if (DTYPE (option->type) == DT_BOOL)
2495 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
2499 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
2500 if (*s == '\\' || *s == '"')
2506 m_strcpy(tmp, sizeof(tmp), pt);
2507 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2515 /* Implement the -Q command line flag */
2516 int mutt_query_variables (LIST * queries)
2520 char errbuff[STRING];
2521 char command[STRING];
2529 err.dsize = sizeof(errbuff);
2531 for (p = queries; p; p = p->next) {
2532 snprintf (command, sizeof(command), "set ?%s\n", p->data);
2533 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2534 fprintf (stderr, "%s\n", err.data);
2535 p_delete(&token.data);
2538 printf ("%s\n", err.data);
2541 p_delete(&token.data);
2545 static int mutt_execute_commands (LIST * p)
2548 char errstr[SHORT_STRING];
2552 err.dsize = sizeof(errstr);
2554 for (; p; p = p->next) {
2555 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2556 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2557 p_delete(&token.data);
2561 p_delete(&token.data);
2565 void mutt_init (int skip_sys_rc, LIST * commands)
2568 struct utsname utsname;
2570 char buffer[STRING], error[STRING];
2571 int default_rc = 0, need_pause = 0;
2577 err.dsize = sizeof(error);
2579 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2580 * to have some room for $user_ vars */
2581 ConfigOptions = hash_create (sizeof(MuttVars) * 3);
2582 for (i = 0; MuttVars[i].option; i++) {
2583 if (DTYPE (MuttVars[i].type) != DT_SYS)
2584 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2586 hash_insert (ConfigOptions, MuttVars[i].option,
2587 add_option (MuttVars[i].option, MuttVars[i].init,
2592 * XXX - use something even more difficult to predict?
2594 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2595 "\033]9;%ld\a", (long) time (NULL));
2597 /* on one of the systems I use, getcwd() does not return the same prefix
2598 as is listed in the passwd file */
2599 if ((p = getenv ("HOME")))
2600 Homedir = m_strdup(p);
2602 /* Get some information about the user */
2603 if ((pw = getpwuid (getuid ()))) {
2606 Username = m_strdup(pw->pw_name);
2608 Homedir = m_strdup(pw->pw_dir);
2610 Realname = m_strdup(mutt_gecos_name (rnbuf, sizeof(rnbuf), pw));
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);
2705 Visual = 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/.muttngrc-%s", NONULL (Homedir),
2768 if (access (buffer, F_OK) == -1)
2770 snprintf (buffer, sizeof(buffer), "%s/.muttngrc", NONULL (Homedir));
2771 if (access (buffer, F_OK) == -1)
2773 snprintf (buffer, sizeof(buffer), "%s/.muttng/muttngrc-%s",
2774 NONULL (Homedir), MUTT_VERSION);
2775 if (access (buffer, F_OK) == -1)
2777 snprintf (buffer, sizeof(buffer), "%s/.muttng/muttngrc",
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/Muttngrc-%s", SYSCONFDIR,
2797 if (access (buffer, F_OK) == -1)
2798 snprintf (buffer, sizeof(buffer), "%s/Muttngrc", SYSCONFDIR);
2799 if (access (buffer, F_OK) == -1)
2800 snprintf (buffer, sizeof(buffer), "%s/Muttngrc-%s", PKGDATADIR,
2802 if (access (buffer, F_OK) == -1)
2803 snprintf (buffer, sizeof(buffer), "%s/Muttngrc", 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);