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>
57 if ((CurrentMenu == MENU_PAGER) && \
58 (!option || (option->flags & R_RESORT))) \
60 snprintf (err->data, err->dsize, \
61 _("Not available in this menu.")); \
68 static const struct mapping_t* get_sortmap (struct option_t* option);
69 static int parse_sort (struct option_t* dst, const char *s,
70 const struct mapping_t *map,
71 char* errbuf, size_t errlen);
73 static HASH* ConfigOptions = NULL;
75 /* for synonym warning reports: synonym found during parsing */
79 struct option_t* n; /* new */
80 struct option_t* o; /* old */
83 /* for synonym warning reports: list of synonyms found */
84 static list2_t* Synonyms;
85 /* for synonym warning reports: current rc file */
86 static const char* CurRCFile = NULL;
87 /* for synonym warning reports: current rc line */
88 static int CurRCLine = 0;
90 /* prototypes for checking for special vars */
91 static int check_dsn_return (const char* option, unsigned long val,
92 char* errbuf, size_t errlen);
93 static int check_dsn_notify (const char* option, unsigned long val,
94 char* errbuf, size_t errlen);
95 static int check_history (const char* option, unsigned long val,
96 char* errbuf, size_t errlen);
97 /* this checks that numbers are >= 0 */
98 static int check_num (const char* option, unsigned long val,
99 char* errbuf, size_t errlen);
101 static int check_debug (const char* option, unsigned long val,
102 char* errbuf, size_t errlen);
105 /* use this to check only */
106 static int check_special (const char* option, unsigned long val,
107 char* errbuf, size_t errlen);
109 /* variable <-> sanity check function mappings
110 * when changing these, make sure the proper _from_string handler
111 * does this checking!
115 int (*check) (const char* option, unsigned long val,
116 char* errbuf, size_t errlen);
118 { "dsn_notify", check_dsn_notify },
119 { "dsn_return", check_dsn_return },
120 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
121 { "smtp_use_tls", mutt_libesmtp_check_usetls },
123 { "history", check_history },
124 { "pager_index_lines", check_num },
126 { "debug_level", check_debug },
132 /* protos for config type handles: convert value to string */
133 static void bool_to_string (char* dst, size_t dstlen, struct option_t* option);
134 static void num_to_string (char* dst, size_t dstlen, struct option_t* option);
135 static void str_to_string (char* dst, size_t dstlen, struct option_t* option);
136 static void quad_to_string (char* dst, size_t dstlen, struct option_t* option);
137 static void sort_to_string (char* dst, size_t dstlen, struct option_t* option);
138 static void rx_to_string (char* dst, size_t dstlen, struct option_t* option);
139 static void magic_to_string (char* dst, size_t dstlen, struct option_t* option);
140 static void addr_to_string (char* dst, size_t dstlen, struct option_t* option);
141 static void user_to_string (char* dst, size_t dstlen, struct option_t* option);
142 static void sys_to_string (char* dst, size_t dstlen, struct option_t* option);
144 /* protos for config type handles: convert to value from string */
145 static int bool_from_string (struct option_t* dst, const char* val,
146 char* errbuf, size_t errlen);
147 static int num_from_string (struct option_t* dst, const char* val,
148 char* errbuf, size_t errlen);
149 static int str_from_string (struct option_t* dst, const char* val,
150 char* errbuf, size_t errlen);
151 static int path_from_string (struct option_t* dst, const char* val,
152 char* errbuf, size_t errlen);
153 static int quad_from_string (struct option_t* dst, const char* val,
154 char* errbuf, size_t errlen);
155 static int sort_from_string (struct option_t* dst, const char* val,
156 char* errbuf, size_t errlen);
157 static int rx_from_string (struct option_t* dst, const char* val,
158 char* errbuf, size_t errlen);
159 static int magic_from_string (struct option_t* dst, const char* val,
160 char* errbuf, size_t errlen);
161 static int addr_from_string (struct option_t* dst, const char* val,
162 char* errbuf, size_t errlen);
163 static int user_from_string (struct option_t* dst, const char* val,
164 char* errbuf, size_t errlen);
168 void (*opt_to_string) (char* dst, size_t dstlen, struct option_t* option);
169 int (*opt_from_string) (struct option_t* dst, const char* val,
170 char* errbuf, size_t errlen);
172 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
173 { DT_BOOL, bool_to_string, bool_from_string },
174 { DT_NUM, num_to_string, num_from_string },
175 { DT_STR, str_to_string, str_from_string },
176 { DT_PATH, str_to_string, path_from_string },
177 { DT_QUAD, quad_to_string, quad_from_string },
178 { DT_SORT, sort_to_string, sort_from_string },
179 { DT_RX, rx_to_string, rx_from_string },
180 { DT_MAGIC, magic_to_string, magic_from_string },
181 /* synonyms should be resolved already so we don't need this
182 * but must define it as DT_ is used for indexing */
183 { DT_SYN, NULL, NULL },
184 { DT_ADDR, addr_to_string, addr_from_string },
185 { DT_USER, user_to_string, user_from_string },
186 { DT_SYS, sys_to_string, NULL },
189 static void bool_to_string (char* dst, size_t dstlen,
190 struct option_t* option) {
191 snprintf (dst, dstlen, "%s=%s", option->option,
192 option (option->data) ? "yes" : "no");
195 static int bool_from_string (struct option_t* dst, const char* val,
196 char* errbuf, size_t errlen) {
201 if (ascii_strncasecmp (val, "yes", 3) == 0)
203 else if (ascii_strncasecmp (val, "no", 2) == 0)
209 set_option (dst->data);
211 unset_option (dst->data);
215 static void num_to_string (char* dst, size_t dstlen,
216 struct option_t* option) {
218 const char* fmt = (str_cmp (option->option, "umask") == 0) ?
220 snprintf (dst, dstlen, fmt, option->option,
221 *((short*) option->data));
224 static int num_from_string (struct option_t* dst, const char* val,
225 char* errbuf, size_t errlen) {
226 int num = 0, old = 0;
232 num = strtol (val, &t, 0);
234 if (!*val || *t || (short) num != num) {
236 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
242 /* just temporarily accept new val so that check_special for
243 * $history already has it when doing history's init() */
244 old = *((short*) dst->data);
245 *((short*) dst->data) = (short) num;
247 if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
248 *((short*) dst->data) = old;
255 static void str_to_string (char* dst, size_t dstlen,
256 struct option_t* option) {
257 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
258 NONULL (*((char**) option->data)));
261 static void user_to_string (char* dst, size_t dstlen,
262 struct option_t* option) {
263 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
264 NONULL (((char*) option->data)));
267 static void sys_to_string (char* dst, size_t dstlen,
268 struct option_t* option) {
269 char* val = NULL, *t = NULL;
272 /* get some $muttng_ values dynamically */
273 if (ascii_strcmp ("muttng_pwd", option->option) == 0) {
274 val = mem_malloc (_POSIX_PATH_MAX);
275 val = getcwd (val, _POSIX_PATH_MAX-1);
277 } else if (ascii_strcmp ("muttng_folder_path", option->option) == 0 &&
278 CurrentFolder && *CurrentFolder) {
280 } else if (ascii_strcmp ("muttng_folder_name", option->option) == 0 &&
281 CurrentFolder && *CurrentFolder) {
283 size_t Maildirlength = str_len (Maildir);
286 * if name starts with $folder, just strip it to keep hierarchy
287 * $folder=imap://host, path=imap://host/inbox/b -> inbox/b
289 if (Maildirlength > 0 && str_ncmp (CurrentFolder, Maildir,
290 Maildirlength) == 0 &&
291 str_len (CurrentFolder) > Maildirlength) {
292 val = CurrentFolder + Maildirlength;
293 if (Maildir[strlen(Maildir)-1]!='/')
295 /* if not $folder, just use everything after last / */
296 } else if ((t = strrchr (CurrentFolder, '/')) != NULL)
298 /* default: use as-is */
305 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (val));
310 static int path_from_string (struct option_t* dst, const char* val,
311 char* errbuf, size_t errlen) {
312 char path[_POSIX_PATH_MAX];
318 mem_free ((char**) dst->data);
323 strfcpy (path, val, sizeof (path));
324 mutt_expand_path (path, sizeof (path));
325 str_replace ((char **) dst->data, path);
329 static int str_from_string (struct option_t* dst, const char* val,
330 char* errbuf, size_t errlen) {
334 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
337 str_replace ((char**) dst->data, val);
341 static int user_from_string (struct option_t* dst, const char* val,
342 char* errbuf, size_t errlen) {
343 /* if dst == NULL, we may get here in case the user did unset it,
344 * see parse_set() where item is free()'d before coming here; so
345 * just silently ignore it */
348 if (str_len ((char*) dst->data) == 0)
349 dst->data = (unsigned long) str_dup (val);
351 char* s = (char*) dst->data;
352 str_replace (&s, val);
354 if (str_len (dst->init) == 0)
355 dst->init = str_dup ((char*) dst->data);
359 static void quad_to_string (char* dst, size_t dstlen,
360 struct option_t* option) {
361 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
362 snprintf (dst, dstlen, "%s=%s", option->option,
363 vals[quadoption (option->data)]);
366 static int quad_from_string (struct option_t* dst, const char* val,
367 char* errbuf, size_t errlen) {
372 if (ascii_strncasecmp (val, "yes", 3) == 0)
374 else if (ascii_strncasecmp (val, "no", 2) == 0)
376 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
378 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
384 set_quadoption (dst->data, flag);
388 static void sort_to_string (char* dst, size_t dstlen,
389 struct option_t* option) {
390 const struct mapping_t *map = get_sortmap (option);
394 snprintf (dst, sizeof (dst), "%s=unknown", option->option);
398 p = mutt_getnamebyvalue (*((short *) option->data) & SORT_MASK,
401 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
402 (*((short *) option->data) & SORT_REVERSE) ?
404 (*((short *) option->data) & SORT_LAST) ? "last-" :
408 static int sort_from_string (struct option_t* dst, const char* val,
409 char* errbuf, size_t errlen) {
410 const struct mapping_t *map = NULL;
411 if (!(map = get_sortmap (dst))) {
413 snprintf (errbuf, errlen, _("%s: Unknown type."),
417 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
422 static void rx_to_string (char* dst, size_t dstlen,
423 struct option_t* option) {
424 rx_t* p = (rx_t*) option->data;
425 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
426 NONULL (p->pattern));
429 static int rx_from_string (struct option_t* dst, const char* val,
430 char* errbuf, size_t errlen) {
433 int flags = 0, e = 0, not = 0;
439 if (option (OPTATTACHMSG) && !str_cmp (dst->option, "reply_regexp")) {
441 snprintf (errbuf, errlen,
442 "Operation not permitted when in attach-message mode.");
446 if (!((rx_t*) dst->data))
447 *((rx_t**) dst->data) = mem_calloc (1, sizeof (rx_t));
449 p = (rx_t*) dst->data;
451 /* something to do? */
452 if (!val || !*val || (p->pattern && str_cmp (p->pattern, val) == 0))
455 if (str_cmp (dst->option, "mask") != 0)
456 flags |= mutt_which_case (val);
459 if (str_cmp (dst->option, "mask") == 0 && *s == '!') {
464 rx = mem_malloc (sizeof (regex_t));
466 if ((e = REGCOMP (rx, s, flags)) != 0) {
467 regerror (e, rx, errbuf, errlen);
478 str_replace (&p->pattern, val);
482 if (str_cmp (dst->option, "reply_regexp") == 0)
483 mutt_adjust_all_subjects ();
488 static void magic_to_string (char* dst, size_t dstlen,
489 struct option_t* option) {
490 const char* s = NULL;
491 switch (option->data) {
492 case M_MBOX: s = "mbox"; break;
493 case M_MMDF: s = "MMDF"; break;
494 case M_MH: s = "MH"; break;
495 case M_MAILDIR: s = "Maildir"; break;
496 default: s = "unknown"; break;
498 snprintf (dst, dstlen, "%s=%s", option->option, s);
501 static int magic_from_string (struct option_t* dst, const char* val,
502 char* errbuf, size_t errlen) {
505 if (!dst || !val || !*val)
507 if (ascii_strncasecmp (val, "mbox", 4) == 0)
509 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
511 else if (ascii_strncasecmp (val, "mh", 2) == 0)
513 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
519 *((short*) dst->data) = flag;
524 static void addr_to_string (char* dst, size_t dstlen,
525 struct option_t* option) {
528 rfc822_write_address (s, sizeof (s), *((ADDRESS**) option->data), 0);
529 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
532 static int addr_from_string (struct option_t* dst, const char* val,
533 char* errbuf, size_t errlen) {
536 rfc822_free_address ((ADDRESS**) dst->data);
538 *((ADDRESS**) dst->data) = rfc822_parse_adrlist (NULL, val);
542 int mutt_option_value (const char* val, char* dst, size_t dstlen) {
543 struct option_t* option = NULL;
544 char* tmp = NULL, *t = NULL;
547 if (!(option = hash_find (ConfigOptions, val))) {
548 debug_print (1, ("var '%s' not found\n", val));
552 tmp = mem_malloc (dstlen+1);
553 FuncTable[DTYPE (option->type)].opt_to_string (tmp, dstlen, option);
555 /* as we get things of type $var=value and don't want to bloat the
556 * above "just" for expansion, we do the stripping here */
557 debug_print (1, ("orig == '%s'\n", tmp));
558 t = strchr (tmp, '=');
562 if (t[l-1] == '"' && *t == '"') {
567 memcpy (dst, t, l+1);
569 debug_print (1, ("stripped == '%s'\n", dst));
574 /* for synonym warning reports: adds synonym to end of list */
575 static void syn_add (struct option_t* n, struct option_t* o) {
576 syn_t* tmp = mem_malloc (sizeof (syn_t));
577 tmp->f = str_dup (CurRCFile);
581 list_push_back (&Synonyms, tmp);
584 /* for synonym warning reports: free single item (for list_del()) */
585 static void syn_del (void** p) {
586 mem_free(&(*(syn_t**) p)->f);
590 void toggle_quadoption (int opt)
593 int b = (opt % 4) * 2;
595 QuadOptions[n] ^= (1 << b);
598 void set_quadoption (int opt, int flag)
601 int b = (opt % 4) * 2;
603 QuadOptions[n] &= ~(0x3 << b);
604 QuadOptions[n] |= (flag & 0x3) << b;
607 int quadoption (int opt)
610 int b = (opt % 4) * 2;
612 return (QuadOptions[n] >> b) & 0x3;
615 int query_quadoption (int opt, const char *prompt)
617 int v = quadoption (opt);
625 v = mutt_yesorno (prompt, (v == M_ASKYES));
626 CLEARLINE (LINES - 1);
633 static void add_to_list (LIST ** list, const char *str)
635 LIST *t, *last = NULL;
637 /* don't add a NULL or empty string to the list */
638 if (!str || *str == '\0')
641 /* check to make sure the item is not already on this list */
642 for (last = *list; last; last = last->next) {
643 if (ascii_strcasecmp (str, last->data) == 0) {
644 /* already on the list, so just ignore it */
652 if (!*list || last) {
653 t = (LIST *) mem_calloc (1, sizeof (LIST));
654 t->data = str_dup (str);
664 static int add_to_rx_list (list2_t** list, const char *s, int flags,
673 if (!(rx = rx_compile (s, flags))) {
674 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
678 i = rx_lookup ((*list), rx->pattern);
682 list_push_back (list, rx);
686 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
687 const char *templ, BUFFER * err)
689 SPAM_LIST *t = NULL, *last = NULL;
694 if (!pat || !*pat || !templ)
697 if (!(rx = rx_compile (pat, REG_ICASE))) {
698 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
702 /* check to make sure the item is not already on this list */
703 for (last = *list; last; last = last->next) {
704 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
705 /* Already on the list. Formerly we just skipped this case, but
706 * now we're supporting removals, which means we're supporting
707 * re-adds conceptually. So we probably want this to imply a
708 * removal, then do an add. We can achieve the removal by freeing
709 * the template, and leaving t pointed at the current item.
712 mem_free(t->template);
719 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
720 * update. Otherwise we want to make a new one to link at the list's end.
723 t = mutt_new_spam_list ();
731 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
732 t->template = str_dup (templ);
734 /* Find highest match number in template string */
736 for (p = templ; *p;) {
741 while (*p && isdigit ((int) *p))
747 t->nmatch++; /* match 0 is always the whole expr */
752 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
754 SPAM_LIST *spam, *prev;
757 /* Being first is a special case. */
761 if (spam->rx && !str_cmp (spam->rx->pattern, pat)) {
764 mem_free(&spam->template);
770 for (spam = prev->next; spam;) {
771 if (!str_cmp (spam->rx->pattern, pat)) {
772 prev->next = spam->next;
774 mem_free(spam->template);
787 static void remove_from_list (LIST ** l, const char *str)
789 LIST *p, *last = NULL;
791 if (str_cmp ("*", str) == 0)
792 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
797 if (ascii_strcasecmp (str, p->data) == 0) {
800 last->next = p->next;
813 static int remove_from_rx_list (list2_t** l, const char *str)
817 if (str_cmp ("*", str) == 0) {
818 list_del (l, (list_del_t*) rx_free);
822 i = rx_lookup ((*l), str);
824 rx_t* r = list_pop_idx ((*l), i);
832 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
837 struct option_t* option = NULL;
839 memset (&token, 0, sizeof (token));
840 mutt_extract_token (tmp, s, 0);
842 /* is the item defined as a variable or a function? */
843 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL)
846 for (i = 0; !res && i < MENU_MAX; i++) {
847 struct binding_t *b = km_get_table (Menus[i].value);
852 for (j = 0; b[j].name; j++)
853 if (!ascii_strncasecmp (tmp->data, b[j].name, str_len (tmp->data))
854 && (str_len (b[j].name) == str_len (tmp->data))) {
860 /* check for feature_* */
861 if (!res && ascii_strncasecmp (tmp->data, "feature_", 8) == 0 &&
862 (j = str_len (tmp->data)) > 8) {
864 while (Features[i]) {
865 if (str_len (Features[i]) == j-8 &&
866 ascii_strncasecmp (Features[i], tmp->data+8, j-8) == 0) {
876 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
878 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
882 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
885 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
886 mutt_error ("Error: %s", err->data);
887 mem_free (&token.data);
890 mem_free (&token.data);
895 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
899 mutt_extract_token (buf, s, 0);
901 /* don't add "*" to the unignore list */
902 if (strcmp (buf->data, "*"))
903 add_to_list (&UnIgnore, buf->data);
905 remove_from_list (&Ignore, buf->data);
907 while (MoreArgs (s));
912 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
916 mutt_extract_token (buf, s, 0);
917 remove_from_list (&UnIgnore, buf->data);
918 add_to_list (&Ignore, buf->data);
920 while (MoreArgs (s));
925 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
929 mutt_extract_token (buf, s, 0);
930 add_to_list ((LIST **) data, buf->data);
932 while (MoreArgs (s));
937 static void _alternates_clean (void)
941 if (Context && Context->msgcount) {
942 for (i = 0; i < Context->msgcount; i++)
943 Context->hdrs[i]->recip_valid = 0;
947 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
950 _alternates_clean ();
952 mutt_extract_token (buf, s, 0);
953 remove_from_rx_list (&UnAlternates, buf->data);
955 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
958 while (MoreArgs (s));
963 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
966 _alternates_clean ();
968 mutt_extract_token (buf, s, 0);
969 remove_from_rx_list (&Alternates, buf->data);
971 if (str_cmp (buf->data, "*") &&
972 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
976 while (MoreArgs (s));
981 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
986 memset (&templ, 0, sizeof (templ));
988 /* Insist on at least one parameter */
991 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
993 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
997 /* Extract the first token, a regexp */
998 mutt_extract_token (buf, s, 0);
1000 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
1001 if (data == M_SPAM) {
1002 /* If there's a second parameter, it's a template for the spam tag. */
1004 mutt_extract_token (&templ, s, 0);
1006 /* Add to the spam list. */
1007 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
1008 mem_free (&templ.data);
1011 mem_free (&templ.data);
1014 /* If not, try to remove from the nospam list. */
1016 remove_from_rx_list (&NoSpamList, buf->data);
1022 /* M_NOSPAM is for nospam commands. */
1023 else if (data == M_NOSPAM) {
1024 /* nospam only ever has one parameter. */
1026 /* "*" is a special case. */
1027 if (!str_cmp (buf->data, "*")) {
1028 mutt_free_spam_list (&SpamList);
1029 list_del (&NoSpamList, (list_del_t*) rx_free);
1033 /* If it's on the spam list, just remove it. */
1034 if (remove_from_spam_list (&SpamList, buf->data) != 0)
1037 /* Otherwise, add it to the nospam list. */
1038 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
1044 /* This should not happen. */
1045 strfcpy (err->data, "This is no good at all.", err->dsize);
1049 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
1053 mutt_extract_token (buf, s, 0);
1055 * Check for deletion of entire list
1057 if (str_cmp (buf->data, "*") == 0) {
1058 mutt_free_list ((LIST **) data);
1061 remove_from_list ((LIST **) data, buf->data);
1063 while (MoreArgs (s));
1068 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
1072 mutt_extract_token (buf, s, 0);
1073 remove_from_rx_list (&UnMailLists, buf->data);
1075 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1078 while (MoreArgs (s));
1083 /* always wise to do what someone else did before */
1084 static void _attachments_clean (void) {
1086 if (Context && Context->msgcount) {
1087 for (i = 0; i < Context->msgcount; i++)
1088 Context->hdrs[i]->attach_valid = 0;
1092 static int parse_attach_list (BUFFER *buf, BUFFER *s, LIST **ldata,
1095 LIST *listp, *lastp;
1100 /* Find the last item in the list that data points to. */
1102 debug_print (5, ("parse_attach_list: ldata = %08x, *ldata = %08x\n",
1103 (unsigned int)ldata, (unsigned int)*ldata));
1104 for (listp = *ldata; listp; listp = listp->next) {
1105 a = (ATTACH_MATCH *)listp->data;
1106 debug_print (5, ("parse_attach_list: skipping %s/%s\n", a->major, a->minor));
1111 mutt_extract_token (buf, s, 0);
1113 if (!buf->data || *buf->data == '\0')
1116 a = mem_malloc(sizeof(ATTACH_MATCH));
1118 /* some cheap hacks that I expect to remove */
1119 if (!str_casecmp(buf->data, "any"))
1120 a->major = str_dup("*/.*");
1121 else if (!str_casecmp(buf->data, "none"))
1122 a->major = str_dup("cheap_hack/this_should_never_match");
1124 a->major = str_dup(buf->data);
1126 if ((p = strchr(a->major, '/'))) {
1131 a->minor = "unknown";
1134 len = str_len (a->minor);
1135 tmpminor = mem_malloc(len+3);
1136 strcpy(&tmpminor[1], a->minor); /* __STRCPY_CHECKED__ */
1138 tmpminor[len+1] = '$';
1139 tmpminor[len+2] = '\0';
1141 a->major_int = mutt_check_mime_type(a->major);
1142 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
1144 mem_free (&tmpminor);
1146 debug_print (5, ("parse_attach_list: added %s/%s [%d]\n",
1147 a->major, a->minor, a->major_int));
1149 listp = mem_malloc(sizeof(LIST));
1150 listp->data = (char *)a;
1153 lastp->next = listp;
1159 while (MoreArgs (s));
1161 _attachments_clean();
1165 static int parse_unattach_list (BUFFER *buf, BUFFER *s, LIST **ldata, BUFFER *err) {
1167 LIST *lp, *lastp, *newlp;
1173 mutt_extract_token (buf, s, 0);
1175 if (!str_casecmp(buf->data, "any"))
1176 tmp = str_dup("*/.*");
1177 else if (!str_casecmp(buf->data, "none"))
1178 tmp = str_dup("cheap_hack/this_should_never_match");
1180 tmp = str_dup(buf->data);
1182 if ((minor = strchr(tmp, '/'))) {
1188 major = mutt_check_mime_type(tmp);
1190 /* We must do our own walk here because remove_from_list() will only
1191 * remove the LIST->data, not anything pointed to by the LIST->data. */
1193 for(lp = *ldata; lp; ) {
1194 a = (ATTACH_MATCH *)lp->data;
1195 debug_print(5, ("parse_unattach_list: check %s/%s [%d] : %s/%s [%d]\n",
1196 a->major, a->minor, a->major_int, tmp, minor, major));
1197 if (a->major_int == major && !str_casecmp(minor, a->minor)) {
1198 debug_print(5, ("parse_unattach_list: removed %s/%s [%d]\n",
1199 a->major, a->minor, a->major_int));
1200 regfree(&a->minor_rx);
1201 mem_free(&a->major);
1203 /* Relink backward */
1205 lastp->next = lp->next;
1210 mem_free(&lp->data); /* same as a */
1220 while (MoreArgs (s));
1223 _attachments_clean();
1227 static int print_attach_list (LIST *lp, char op, char *name) {
1229 printf("attachments %c%s %s/%s\n", op, name,
1230 ((ATTACH_MATCH *)lp->data)->major,
1231 ((ATTACH_MATCH *)lp->data)->minor);
1238 static int parse_attachments (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) {
1242 mutt_extract_token(buf, s, 0);
1243 if (!buf->data || *buf->data == '\0') {
1244 strfcpy(err->data, _("attachments: no disposition"), err->dsize);
1248 category = buf->data;
1254 printf("\nCurrent attachments settings:\n\n");
1255 print_attach_list(AttachAllow, '+', "A");
1256 print_attach_list(AttachExclude, '-', "A");
1257 print_attach_list(InlineAllow, '+', "I");
1258 print_attach_list(InlineExclude, '-', "I");
1259 set_option (OPTFORCEREDRAWINDEX);
1260 set_option (OPTFORCEREDRAWPAGER);
1261 mutt_any_key_to_continue (NULL);
1265 if (op != '+' && op != '-') {
1269 if (!str_ncasecmp(category, "attachment", strlen(category))) {
1271 listp = &AttachAllow;
1273 listp = &AttachExclude;
1275 else if (!str_ncasecmp(category, "inline", strlen(category))) {
1277 listp = &InlineAllow;
1279 listp = &InlineExclude;
1281 strfcpy(err->data, _("attachments: invalid disposition"), err->dsize);
1285 return parse_attach_list(buf, s, listp, err);
1288 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) {
1292 mutt_extract_token(buf, s, 0);
1293 if (!buf->data || *buf->data == '\0') {
1294 strfcpy(err->data, _("unattachments: no disposition"), err->dsize);
1300 if (op != '+' && op != '-') {
1304 if (!str_ncasecmp(p, "attachment", strlen(p))) {
1306 listp = &AttachAllow;
1308 listp = &AttachExclude;
1310 else if (!str_ncasecmp(p, "inline", strlen(p))) {
1312 listp = &InlineAllow;
1314 listp = &InlineExclude;
1317 strfcpy(err->data, _("unattachments: invalid disposition"), err->dsize);
1321 return parse_unattach_list(buf, s, listp, err);
1324 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
1328 mutt_extract_token (buf, s, 0);
1329 remove_from_rx_list (&SubscribedLists, buf->data);
1330 remove_from_rx_list (&MailLists, buf->data);
1332 if (str_cmp (buf->data, "*") &&
1333 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1336 while (MoreArgs (s));
1341 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1345 mutt_extract_token (buf, s, 0);
1346 remove_from_rx_list (&UnMailLists, buf->data);
1347 remove_from_rx_list (&UnSubscribedLists, buf->data);
1349 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1351 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1354 while (MoreArgs (s));
1359 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1363 mutt_extract_token (buf, s, 0);
1364 remove_from_rx_list (&SubscribedLists, buf->data);
1366 if (str_cmp (buf->data, "*") &&
1367 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1370 while (MoreArgs (s));
1375 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
1378 ALIAS *tmp, *last = NULL;
1381 mutt_extract_token (buf, s, 0);
1383 if (str_cmp ("*", buf->data) == 0) {
1384 if (CurrentMenu == MENU_ALIAS) {
1385 for (tmp = Aliases; tmp; tmp = tmp->next)
1387 set_option (OPTFORCEREDRAWINDEX);
1390 mutt_free_alias (&Aliases);
1394 for (tmp = Aliases; tmp; tmp = tmp->next) {
1395 if (str_casecmp (buf->data, tmp->name) == 0) {
1396 if (CurrentMenu == MENU_ALIAS) {
1398 set_option (OPTFORCEREDRAWINDEX);
1403 last->next = tmp->next;
1405 Aliases = tmp->next;
1407 mutt_free_alias (&tmp);
1413 while (MoreArgs (s));
1417 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
1420 ALIAS *tmp = Aliases;
1424 if (!MoreArgs (s)) {
1425 strfcpy (err->data, _("alias: no address"), err->dsize);
1429 mutt_extract_token (buf, s, 0);
1431 debug_print (2, ("first token is '%s'.\n", buf->data));
1433 /* check to see if an alias with this name already exists */
1434 for (; tmp; tmp = tmp->next) {
1435 if (!str_casecmp (tmp->name, buf->data))
1441 /* create a new alias */
1442 tmp = (ALIAS *) mem_calloc (1, sizeof (ALIAS));
1444 tmp->name = str_dup (buf->data);
1445 /* give the main addressbook code a chance */
1446 if (CurrentMenu == MENU_ALIAS)
1447 set_option (OPTMENUCALLER);
1450 /* override the previous value */
1451 rfc822_free_address (&tmp->addr);
1452 if (CurrentMenu == MENU_ALIAS)
1453 set_option (OPTFORCEREDRAWINDEX);
1456 mutt_extract_token (buf, s,
1457 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1458 debug_print (2, ("second token is '%s'.\n", buf->data));
1459 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1464 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1465 snprintf (err->data, err->dsize,
1466 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1470 if (DebugLevel >= 2) {
1473 /* A group is terminated with an empty address, so check a->mailbox */
1474 for (a = tmp->addr; a && a->mailbox; a = a->next) {
1476 debug_print (2, ("%s\n", a->mailbox));
1478 debug_print (2, ("group %s\n", a->mailbox));
1486 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
1489 LIST *tmp = UserHeader;
1494 mutt_extract_token (buf, s, 0);
1495 if (str_cmp ("*", buf->data) == 0)
1496 mutt_free_list (&UserHeader);
1501 l = str_len (buf->data);
1502 if (buf->data[l - 1] == ':')
1506 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1507 && tmp->data[l] == ':') {
1510 last->next = tmp->next;
1512 UserHeader = tmp->next;
1515 mutt_free_list (&ptr);
1524 while (MoreArgs (s));
1528 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
1535 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1536 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1537 strfcpy (err->data, _("invalid header field"), err->dsize);
1540 keylen = p - buf->data + 1;
1543 for (tmp = UserHeader;; tmp = tmp->next) {
1544 /* see if there is already a field by this name */
1545 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1546 /* replace the old value */
1547 mem_free (&tmp->data);
1548 tmp->data = buf->data;
1549 memset (buf, 0, sizeof (BUFFER));
1555 tmp->next = mutt_new_list ();
1559 tmp = mutt_new_list ();
1562 tmp->data = buf->data;
1563 memset (buf, 0, sizeof (BUFFER));
1568 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1569 char* errbuf, size_t errlen) {
1572 if (str_ncmp ("reverse-", s, 8) == 0) {
1574 flags = SORT_REVERSE;
1577 if (str_ncmp ("last-", s, 5) == 0) {
1582 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1584 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1588 *((short*) dst->data) = i | flags;
1592 /* if additional data more == 1, we want to resolve synonyms */
1593 static void mutt_set_default (const char* name, void* p, unsigned long more) {
1594 char buf[LONG_STRING];
1595 struct option_t* ptr = (struct option_t*) p;
1597 if (DTYPE (ptr->type) == DT_SYN) {
1600 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1602 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_from_string)
1604 mutt_option_value (ptr->option, buf, sizeof (buf));
1605 if (str_len (ptr->init) == 0 && buf && *buf)
1606 ptr->init = str_dup (buf);
1609 static struct option_t* add_option (const char* name, const char* init,
1610 short type, short dup) {
1611 struct option_t* option = mem_calloc (1, sizeof (struct option_t));
1613 debug_print (1, ("adding $%s\n", name));
1615 option->option = str_dup (name);
1616 option->type = type;
1618 option->init = dup ? str_dup (init) : (char*) init;
1622 /* creates new option_t* of type DT_USER for $user_ var */
1623 static struct option_t* add_user_option (const char* name) {
1624 return (add_option (name, NULL, DT_USER, 1));
1627 /* free()'s option_t* */
1628 static void del_option (void* p) {
1629 struct option_t* ptr = (struct option_t*) p;
1630 char* s = (char*) ptr->data;
1631 debug_print (1, ("removing option '%s' from table\n", NONULL (ptr->option)));
1632 mem_free (&ptr->option);
1634 mem_free (&ptr->init);
1638 static int init_expand (char** dst, struct option_t* src) {
1644 if (DTYPE(src->type) == DT_STR ||
1645 DTYPE(src->type) == DT_PATH) {
1646 /* only expand for string as it's the only place where
1647 * we want to expand vars right now */
1648 if (src->init && *src->init) {
1649 memset (&token, 0, sizeof (BUFFER));
1650 memset (&in, 0, sizeof (BUFFER));
1651 len = str_len (src->init) + 2;
1652 in.data = mem_malloc (len+1);
1653 snprintf (in.data, len, "\"%s\"", src->init);
1656 mutt_extract_token (&token, &in, 0);
1657 if (token.data && *token.data)
1658 *dst = str_dup (token.data);
1660 *dst = str_dup ("");
1661 mem_free (&in.data);
1662 mem_free (&token.data);
1664 *dst = str_dup ("");
1666 /* for non-string: take value as is */
1667 *dst = str_dup (src->init);
1671 /* if additional data more == 1, we want to resolve synonyms */
1672 static void mutt_restore_default (const char* name, void* p,
1673 unsigned long more) {
1674 char errbuf[STRING];
1675 struct option_t* ptr = (struct option_t*) p;
1678 if (DTYPE (ptr->type) == DT_SYN) {
1681 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1685 if (FuncTable[DTYPE (ptr->type)].opt_from_string) {
1686 init_expand (&init, ptr);
1687 if (!FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, init, errbuf,
1689 if (!option (OPTNOCURSES))
1691 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1692 "Please report this error: \"%s\"\n"),
1693 ptr->option, NONULL (init), errbuf);
1699 if (ptr->flags & R_INDEX)
1700 set_option (OPTFORCEREDRAWINDEX);
1701 if (ptr->flags & R_PAGER)
1702 set_option (OPTFORCEREDRAWPAGER);
1703 if (ptr->flags & R_RESORT_SUB)
1704 set_option (OPTSORTSUBTHREADS);
1705 if (ptr->flags & R_RESORT)
1706 set_option (OPTNEEDRESORT);
1707 if (ptr->flags & R_RESORT_INIT)
1708 set_option (OPTRESORTINIT);
1709 if (ptr->flags & R_TREE)
1710 set_option (OPTREDRAWTREE);
1713 /* check whether value for $dsn_return would be valid */
1714 static int check_dsn_return (const char* option, unsigned long p,
1715 char* errbuf, size_t errlen) {
1716 char* val = (char*) p;
1717 if (val && *val && str_ncmp (val, "hdrs", 4) != 0 &&
1718 str_ncmp (val, "full", 4) != 0) {
1720 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1726 /* check whether value for $dsn_notify would be valid */
1727 static int check_dsn_notify (const char* option, unsigned long p,
1728 char* errbuf, size_t errlen) {
1729 list2_t* list = NULL;
1731 char* val = (char*) p;
1735 list = list_from_str (val, ",");
1736 if (list_empty (list))
1739 for (i = 0; i < list->length; i++)
1740 if (str_ncmp (list->data[i], "never", 5) != 0 &&
1741 str_ncmp (list->data[i], "failure", 7) != 0 &&
1742 str_ncmp (list->data[i], "delay", 5) != 0 &&
1743 str_ncmp (list->data[i], "success", 7) != 0) {
1745 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1746 (char*) list->data[i], "dsn_notify");
1750 list_del (&list, (list_del_t*) _mem_free);
1754 static int check_num (const char* option, unsigned long p,
1755 char* errbuf, size_t errlen) {
1758 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1765 static int check_debug (const char* option, unsigned long p,
1766 char* errbuf, size_t errlen) {
1767 if ((int) p <= DEBUG_MAX_LEVEL &&
1768 (int) p >= DEBUG_MIN_LEVEL)
1772 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1777 static int check_history (const char* option, unsigned long p,
1778 char* errbuf, size_t errlen) {
1779 if (!check_num ("history", p, errbuf, errlen))
1781 mutt_init_history ();
1785 static int check_special (const char* name, unsigned long val,
1786 char* errbuf, size_t errlen) {
1789 for (i = 0; SpecialVars[i].name; i++) {
1790 if (str_cmp (SpecialVars[i].name, name) == 0) {
1791 return (SpecialVars[i].check (SpecialVars[i].name,
1792 val, errbuf, errlen));
1798 static const struct mapping_t* get_sortmap (struct option_t* option) {
1799 const struct mapping_t* map = NULL;
1801 switch (option->type & DT_SUBTYPE_MASK) {
1803 map = SortAliasMethods;
1805 case DT_SORT_BROWSER:
1806 map = SortBrowserMethods;
1809 if ((WithCrypto & APPLICATION_PGP))
1810 map = SortKeyMethods;
1813 map = SortAuxMethods;
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, 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 char *prefixes[] = { "no", "inv", "?", "&", 0 };
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 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 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;
2576 char *p, buffer[STRING], error[STRING];
2577 int i, default_rc = 0, need_pause = 0;
2580 memset (&err, 0, sizeof (err));
2582 err.dsize = sizeof (error);
2584 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2585 * to have some room for $user_ vars */
2586 ConfigOptions = hash_create (sizeof (MuttVars) * 3);
2587 for (i = 0; MuttVars[i].option; i++) {
2588 if (DTYPE (MuttVars[i].type) != DT_SYS)
2589 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2591 hash_insert (ConfigOptions, MuttVars[i].option,
2592 add_option (MuttVars[i].option, MuttVars[i].init,
2597 * XXX - use something even more difficult to predict?
2599 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2600 "\033]9;%ld\a", (long) time (NULL));
2602 /* on one of the systems I use, getcwd() does not return the same prefix
2603 as is listed in the passwd file */
2604 if ((p = getenv ("HOME")))
2605 Homedir = str_dup (p);
2607 /* Get some information about the user */
2608 if ((pw = getpwuid (getuid ()))) {
2611 Username = str_dup (pw->pw_name);
2613 Homedir = str_dup (pw->pw_dir);
2615 Realname = str_dup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2616 Shell = str_dup (pw->pw_shell);
2622 fputs (_("unable to determine home directory"), stderr);
2625 if ((p = getenv ("USER")))
2626 Username = str_dup (p);
2629 fputs (_("unable to determine username"), stderr);
2632 Shell = str_dup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2635 debug_start(Homedir);
2637 /* And about the host... */
2639 /* some systems report the FQDN instead of just the hostname */
2640 if ((p = strchr (utsname.nodename, '.'))) {
2641 Hostname = str_substrdup (utsname.nodename, p);
2643 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2646 Hostname = str_dup (utsname.nodename);
2649 #define DOMAIN buffer
2650 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2651 Fqdn = str_dup ("@");
2654 if (*DOMAIN != '@') {
2655 Fqdn = mem_malloc (str_len (DOMAIN) + str_len (Hostname) + 2);
2656 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2659 Fqdn = str_dup (NONULL (Hostname));
2666 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2668 fgets (buffer, sizeof (buffer), f);
2669 p = (char*) &buffer;
2672 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r')
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);