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 if ((WithCrypto & APPLICATION_PGP))
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 $muttng_ variables are read-only */
2011 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
2012 snprintf (err->data, err->dsize, _("$%s is read-only"),
2019 mutt_extract_token (tmp, s, 0);
2020 if (!FuncTable[DTYPE (option->type)].opt_from_string
2021 (option, tmp->data, err->data, err->dsize))
2025 else if (DTYPE (option->type) == DT_QUAD) {
2028 quad_to_string (err->data, err->dsize, option);
2032 if (*s->dptr == '=') {
2035 mutt_extract_token (tmp, s, 0);
2036 if (ascii_strcasecmp ("yes", tmp->data) == 0)
2037 set_quadoption (option->data, M_YES);
2038 else if (ascii_strcasecmp ("no", tmp->data) == 0)
2039 set_quadoption (option->data, M_NO);
2040 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
2041 set_quadoption (option->data, M_ASKYES);
2042 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
2043 set_quadoption (option->data, M_ASKNO);
2045 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
2046 tmp->data, option->option);
2053 toggle_quadoption (option->data);
2055 set_quadoption (option->data, M_NO);
2057 set_quadoption (option->data, M_YES);
2061 snprintf (err->data, err->dsize, _("%s: unknown type"),
2067 if (option->flags & R_INDEX)
2068 set_option (OPTFORCEREDRAWINDEX);
2069 if (option->flags & R_PAGER)
2070 set_option (OPTFORCEREDRAWPAGER);
2071 if (option->flags & R_RESORT_SUB)
2072 set_option (OPTSORTSUBTHREADS);
2073 if (option->flags & R_RESORT)
2074 set_option (OPTNEEDRESORT);
2075 if (option->flags & R_RESORT_INIT)
2076 set_option (OPTRESORTINIT);
2077 if (option->flags & R_TREE)
2078 set_option (OPTREDRAWTREE);
2085 /* reads the specified initialization file. returns -1 if errors were found
2086 so that we can pause to let the user know... */
2087 static int source_rc (const char *rcfile, BUFFER * err)
2090 int line = 0, rc = 0, conv = 0;
2092 char *linebuf = NULL;
2093 char *currentline = NULL;
2097 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
2099 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
2100 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
2105 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
2106 conv = ConfigCharset && (*ConfigCharset) && Charset;
2108 currentline = m_strdup(linebuf);
2111 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
2114 currentline = linebuf;
2119 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
2120 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
2121 if (--rc < -MAXERRS) {
2123 p_delete(¤tline);
2132 p_delete(¤tline);
2134 p_delete(&token.data);
2138 mutt_wait_filter (pid);
2140 /* the muttrc source keyword */
2141 snprintf (err->data, err->dsize,
2142 rc >= -MAXERRS ? _("source: errors in %s")
2143 : _("source: reading aborted due too many errors in %s"),
2152 static int parse_source (BUFFER * tmp, BUFFER * s,
2153 unsigned long data __attribute__ ((unused)),
2156 char path[_POSIX_PATH_MAX];
2160 if (mutt_extract_token (tmp, s, 0) != 0) {
2161 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
2165 m_strcpy(path, sizeof(path), tmp->data);
2166 mutt_expand_path (path, sizeof(path));
2168 rc += source_rc (path, err);
2170 while (MoreArgs (s));
2172 return ((rc < 0) ? -1 : 0);
2175 /* line command to execute
2177 token scratch buffer to be used by parser. caller should free
2178 token->data when finished. the reason for this variable is
2179 to avoid having to allocate and deallocate a lot of memory
2180 if we are parsing many lines. the caller can pass in the
2181 memory to use, which avoids having to create new space for
2182 every call to this function.
2184 err where to write error messages */
2185 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
2191 expn.data = expn.dptr = line;
2192 expn.dsize = m_strlen(line);
2196 debug_print (1, ("expand '%s'\n", line));
2198 expn.dptr = vskipspaces(expn.dptr);
2199 while (*expn.dptr) {
2200 if (*expn.dptr == '#')
2201 break; /* rest of line is a comment */
2202 if (*expn.dptr == ';') {
2206 mutt_extract_token (token, &expn, 0);
2207 for (i = 0; Commands[i].name; i++) {
2208 if (!m_strcmp(token->data, Commands[i].name)) {
2209 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
2214 if (!Commands[i].name) {
2215 snprintf (err->data, err->dsize, _("%s: unknown command"),
2216 NONULL (token->data));
2223 p_delete(&expn.data);
2228 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
2229 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
2230 /* initial string that starts completion. No telling how much crap
2231 * the user has typed so far. Allocate LONG_STRING just to be sure! */
2232 char User_typed[LONG_STRING] = { 0 };
2234 int Num_matched = 0; /* Number of matches for completion */
2235 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
2236 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
2238 /* helper function for completion. Changes the dest buffer if
2239 necessary/possible to aid completion.
2240 dest == completion result gets here.
2241 src == candidate for completion.
2242 try == user entered data for completion.
2243 len == length of dest buffer.
2245 static void candidate (char *dest, char *try, const char *src, int len)
2249 if (strstr (src, try) == src) {
2250 Matches[Num_matched++] = src;
2252 m_strcpy(dest, len, src);
2254 for (l = 0; src[l] && src[l] == dest[l]; l++);
2260 int mutt_command_complete (char *buffer, 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, sizeof(Matches));
2279 p_clear(Completed, sizeof(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, sizeof(Matches));
2328 p_clear(Completed, sizeof(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, sizeof(Matches));
2366 p_clear(Completed, sizeof(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 if ((WithCrypto & APPLICATION_PGP))
2465 map = SortKeyMethods;
2473 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2474 snprintf(tmp, sizeof(tmp), "%s%s%s",
2475 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2476 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2478 else if (DTYPE (option->type) == DT_MAGIC) {
2480 switch (DefaultMagic) {
2496 m_strcpy(tmp, sizeof(tmp), p);
2498 else if (DTYPE (option->type) == DT_BOOL)
2499 m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
2503 for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
2504 if (*s == '\\' || *s == '"')
2510 m_strcpy(tmp, sizeof(tmp), pt);
2511 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2519 /* Implement the -Q command line flag */
2520 int mutt_query_variables (LIST * queries)
2524 char errbuff[STRING];
2525 char command[STRING];
2533 err.dsize = sizeof(errbuff);
2535 for (p = queries; p; p = p->next) {
2536 snprintf (command, sizeof(command), "set ?%s\n", p->data);
2537 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2538 fprintf (stderr, "%s\n", err.data);
2539 p_delete(&token.data);
2542 printf ("%s\n", err.data);
2545 p_delete(&token.data);
2549 static int mutt_execute_commands (LIST * p)
2552 char errstr[SHORT_STRING];
2556 err.dsize = sizeof(errstr);
2558 for (; p; p = p->next) {
2559 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2560 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2561 p_delete(&token.data);
2565 p_delete(&token.data);
2569 void mutt_init (int skip_sys_rc, LIST * commands)
2572 struct utsname utsname;
2574 char buffer[STRING], error[STRING];
2575 int default_rc = 0, need_pause = 0;
2581 err.dsize = sizeof(error);
2583 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2584 * to have some room for $user_ vars */
2585 ConfigOptions = hash_create (sizeof(MuttVars) * 3);
2586 for (i = 0; MuttVars[i].option; i++) {
2587 if (DTYPE (MuttVars[i].type) != DT_SYS)
2588 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2590 hash_insert (ConfigOptions, MuttVars[i].option,
2591 add_option (MuttVars[i].option, MuttVars[i].init,
2596 * XXX - use something even more difficult to predict?
2598 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2599 "\033]9;%ld\a", (long) time (NULL));
2601 /* on one of the systems I use, getcwd() does not return the same prefix
2602 as is listed in the passwd file */
2603 if ((p = getenv ("HOME")))
2604 Homedir = m_strdup(p);
2606 /* Get some information about the user */
2607 if ((pw = getpwuid (getuid ()))) {
2610 Username = m_strdup(pw->pw_name);
2612 Homedir = m_strdup(pw->pw_dir);
2614 Realname = m_strdup(mutt_gecos_name (rnbuf, sizeof(rnbuf), pw));
2615 Shell = m_strdup(pw->pw_shell);
2621 fputs (_("unable to determine home directory"), stderr);
2624 if ((p = getenv ("USER")))
2625 Username = m_strdup(p);
2628 fputs (_("unable to determine username"), stderr);
2631 Shell = m_strdup((p = getenv ("SHELL")) ? p : "/bin/sh");
2634 debug_start(Homedir);
2636 /* And about the host... */
2638 /* some systems report the FQDN instead of just the hostname */
2639 if ((p = strchr (utsname.nodename, '.'))) {
2640 Hostname = p_dupstr(utsname.nodename, p - utsname.nodename);
2642 m_strcpy(buffer, sizeof(buffer), p); /* save the domain for below */
2645 Hostname = m_strdup(utsname.nodename);
2648 #define DOMAIN buffer
2649 if (!p && getdnsdomainname (buffer, sizeof(buffer)) == -1)
2650 Fqdn = m_strdup("@");
2653 if (*DOMAIN != '@') {
2654 Fqdn = p_new(char, m_strlen(DOMAIN) + m_strlen(Hostname) + 2);
2655 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2658 Fqdn = m_strdup(NONULL (Hostname));
2665 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2667 fgets (buffer, sizeof(buffer), f);
2668 p = vskipspaces(buffer);
2670 while (*q && !isspace(*q))
2673 NewsServer = m_strdup(p);
2677 if ((p = getenv ("NNTPSERVER")))
2678 NewsServer = m_strdup(p);
2681 if ((p = getenv ("MAIL")))
2682 Spoolfile = m_strdup(p);
2683 else if ((p = getenv ("MAILDIR")))
2684 Spoolfile = m_strdup(p);
2687 mutt_concat_path(buffer, sizeof(buffer), NONULL(Homedir), MAILPATH);
2689 mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(Username));
2691 Spoolfile = m_strdup(buffer);
2694 if ((p = getenv ("MAILCAPS")))
2695 MailcapPath = m_strdup(p);
2697 /* Default search path from RFC1524 */
2699 m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2700 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2703 Tempdir = m_strdup((p = getenv ("TMPDIR")) ? p : "/tmp");
2705 p = getenv ("VISUAL");
2707 p = getenv ("EDITOR");
2711 Editor = m_strdup(p);
2712 Visual = m_strdup(p);
2714 if ((p = getenv ("REPLYTO")) != NULL) {
2717 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2720 buf.data = buf.dptr = buffer;
2721 buf.dsize = m_strlen(buffer);
2724 parse_my_hdr (&token, &buf, 0, &err);
2725 p_delete(&token.data);
2728 if ((p = getenv ("EMAIL")) != NULL)
2729 From = rfc822_parse_adrlist (NULL, p);
2731 mutt_set_langinfo_charset ();
2732 mutt_set_charset (Charset);
2735 /* Set standard defaults */
2736 hash_map (ConfigOptions, mutt_set_default, 0);
2737 hash_map (ConfigOptions, mutt_restore_default, 0);
2739 CurrentMenu = MENU_MAIN;
2742 #ifndef LOCALES_HACK
2743 /* Do we have a locale definition? */
2744 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2745 ((p = getenv ("LANG")) != NULL && p[0]) ||
2746 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2747 set_option (OPTLOCALES);
2751 /* Unset suspend by default if we're the session leader */
2752 if (getsid (0) == getpid ())
2753 unset_option (OPTSUSPEND);
2756 mutt_init_history ();
2765 * When changing the code which looks for a configuration file,
2766 * please also change the corresponding code in muttbug.sh.in.
2776 snprintf (buffer, sizeof(buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2778 if (access (buffer, F_OK) == -1)
2780 snprintf (buffer, sizeof(buffer), "%s/.muttngrc", NONULL (Homedir));
2781 if (access (buffer, F_OK) == -1)
2783 snprintf (buffer, sizeof(buffer), "%s/.muttng/muttngrc-%s",
2784 NONULL (Homedir), MUTT_VERSION);
2785 if (access (buffer, F_OK) == -1)
2787 snprintf (buffer, sizeof(buffer), "%s/.muttng/muttngrc",
2791 Muttrc = m_strdup(buffer);
2794 m_strcpy(buffer, sizeof(buffer), Muttrc);
2796 mutt_expand_path (buffer, sizeof(buffer));
2797 Muttrc = m_strdup(buffer);
2799 p_delete(&AliasFile);
2800 AliasFile = m_strdup(NONULL (Muttrc));
2802 /* Process the global rc file if it exists and the user hasn't explicity
2803 requested not to via "-n". */
2805 snprintf (buffer, sizeof(buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2807 if (access (buffer, F_OK) == -1)
2808 snprintf (buffer, sizeof(buffer), "%s/Muttngrc", SYSCONFDIR);
2809 if (access (buffer, F_OK) == -1)
2810 snprintf (buffer, sizeof(buffer), "%s/Muttngrc-%s", PKGDATADIR,
2812 if (access (buffer, F_OK) == -1)
2813 snprintf (buffer, sizeof(buffer), "%s/Muttngrc", PKGDATADIR);
2814 if (access (buffer, F_OK) != -1) {
2815 if (source_rc (buffer, &err) != 0) {
2816 fputs (err.data, stderr);
2817 fputc ('\n', stderr);
2823 /* Read the user's initialization file. */
2824 if (access (Muttrc, F_OK) != -1) {
2825 if (!option (OPTNOCURSES))
2827 if (source_rc (Muttrc, &err) != 0) {
2828 fputs (err.data, stderr);
2829 fputc ('\n', stderr);
2833 else if (!default_rc) {
2834 /* file specified by -F does not exist */
2835 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2836 mutt_endwin (buffer);
2840 if (mutt_execute_commands (commands) != 0)
2843 /* warn about synonym variables */
2844 if (!list_empty(Synonyms)) {
2846 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2847 for (i = 0; i < Synonyms->length; i++) {
2848 struct option_t* newopt = NULL, *oldopt = NULL;
2849 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2850 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2851 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2852 oldopt ? NONULL (oldopt->option) : "",
2853 newopt ? NONULL (newopt->option) : "",
2854 NONULL(((syn_t*) Synonyms->data[i])->f),
2855 ((syn_t*) Synonyms->data[i])->l);
2857 fprintf (stderr, _("Warning: synonym variables are scheduled"
2858 " for removal.\n"));
2859 list_del (&Synonyms, syn_del);
2863 if (need_pause && !option (OPTNOCURSES)) {
2864 if (mutt_any_key_to_continue (NULL) == -1)
2869 set_option (OPTWEED); /* turn weeding on by default */
2873 int mutt_get_hook_type (const char *name)
2875 struct command_t *c;
2877 for (c = Commands; c->name; c++)
2878 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2883 /* compare two option_t*'s for sorting -t/-T output */
2884 static int opt_cmp (const void* a, const void* b) {
2885 return (m_strcmp((*(struct option_t**) a)->option,
2886 (*(struct option_t**) b)->option));
2889 /* callback for hash_map() to put all non-synonym vars into list */
2890 static void opt_sel_full (const char* key __attribute__ ((unused)),
2892 unsigned long more) {
2893 list2_t** l = (list2_t**) more;
2894 struct option_t* option = (struct option_t*) data;
2896 if (DTYPE (option->type) == DT_SYN)
2898 list_push_back (l, option);
2901 /* callback for hash_map() to put all changed non-synonym vars into list */
2902 static void opt_sel_diff (const char* key __attribute__ ((unused)),
2904 unsigned long more) {
2905 list2_t** l = (list2_t**) more;
2906 struct option_t* option = (struct option_t*) data;
2907 char buf[LONG_STRING];
2909 if (DTYPE (option->type) == DT_SYN)
2912 mutt_option_value (option->option, buf, sizeof(buf));
2913 if (m_strcmp(buf, option->init) != 0)
2914 list_push_back (l, option);
2917 /* dump out the value of all the variables we have */
2918 int mutt_dump_variables (int full) {
2920 char outbuf[STRING];
2921 list2_t* tmp = NULL;
2922 struct option_t* option = NULL;
2924 /* get all non-synonyms into list... */
2925 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2926 (unsigned long) &tmp);
2928 if (!list_empty(tmp)) {
2929 /* ...and dump list sorted */
2930 qsort (tmp->data, tmp->length, sizeof(void*), opt_cmp);
2931 for (i = 0; i < tmp->length; i++) {
2932 option = (struct option_t*) tmp->data[i];
2933 FuncTable[DTYPE (option->type)].opt_to_string
2934 (outbuf, sizeof(outbuf), option);
2935 printf ("%s\n", outbuf);
2938 list_del (&tmp, NULL);