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);
385 snprintf (dst, sizeof (dst), "%s=unknown", option->option);
389 p = mutt_getnamebyvalue (*((short *) option->data) & SORT_MASK,
392 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
393 (*((short *) option->data) & SORT_REVERSE) ?
395 (*((short *) option->data) & SORT_LAST) ? "last-" :
399 static int sort_from_string (struct option_t* dst, const char* val,
400 char* errbuf, size_t errlen) {
401 const struct mapping_t *map = NULL;
402 if (!(map = get_sortmap (dst))) {
404 snprintf (errbuf, errlen, _("%s: Unknown type."),
408 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
413 static void rx_to_string (char* dst, size_t dstlen,
414 struct option_t* option) {
415 rx_t* p = (rx_t*) option->data;
416 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
417 NONULL (p->pattern));
420 static int rx_from_string (struct option_t* dst, const char* val,
421 char* errbuf, size_t errlen) {
424 int flags = 0, e = 0, not = 0;
430 if (option (OPTATTACHMSG) && !str_cmp (dst->option, "reply_regexp")) {
432 snprintf (errbuf, errlen,
433 "Operation not permitted when in attach-message mode.");
437 if (!((rx_t*) dst->data))
438 *((rx_t**) dst->data) = mem_calloc (1, sizeof (rx_t));
440 p = (rx_t*) dst->data;
442 /* something to do? */
443 if (!val || !*val || (p->pattern && str_cmp (p->pattern, val) == 0))
446 if (str_cmp (dst->option, "mask") != 0)
447 flags |= mutt_which_case (val);
450 if (str_cmp (dst->option, "mask") == 0 && *s == '!') {
455 rx = mem_malloc (sizeof (regex_t));
457 if ((e = REGCOMP (rx, s, flags)) != 0) {
458 regerror (e, rx, errbuf, errlen);
469 str_replace (&p->pattern, val);
473 if (str_cmp (dst->option, "reply_regexp") == 0)
474 mutt_adjust_all_subjects ();
479 static void magic_to_string (char* dst, size_t dstlen,
480 struct option_t* option) {
481 const char* s = NULL;
482 switch (option->data) {
483 case M_MBOX: s = "mbox"; break;
484 case M_MMDF: s = "MMDF"; break;
485 case M_MH: s = "MH"; break;
486 case M_MAILDIR: s = "Maildir"; break;
487 default: s = "unknown"; break;
489 snprintf (dst, dstlen, "%s=%s", option->option, s);
492 static int magic_from_string (struct option_t* dst, const char* val,
493 char* errbuf, size_t errlen) {
496 if (!dst || !val || !*val)
498 if (ascii_strncasecmp (val, "mbox", 4) == 0)
500 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
502 else if (ascii_strncasecmp (val, "mh", 2) == 0)
504 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
510 *((short*) dst->data) = flag;
515 static void addr_to_string (char* dst, size_t dstlen,
516 struct option_t* option) {
519 rfc822_write_address (s, sizeof (s), *((ADDRESS**) option->data), 0);
520 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
523 static int addr_from_string (struct option_t* dst, const char* val,
524 char* errbuf, size_t errlen) {
527 rfc822_free_address ((ADDRESS**) dst->data);
529 *((ADDRESS**) dst->data) = rfc822_parse_adrlist (NULL, val);
533 int mutt_option_value (const char* val, char* dst, size_t dstlen) {
534 struct option_t* option = NULL;
535 char* tmp = NULL, *t = NULL;
538 if (!(option = hash_find (ConfigOptions, val))) {
539 debug_print (1, ("var '%s' not found\n", val));
543 tmp = mem_malloc (dstlen+1);
544 FuncTable[DTYPE (option->type)].opt_to_string (tmp, dstlen, option);
546 /* as we get things of type $var=value and don't want to bloat the
547 * above "just" for expansion, we do the stripping here */
548 debug_print (1, ("orig == '%s'\n", tmp));
549 t = strchr (tmp, '=');
553 if (t[l-1] == '"' && *t == '"') {
558 memcpy (dst, t, l+1);
560 debug_print (1, ("stripped == '%s'\n", dst));
565 /* for synonym warning reports: adds synonym to end of list */
566 static void syn_add (struct option_t* n, struct option_t* o) {
567 syn_t* tmp = mem_malloc (sizeof (syn_t));
568 tmp->f = str_dup (CurRCFile);
572 list_push_back (&Synonyms, tmp);
575 /* for synonym warning reports: free single item (for list_del()) */
576 static void syn_del (void** p) {
577 mem_free(&(*(syn_t**) p)->f);
581 void toggle_quadoption (int opt)
584 int b = (opt % 4) * 2;
586 QuadOptions[n] ^= (1 << b);
589 void set_quadoption (int opt, int flag)
592 int b = (opt % 4) * 2;
594 QuadOptions[n] &= ~(0x3 << b);
595 QuadOptions[n] |= (flag & 0x3) << b;
598 int quadoption (int opt)
601 int b = (opt % 4) * 2;
603 return (QuadOptions[n] >> b) & 0x3;
606 int query_quadoption (int opt, const char *prompt)
608 int v = quadoption (opt);
616 v = mutt_yesorno (prompt, (v == M_ASKYES));
617 CLEARLINE (LINES - 1);
624 static void add_to_list (LIST ** list, const char *str)
626 LIST *t, *last = NULL;
628 /* don't add a NULL or empty string to the list */
629 if (!str || *str == '\0')
632 /* check to make sure the item is not already on this list */
633 for (last = *list; last; last = last->next) {
634 if (ascii_strcasecmp (str, last->data) == 0) {
635 /* already on the list, so just ignore it */
643 if (!*list || last) {
644 t = (LIST *) mem_calloc (1, sizeof (LIST));
645 t->data = str_dup (str);
655 static int add_to_rx_list (list2_t** list, const char *s, int flags,
664 if (!(rx = rx_compile (s, flags))) {
665 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
669 i = rx_lookup ((*list), rx->pattern);
673 list_push_back (list, rx);
677 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
678 const char *templ, BUFFER * err)
680 SPAM_LIST *t = NULL, *last = NULL;
685 if (!pat || !*pat || !templ)
688 if (!(rx = rx_compile (pat, REG_ICASE))) {
689 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
693 /* check to make sure the item is not already on this list */
694 for (last = *list; last; last = last->next) {
695 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
696 /* Already on the list. Formerly we just skipped this case, but
697 * now we're supporting removals, which means we're supporting
698 * re-adds conceptually. So we probably want this to imply a
699 * removal, then do an add. We can achieve the removal by freeing
700 * the template, and leaving t pointed at the current item.
703 mem_free(t->template);
710 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
711 * update. Otherwise we want to make a new one to link at the list's end.
714 t = mutt_new_spam_list ();
722 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
723 t->template = str_dup (templ);
725 /* Find highest match number in template string */
727 for (p = templ; *p;) {
732 while (*p && isdigit ((int) *p))
738 t->nmatch++; /* match 0 is always the whole expr */
743 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
745 SPAM_LIST *spam, *prev;
748 /* Being first is a special case. */
752 if (spam->rx && !str_cmp (spam->rx->pattern, pat)) {
755 mem_free(&spam->template);
761 for (spam = prev->next; spam;) {
762 if (!str_cmp (spam->rx->pattern, pat)) {
763 prev->next = spam->next;
765 mem_free(spam->template);
778 static void remove_from_list (LIST ** l, const char *str)
780 LIST *p, *last = NULL;
782 if (str_cmp ("*", str) == 0)
783 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
788 if (ascii_strcasecmp (str, p->data) == 0) {
791 last->next = p->next;
804 static int remove_from_rx_list (list2_t** l, const char *str)
808 if (str_cmp ("*", str) == 0) {
809 list_del (l, (list_del_t*) rx_free);
813 i = rx_lookup ((*l), str);
815 rx_t* r = list_pop_idx ((*l), i);
823 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
828 struct option_t* option = NULL;
830 memset (&token, 0, sizeof (token));
831 mutt_extract_token (tmp, s, 0);
833 /* is the item defined as a variable or a function? */
834 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL)
837 for (i = 0; !res && i < MENU_MAX; i++) {
838 struct binding_t *b = km_get_table (Menus[i].value);
843 for (j = 0; b[j].name; j++)
844 if (!ascii_strncasecmp (tmp->data, b[j].name, str_len (tmp->data))
845 && (str_len (b[j].name) == str_len (tmp->data))) {
851 /* check for feature_* */
852 if (!res && ascii_strncasecmp (tmp->data, "feature_", 8) == 0 &&
853 (j = str_len (tmp->data)) > 8) {
855 while (Features[i]) {
856 if (str_len (Features[i]) == j-8 &&
857 ascii_strncasecmp (Features[i], tmp->data+8, j-8) == 0) {
867 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
869 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
873 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
876 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
877 mutt_error ("Error: %s", err->data);
878 mem_free (&token.data);
881 mem_free (&token.data);
886 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
890 mutt_extract_token (buf, s, 0);
892 /* don't add "*" to the unignore list */
893 if (strcmp (buf->data, "*"))
894 add_to_list (&UnIgnore, buf->data);
896 remove_from_list (&Ignore, buf->data);
898 while (MoreArgs (s));
903 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
907 mutt_extract_token (buf, s, 0);
908 remove_from_list (&UnIgnore, buf->data);
909 add_to_list (&Ignore, buf->data);
911 while (MoreArgs (s));
916 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
920 mutt_extract_token (buf, s, 0);
921 add_to_list ((LIST **) data, buf->data);
923 while (MoreArgs (s));
928 static void _alternates_clean (void)
932 if (Context && Context->msgcount) {
933 for (i = 0; i < Context->msgcount; i++)
934 Context->hdrs[i]->recip_valid = 0;
938 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
941 _alternates_clean ();
943 mutt_extract_token (buf, s, 0);
944 remove_from_rx_list (&UnAlternates, buf->data);
946 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
949 while (MoreArgs (s));
954 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
957 _alternates_clean ();
959 mutt_extract_token (buf, s, 0);
960 remove_from_rx_list (&Alternates, buf->data);
962 if (str_cmp (buf->data, "*") &&
963 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
967 while (MoreArgs (s));
972 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
977 memset (&templ, 0, sizeof (templ));
979 /* Insist on at least one parameter */
982 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
984 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
988 /* Extract the first token, a regexp */
989 mutt_extract_token (buf, s, 0);
991 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
992 if (data == M_SPAM) {
993 /* If there's a second parameter, it's a template for the spam tag. */
995 mutt_extract_token (&templ, s, 0);
997 /* Add to the spam list. */
998 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
999 mem_free (&templ.data);
1002 mem_free (&templ.data);
1005 /* If not, try to remove from the nospam list. */
1007 remove_from_rx_list (&NoSpamList, buf->data);
1013 /* M_NOSPAM is for nospam commands. */
1014 else if (data == M_NOSPAM) {
1015 /* nospam only ever has one parameter. */
1017 /* "*" is a special case. */
1018 if (!str_cmp (buf->data, "*")) {
1019 mutt_free_spam_list (&SpamList);
1020 list_del (&NoSpamList, (list_del_t*) rx_free);
1024 /* If it's on the spam list, just remove it. */
1025 if (remove_from_spam_list (&SpamList, buf->data) != 0)
1028 /* Otherwise, add it to the nospam list. */
1029 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
1035 /* This should not happen. */
1036 strfcpy (err->data, "This is no good at all.", err->dsize);
1040 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
1044 mutt_extract_token (buf, s, 0);
1046 * Check for deletion of entire list
1048 if (str_cmp (buf->data, "*") == 0) {
1049 mutt_free_list ((LIST **) data);
1052 remove_from_list ((LIST **) data, buf->data);
1054 while (MoreArgs (s));
1059 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
1063 mutt_extract_token (buf, s, 0);
1064 remove_from_rx_list (&UnMailLists, buf->data);
1066 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1069 while (MoreArgs (s));
1074 /* always wise to do what someone else did before */
1075 static void _attachments_clean (void) {
1077 if (Context && Context->msgcount) {
1078 for (i = 0; i < Context->msgcount; i++)
1079 Context->hdrs[i]->attach_valid = 0;
1083 static int parse_attach_list (BUFFER *buf, BUFFER *s, LIST **ldata,
1086 LIST *listp, *lastp;
1091 /* Find the last item in the list that data points to. */
1093 debug_print (5, ("parse_attach_list: ldata = %08x, *ldata = %08x\n",
1094 (unsigned int)ldata, (unsigned int)*ldata));
1095 for (listp = *ldata; listp; listp = listp->next) {
1096 a = (ATTACH_MATCH *)listp->data;
1097 debug_print (5, ("parse_attach_list: skipping %s/%s\n", a->major, a->minor));
1102 mutt_extract_token (buf, s, 0);
1104 if (!buf->data || *buf->data == '\0')
1107 a = mem_malloc(sizeof(ATTACH_MATCH));
1109 /* some cheap hacks that I expect to remove */
1110 if (!str_casecmp(buf->data, "any"))
1111 a->major = str_dup("*/.*");
1112 else if (!str_casecmp(buf->data, "none"))
1113 a->major = str_dup("cheap_hack/this_should_never_match");
1115 a->major = str_dup(buf->data);
1117 if ((p = strchr(a->major, '/'))) {
1122 a->minor = "unknown";
1125 len = str_len (a->minor);
1126 tmpminor = mem_malloc(len+3);
1127 strcpy(&tmpminor[1], a->minor); /* __STRCPY_CHECKED__ */
1129 tmpminor[len+1] = '$';
1130 tmpminor[len+2] = '\0';
1132 a->major_int = mutt_check_mime_type(a->major);
1133 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
1135 mem_free (&tmpminor);
1137 debug_print (5, ("parse_attach_list: added %s/%s [%d]\n",
1138 a->major, a->minor, a->major_int));
1140 listp = mem_malloc(sizeof(LIST));
1141 listp->data = (char *)a;
1144 lastp->next = listp;
1150 while (MoreArgs (s));
1152 _attachments_clean();
1156 static int parse_unattach_list (BUFFER *buf, BUFFER *s, LIST **ldata, BUFFER *err) {
1158 LIST *lp, *lastp, *newlp;
1164 mutt_extract_token (buf, s, 0);
1166 if (!str_casecmp(buf->data, "any"))
1167 tmp = str_dup("*/.*");
1168 else if (!str_casecmp(buf->data, "none"))
1169 tmp = str_dup("cheap_hack/this_should_never_match");
1171 tmp = str_dup(buf->data);
1173 if ((minor = strchr(tmp, '/'))) {
1179 major = mutt_check_mime_type(tmp);
1181 /* We must do our own walk here because remove_from_list() will only
1182 * remove the LIST->data, not anything pointed to by the LIST->data. */
1184 for(lp = *ldata; lp; ) {
1185 a = (ATTACH_MATCH *)lp->data;
1186 debug_print(5, ("parse_unattach_list: check %s/%s [%d] : %s/%s [%d]\n",
1187 a->major, a->minor, a->major_int, tmp, minor, major));
1188 if (a->major_int == major && !str_casecmp(minor, a->minor)) {
1189 debug_print(5, ("parse_unattach_list: removed %s/%s [%d]\n",
1190 a->major, a->minor, a->major_int));
1191 regfree(&a->minor_rx);
1192 mem_free(&a->major);
1194 /* Relink backward */
1196 lastp->next = lp->next;
1201 mem_free(&lp->data); /* same as a */
1211 while (MoreArgs (s));
1214 _attachments_clean();
1218 static int print_attach_list (LIST *lp, char op, const char *name) {
1220 printf("attachments %c%s %s/%s\n", op, name,
1221 ((ATTACH_MATCH *)lp->data)->major,
1222 ((ATTACH_MATCH *)lp->data)->minor);
1229 static int parse_attachments (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) {
1233 mutt_extract_token(buf, s, 0);
1234 if (!buf->data || *buf->data == '\0') {
1235 strfcpy(err->data, _("attachments: no disposition"), err->dsize);
1239 category = buf->data;
1245 printf("\nCurrent attachments settings:\n\n");
1246 print_attach_list(AttachAllow, '+', "A");
1247 print_attach_list(AttachExclude, '-', "A");
1248 print_attach_list(InlineAllow, '+', "I");
1249 print_attach_list(InlineExclude, '-', "I");
1250 set_option (OPTFORCEREDRAWINDEX);
1251 set_option (OPTFORCEREDRAWPAGER);
1252 mutt_any_key_to_continue (NULL);
1256 if (op != '+' && op != '-') {
1260 if (!str_ncasecmp(category, "attachment", strlen(category))) {
1262 listp = &AttachAllow;
1264 listp = &AttachExclude;
1266 else if (!str_ncasecmp(category, "inline", strlen(category))) {
1268 listp = &InlineAllow;
1270 listp = &InlineExclude;
1272 strfcpy(err->data, _("attachments: invalid disposition"), err->dsize);
1276 return parse_attach_list(buf, s, listp, err);
1279 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) {
1283 mutt_extract_token(buf, s, 0);
1284 if (!buf->data || *buf->data == '\0') {
1285 strfcpy(err->data, _("unattachments: no disposition"), err->dsize);
1291 if (op != '+' && op != '-') {
1295 if (!str_ncasecmp(p, "attachment", strlen(p))) {
1297 listp = &AttachAllow;
1299 listp = &AttachExclude;
1301 else if (!str_ncasecmp(p, "inline", strlen(p))) {
1303 listp = &InlineAllow;
1305 listp = &InlineExclude;
1308 strfcpy(err->data, _("unattachments: invalid disposition"), err->dsize);
1312 return parse_unattach_list(buf, s, listp, err);
1315 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
1319 mutt_extract_token (buf, s, 0);
1320 remove_from_rx_list (&SubscribedLists, buf->data);
1321 remove_from_rx_list (&MailLists, buf->data);
1323 if (str_cmp (buf->data, "*") &&
1324 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1327 while (MoreArgs (s));
1332 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1336 mutt_extract_token (buf, s, 0);
1337 remove_from_rx_list (&UnMailLists, buf->data);
1338 remove_from_rx_list (&UnSubscribedLists, buf->data);
1340 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1342 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1345 while (MoreArgs (s));
1350 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1354 mutt_extract_token (buf, s, 0);
1355 remove_from_rx_list (&SubscribedLists, buf->data);
1357 if (str_cmp (buf->data, "*") &&
1358 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1361 while (MoreArgs (s));
1366 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
1369 ALIAS *tmp, *last = NULL;
1372 mutt_extract_token (buf, s, 0);
1374 if (str_cmp ("*", buf->data) == 0) {
1375 if (CurrentMenu == MENU_ALIAS) {
1376 for (tmp = Aliases; tmp; tmp = tmp->next)
1378 set_option (OPTFORCEREDRAWINDEX);
1381 mutt_free_alias (&Aliases);
1385 for (tmp = Aliases; tmp; tmp = tmp->next) {
1386 if (str_casecmp (buf->data, tmp->name) == 0) {
1387 if (CurrentMenu == MENU_ALIAS) {
1389 set_option (OPTFORCEREDRAWINDEX);
1394 last->next = tmp->next;
1396 Aliases = tmp->next;
1398 mutt_free_alias (&tmp);
1404 while (MoreArgs (s));
1408 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
1411 ALIAS *tmp = Aliases;
1415 if (!MoreArgs (s)) {
1416 strfcpy (err->data, _("alias: no address"), err->dsize);
1420 mutt_extract_token (buf, s, 0);
1422 debug_print (2, ("first token is '%s'.\n", buf->data));
1424 /* check to see if an alias with this name already exists */
1425 for (; tmp; tmp = tmp->next) {
1426 if (!str_casecmp (tmp->name, buf->data))
1432 /* create a new alias */
1433 tmp = (ALIAS *) mem_calloc (1, sizeof (ALIAS));
1435 tmp->name = str_dup (buf->data);
1436 /* give the main addressbook code a chance */
1437 if (CurrentMenu == MENU_ALIAS)
1438 set_option (OPTMENUCALLER);
1441 /* override the previous value */
1442 rfc822_free_address (&tmp->addr);
1443 if (CurrentMenu == MENU_ALIAS)
1444 set_option (OPTFORCEREDRAWINDEX);
1447 mutt_extract_token (buf, s,
1448 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1449 debug_print (2, ("second token is '%s'.\n", buf->data));
1450 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1455 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1456 snprintf (err->data, err->dsize,
1457 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1461 if (DebugLevel >= 2) {
1464 /* A group is terminated with an empty address, so check a->mailbox */
1465 for (a = tmp->addr; a && a->mailbox; a = a->next) {
1467 debug_print (2, ("%s\n", a->mailbox));
1469 debug_print (2, ("group %s\n", a->mailbox));
1477 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
1480 LIST *tmp = UserHeader;
1485 mutt_extract_token (buf, s, 0);
1486 if (str_cmp ("*", buf->data) == 0)
1487 mutt_free_list (&UserHeader);
1492 l = str_len (buf->data);
1493 if (buf->data[l - 1] == ':')
1497 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1498 && tmp->data[l] == ':') {
1501 last->next = tmp->next;
1503 UserHeader = tmp->next;
1506 mutt_free_list (&ptr);
1515 while (MoreArgs (s));
1519 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
1526 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1527 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1528 strfcpy (err->data, _("invalid header field"), err->dsize);
1531 keylen = p - buf->data + 1;
1534 for (tmp = UserHeader;; tmp = tmp->next) {
1535 /* see if there is already a field by this name */
1536 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1537 /* replace the old value */
1538 mem_free (&tmp->data);
1539 tmp->data = buf->data;
1540 memset (buf, 0, sizeof (BUFFER));
1546 tmp->next = mutt_new_list ();
1550 tmp = mutt_new_list ();
1553 tmp->data = buf->data;
1554 memset (buf, 0, sizeof (BUFFER));
1559 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1560 char* errbuf, size_t errlen) {
1563 if (str_ncmp ("reverse-", s, 8) == 0) {
1565 flags = SORT_REVERSE;
1568 if (str_ncmp ("last-", s, 5) == 0) {
1573 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1575 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1579 *((short*) dst->data) = i | flags;
1583 /* if additional data more == 1, we want to resolve synonyms */
1584 static void mutt_set_default (const char* name, void* p, unsigned long more) {
1585 char buf[LONG_STRING];
1586 struct option_t* ptr = (struct option_t*) p;
1588 if (DTYPE (ptr->type) == DT_SYN) {
1591 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1593 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_from_string)
1595 mutt_option_value (ptr->option, buf, sizeof (buf));
1596 if (str_len (ptr->init) == 0 && buf && *buf)
1597 ptr->init = str_dup (buf);
1600 static struct option_t* add_option (const char* name, const char* init,
1601 short type, short dodup) {
1602 struct option_t* option = mem_calloc (1, sizeof (struct option_t));
1604 debug_print (1, ("adding $%s\n", name));
1606 option->option = str_dup (name);
1607 option->type = type;
1609 option->init = dodup ? str_dup (init) : (char*) init;
1613 /* creates new option_t* of type DT_USER for $user_ var */
1614 static struct option_t* add_user_option (const char* name) {
1615 return (add_option (name, NULL, DT_USER, 1));
1618 /* free()'s option_t* */
1619 static void del_option (void* p) {
1620 struct option_t* ptr = (struct option_t*) p;
1621 char* s = (char*) ptr->data;
1622 debug_print (1, ("removing option '%s' from table\n", NONULL (ptr->option)));
1623 mem_free (&ptr->option);
1625 mem_free (&ptr->init);
1629 static int init_expand (char** dst, struct option_t* src) {
1635 if (DTYPE(src->type) == DT_STR ||
1636 DTYPE(src->type) == DT_PATH) {
1637 /* only expand for string as it's the only place where
1638 * we want to expand vars right now */
1639 if (src->init && *src->init) {
1640 memset (&token, 0, sizeof (BUFFER));
1641 memset (&in, 0, sizeof (BUFFER));
1642 len = str_len (src->init) + 2;
1643 in.data = mem_malloc (len+1);
1644 snprintf (in.data, len, "\"%s\"", src->init);
1647 mutt_extract_token (&token, &in, 0);
1648 if (token.data && *token.data)
1649 *dst = str_dup (token.data);
1651 *dst = str_dup ("");
1652 mem_free (&in.data);
1653 mem_free (&token.data);
1655 *dst = str_dup ("");
1657 /* for non-string: take value as is */
1658 *dst = str_dup (src->init);
1662 /* if additional data more == 1, we want to resolve synonyms */
1663 static void mutt_restore_default (const char* name, void* p,
1664 unsigned long more) {
1665 char errbuf[STRING];
1666 struct option_t* ptr = (struct option_t*) p;
1669 if (DTYPE (ptr->type) == DT_SYN) {
1672 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1676 if (FuncTable[DTYPE (ptr->type)].opt_from_string) {
1677 init_expand (&init, ptr);
1678 if (!FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, init, errbuf,
1680 if (!option (OPTNOCURSES))
1682 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1683 "Please report this error: \"%s\"\n"),
1684 ptr->option, NONULL (init), errbuf);
1690 if (ptr->flags & R_INDEX)
1691 set_option (OPTFORCEREDRAWINDEX);
1692 if (ptr->flags & R_PAGER)
1693 set_option (OPTFORCEREDRAWPAGER);
1694 if (ptr->flags & R_RESORT_SUB)
1695 set_option (OPTSORTSUBTHREADS);
1696 if (ptr->flags & R_RESORT)
1697 set_option (OPTNEEDRESORT);
1698 if (ptr->flags & R_RESORT_INIT)
1699 set_option (OPTRESORTINIT);
1700 if (ptr->flags & R_TREE)
1701 set_option (OPTREDRAWTREE);
1704 /* check whether value for $dsn_return would be valid */
1705 static int check_dsn_return (const char* option, unsigned long p,
1706 char* errbuf, size_t errlen) {
1707 char* val = (char*) p;
1708 if (val && *val && str_ncmp (val, "hdrs", 4) != 0 &&
1709 str_ncmp (val, "full", 4) != 0) {
1711 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1717 /* check whether value for $dsn_notify would be valid */
1718 static int check_dsn_notify (const char* option, unsigned long p,
1719 char* errbuf, size_t errlen) {
1720 list2_t* list = NULL;
1722 char* val = (char*) p;
1726 list = list_from_str (val, ",");
1727 if (list_empty (list))
1730 for (i = 0; i < list->length; i++)
1731 if (str_ncmp (list->data[i], "never", 5) != 0 &&
1732 str_ncmp (list->data[i], "failure", 7) != 0 &&
1733 str_ncmp (list->data[i], "delay", 5) != 0 &&
1734 str_ncmp (list->data[i], "success", 7) != 0) {
1736 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1737 (char*) list->data[i], "dsn_notify");
1741 list_del (&list, (list_del_t*) _mem_free);
1745 static int check_num (const char* option, unsigned long p,
1746 char* errbuf, size_t errlen) {
1749 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1756 static int check_debug (const char* option, unsigned long p,
1757 char* errbuf, size_t errlen) {
1758 if ((int) p <= DEBUG_MAX_LEVEL &&
1759 (int) p >= DEBUG_MIN_LEVEL)
1763 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1768 static int check_history (const char* option, unsigned long p,
1769 char* errbuf, size_t errlen) {
1770 if (!check_num ("history", p, errbuf, errlen))
1772 mutt_init_history ();
1776 static int check_special (const char* name, unsigned long val,
1777 char* errbuf, size_t errlen) {
1780 for (i = 0; SpecialVars[i].name; i++) {
1781 if (str_cmp (SpecialVars[i].name, name) == 0) {
1782 return (SpecialVars[i].check (SpecialVars[i].name,
1783 val, errbuf, errlen));
1789 static const struct mapping_t* get_sortmap (struct option_t* option) {
1790 const struct mapping_t* map = NULL;
1792 switch (option->type & DT_SUBTYPE_MASK) {
1794 map = SortAliasMethods;
1796 case DT_SORT_BROWSER:
1797 map = SortBrowserMethods;
1800 if ((WithCrypto & APPLICATION_PGP))
1801 map = SortKeyMethods;
1804 map = SortAuxMethods;
1813 #define CHECK_PAGER \
1814 if ((CurrentMenu == MENU_PAGER) && \
1815 (!option || (option->flags & R_RESORT))) \
1817 snprintf (err->data, err->dsize, \
1818 _("Not available in this menu.")); \
1822 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1825 int query, unset, inv, reset, r = 0;
1826 struct option_t* option = NULL;
1828 while (MoreArgs (s)) {
1829 /* reset state variables */
1831 unset = data & M_SET_UNSET;
1832 inv = data & M_SET_INV;
1833 reset = data & M_SET_RESET;
1835 if (*s->dptr == '?') {
1839 else if (str_ncmp ("no", s->dptr, 2) == 0) {
1843 else if (str_ncmp ("inv", s->dptr, 3) == 0) {
1847 else if (*s->dptr == '&') {
1852 /* get the variable name */
1853 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1855 /* resolve synonyms */
1856 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1857 DTYPE (option->type == DT_SYN)) {
1858 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1859 syn_add (newopt, option);
1863 /* see if we need to add $user_ var */
1864 if (!option && ascii_strncmp ("user_", tmp->data, 5) == 0) {
1865 /* there's no option named like this yet so only add one
1866 * if the action isn't any of: reset, unset, query */
1867 if (!(reset || unset || query || *s->dptr != '=')) {
1868 debug_print (1, ("adding user option '%s'\n", tmp->data));
1869 option = add_user_option (tmp->data);
1870 hash_insert (ConfigOptions, option->option, option, 0);
1874 if (!option && !(reset && str_cmp ("all", tmp->data) == 0)) {
1875 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1881 if (query || unset || inv) {
1882 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1886 if (s && *s->dptr == '=') {
1887 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1891 if (!str_cmp ("all", tmp->data)) {
1892 if (CurrentMenu == MENU_PAGER) {
1893 snprintf (err->data, err->dsize, _("Not available in this menu."));
1896 hash_map (ConfigOptions, mutt_restore_default, 1);
1897 set_option (OPTFORCEREDRAWINDEX);
1898 set_option (OPTFORCEREDRAWPAGER);
1899 set_option (OPTSORTSUBTHREADS);
1900 set_option (OPTNEEDRESORT);
1901 set_option (OPTRESORTINIT);
1902 set_option (OPTREDRAWTREE);
1905 else if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1906 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1911 mutt_restore_default (NULL, option, 1);
1914 else if (DTYPE (option->type) == DT_BOOL) {
1915 /* XXX this currently ignores the function table
1916 * as we don't get invert and stuff into it */
1917 if (s && *s->dptr == '=') {
1918 if (unset || inv || query) {
1919 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1924 mutt_extract_token (tmp, s, 0);
1925 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1927 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1930 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1936 bool_to_string (err->data, err->dsize, option);
1942 unset_option (option->data);
1944 toggle_option (option->data);
1946 set_option (option->data);
1948 else if (DTYPE (option->type) == DT_STR ||
1949 DTYPE (option->type) == DT_PATH ||
1950 DTYPE (option->type) == DT_ADDR ||
1951 DTYPE (option->type) == DT_MAGIC ||
1952 DTYPE (option->type) == DT_NUM ||
1953 DTYPE (option->type) == DT_SORT ||
1954 DTYPE (option->type) == DT_RX ||
1955 DTYPE (option->type) == DT_USER ||
1956 DTYPE (option->type) == DT_SYS) {
1958 /* XXX maybe we need to get unset into handlers? */
1959 if (DTYPE (option->type) == DT_STR ||
1960 DTYPE (option->type) == DT_PATH ||
1961 DTYPE (option->type) == DT_ADDR ||
1962 DTYPE (option->type) == DT_USER ||
1963 DTYPE (option->type) == DT_SYS) {
1966 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1967 snprintf (err->data, err->dsize, _("$%s is read-only"),
1971 } else if (DTYPE (option->type) == DT_ADDR)
1972 rfc822_free_address ((ADDRESS **) option->data);
1973 else if (DTYPE (option->type) == DT_USER)
1974 /* to unset $user_ means remove */
1975 hash_delete (ConfigOptions, option->option,
1976 option, del_option);
1978 mem_free ((void *) option->data);
1983 if (query || *s->dptr != '=') {
1984 FuncTable[DTYPE (option->type)].opt_to_string
1985 (err->data, err->dsize, option);
1989 /* the $muttng_ variables are read-only */
1990 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1991 snprintf (err->data, err->dsize, _("$%s is read-only"),
1998 mutt_extract_token (tmp, s, 0);
1999 if (!FuncTable[DTYPE (option->type)].opt_from_string
2000 (option, tmp->data, err->data, err->dsize))
2004 else if (DTYPE (option->type) == DT_QUAD) {
2007 quad_to_string (err->data, err->dsize, option);
2011 if (*s->dptr == '=') {
2014 mutt_extract_token (tmp, s, 0);
2015 if (ascii_strcasecmp ("yes", tmp->data) == 0)
2016 set_quadoption (option->data, M_YES);
2017 else if (ascii_strcasecmp ("no", tmp->data) == 0)
2018 set_quadoption (option->data, M_NO);
2019 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
2020 set_quadoption (option->data, M_ASKYES);
2021 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
2022 set_quadoption (option->data, M_ASKNO);
2024 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
2025 tmp->data, option->option);
2032 toggle_quadoption (option->data);
2034 set_quadoption (option->data, M_NO);
2036 set_quadoption (option->data, M_YES);
2040 snprintf (err->data, err->dsize, _("%s: unknown type"),
2046 if (option->flags & R_INDEX)
2047 set_option (OPTFORCEREDRAWINDEX);
2048 if (option->flags & R_PAGER)
2049 set_option (OPTFORCEREDRAWPAGER);
2050 if (option->flags & R_RESORT_SUB)
2051 set_option (OPTSORTSUBTHREADS);
2052 if (option->flags & R_RESORT)
2053 set_option (OPTNEEDRESORT);
2054 if (option->flags & R_RESORT_INIT)
2055 set_option (OPTRESORTINIT);
2056 if (option->flags & R_TREE)
2057 set_option (OPTREDRAWTREE);
2064 /* reads the specified initialization file. returns -1 if errors were found
2065 so that we can pause to let the user know... */
2066 static int source_rc (const char *rcfile, BUFFER * err)
2069 int line = 0, rc = 0, conv = 0;
2071 char *linebuf = NULL;
2072 char *currentline = NULL;
2076 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
2078 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
2079 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
2083 memset (&token, 0, sizeof (token));
2084 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
2085 conv = ConfigCharset && (*ConfigCharset) && Charset;
2087 currentline = str_dup (linebuf);
2090 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
2093 currentline = linebuf;
2098 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
2099 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
2100 if (--rc < -MAXERRS) {
2102 mem_free (¤tline);
2111 mem_free (¤tline);
2113 mem_free (&token.data);
2114 mem_free (&linebuf);
2117 mutt_wait_filter (pid);
2119 /* the muttrc source keyword */
2120 snprintf (err->data, err->dsize,
2121 rc >= -MAXERRS ? _("source: errors in %s")
2122 : _("source: reading aborted due too many errors in %s"),
2131 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
2134 char path[_POSIX_PATH_MAX];
2138 if (mutt_extract_token (tmp, s, 0) != 0) {
2139 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
2143 strfcpy (path, tmp->data, sizeof (path));
2144 mutt_expand_path (path, sizeof (path));
2146 rc += source_rc (path, err);
2148 while (MoreArgs (s));
2150 return ((rc < 0) ? -1 : 0);
2153 /* line command to execute
2155 token scratch buffer to be used by parser. caller should free
2156 token->data when finished. the reason for this variable is
2157 to avoid having to allocate and deallocate a lot of memory
2158 if we are parsing many lines. the caller can pass in the
2159 memory to use, which avoids having to create new space for
2160 every call to this function.
2162 err where to write error messages */
2163 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
2168 memset (&expn, 0, sizeof (expn));
2169 expn.data = expn.dptr = line;
2170 expn.dsize = str_len (line);
2174 debug_print (1, ("expand '%s'\n", line));
2177 while (*expn.dptr) {
2178 if (*expn.dptr == '#')
2179 break; /* rest of line is a comment */
2180 if (*expn.dptr == ';') {
2184 mutt_extract_token (token, &expn, 0);
2185 for (i = 0; Commands[i].name; i++) {
2186 if (!str_cmp (token->data, Commands[i].name)) {
2187 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
2192 if (!Commands[i].name) {
2193 snprintf (err->data, err->dsize, _("%s: unknown command"),
2194 NONULL (token->data));
2201 mem_free (&expn.data);
2206 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
2207 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
2208 /* initial string that starts completion. No telling how much crap
2209 * the user has typed so far. Allocate LONG_STRING just to be sure! */
2210 char User_typed[LONG_STRING] = { 0 };
2212 int Num_matched = 0; /* Number of matches for completion */
2213 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
2214 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
2216 /* helper function for completion. Changes the dest buffer if
2217 necessary/possible to aid completion.
2218 dest == completion result gets here.
2219 src == candidate for completion.
2220 try == user entered data for completion.
2221 len == length of dest buffer.
2223 static void candidate (char *dest, char *try, const char *src, int len)
2227 if (strstr (src, try) == src) {
2228 Matches[Num_matched++] = src;
2230 strfcpy (dest, src, len);
2232 for (l = 0; src[l] && src[l] == dest[l]; l++);
2238 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
2242 int spaces; /* keep track of the number of leading spaces on the line */
2245 spaces = buffer - pt;
2247 pt = buffer + pos - spaces;
2248 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2251 if (pt == buffer) { /* complete cmd */
2252 /* first TAB. Collect all the matches */
2255 strfcpy (User_typed, pt, sizeof (User_typed));
2256 memset (Matches, 0, sizeof (Matches));
2257 memset (Completed, 0, sizeof (Completed));
2258 for (num = 0; Commands[num].name; num++)
2259 candidate (Completed, User_typed, Commands[num].name,
2260 sizeof (Completed));
2261 Matches[Num_matched++] = User_typed;
2263 /* All matches are stored. Longest non-ambiguous string is ""
2264 * i.e. dont change 'buffer'. Fake successful return this time */
2265 if (User_typed[0] == 0)
2269 if (Completed[0] == 0 && User_typed[0])
2272 /* Num_matched will _always_ be atleast 1 since the initial
2273 * user-typed string is always stored */
2274 if (numtabs == 1 && Num_matched == 2)
2275 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
2276 else if (numtabs > 1 && Num_matched > 2)
2277 /* cycle thru all the matches */
2278 snprintf (Completed, sizeof (Completed), "%s",
2279 Matches[(numtabs - 2) % Num_matched]);
2281 /* return the completed command */
2282 strncpy (buffer, Completed, len - spaces);
2284 else if (!str_ncmp (buffer, "set", 3)
2285 || !str_ncmp (buffer, "unset", 5)
2286 || !str_ncmp (buffer, "reset", 5)
2287 || !str_ncmp (buffer, "toggle", 6)) { /* complete variables */
2288 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
2291 /* loop through all the possible prefixes (no, inv, ...) */
2292 if (!str_ncmp (buffer, "set", 3)) {
2293 for (num = 0; prefixes[num]; num++) {
2294 if (!str_ncmp (pt, prefixes[num], str_len (prefixes[num]))) {
2295 pt += str_len (prefixes[num]);
2301 /* first TAB. Collect all the matches */
2304 strfcpy (User_typed, pt, sizeof (User_typed));
2305 memset (Matches, 0, sizeof (Matches));
2306 memset (Completed, 0, sizeof (Completed));
2307 for (num = 0; MuttVars[num].option; num++)
2308 candidate (Completed, User_typed, MuttVars[num].option,
2309 sizeof (Completed));
2310 Matches[Num_matched++] = User_typed;
2312 /* All matches are stored. Longest non-ambiguous string is ""
2313 * i.e. dont change 'buffer'. Fake successful return this time */
2314 if (User_typed[0] == 0)
2318 if (Completed[0] == 0 && User_typed[0])
2321 /* Num_matched will _always_ be atleast 1 since the initial
2322 * user-typed string is always stored */
2323 if (numtabs == 1 && Num_matched == 2)
2324 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
2325 else if (numtabs > 1 && Num_matched > 2)
2326 /* cycle thru all the matches */
2327 snprintf (Completed, sizeof (Completed), "%s",
2328 Matches[(numtabs - 2) % Num_matched]);
2330 strncpy (pt, Completed, buffer + len - pt - spaces);
2332 else if (!str_ncmp (buffer, "exec", 4)) {
2333 struct binding_t *menu = km_get_table (CurrentMenu);
2335 if (!menu && CurrentMenu != MENU_PAGER)
2339 /* first TAB. Collect all the matches */
2342 strfcpy (User_typed, pt, sizeof (User_typed));
2343 memset (Matches, 0, sizeof (Matches));
2344 memset (Completed, 0, sizeof (Completed));
2345 for (num = 0; menu[num].name; num++)
2346 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
2347 /* try the generic menu */
2348 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2350 for (num = 0; menu[num].name; num++)
2351 candidate (Completed, User_typed, menu[num].name,
2352 sizeof (Completed));
2354 Matches[Num_matched++] = User_typed;
2356 /* All matches are stored. Longest non-ambiguous string is ""
2357 * i.e. dont change 'buffer'. Fake successful return this time */
2358 if (User_typed[0] == 0)
2362 if (Completed[0] == 0 && User_typed[0])
2365 /* Num_matched will _always_ be atleast 1 since the initial
2366 * user-typed string is always stored */
2367 if (numtabs == 1 && Num_matched == 2)
2368 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
2369 else if (numtabs > 1 && Num_matched > 2)
2370 /* cycle thru all the matches */
2371 snprintf (Completed, sizeof (Completed), "%s",
2372 Matches[(numtabs - 2) % Num_matched]);
2374 strncpy (pt, Completed, buffer + len - pt - spaces);
2382 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2384 char var[STRING], *pt = buffer;
2386 struct option_t* option = NULL;
2392 spaces = buffer - pt;
2394 pt = buffer + pos - spaces;
2395 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2397 pt++; /* move past the space */
2398 if (*pt == '=') /* abort if no var before the '=' */
2401 if (str_ncmp (buffer, "set", 3) == 0) {
2402 strfcpy (var, pt, sizeof (var));
2403 /* ignore the trailing '=' when comparing */
2404 var[str_len (var) - 1] = 0;
2405 if (!(option = hash_find (ConfigOptions, var)))
2406 return 0; /* no such variable. */
2408 char tmp[LONG_STRING], tmp2[LONG_STRING];
2410 size_t dlen = buffer + len - pt - spaces;
2411 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2415 if ((DTYPE (option->type) == DT_STR) ||
2416 (DTYPE (option->type) == DT_PATH) ||
2417 (DTYPE (option->type) == DT_RX)) {
2418 strfcpy (tmp, NONULL (*((char **) option->data)), sizeof (tmp));
2419 if (DTYPE (option->type) == DT_PATH)
2420 mutt_pretty_mailbox (tmp);
2422 else if (DTYPE (option->type) == DT_ADDR) {
2423 rfc822_write_address (tmp, sizeof (tmp),
2424 *((ADDRESS **) option->data), 0);
2426 else if (DTYPE (option->type) == DT_QUAD)
2427 strfcpy (tmp, vals[quadoption (option->data)], sizeof (tmp));
2428 else if (DTYPE (option->type) == DT_NUM)
2429 snprintf (tmp, sizeof (tmp), "%d", (*((short *) option->data)));
2430 else if (DTYPE (option->type) == DT_SORT) {
2431 const struct mapping_t *map;
2434 switch (option->type & DT_SUBTYPE_MASK) {
2436 map = SortAliasMethods;
2438 case DT_SORT_BROWSER:
2439 map = SortBrowserMethods;
2442 if ((WithCrypto & APPLICATION_PGP))
2443 map = SortKeyMethods;
2452 mutt_getnamebyvalue (*((short *) option->data) & SORT_MASK,
2454 snprintf (tmp, sizeof (tmp), "%s%s%s",
2455 (*((short *) option->data) & SORT_REVERSE) ?
2457 (*((short *) option->data) & SORT_LAST) ? "last-" :
2460 else if (DTYPE (option->type) == DT_MAGIC) {
2462 switch (DefaultMagic) {
2478 strfcpy (tmp, p, sizeof (tmp));
2480 else if (DTYPE (option->type) == DT_BOOL)
2481 strfcpy (tmp, option (option->data) ? "yes" : "no",
2486 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
2487 if (*s == '\\' || *s == '"')
2493 strfcpy (tmp, pt, sizeof (tmp));
2494 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2502 /* Implement the -Q command line flag */
2503 int mutt_query_variables (LIST * queries)
2507 char errbuff[STRING];
2508 char command[STRING];
2512 memset (&err, 0, sizeof (err));
2513 memset (&token, 0, sizeof (token));
2516 err.dsize = sizeof (errbuff);
2518 for (p = queries; p; p = p->next) {
2519 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2520 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2521 fprintf (stderr, "%s\n", err.data);
2522 mem_free (&token.data);
2525 printf ("%s\n", err.data);
2528 mem_free (&token.data);
2532 const char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2536 for (i = 0; map[i].name; i++)
2537 if (map[i].value == val)
2538 return (map[i].name);
2542 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2546 for (i = 0; map[i].name; i++)
2547 if (ascii_strcasecmp (map[i].name, name) == 0)
2548 return (map[i].value);
2552 static int mutt_execute_commands (LIST * p)
2555 char errstr[SHORT_STRING];
2557 memset (&err, 0, sizeof (err));
2559 err.dsize = sizeof (errstr);
2560 memset (&token, 0, sizeof (token));
2561 for (; p; p = p->next) {
2562 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2563 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2564 mem_free (&token.data);
2568 mem_free (&token.data);
2572 void mutt_init (int skip_sys_rc, LIST * commands)
2575 struct utsname utsname;
2577 char buffer[STRING], error[STRING];
2578 int i, default_rc = 0, need_pause = 0;
2581 memset (&err, 0, sizeof (err));
2583 err.dsize = sizeof (error);
2585 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2586 * to have some room for $user_ vars */
2587 ConfigOptions = hash_create (sizeof (MuttVars) * 3);
2588 for (i = 0; MuttVars[i].option; i++) {
2589 if (DTYPE (MuttVars[i].type) != DT_SYS)
2590 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2592 hash_insert (ConfigOptions, MuttVars[i].option,
2593 add_option (MuttVars[i].option, MuttVars[i].init,
2598 * XXX - use something even more difficult to predict?
2600 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2601 "\033]9;%ld\a", (long) time (NULL));
2603 /* on one of the systems I use, getcwd() does not return the same prefix
2604 as is listed in the passwd file */
2605 if ((p = getenv ("HOME")))
2606 Homedir = str_dup (p);
2608 /* Get some information about the user */
2609 if ((pw = getpwuid (getuid ()))) {
2612 Username = str_dup (pw->pw_name);
2614 Homedir = str_dup (pw->pw_dir);
2616 Realname = str_dup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2617 Shell = str_dup (pw->pw_shell);
2623 fputs (_("unable to determine home directory"), stderr);
2626 if ((p = getenv ("USER")))
2627 Username = str_dup (p);
2630 fputs (_("unable to determine username"), stderr);
2633 Shell = str_dup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2636 debug_start(Homedir);
2638 /* And about the host... */
2640 /* some systems report the FQDN instead of just the hostname */
2641 if ((p = strchr (utsname.nodename, '.'))) {
2642 Hostname = str_substrdup (utsname.nodename, p);
2644 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2647 Hostname = str_dup (utsname.nodename);
2650 #define DOMAIN buffer
2651 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2652 Fqdn = str_dup ("@");
2655 if (*DOMAIN != '@') {
2656 Fqdn = mem_malloc (str_len (DOMAIN) + str_len (Hostname) + 2);
2657 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2660 Fqdn = str_dup (NONULL (Hostname));
2667 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2669 fgets (buffer, sizeof (buffer), f);
2673 while (*q && !isspace(*q))
2676 NewsServer = str_dup (p);
2680 if ((p = getenv ("NNTPSERVER")))
2681 NewsServer = str_dup (p);
2684 if ((p = getenv ("MAIL")))
2685 Spoolfile = str_dup (p);
2686 else if ((p = getenv ("MAILDIR")))
2687 Spoolfile = str_dup (p);
2690 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2692 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
2694 Spoolfile = str_dup (buffer);
2697 if ((p = getenv ("MAILCAPS")))
2698 MailcapPath = str_dup (p);
2700 /* Default search path from RFC1524 */
2702 str_dup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2703 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2706 Tempdir = str_dup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2708 p = getenv ("VISUAL");
2710 p = getenv ("EDITOR");
2714 Editor = str_dup (p);
2715 Visual = str_dup (p);
2717 if ((p = getenv ("REPLYTO")) != NULL) {
2720 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2722 memset (&buf, 0, sizeof (buf));
2723 buf.data = buf.dptr = buffer;
2724 buf.dsize = str_len (buffer);
2726 memset (&token, 0, sizeof (token));
2727 parse_my_hdr (&token, &buf, 0, &err);
2728 mem_free (&token.data);
2731 if ((p = getenv ("EMAIL")) != NULL)
2732 From = rfc822_parse_adrlist (NULL, p);
2734 mutt_set_langinfo_charset ();
2735 mutt_set_charset (Charset);
2738 /* Set standard defaults */
2739 hash_map (ConfigOptions, mutt_set_default, 0);
2740 hash_map (ConfigOptions, mutt_restore_default, 0);
2742 CurrentMenu = MENU_MAIN;
2745 #ifndef LOCALES_HACK
2746 /* Do we have a locale definition? */
2747 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2748 ((p = getenv ("LANG")) != NULL && p[0]) ||
2749 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2750 set_option (OPTLOCALES);
2754 /* Unset suspend by default if we're the session leader */
2755 if (getsid (0) == getpid ())
2756 unset_option (OPTSUSPEND);
2759 mutt_init_history ();
2768 * When changing the code which looks for a configuration file,
2769 * please also change the corresponding code in muttbug.sh.in.
2779 snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2781 if (access (buffer, F_OK) == -1)
2783 snprintf (buffer, sizeof (buffer), "%s/.muttngrc", NONULL (Homedir));
2784 if (access (buffer, F_OK) == -1)
2786 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s",
2787 NONULL (Homedir), MUTT_VERSION);
2788 if (access (buffer, F_OK) == -1)
2790 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc",
2794 Muttrc = str_dup (buffer);
2797 strfcpy (buffer, Muttrc, sizeof (buffer));
2799 mutt_expand_path (buffer, sizeof (buffer));
2800 Muttrc = str_dup (buffer);
2802 mem_free (&AliasFile);
2803 AliasFile = str_dup (NONULL (Muttrc));
2805 /* Process the global rc file if it exists and the user hasn't explicity
2806 requested not to via "-n". */
2808 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2810 if (access (buffer, F_OK) == -1)
2811 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", SYSCONFDIR);
2812 if (access (buffer, F_OK) == -1)
2813 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR,
2815 if (access (buffer, F_OK) == -1)
2816 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2817 if (access (buffer, F_OK) != -1) {
2818 if (source_rc (buffer, &err) != 0) {
2819 fputs (err.data, stderr);
2820 fputc ('\n', stderr);
2826 /* Read the user's initialization file. */
2827 if (access (Muttrc, F_OK) != -1) {
2828 if (!option (OPTNOCURSES))
2830 if (source_rc (Muttrc, &err) != 0) {
2831 fputs (err.data, stderr);
2832 fputc ('\n', stderr);
2836 else if (!default_rc) {
2837 /* file specified by -F does not exist */
2838 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2839 mutt_endwin (buffer);
2843 if (mutt_execute_commands (commands) != 0)
2846 /* warn about synonym variables */
2847 if (!list_empty(Synonyms)) {
2849 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2850 for (i = 0; i < Synonyms->length; i++) {
2851 struct option_t* newopt = NULL, *oldopt = NULL;
2852 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2853 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2854 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2855 oldopt ? NONULL (oldopt->option) : "",
2856 newopt ? NONULL (newopt->option) : "",
2857 NONULL(((syn_t*) Synonyms->data[i])->f),
2858 ((syn_t*) Synonyms->data[i])->l);
2860 fprintf (stderr, _("Warning: synonym variables are scheduled"
2861 " for removal.\n"));
2862 list_del (&Synonyms, syn_del);
2866 if (need_pause && !option (OPTNOCURSES)) {
2867 if (mutt_any_key_to_continue (NULL) == -1)
2872 set_option (OPTWEED); /* turn weeding on by default */
2876 int mutt_get_hook_type (const char *name)
2878 struct command_t *c;
2880 for (c = Commands; c->name; c++)
2881 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2886 /* compare two option_t*'s for sorting -t/-T output */
2887 static int opt_cmp (const void* a, const void* b) {
2888 return (str_cmp ((*(struct option_t**) a)->option,
2889 (*(struct option_t**) b)->option));
2892 /* callback for hash_map() to put all non-synonym vars into list */
2893 static void opt_sel_full (const char* key, void* data,
2894 unsigned long more) {
2895 list2_t** l = (list2_t**) more;
2896 struct option_t* option = (struct option_t*) data;
2898 if (DTYPE (option->type) == DT_SYN)
2900 list_push_back (l, option);
2903 /* callback for hash_map() to put all changed non-synonym vars into list */
2904 static void opt_sel_diff (const char* key, void* data,
2905 unsigned long more) {
2906 list2_t** l = (list2_t**) more;
2907 struct option_t* option = (struct option_t*) data;
2908 char buf[LONG_STRING];
2910 if (DTYPE (option->type) == DT_SYN)
2913 mutt_option_value (option->option, buf, sizeof (buf));
2914 if (str_cmp (buf, option->init) != 0)
2915 list_push_back (l, option);
2918 /* dump out the value of all the variables we have */
2919 int mutt_dump_variables (int full) {
2921 char outbuf[STRING];
2922 list2_t* tmp = NULL;
2923 struct option_t* option = NULL;
2925 /* get all non-synonyms into list... */
2926 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2927 (unsigned long) &tmp);
2929 if (!list_empty(tmp)) {
2930 /* ...and dump list sorted */
2931 qsort (tmp->data, tmp->length, sizeof (void*), opt_cmp);
2932 for (i = 0; i < tmp->length; i++) {
2933 option = (struct option_t*) tmp->data[i];
2934 FuncTable[DTYPE (option->type)].opt_to_string
2935 (outbuf, sizeof (outbuf), option);
2936 printf ("%s\n", outbuf);
2939 list_del (&tmp, NULL);