2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
5 * Parts were written/modified by:
6 * Rocco Rutte <pdmef@cs.tu-berlin.de>
8 * This file is part of mutt-ng, see http://www.muttng.org/.
9 * It's licensed under the GNU General Public License,
10 * please see the file GPL in the top level source directory.
21 #include "mutt_curses.h"
27 #include "mutt_crypt.h"
28 #include "mutt_idna.h"
30 #if defined(USE_SSL) || defined(USE_GNUTLS)
34 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
35 #include "mutt_libesmtp.h"
46 #include "lib/debug.h"
52 #include <sys/utsname.h>
59 static const struct mapping_t* get_sortmap (struct option_t* option);
60 static int parse_sort (struct option_t* dst, const char *s,
61 const struct mapping_t *map,
62 char* errbuf, size_t errlen);
64 static HASH* ConfigOptions = NULL;
66 /* for synonym warning reports: synonym found during parsing */
70 struct option_t* n; /* new */
71 struct option_t* o; /* old */
74 /* for synonym warning reports: list of synonyms found */
75 static list2_t* Synonyms;
76 /* for synonym warning reports: current rc file */
77 static const char* CurRCFile = NULL;
78 /* for synonym warning reports: current rc line */
79 static int CurRCLine = 0;
81 /* prototypes for checking for special vars */
82 static int check_dsn_return (const char* option, unsigned long val,
83 char* errbuf, size_t errlen);
84 static int check_dsn_notify (const char* option, unsigned long val,
85 char* errbuf, size_t errlen);
86 static int check_history (const char* option, unsigned long val,
87 char* errbuf, size_t errlen);
88 /* this checks that numbers are >= 0 */
89 static int check_num (const char* option, unsigned long val,
90 char* errbuf, size_t errlen);
92 static int check_debug (const char* option, unsigned long val,
93 char* errbuf, size_t errlen);
96 /* use this to check only */
97 static int check_special (const char* option, unsigned long val,
98 char* errbuf, size_t errlen);
100 /* variable <-> sanity check function mappings
101 * when changing these, make sure the proper _from_string handler
102 * does this checking!
106 int (*check) (const char* option, unsigned long val,
107 char* errbuf, size_t errlen);
109 { "dsn_notify", check_dsn_notify },
110 { "dsn_return", check_dsn_return },
111 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
112 { "smtp_use_tls", mutt_libesmtp_check_usetls },
114 { "history", check_history },
115 { "pager_index_lines", check_num },
117 { "debug_level", check_debug },
123 /* protos for config type handles: convert value to string */
124 static void bool_to_string (char* dst, size_t dstlen, struct option_t* option);
125 static void num_to_string (char* dst, size_t dstlen, struct option_t* option);
126 static void str_to_string (char* dst, size_t dstlen, struct option_t* option);
127 static void quad_to_string (char* dst, size_t dstlen, struct option_t* option);
128 static void sort_to_string (char* dst, size_t dstlen, struct option_t* option);
129 static void rx_to_string (char* dst, size_t dstlen, struct option_t* option);
130 static void magic_to_string (char* dst, size_t dstlen, struct option_t* option);
131 static void addr_to_string (char* dst, size_t dstlen, struct option_t* option);
132 static void user_to_string (char* dst, size_t dstlen, struct option_t* option);
133 static void sys_to_string (char* dst, size_t dstlen, struct option_t* option);
135 /* protos for config type handles: convert to value from string */
136 static int bool_from_string (struct option_t* dst, const char* val,
137 char* errbuf, size_t errlen);
138 static int num_from_string (struct option_t* dst, const char* val,
139 char* errbuf, size_t errlen);
140 static int str_from_string (struct option_t* dst, const char* val,
141 char* errbuf, size_t errlen);
142 static int path_from_string (struct option_t* dst, const char* val,
143 char* errbuf, size_t errlen);
144 static int quad_from_string (struct option_t* dst, const char* val,
145 char* errbuf, size_t errlen);
146 static int sort_from_string (struct option_t* dst, const char* val,
147 char* errbuf, size_t errlen);
148 static int rx_from_string (struct option_t* dst, const char* val,
149 char* errbuf, size_t errlen);
150 static int magic_from_string (struct option_t* dst, const char* val,
151 char* errbuf, size_t errlen);
152 static int addr_from_string (struct option_t* dst, const char* val,
153 char* errbuf, size_t errlen);
154 static int user_from_string (struct option_t* dst, const char* val,
155 char* errbuf, size_t errlen);
159 void (*opt_to_string) (char* dst, size_t dstlen, struct option_t* option);
160 int (*opt_from_string) (struct option_t* dst, const char* val,
161 char* errbuf, size_t errlen);
163 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
164 { DT_BOOL, bool_to_string, bool_from_string },
165 { DT_NUM, num_to_string, num_from_string },
166 { DT_STR, str_to_string, str_from_string },
167 { DT_PATH, str_to_string, path_from_string },
168 { DT_QUAD, quad_to_string, quad_from_string },
169 { DT_SORT, sort_to_string, sort_from_string },
170 { DT_RX, rx_to_string, rx_from_string },
171 { DT_MAGIC, magic_to_string, magic_from_string },
172 /* synonyms should be resolved already so we don't need this
173 * but must define it as DT_ is used for indexing */
174 { DT_SYN, NULL, NULL },
175 { DT_ADDR, addr_to_string, addr_from_string },
176 { DT_USER, user_to_string, user_from_string },
177 { DT_SYS, sys_to_string, NULL },
180 static void bool_to_string (char* dst, size_t dstlen,
181 struct option_t* option) {
182 snprintf (dst, dstlen, "%s=%s", option->option,
183 option (option->data) ? "yes" : "no");
186 static int bool_from_string (struct option_t* dst, const char* val,
187 char* errbuf, size_t errlen) {
192 if (ascii_strncasecmp (val, "yes", 3) == 0)
194 else if (ascii_strncasecmp (val, "no", 2) == 0)
200 set_option (dst->data);
202 unset_option (dst->data);
206 static void num_to_string (char* dst, size_t dstlen,
207 struct option_t* option) {
209 const char* fmt = (str_cmp (option->option, "umask") == 0) ?
211 snprintf (dst, dstlen, fmt, option->option,
212 *((short*) option->data));
215 static int num_from_string (struct option_t* dst, const char* val,
216 char* errbuf, size_t errlen) {
217 int num = 0, old = 0;
223 num = strtol (val, &t, 0);
225 if (!*val || *t || (short) num != num) {
227 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
233 /* just temporarily accept new val so that check_special for
234 * $history already has it when doing history's init() */
235 old = *((short*) dst->data);
236 *((short*) dst->data) = (short) num;
238 if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
239 *((short*) dst->data) = old;
246 static void str_to_string (char* dst, size_t dstlen,
247 struct option_t* option) {
248 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
249 NONULL (*((char**) option->data)));
252 static void user_to_string (char* dst, size_t dstlen,
253 struct option_t* option) {
254 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
255 NONULL (((char*) option->data)));
258 static void sys_to_string (char* dst, size_t dstlen,
259 struct option_t* option) {
260 char *val = NULL, *t = NULL;
263 /* get some $muttng_ values dynamically */
264 if (ascii_strcmp ("muttng_pwd", option->option) == 0) {
265 val = mem_malloc (_POSIX_PATH_MAX);
266 val = getcwd (val, _POSIX_PATH_MAX-1);
268 } else if (ascii_strcmp ("muttng_folder_path", option->option) == 0 &&
269 CurrentFolder && *CurrentFolder) {
271 } else if (ascii_strcmp ("muttng_folder_name", option->option) == 0 &&
272 CurrentFolder && *CurrentFolder) {
274 size_t Maildirlength = str_len (Maildir);
277 * if name starts with $folder, just strip it to keep hierarchy
278 * $folder=imap://host, path=imap://host/inbox/b -> inbox/b
280 if (Maildirlength > 0 && str_ncmp (CurrentFolder, Maildir,
281 Maildirlength) == 0 &&
282 str_len (CurrentFolder) > Maildirlength) {
283 val = CurrentFolder + Maildirlength;
284 if (Maildir[strlen(Maildir)-1]!='/')
286 /* if not $folder, just use everything after last / */
287 } else if ((t = strrchr (CurrentFolder, '/')) != NULL)
289 /* default: use as-is */
296 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (val));
301 static int path_from_string (struct option_t* dst, const char* val,
302 char* errbuf, size_t errlen) {
303 char path[_POSIX_PATH_MAX];
309 mem_free ((char**) dst->data);
314 strfcpy (path, val, sizeof (path));
315 mutt_expand_path (path, sizeof (path));
316 str_replace ((char **) dst->data, path);
320 static int str_from_string (struct option_t* dst, const char* val,
321 char* errbuf, size_t errlen) {
325 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
328 str_replace ((char**) dst->data, val);
332 static int user_from_string (struct option_t* dst, const char* val,
333 char* errbuf, size_t errlen) {
334 /* if dst == NULL, we may get here in case the user did unset it,
335 * see parse_set() where item is free()'d before coming here; so
336 * just silently ignore it */
339 if (str_len ((char*) dst->data) == 0)
340 dst->data = (unsigned long) str_dup (val);
342 char* s = (char*) dst->data;
343 str_replace (&s, val);
345 if (str_len (dst->init) == 0)
346 dst->init = str_dup ((char*) dst->data);
350 static void quad_to_string (char* dst, size_t dstlen,
351 struct option_t* option) {
352 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
353 snprintf (dst, dstlen, "%s=%s", option->option,
354 vals[quadoption (option->data)]);
357 static int quad_from_string (struct option_t* dst, const char* val,
358 char* errbuf, size_t errlen) {
363 if (ascii_strncasecmp (val, "yes", 3) == 0)
365 else if (ascii_strncasecmp (val, "no", 2) == 0)
367 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
369 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
375 set_quadoption (dst->data, flag);
379 static void sort_to_string (char* dst, size_t dstlen,
380 struct option_t* option) {
381 const struct mapping_t *map = get_sortmap (option);
382 const char *p = NULL;
385 snprintf (dst, sizeof (dst), "%s=unknown", option->option);
389 p = mutt_getnamebyvalue(*((short *)option->data) & SORT_MASK, map);
391 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
392 (*((short *) option->data) & SORT_REVERSE) ?
394 (*((short *) option->data) & SORT_LAST) ? "last-" :
398 static int sort_from_string (struct option_t* dst, const char* val,
399 char* errbuf, size_t errlen) {
400 const struct mapping_t *map = NULL;
401 if (!(map = get_sortmap (dst))) {
403 snprintf (errbuf, errlen, _("%s: Unknown type."),
407 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
412 static void rx_to_string (char* dst, size_t dstlen,
413 struct option_t* option) {
414 rx_t* p = (rx_t*) option->data;
415 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
416 NONULL (p->pattern));
419 static int rx_from_string (struct option_t* dst, const char* val,
420 char* errbuf, size_t errlen) {
423 int flags = 0, e = 0, not = 0;
429 if (option (OPTATTACHMSG) && !str_cmp (dst->option, "reply_regexp")) {
431 snprintf (errbuf, errlen,
432 "Operation not permitted when in attach-message mode.");
436 if (!((rx_t*) dst->data))
437 *((rx_t**) dst->data) = mem_calloc (1, sizeof (rx_t));
439 p = (rx_t*) dst->data;
441 /* something to do? */
442 if (!val || !*val || (p->pattern && str_cmp (p->pattern, val) == 0))
445 if (str_cmp (dst->option, "mask") != 0)
446 flags |= mutt_which_case (val);
449 if (str_cmp (dst->option, "mask") == 0 && *s == '!') {
454 rx = mem_malloc (sizeof (regex_t));
456 if ((e = REGCOMP (rx, s, flags)) != 0) {
457 regerror (e, rx, errbuf, errlen);
468 str_replace (&p->pattern, val);
472 if (str_cmp (dst->option, "reply_regexp") == 0)
473 mutt_adjust_all_subjects ();
478 static void magic_to_string (char* dst, size_t dstlen,
479 struct option_t* option) {
480 const char* s = NULL;
481 switch (option->data) {
482 case M_MBOX: s = "mbox"; break;
483 case M_MMDF: s = "MMDF"; break;
484 case M_MH: s = "MH"; break;
485 case M_MAILDIR: s = "Maildir"; break;
486 default: s = "unknown"; break;
488 snprintf (dst, dstlen, "%s=%s", option->option, s);
491 static int magic_from_string (struct option_t* dst, const char* val,
492 char* errbuf, size_t errlen) {
495 if (!dst || !val || !*val)
497 if (ascii_strncasecmp (val, "mbox", 4) == 0)
499 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
501 else if (ascii_strncasecmp (val, "mh", 2) == 0)
503 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
509 *((short*) dst->data) = flag;
514 static void addr_to_string (char* dst, size_t dstlen,
515 struct option_t* option) {
518 rfc822_write_address (s, sizeof (s), *((ADDRESS**) option->data), 0);
519 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
522 static int addr_from_string (struct option_t* dst, const char* val,
523 char* errbuf, size_t errlen) {
526 rfc822_free_address ((ADDRESS**) dst->data);
528 *((ADDRESS**) dst->data) = rfc822_parse_adrlist (NULL, val);
532 int mutt_option_value (const char* val, char* dst, size_t dstlen) {
533 struct option_t* option = NULL;
534 char* tmp = NULL, *t = NULL;
537 if (!(option = hash_find (ConfigOptions, val))) {
538 debug_print (1, ("var '%s' not found\n", val));
542 tmp = mem_malloc (dstlen+1);
543 FuncTable[DTYPE (option->type)].opt_to_string (tmp, dstlen, option);
545 /* as we get things of type $var=value and don't want to bloat the
546 * above "just" for expansion, we do the stripping here */
547 debug_print (1, ("orig == '%s'\n", tmp));
548 t = strchr (tmp, '=');
552 if (t[l-1] == '"' && *t == '"') {
557 memcpy (dst, t, l+1);
559 debug_print (1, ("stripped == '%s'\n", dst));
564 /* for synonym warning reports: adds synonym to end of list */
565 static void syn_add (struct option_t* n, struct option_t* o) {
566 syn_t* tmp = mem_malloc (sizeof (syn_t));
567 tmp->f = str_dup (CurRCFile);
571 list_push_back (&Synonyms, tmp);
574 /* for synonym warning reports: free single item (for list_del()) */
575 static void syn_del (void** p) {
576 mem_free(&(*(syn_t**) p)->f);
580 void toggle_quadoption (int opt)
583 int b = (opt % 4) * 2;
585 QuadOptions[n] ^= (1 << b);
588 void set_quadoption (int opt, int flag)
591 int b = (opt % 4) * 2;
593 QuadOptions[n] &= ~(0x3 << b);
594 QuadOptions[n] |= (flag & 0x3) << b;
597 int quadoption (int opt)
600 int b = (opt % 4) * 2;
602 return (QuadOptions[n] >> b) & 0x3;
605 int query_quadoption (int opt, const char *prompt)
607 int v = quadoption (opt);
615 v = mutt_yesorno (prompt, (v == M_ASKYES));
616 CLEARLINE (LINES - 1);
623 static void add_to_list (LIST ** list, const char *str)
625 LIST *t, *last = NULL;
627 /* don't add a NULL or empty string to the list */
628 if (!str || *str == '\0')
631 /* check to make sure the item is not already on this list */
632 for (last = *list; last; last = last->next) {
633 if (ascii_strcasecmp (str, last->data) == 0) {
634 /* already on the list, so just ignore it */
642 if (!*list || last) {
643 t = (LIST *) mem_calloc (1, sizeof (LIST));
644 t->data = str_dup (str);
654 static int add_to_rx_list (list2_t** list, const char *s, int flags,
663 if (!(rx = rx_compile (s, flags))) {
664 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
668 i = rx_lookup ((*list), rx->pattern);
672 list_push_back (list, rx);
676 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
677 const char *templ, BUFFER * err)
679 SPAM_LIST *t = NULL, *last = NULL;
684 if (!pat || !*pat || !templ)
687 if (!(rx = rx_compile (pat, REG_ICASE))) {
688 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
692 /* check to make sure the item is not already on this list */
693 for (last = *list; last; last = last->next) {
694 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
695 /* Already on the list. Formerly we just skipped this case, but
696 * now we're supporting removals, which means we're supporting
697 * re-adds conceptually. So we probably want this to imply a
698 * removal, then do an add. We can achieve the removal by freeing
699 * the template, and leaving t pointed at the current item.
702 mem_free(t->template);
709 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
710 * update. Otherwise we want to make a new one to link at the list's end.
713 t = mutt_new_spam_list ();
721 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
722 t->template = str_dup (templ);
724 /* Find highest match number in template string */
726 for (p = templ; *p;) {
731 while (*p && isdigit ((int) *p))
737 t->nmatch++; /* match 0 is always the whole expr */
742 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
744 SPAM_LIST *spam, *prev;
747 /* Being first is a special case. */
751 if (spam->rx && !str_cmp (spam->rx->pattern, pat)) {
754 mem_free(&spam->template);
760 for (spam = prev->next; spam;) {
761 if (!str_cmp (spam->rx->pattern, pat)) {
762 prev->next = spam->next;
764 mem_free(spam->template);
777 static void remove_from_list (LIST ** l, const char *str)
779 LIST *p, *last = NULL;
781 if (str_cmp ("*", str) == 0)
782 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
787 if (ascii_strcasecmp (str, p->data) == 0) {
790 last->next = p->next;
803 static int remove_from_rx_list (list2_t** l, const char *str)
807 if (str_cmp ("*", str) == 0) {
808 list_del (l, (list_del_t*) rx_free);
812 i = rx_lookup ((*l), str);
814 rx_t* r = list_pop_idx ((*l), i);
822 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
827 struct option_t* option = NULL;
829 memset (&token, 0, sizeof (token));
830 mutt_extract_token (tmp, s, 0);
832 /* is the item defined as a variable or a function? */
833 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL)
836 for (i = 0; !res && i < MENU_MAX; i++) {
837 struct binding_t *b = km_get_table (Menus[i].value);
842 for (j = 0; b[j].name; j++)
843 if (!ascii_strncasecmp (tmp->data, b[j].name, str_len (tmp->data))
844 && (str_len (b[j].name) == str_len (tmp->data))) {
850 /* check for feature_* */
851 if (!res && ascii_strncasecmp (tmp->data, "feature_", 8) == 0 &&
852 (j = str_len (tmp->data)) > 8) {
854 while (Features[i]) {
855 if (str_len (Features[i]) == j-8 &&
856 ascii_strncasecmp (Features[i], tmp->data+8, j-8) == 0) {
866 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
868 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
872 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
875 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
876 mutt_error ("Error: %s", err->data);
877 mem_free (&token.data);
880 mem_free (&token.data);
885 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
889 mutt_extract_token (buf, s, 0);
891 /* don't add "*" to the unignore list */
892 if (strcmp (buf->data, "*"))
893 add_to_list (&UnIgnore, buf->data);
895 remove_from_list (&Ignore, buf->data);
897 while (MoreArgs (s));
902 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
906 mutt_extract_token (buf, s, 0);
907 remove_from_list (&UnIgnore, buf->data);
908 add_to_list (&Ignore, buf->data);
910 while (MoreArgs (s));
915 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
919 mutt_extract_token (buf, s, 0);
920 add_to_list ((LIST **) data, buf->data);
922 while (MoreArgs (s));
927 static void _alternates_clean (void)
931 if (Context && Context->msgcount) {
932 for (i = 0; i < Context->msgcount; i++)
933 Context->hdrs[i]->recip_valid = 0;
937 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
940 _alternates_clean ();
942 mutt_extract_token (buf, s, 0);
943 remove_from_rx_list (&UnAlternates, buf->data);
945 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
948 while (MoreArgs (s));
953 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
956 _alternates_clean ();
958 mutt_extract_token (buf, s, 0);
959 remove_from_rx_list (&Alternates, buf->data);
961 if (str_cmp (buf->data, "*") &&
962 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
966 while (MoreArgs (s));
971 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
976 memset (&templ, 0, sizeof (templ));
978 /* Insist on at least one parameter */
981 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
983 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
987 /* Extract the first token, a regexp */
988 mutt_extract_token (buf, s, 0);
990 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
991 if (data == M_SPAM) {
992 /* If there's a second parameter, it's a template for the spam tag. */
994 mutt_extract_token (&templ, s, 0);
996 /* Add to the spam list. */
997 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
998 mem_free (&templ.data);
1001 mem_free (&templ.data);
1004 /* If not, try to remove from the nospam list. */
1006 remove_from_rx_list (&NoSpamList, buf->data);
1012 /* M_NOSPAM is for nospam commands. */
1013 else if (data == M_NOSPAM) {
1014 /* nospam only ever has one parameter. */
1016 /* "*" is a special case. */
1017 if (!str_cmp (buf->data, "*")) {
1018 mutt_free_spam_list (&SpamList);
1019 list_del (&NoSpamList, (list_del_t*) rx_free);
1023 /* If it's on the spam list, just remove it. */
1024 if (remove_from_spam_list (&SpamList, buf->data) != 0)
1027 /* Otherwise, add it to the nospam list. */
1028 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
1034 /* This should not happen. */
1035 strfcpy (err->data, "This is no good at all.", err->dsize);
1039 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
1043 mutt_extract_token (buf, s, 0);
1045 * Check for deletion of entire list
1047 if (str_cmp (buf->data, "*") == 0) {
1048 mutt_free_list ((LIST **) data);
1051 remove_from_list ((LIST **) data, buf->data);
1053 while (MoreArgs (s));
1058 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
1062 mutt_extract_token (buf, s, 0);
1063 remove_from_rx_list (&UnMailLists, buf->data);
1065 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1068 while (MoreArgs (s));
1073 /* always wise to do what someone else did before */
1074 static void _attachments_clean (void) {
1076 if (Context && Context->msgcount) {
1077 for (i = 0; i < Context->msgcount; i++)
1078 Context->hdrs[i]->attach_valid = 0;
1082 static int parse_attach_list (BUFFER *buf, BUFFER *s, LIST **ldata,
1085 LIST *listp, *lastp;
1090 /* Find the last item in the list that data points to. */
1092 debug_print (5, ("parse_attach_list: ldata = %08x, *ldata = %08x\n",
1093 (unsigned int)ldata, (unsigned int)*ldata));
1094 for (listp = *ldata; listp; listp = listp->next) {
1095 a = (ATTACH_MATCH *)listp->data;
1096 debug_print (5, ("parse_attach_list: skipping %s/%s\n", a->major, a->minor));
1101 mutt_extract_token (buf, s, 0);
1103 if (!buf->data || *buf->data == '\0')
1106 a = mem_malloc(sizeof(ATTACH_MATCH));
1108 /* some cheap hacks that I expect to remove */
1109 if (!str_casecmp(buf->data, "any"))
1110 a->major = str_dup("*/.*");
1111 else if (!str_casecmp(buf->data, "none"))
1112 a->major = str_dup("cheap_hack/this_should_never_match");
1114 a->major = str_dup(buf->data);
1116 if ((p = strchr(a->major, '/'))) {
1121 a->minor = "unknown";
1124 len = str_len (a->minor);
1125 tmpminor = mem_malloc(len+3);
1126 strcpy(&tmpminor[1], a->minor); /* __STRCPY_CHECKED__ */
1128 tmpminor[len+1] = '$';
1129 tmpminor[len+2] = '\0';
1131 a->major_int = mutt_check_mime_type(a->major);
1132 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
1134 mem_free (&tmpminor);
1136 debug_print (5, ("parse_attach_list: added %s/%s [%d]\n",
1137 a->major, a->minor, a->major_int));
1139 listp = mem_malloc(sizeof(LIST));
1140 listp->data = (char *)a;
1143 lastp->next = listp;
1149 while (MoreArgs (s));
1151 _attachments_clean();
1155 static int parse_unattach_list (BUFFER *buf, BUFFER *s, LIST **ldata, BUFFER *err) {
1157 LIST *lp, *lastp, *newlp;
1163 mutt_extract_token (buf, s, 0);
1165 if (!str_casecmp(buf->data, "any"))
1166 tmp = str_dup("*/.*");
1167 else if (!str_casecmp(buf->data, "none"))
1168 tmp = str_dup("cheap_hack/this_should_never_match");
1170 tmp = str_dup(buf->data);
1172 if ((minor = strchr(tmp, '/'))) {
1178 major = mutt_check_mime_type(tmp);
1180 /* We must do our own walk here because remove_from_list() will only
1181 * remove the LIST->data, not anything pointed to by the LIST->data. */
1183 for(lp = *ldata; lp; ) {
1184 a = (ATTACH_MATCH *)lp->data;
1185 debug_print(5, ("parse_unattach_list: check %s/%s [%d] : %s/%s [%d]\n",
1186 a->major, a->minor, a->major_int, tmp, minor, major));
1187 if (a->major_int == major && !str_casecmp(minor, a->minor)) {
1188 debug_print(5, ("parse_unattach_list: removed %s/%s [%d]\n",
1189 a->major, a->minor, a->major_int));
1190 regfree(&a->minor_rx);
1191 mem_free(&a->major);
1193 /* Relink backward */
1195 lastp->next = lp->next;
1200 mem_free(&lp->data); /* same as a */
1210 while (MoreArgs (s));
1213 _attachments_clean();
1217 static int print_attach_list (LIST *lp, char op, const char *name) {
1219 printf("attachments %c%s %s/%s\n", op, name,
1220 ((ATTACH_MATCH *)lp->data)->major,
1221 ((ATTACH_MATCH *)lp->data)->minor);
1228 static int parse_attachments (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) {
1232 mutt_extract_token(buf, s, 0);
1233 if (!buf->data || *buf->data == '\0') {
1234 strfcpy(err->data, _("attachments: no disposition"), err->dsize);
1238 category = buf->data;
1244 printf("\nCurrent attachments settings:\n\n");
1245 print_attach_list(AttachAllow, '+', "A");
1246 print_attach_list(AttachExclude, '-', "A");
1247 print_attach_list(InlineAllow, '+', "I");
1248 print_attach_list(InlineExclude, '-', "I");
1249 set_option (OPTFORCEREDRAWINDEX);
1250 set_option (OPTFORCEREDRAWPAGER);
1251 mutt_any_key_to_continue (NULL);
1255 if (op != '+' && op != '-') {
1259 if (!str_ncasecmp(category, "attachment", strlen(category))) {
1261 listp = &AttachAllow;
1263 listp = &AttachExclude;
1265 else if (!str_ncasecmp(category, "inline", strlen(category))) {
1267 listp = &InlineAllow;
1269 listp = &InlineExclude;
1271 strfcpy(err->data, _("attachments: invalid disposition"), err->dsize);
1275 return parse_attach_list(buf, s, listp, err);
1278 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) {
1282 mutt_extract_token(buf, s, 0);
1283 if (!buf->data || *buf->data == '\0') {
1284 strfcpy(err->data, _("unattachments: no disposition"), err->dsize);
1290 if (op != '+' && op != '-') {
1294 if (!str_ncasecmp(p, "attachment", strlen(p))) {
1296 listp = &AttachAllow;
1298 listp = &AttachExclude;
1300 else if (!str_ncasecmp(p, "inline", strlen(p))) {
1302 listp = &InlineAllow;
1304 listp = &InlineExclude;
1307 strfcpy(err->data, _("unattachments: invalid disposition"), err->dsize);
1311 return parse_unattach_list(buf, s, listp, err);
1314 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
1318 mutt_extract_token (buf, s, 0);
1319 remove_from_rx_list (&SubscribedLists, buf->data);
1320 remove_from_rx_list (&MailLists, buf->data);
1322 if (str_cmp (buf->data, "*") &&
1323 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1326 while (MoreArgs (s));
1331 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1335 mutt_extract_token (buf, s, 0);
1336 remove_from_rx_list (&UnMailLists, buf->data);
1337 remove_from_rx_list (&UnSubscribedLists, buf->data);
1339 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1341 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1344 while (MoreArgs (s));
1349 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1353 mutt_extract_token (buf, s, 0);
1354 remove_from_rx_list (&SubscribedLists, buf->data);
1356 if (str_cmp (buf->data, "*") &&
1357 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1360 while (MoreArgs (s));
1365 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
1368 ALIAS *tmp, *last = NULL;
1371 mutt_extract_token (buf, s, 0);
1373 if (str_cmp ("*", buf->data) == 0) {
1374 if (CurrentMenu == MENU_ALIAS) {
1375 for (tmp = Aliases; tmp; tmp = tmp->next)
1377 set_option (OPTFORCEREDRAWINDEX);
1380 mutt_free_alias (&Aliases);
1384 for (tmp = Aliases; tmp; tmp = tmp->next) {
1385 if (str_casecmp (buf->data, tmp->name) == 0) {
1386 if (CurrentMenu == MENU_ALIAS) {
1388 set_option (OPTFORCEREDRAWINDEX);
1393 last->next = tmp->next;
1395 Aliases = tmp->next;
1397 mutt_free_alias (&tmp);
1403 while (MoreArgs (s));
1407 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
1410 ALIAS *tmp = Aliases;
1414 if (!MoreArgs (s)) {
1415 strfcpy (err->data, _("alias: no address"), err->dsize);
1419 mutt_extract_token (buf, s, 0);
1421 debug_print (2, ("first token is '%s'.\n", buf->data));
1423 /* check to see if an alias with this name already exists */
1424 for (; tmp; tmp = tmp->next) {
1425 if (!str_casecmp (tmp->name, buf->data))
1431 /* create a new alias */
1432 tmp = (ALIAS *) mem_calloc (1, sizeof (ALIAS));
1434 tmp->name = str_dup (buf->data);
1435 /* give the main addressbook code a chance */
1436 if (CurrentMenu == MENU_ALIAS)
1437 set_option (OPTMENUCALLER);
1440 /* override the previous value */
1441 rfc822_free_address (&tmp->addr);
1442 if (CurrentMenu == MENU_ALIAS)
1443 set_option (OPTFORCEREDRAWINDEX);
1446 mutt_extract_token (buf, s,
1447 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1448 debug_print (2, ("second token is '%s'.\n", buf->data));
1449 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1454 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1455 snprintf (err->data, err->dsize,
1456 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1460 if (DebugLevel >= 2) {
1463 /* A group is terminated with an empty address, so check a->mailbox */
1464 for (a = tmp->addr; a && a->mailbox; a = a->next) {
1466 debug_print (2, ("%s\n", a->mailbox));
1468 debug_print (2, ("group %s\n", a->mailbox));
1476 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
1479 LIST *tmp = UserHeader;
1484 mutt_extract_token (buf, s, 0);
1485 if (str_cmp ("*", buf->data) == 0)
1486 mutt_free_list (&UserHeader);
1491 l = str_len (buf->data);
1492 if (buf->data[l - 1] == ':')
1496 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1497 && tmp->data[l] == ':') {
1500 last->next = tmp->next;
1502 UserHeader = tmp->next;
1505 mutt_free_list (&ptr);
1514 while (MoreArgs (s));
1518 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
1525 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1526 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1527 strfcpy (err->data, _("invalid header field"), err->dsize);
1530 keylen = p - buf->data + 1;
1533 for (tmp = UserHeader;; tmp = tmp->next) {
1534 /* see if there is already a field by this name */
1535 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1536 /* replace the old value */
1537 mem_free (&tmp->data);
1538 tmp->data = buf->data;
1539 memset (buf, 0, sizeof (BUFFER));
1545 tmp->next = mutt_new_list ();
1549 tmp = mutt_new_list ();
1552 tmp->data = buf->data;
1553 memset (buf, 0, sizeof (BUFFER));
1558 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1559 char* errbuf, size_t errlen) {
1562 if (str_ncmp ("reverse-", s, 8) == 0) {
1564 flags = SORT_REVERSE;
1567 if (str_ncmp ("last-", s, 5) == 0) {
1572 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1574 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1578 *((short*) dst->data) = i | flags;
1582 /* if additional data more == 1, we want to resolve synonyms */
1583 static void mutt_set_default (const char* name, void* p, unsigned long more) {
1584 char buf[LONG_STRING];
1585 struct option_t* ptr = (struct option_t*) p;
1587 if (DTYPE (ptr->type) == DT_SYN) {
1590 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1592 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_from_string)
1594 mutt_option_value (ptr->option, buf, sizeof (buf));
1595 if (str_len (ptr->init) == 0 && buf && *buf)
1596 ptr->init = str_dup (buf);
1599 static struct option_t* add_option (const char* name, const char* init,
1600 short type, short dodup) {
1601 struct option_t* option = mem_calloc (1, sizeof (struct option_t));
1603 debug_print (1, ("adding $%s\n", name));
1605 option->option = str_dup (name);
1606 option->type = type;
1608 option->init = dodup ? str_dup (init) : (char*) init;
1612 /* creates new option_t* of type DT_USER for $user_ var */
1613 static struct option_t* add_user_option (const char* name) {
1614 return (add_option (name, NULL, DT_USER, 1));
1617 /* free()'s option_t* */
1618 static void del_option (void* p) {
1619 struct option_t* ptr = (struct option_t*) p;
1620 char* s = (char*) ptr->data;
1621 debug_print (1, ("removing option '%s' from table\n", NONULL (ptr->option)));
1622 mem_free (&ptr->option);
1624 mem_free (&ptr->init);
1628 static int init_expand (char** dst, struct option_t* src) {
1634 if (DTYPE(src->type) == DT_STR ||
1635 DTYPE(src->type) == DT_PATH) {
1636 /* only expand for string as it's the only place where
1637 * we want to expand vars right now */
1638 if (src->init && *src->init) {
1639 memset (&token, 0, sizeof (BUFFER));
1640 memset (&in, 0, sizeof (BUFFER));
1641 len = str_len (src->init) + 2;
1642 in.data = mem_malloc (len+1);
1643 snprintf (in.data, len, "\"%s\"", src->init);
1646 mutt_extract_token (&token, &in, 0);
1647 if (token.data && *token.data)
1648 *dst = str_dup (token.data);
1650 *dst = str_dup ("");
1651 mem_free (&in.data);
1652 mem_free (&token.data);
1654 *dst = str_dup ("");
1656 /* for non-string: take value as is */
1657 *dst = str_dup (src->init);
1661 /* if additional data more == 1, we want to resolve synonyms */
1662 static void mutt_restore_default (const char* name, void* p,
1663 unsigned long more) {
1664 char errbuf[STRING];
1665 struct option_t* ptr = (struct option_t*) p;
1668 if (DTYPE (ptr->type) == DT_SYN) {
1671 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1675 if (FuncTable[DTYPE (ptr->type)].opt_from_string) {
1676 init_expand (&init, ptr);
1677 if (!FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, init, errbuf,
1679 if (!option (OPTNOCURSES))
1681 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1682 "Please report this error: \"%s\"\n"),
1683 ptr->option, NONULL (init), errbuf);
1689 if (ptr->flags & R_INDEX)
1690 set_option (OPTFORCEREDRAWINDEX);
1691 if (ptr->flags & R_PAGER)
1692 set_option (OPTFORCEREDRAWPAGER);
1693 if (ptr->flags & R_RESORT_SUB)
1694 set_option (OPTSORTSUBTHREADS);
1695 if (ptr->flags & R_RESORT)
1696 set_option (OPTNEEDRESORT);
1697 if (ptr->flags & R_RESORT_INIT)
1698 set_option (OPTRESORTINIT);
1699 if (ptr->flags & R_TREE)
1700 set_option (OPTREDRAWTREE);
1703 /* check whether value for $dsn_return would be valid */
1704 static int check_dsn_return (const char* option, unsigned long p,
1705 char* errbuf, size_t errlen) {
1706 char* val = (char*) p;
1707 if (val && *val && str_ncmp (val, "hdrs", 4) != 0 &&
1708 str_ncmp (val, "full", 4) != 0) {
1710 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1716 /* check whether value for $dsn_notify would be valid */
1717 static int check_dsn_notify (const char* option, unsigned long p,
1718 char* errbuf, size_t errlen) {
1719 list2_t* list = NULL;
1721 char* val = (char*) p;
1725 list = list_from_str (val, ",");
1726 if (list_empty (list))
1729 for (i = 0; i < list->length; i++)
1730 if (str_ncmp (list->data[i], "never", 5) != 0 &&
1731 str_ncmp (list->data[i], "failure", 7) != 0 &&
1732 str_ncmp (list->data[i], "delay", 5) != 0 &&
1733 str_ncmp (list->data[i], "success", 7) != 0) {
1735 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1736 (char*) list->data[i], "dsn_notify");
1740 list_del (&list, (list_del_t*) _mem_free);
1744 static int check_num (const char* option, unsigned long p,
1745 char* errbuf, size_t errlen) {
1748 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1755 static int check_debug (const char* option, unsigned long p,
1756 char* errbuf, size_t errlen) {
1757 if ((int) p <= DEBUG_MAX_LEVEL &&
1758 (int) p >= DEBUG_MIN_LEVEL)
1762 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1767 static int check_history (const char* option, unsigned long p,
1768 char* errbuf, size_t errlen) {
1769 if (!check_num ("history", p, errbuf, errlen))
1771 mutt_init_history ();
1775 static int check_special (const char* name, unsigned long val,
1776 char* errbuf, size_t errlen) {
1779 for (i = 0; SpecialVars[i].name; i++) {
1780 if (str_cmp (SpecialVars[i].name, name) == 0) {
1781 return (SpecialVars[i].check (SpecialVars[i].name,
1782 val, errbuf, errlen));
1788 static const struct mapping_t* get_sortmap (struct option_t* option) {
1789 const struct mapping_t* map = NULL;
1791 switch (option->type & DT_SUBTYPE_MASK) {
1793 map = SortAliasMethods;
1795 case DT_SORT_BROWSER:
1796 map = SortBrowserMethods;
1799 if ((WithCrypto & APPLICATION_PGP))
1800 map = SortKeyMethods;
1803 map = SortAuxMethods;
1812 #define CHECK_PAGER \
1813 if ((CurrentMenu == MENU_PAGER) && \
1814 (!option || (option->flags & R_RESORT))) \
1816 snprintf (err->data, err->dsize, \
1817 _("Not available in this menu.")); \
1821 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1824 int query, unset, inv, reset, r = 0;
1825 struct option_t* option = NULL;
1827 while (MoreArgs (s)) {
1828 /* reset state variables */
1830 unset = data & M_SET_UNSET;
1831 inv = data & M_SET_INV;
1832 reset = data & M_SET_RESET;
1834 if (*s->dptr == '?') {
1838 else if (str_ncmp ("no", s->dptr, 2) == 0) {
1842 else if (str_ncmp ("inv", s->dptr, 3) == 0) {
1846 else if (*s->dptr == '&') {
1851 /* get the variable name */
1852 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1854 /* resolve synonyms */
1855 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1856 DTYPE (option->type == DT_SYN)) {
1857 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1858 syn_add (newopt, option);
1862 /* see if we need to add $user_ var */
1863 if (!option && ascii_strncmp ("user_", tmp->data, 5) == 0) {
1864 /* there's no option named like this yet so only add one
1865 * if the action isn't any of: reset, unset, query */
1866 if (!(reset || unset || query || *s->dptr != '=')) {
1867 debug_print (1, ("adding user option '%s'\n", tmp->data));
1868 option = add_user_option (tmp->data);
1869 hash_insert (ConfigOptions, option->option, option, 0);
1873 if (!option && !(reset && str_cmp ("all", tmp->data) == 0)) {
1874 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1880 if (query || unset || inv) {
1881 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1885 if (s && *s->dptr == '=') {
1886 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1890 if (!str_cmp ("all", tmp->data)) {
1891 if (CurrentMenu == MENU_PAGER) {
1892 snprintf (err->data, err->dsize, _("Not available in this menu."));
1895 hash_map (ConfigOptions, mutt_restore_default, 1);
1896 set_option (OPTFORCEREDRAWINDEX);
1897 set_option (OPTFORCEREDRAWPAGER);
1898 set_option (OPTSORTSUBTHREADS);
1899 set_option (OPTNEEDRESORT);
1900 set_option (OPTRESORTINIT);
1901 set_option (OPTREDRAWTREE);
1904 else if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1905 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1910 mutt_restore_default (NULL, option, 1);
1913 else if (DTYPE (option->type) == DT_BOOL) {
1914 /* XXX this currently ignores the function table
1915 * as we don't get invert and stuff into it */
1916 if (s && *s->dptr == '=') {
1917 if (unset || inv || query) {
1918 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1923 mutt_extract_token (tmp, s, 0);
1924 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1926 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1929 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1935 bool_to_string (err->data, err->dsize, option);
1941 unset_option (option->data);
1943 toggle_option (option->data);
1945 set_option (option->data);
1947 else if (DTYPE (option->type) == DT_STR ||
1948 DTYPE (option->type) == DT_PATH ||
1949 DTYPE (option->type) == DT_ADDR ||
1950 DTYPE (option->type) == DT_MAGIC ||
1951 DTYPE (option->type) == DT_NUM ||
1952 DTYPE (option->type) == DT_SORT ||
1953 DTYPE (option->type) == DT_RX ||
1954 DTYPE (option->type) == DT_USER ||
1955 DTYPE (option->type) == DT_SYS) {
1957 /* XXX maybe we need to get unset into handlers? */
1958 if (DTYPE (option->type) == DT_STR ||
1959 DTYPE (option->type) == DT_PATH ||
1960 DTYPE (option->type) == DT_ADDR ||
1961 DTYPE (option->type) == DT_USER ||
1962 DTYPE (option->type) == DT_SYS) {
1965 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1966 snprintf (err->data, err->dsize, _("$%s is read-only"),
1970 } else if (DTYPE (option->type) == DT_ADDR)
1971 rfc822_free_address ((ADDRESS **) option->data);
1972 else if (DTYPE (option->type) == DT_USER)
1973 /* to unset $user_ means remove */
1974 hash_delete (ConfigOptions, option->option,
1975 option, del_option);
1977 mem_free ((void *) option->data);
1982 if (query || *s->dptr != '=') {
1983 FuncTable[DTYPE (option->type)].opt_to_string
1984 (err->data, err->dsize, option);
1988 /* the $muttng_ variables are read-only */
1989 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1990 snprintf (err->data, err->dsize, _("$%s is read-only"),
1997 mutt_extract_token (tmp, s, 0);
1998 if (!FuncTable[DTYPE (option->type)].opt_from_string
1999 (option, tmp->data, err->data, err->dsize))
2003 else if (DTYPE (option->type) == DT_QUAD) {
2006 quad_to_string (err->data, err->dsize, option);
2010 if (*s->dptr == '=') {
2013 mutt_extract_token (tmp, s, 0);
2014 if (ascii_strcasecmp ("yes", tmp->data) == 0)
2015 set_quadoption (option->data, M_YES);
2016 else if (ascii_strcasecmp ("no", tmp->data) == 0)
2017 set_quadoption (option->data, M_NO);
2018 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
2019 set_quadoption (option->data, M_ASKYES);
2020 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
2021 set_quadoption (option->data, M_ASKNO);
2023 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
2024 tmp->data, option->option);
2031 toggle_quadoption (option->data);
2033 set_quadoption (option->data, M_NO);
2035 set_quadoption (option->data, M_YES);
2039 snprintf (err->data, err->dsize, _("%s: unknown type"),
2045 if (option->flags & R_INDEX)
2046 set_option (OPTFORCEREDRAWINDEX);
2047 if (option->flags & R_PAGER)
2048 set_option (OPTFORCEREDRAWPAGER);
2049 if (option->flags & R_RESORT_SUB)
2050 set_option (OPTSORTSUBTHREADS);
2051 if (option->flags & R_RESORT)
2052 set_option (OPTNEEDRESORT);
2053 if (option->flags & R_RESORT_INIT)
2054 set_option (OPTRESORTINIT);
2055 if (option->flags & R_TREE)
2056 set_option (OPTREDRAWTREE);
2063 /* reads the specified initialization file. returns -1 if errors were found
2064 so that we can pause to let the user know... */
2065 static int source_rc (const char *rcfile, BUFFER * err)
2068 int line = 0, rc = 0, conv = 0;
2070 char *linebuf = NULL;
2071 char *currentline = NULL;
2075 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
2077 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
2078 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
2082 memset (&token, 0, sizeof (token));
2083 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
2084 conv = ConfigCharset && (*ConfigCharset) && Charset;
2086 currentline = str_dup (linebuf);
2089 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
2092 currentline = linebuf;
2097 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
2098 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
2099 if (--rc < -MAXERRS) {
2101 mem_free (¤tline);
2110 mem_free (¤tline);
2112 mem_free (&token.data);
2113 mem_free (&linebuf);
2116 mutt_wait_filter (pid);
2118 /* the muttrc source keyword */
2119 snprintf (err->data, err->dsize,
2120 rc >= -MAXERRS ? _("source: errors in %s")
2121 : _("source: reading aborted due too many errors in %s"),
2130 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
2133 char path[_POSIX_PATH_MAX];
2137 if (mutt_extract_token (tmp, s, 0) != 0) {
2138 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
2142 strfcpy (path, tmp->data, sizeof (path));
2143 mutt_expand_path (path, sizeof (path));
2145 rc += source_rc (path, err);
2147 while (MoreArgs (s));
2149 return ((rc < 0) ? -1 : 0);
2152 /* line command to execute
2154 token scratch buffer to be used by parser. caller should free
2155 token->data when finished. the reason for this variable is
2156 to avoid having to allocate and deallocate a lot of memory
2157 if we are parsing many lines. the caller can pass in the
2158 memory to use, which avoids having to create new space for
2159 every call to this function.
2161 err where to write error messages */
2162 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
2167 memset (&expn, 0, sizeof (expn));
2168 expn.data = expn.dptr = line;
2169 expn.dsize = str_len (line);
2173 debug_print (1, ("expand '%s'\n", line));
2176 while (*expn.dptr) {
2177 if (*expn.dptr == '#')
2178 break; /* rest of line is a comment */
2179 if (*expn.dptr == ';') {
2183 mutt_extract_token (token, &expn, 0);
2184 for (i = 0; Commands[i].name; i++) {
2185 if (!str_cmp (token->data, Commands[i].name)) {
2186 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
2191 if (!Commands[i].name) {
2192 snprintf (err->data, err->dsize, _("%s: unknown command"),
2193 NONULL (token->data));
2200 mem_free (&expn.data);
2205 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
2206 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
2207 /* initial string that starts completion. No telling how much crap
2208 * the user has typed so far. Allocate LONG_STRING just to be sure! */
2209 char User_typed[LONG_STRING] = { 0 };
2211 int Num_matched = 0; /* Number of matches for completion */
2212 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
2213 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
2215 /* helper function for completion. Changes the dest buffer if
2216 necessary/possible to aid completion.
2217 dest == completion result gets here.
2218 src == candidate for completion.
2219 try == user entered data for completion.
2220 len == length of dest buffer.
2222 static void candidate (char *dest, char *try, const char *src, int len)
2226 if (strstr (src, try) == src) {
2227 Matches[Num_matched++] = src;
2229 strfcpy (dest, src, len);
2231 for (l = 0; src[l] && src[l] == dest[l]; l++);
2237 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
2241 int spaces; /* keep track of the number of leading spaces on the line */
2244 spaces = buffer - pt;
2246 pt = buffer + pos - spaces;
2247 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2250 if (pt == buffer) { /* complete cmd */
2251 /* first TAB. Collect all the matches */
2254 strfcpy (User_typed, pt, sizeof (User_typed));
2255 memset (Matches, 0, sizeof (Matches));
2256 memset (Completed, 0, sizeof (Completed));
2257 for (num = 0; Commands[num].name; num++)
2258 candidate (Completed, User_typed, Commands[num].name,
2259 sizeof (Completed));
2260 Matches[Num_matched++] = User_typed;
2262 /* All matches are stored. Longest non-ambiguous string is ""
2263 * i.e. dont change 'buffer'. Fake successful return this time */
2264 if (User_typed[0] == 0)
2268 if (Completed[0] == 0 && User_typed[0])
2271 /* Num_matched will _always_ be atleast 1 since the initial
2272 * user-typed string is always stored */
2273 if (numtabs == 1 && Num_matched == 2)
2274 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
2275 else if (numtabs > 1 && Num_matched > 2)
2276 /* cycle thru all the matches */
2277 snprintf (Completed, sizeof (Completed), "%s",
2278 Matches[(numtabs - 2) % Num_matched]);
2280 /* return the completed command */
2281 strncpy (buffer, Completed, len - spaces);
2283 else if (!str_ncmp (buffer, "set", 3)
2284 || !str_ncmp (buffer, "unset", 5)
2285 || !str_ncmp (buffer, "reset", 5)
2286 || !str_ncmp (buffer, "toggle", 6)) { /* complete variables */
2287 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
2290 /* loop through all the possible prefixes (no, inv, ...) */
2291 if (!str_ncmp (buffer, "set", 3)) {
2292 for (num = 0; prefixes[num]; num++) {
2293 if (!str_ncmp (pt, prefixes[num], str_len (prefixes[num]))) {
2294 pt += str_len (prefixes[num]);
2300 /* first TAB. Collect all the matches */
2303 strfcpy (User_typed, pt, sizeof (User_typed));
2304 memset (Matches, 0, sizeof (Matches));
2305 memset (Completed, 0, sizeof (Completed));
2306 for (num = 0; MuttVars[num].option; num++)
2307 candidate (Completed, User_typed, MuttVars[num].option,
2308 sizeof (Completed));
2309 Matches[Num_matched++] = User_typed;
2311 /* All matches are stored. Longest non-ambiguous string is ""
2312 * i.e. dont change 'buffer'. Fake successful return this time */
2313 if (User_typed[0] == 0)
2317 if (Completed[0] == 0 && User_typed[0])
2320 /* Num_matched will _always_ be atleast 1 since the initial
2321 * user-typed string is always stored */
2322 if (numtabs == 1 && Num_matched == 2)
2323 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
2324 else if (numtabs > 1 && Num_matched > 2)
2325 /* cycle thru all the matches */
2326 snprintf (Completed, sizeof (Completed), "%s",
2327 Matches[(numtabs - 2) % Num_matched]);
2329 strncpy (pt, Completed, buffer + len - pt - spaces);
2331 else if (!str_ncmp (buffer, "exec", 4)) {
2332 struct binding_t *menu = km_get_table (CurrentMenu);
2334 if (!menu && CurrentMenu != MENU_PAGER)
2338 /* first TAB. Collect all the matches */
2341 strfcpy (User_typed, pt, sizeof (User_typed));
2342 memset (Matches, 0, sizeof (Matches));
2343 memset (Completed, 0, sizeof (Completed));
2344 for (num = 0; menu[num].name; num++)
2345 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
2346 /* try the generic menu */
2347 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2349 for (num = 0; menu[num].name; num++)
2350 candidate (Completed, User_typed, menu[num].name,
2351 sizeof (Completed));
2353 Matches[Num_matched++] = User_typed;
2355 /* All matches are stored. Longest non-ambiguous string is ""
2356 * i.e. dont change 'buffer'. Fake successful return this time */
2357 if (User_typed[0] == 0)
2361 if (Completed[0] == 0 && User_typed[0])
2364 /* Num_matched will _always_ be atleast 1 since the initial
2365 * user-typed string is always stored */
2366 if (numtabs == 1 && Num_matched == 2)
2367 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
2368 else if (numtabs > 1 && Num_matched > 2)
2369 /* cycle thru all the matches */
2370 snprintf (Completed, sizeof (Completed), "%s",
2371 Matches[(numtabs - 2) % Num_matched]);
2373 strncpy (pt, Completed, buffer + len - pt - spaces);
2381 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2383 char var[STRING], *pt = buffer;
2385 struct option_t* option = NULL;
2391 spaces = buffer - pt;
2393 pt = buffer + pos - spaces;
2394 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2396 pt++; /* move past the space */
2397 if (*pt == '=') /* abort if no var before the '=' */
2400 if (str_ncmp (buffer, "set", 3) == 0) {
2401 strfcpy (var, pt, sizeof (var));
2402 /* ignore the trailing '=' when comparing */
2403 var[str_len (var) - 1] = 0;
2404 if (!(option = hash_find (ConfigOptions, var)))
2405 return 0; /* no such variable. */
2407 char tmp[LONG_STRING], tmp2[LONG_STRING];
2409 size_t dlen = buffer + len - pt - spaces;
2410 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2414 if ((DTYPE (option->type) == DT_STR) ||
2415 (DTYPE (option->type) == DT_PATH) ||
2416 (DTYPE (option->type) == DT_RX)) {
2417 strfcpy (tmp, NONULL (*((char **) option->data)), sizeof (tmp));
2418 if (DTYPE (option->type) == DT_PATH)
2419 mutt_pretty_mailbox (tmp);
2421 else if (DTYPE (option->type) == DT_ADDR) {
2422 rfc822_write_address (tmp, sizeof (tmp),
2423 *((ADDRESS **) option->data), 0);
2425 else if (DTYPE (option->type) == DT_QUAD)
2426 strfcpy (tmp, vals[quadoption (option->data)], sizeof (tmp));
2427 else if (DTYPE (option->type) == DT_NUM)
2428 snprintf (tmp, sizeof (tmp), "%d", (*((short *) option->data)));
2429 else if (DTYPE (option->type) == DT_SORT) {
2430 const struct mapping_t *map;
2433 switch (option->type & DT_SUBTYPE_MASK) {
2435 map = SortAliasMethods;
2437 case DT_SORT_BROWSER:
2438 map = SortBrowserMethods;
2441 if ((WithCrypto & APPLICATION_PGP))
2442 map = SortKeyMethods;
2450 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2451 snprintf(tmp, sizeof (tmp), "%s%s%s",
2452 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2453 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2455 else if (DTYPE (option->type) == DT_MAGIC) {
2457 switch (DefaultMagic) {
2473 strfcpy (tmp, p, sizeof (tmp));
2475 else if (DTYPE (option->type) == DT_BOOL)
2476 strfcpy (tmp, option (option->data) ? "yes" : "no",
2481 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
2482 if (*s == '\\' || *s == '"')
2488 strfcpy (tmp, pt, sizeof (tmp));
2489 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2497 /* Implement the -Q command line flag */
2498 int mutt_query_variables (LIST * queries)
2502 char errbuff[STRING];
2503 char command[STRING];
2507 memset (&err, 0, sizeof (err));
2508 memset (&token, 0, sizeof (token));
2511 err.dsize = sizeof (errbuff);
2513 for (p = queries; p; p = p->next) {
2514 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2515 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2516 fprintf (stderr, "%s\n", err.data);
2517 mem_free (&token.data);
2520 printf ("%s\n", err.data);
2523 mem_free (&token.data);
2527 const char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2531 for (i = 0; map[i].name; i++)
2532 if (map[i].value == val)
2533 return (map[i].name);
2537 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2541 for (i = 0; map[i].name; i++)
2542 if (ascii_strcasecmp (map[i].name, name) == 0)
2543 return (map[i].value);
2547 static int mutt_execute_commands (LIST * p)
2550 char errstr[SHORT_STRING];
2552 memset (&err, 0, sizeof (err));
2554 err.dsize = sizeof (errstr);
2555 memset (&token, 0, sizeof (token));
2556 for (; p; p = p->next) {
2557 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2558 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2559 mem_free (&token.data);
2563 mem_free (&token.data);
2567 void mutt_init (int skip_sys_rc, LIST * commands)
2570 struct utsname utsname;
2572 char buffer[STRING], error[STRING];
2573 int i, default_rc = 0, need_pause = 0;
2576 memset (&err, 0, sizeof (err));
2578 err.dsize = sizeof (error);
2580 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2581 * to have some room for $user_ vars */
2582 ConfigOptions = hash_create (sizeof (MuttVars) * 3);
2583 for (i = 0; MuttVars[i].option; i++) {
2584 if (DTYPE (MuttVars[i].type) != DT_SYS)
2585 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2587 hash_insert (ConfigOptions, MuttVars[i].option,
2588 add_option (MuttVars[i].option, MuttVars[i].init,
2593 * XXX - use something even more difficult to predict?
2595 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2596 "\033]9;%ld\a", (long) time (NULL));
2598 /* on one of the systems I use, getcwd() does not return the same prefix
2599 as is listed in the passwd file */
2600 if ((p = getenv ("HOME")))
2601 Homedir = str_dup (p);
2603 /* Get some information about the user */
2604 if ((pw = getpwuid (getuid ()))) {
2607 Username = str_dup (pw->pw_name);
2609 Homedir = str_dup (pw->pw_dir);
2611 Realname = str_dup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2612 Shell = str_dup (pw->pw_shell);
2618 fputs (_("unable to determine home directory"), stderr);
2621 if ((p = getenv ("USER")))
2622 Username = str_dup (p);
2625 fputs (_("unable to determine username"), stderr);
2628 Shell = str_dup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2631 debug_start(Homedir);
2633 /* And about the host... */
2635 /* some systems report the FQDN instead of just the hostname */
2636 if ((p = strchr (utsname.nodename, '.'))) {
2637 Hostname = str_substrdup (utsname.nodename, p);
2639 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2642 Hostname = str_dup (utsname.nodename);
2645 #define DOMAIN buffer
2646 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2647 Fqdn = str_dup ("@");
2650 if (*DOMAIN != '@') {
2651 Fqdn = mem_malloc (str_len (DOMAIN) + str_len (Hostname) + 2);
2652 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2655 Fqdn = str_dup (NONULL (Hostname));
2662 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2664 fgets (buffer, sizeof (buffer), f);
2668 while (*q && !isspace(*q))
2671 NewsServer = str_dup (p);
2675 if ((p = getenv ("NNTPSERVER")))
2676 NewsServer = str_dup (p);
2679 if ((p = getenv ("MAIL")))
2680 Spoolfile = str_dup (p);
2681 else if ((p = getenv ("MAILDIR")))
2682 Spoolfile = str_dup (p);
2685 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2687 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
2689 Spoolfile = str_dup (buffer);
2692 if ((p = getenv ("MAILCAPS")))
2693 MailcapPath = str_dup (p);
2695 /* Default search path from RFC1524 */
2697 str_dup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2698 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2701 Tempdir = str_dup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2703 p = getenv ("VISUAL");
2705 p = getenv ("EDITOR");
2709 Editor = str_dup (p);
2710 Visual = str_dup (p);
2712 if ((p = getenv ("REPLYTO")) != NULL) {
2715 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2717 memset (&buf, 0, sizeof (buf));
2718 buf.data = buf.dptr = buffer;
2719 buf.dsize = str_len (buffer);
2721 memset (&token, 0, sizeof (token));
2722 parse_my_hdr (&token, &buf, 0, &err);
2723 mem_free (&token.data);
2726 if ((p = getenv ("EMAIL")) != NULL)
2727 From = rfc822_parse_adrlist (NULL, p);
2729 mutt_set_langinfo_charset ();
2730 mutt_set_charset (Charset);
2733 /* Set standard defaults */
2734 hash_map (ConfigOptions, mutt_set_default, 0);
2735 hash_map (ConfigOptions, mutt_restore_default, 0);
2737 CurrentMenu = MENU_MAIN;
2740 #ifndef LOCALES_HACK
2741 /* Do we have a locale definition? */
2742 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2743 ((p = getenv ("LANG")) != NULL && p[0]) ||
2744 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2745 set_option (OPTLOCALES);
2749 /* Unset suspend by default if we're the session leader */
2750 if (getsid (0) == getpid ())
2751 unset_option (OPTSUSPEND);
2754 mutt_init_history ();
2763 * When changing the code which looks for a configuration file,
2764 * please also change the corresponding code in muttbug.sh.in.
2774 snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2776 if (access (buffer, F_OK) == -1)
2778 snprintf (buffer, sizeof (buffer), "%s/.muttngrc", NONULL (Homedir));
2779 if (access (buffer, F_OK) == -1)
2781 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s",
2782 NONULL (Homedir), MUTT_VERSION);
2783 if (access (buffer, F_OK) == -1)
2785 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc",
2789 Muttrc = str_dup (buffer);
2792 strfcpy (buffer, Muttrc, sizeof (buffer));
2794 mutt_expand_path (buffer, sizeof (buffer));
2795 Muttrc = str_dup (buffer);
2797 mem_free (&AliasFile);
2798 AliasFile = str_dup (NONULL (Muttrc));
2800 /* Process the global rc file if it exists and the user hasn't explicity
2801 requested not to via "-n". */
2803 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2805 if (access (buffer, F_OK) == -1)
2806 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", SYSCONFDIR);
2807 if (access (buffer, F_OK) == -1)
2808 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR,
2810 if (access (buffer, F_OK) == -1)
2811 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2812 if (access (buffer, F_OK) != -1) {
2813 if (source_rc (buffer, &err) != 0) {
2814 fputs (err.data, stderr);
2815 fputc ('\n', stderr);
2821 /* Read the user's initialization file. */
2822 if (access (Muttrc, F_OK) != -1) {
2823 if (!option (OPTNOCURSES))
2825 if (source_rc (Muttrc, &err) != 0) {
2826 fputs (err.data, stderr);
2827 fputc ('\n', stderr);
2831 else if (!default_rc) {
2832 /* file specified by -F does not exist */
2833 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2834 mutt_endwin (buffer);
2838 if (mutt_execute_commands (commands) != 0)
2841 /* warn about synonym variables */
2842 if (!list_empty(Synonyms)) {
2844 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2845 for (i = 0; i < Synonyms->length; i++) {
2846 struct option_t* newopt = NULL, *oldopt = NULL;
2847 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2848 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2849 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2850 oldopt ? NONULL (oldopt->option) : "",
2851 newopt ? NONULL (newopt->option) : "",
2852 NONULL(((syn_t*) Synonyms->data[i])->f),
2853 ((syn_t*) Synonyms->data[i])->l);
2855 fprintf (stderr, _("Warning: synonym variables are scheduled"
2856 " for removal.\n"));
2857 list_del (&Synonyms, syn_del);
2861 if (need_pause && !option (OPTNOCURSES)) {
2862 if (mutt_any_key_to_continue (NULL) == -1)
2867 set_option (OPTWEED); /* turn weeding on by default */
2871 int mutt_get_hook_type (const char *name)
2873 struct command_t *c;
2875 for (c = Commands; c->name; c++)
2876 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2881 /* compare two option_t*'s for sorting -t/-T output */
2882 static int opt_cmp (const void* a, const void* b) {
2883 return (str_cmp ((*(struct option_t**) a)->option,
2884 (*(struct option_t**) b)->option));
2887 /* callback for hash_map() to put all non-synonym vars into list */
2888 static void opt_sel_full (const char* key, void* data,
2889 unsigned long more) {
2890 list2_t** l = (list2_t**) more;
2891 struct option_t* option = (struct option_t*) data;
2893 if (DTYPE (option->type) == DT_SYN)
2895 list_push_back (l, option);
2898 /* callback for hash_map() to put all changed non-synonym vars into list */
2899 static void opt_sel_diff (const char* key, void* data,
2900 unsigned long more) {
2901 list2_t** l = (list2_t**) more;
2902 struct option_t* option = (struct option_t*) data;
2903 char buf[LONG_STRING];
2905 if (DTYPE (option->type) == DT_SYN)
2908 mutt_option_value (option->option, buf, sizeof (buf));
2909 if (str_cmp (buf, option->init) != 0)
2910 list_push_back (l, option);
2913 /* dump out the value of all the variables we have */
2914 int mutt_dump_variables (int full) {
2916 char outbuf[STRING];
2917 list2_t* tmp = NULL;
2918 struct option_t* option = NULL;
2920 /* get all non-synonyms into list... */
2921 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2922 (unsigned long) &tmp);
2924 if (!list_empty(tmp)) {
2925 /* ...and dump list sorted */
2926 qsort (tmp->data, tmp->length, sizeof (void*), opt_cmp);
2927 for (i = 0; i < tmp->length; i++) {
2928 option = (struct option_t*) tmp->data[i];
2929 FuncTable[DTYPE (option->type)].opt_to_string
2930 (outbuf, sizeof (outbuf), option);
2931 printf ("%s\n", outbuf);
2934 list_del (&tmp, NULL);