2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
5 * Parts were written/modified by:
6 * Rocco Rutte <pdmef@cs.tu-berlin.de>
8 * This file is part of mutt-ng, see http://www.muttng.org/.
9 * It's licensed under the GNU General Public License,
10 * please see the file GPL in the top level source directory.
21 #include "mutt_curses.h"
27 #include "mutt_crypt.h"
28 #include "mutt_idna.h"
30 #if defined(USE_SSL) || defined(USE_GNUTLS)
34 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
35 #include "mutt_libesmtp.h"
46 #include "lib/debug.h"
52 #include <sys/utsname.h>
59 static const struct mapping_t* get_sortmap (struct option_t* option);
60 static int parse_sort (struct option_t* dst, const char *s,
61 const struct mapping_t *map,
62 char* errbuf, size_t errlen);
64 static HASH *ConfigOptions = NULL;
66 /* for synonym warning reports: synonym found during parsing */
70 struct option_t* n; /* new */
71 struct option_t* o; /* old */
74 /* for synonym warning reports: list of synonyms found */
75 static list2_t* Synonyms;
76 /* for synonym warning reports: current rc file */
77 static const char* CurRCFile = NULL;
78 /* for synonym warning reports: current rc line */
79 static int CurRCLine = 0;
81 /* prototypes for checking for special vars */
82 static int check_dsn_return (const char* option, unsigned long val,
83 char* errbuf, size_t errlen);
84 static int check_dsn_notify (const char* option, unsigned long val,
85 char* errbuf, size_t errlen);
86 static int check_history (const char* option, unsigned long val,
87 char* errbuf, size_t errlen);
88 /* this checks that numbers are >= 0 */
89 static int check_num (const char* option, unsigned long val,
90 char* errbuf, size_t errlen);
92 static int check_debug (const char* option, unsigned long val,
93 char* errbuf, size_t errlen);
96 /* use this to check only */
97 static int check_special (const char* option, unsigned long val,
98 char* errbuf, size_t errlen);
100 /* variable <-> sanity check function mappings
101 * when changing these, make sure the proper _from_string handler
102 * does this checking!
106 int (*check) (const char* option, unsigned long val,
107 char* errbuf, size_t errlen);
109 { "dsn_notify", check_dsn_notify },
110 { "dsn_return", check_dsn_return },
111 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
112 { "smtp_use_tls", mutt_libesmtp_check_usetls },
114 { "history", check_history },
115 { "pager_index_lines", check_num },
117 { "debug_level", check_debug },
123 /* protos for config type handles: convert value to string */
124 static void bool_to_string (char* dst, size_t dstlen, struct option_t* option);
125 static void num_to_string (char* dst, size_t dstlen, struct option_t* option);
126 static void str_to_string (char* dst, size_t dstlen, struct option_t* option);
127 static void quad_to_string (char* dst, size_t dstlen, struct option_t* option);
128 static void sort_to_string (char* dst, size_t dstlen, struct option_t* option);
129 static void rx_to_string (char* dst, size_t dstlen, struct option_t* option);
130 static void magic_to_string (char* dst, size_t dstlen, struct option_t* option);
131 static void addr_to_string (char* dst, size_t dstlen, struct option_t* option);
132 static void user_to_string (char* dst, size_t dstlen, struct option_t* option);
133 static void sys_to_string (char* dst, size_t dstlen, struct option_t* option);
135 /* protos for config type handles: convert to value from string */
136 static int bool_from_string (struct option_t* dst, const char* val,
137 char* errbuf, size_t errlen);
138 static int num_from_string (struct option_t* dst, const char* val,
139 char* errbuf, size_t errlen);
140 static int str_from_string (struct option_t* dst, const char* val,
141 char* errbuf, size_t errlen);
142 static int path_from_string (struct option_t* dst, const char* val,
143 char* errbuf, size_t errlen);
144 static int quad_from_string (struct option_t* dst, const char* val,
145 char* errbuf, size_t errlen);
146 static int sort_from_string (struct option_t* dst, const char* val,
147 char* errbuf, size_t errlen);
148 static int rx_from_string (struct option_t* dst, const char* val,
149 char* errbuf, size_t errlen);
150 static int magic_from_string (struct option_t* dst, const char* val,
151 char* errbuf, size_t errlen);
152 static int addr_from_string (struct option_t* dst, const char* val,
153 char* errbuf, size_t errlen);
154 static int user_from_string (struct option_t* dst, const char* val,
155 char* errbuf, size_t errlen);
159 void (*opt_to_string) (char* dst, size_t dstlen, struct option_t* option);
160 int (*opt_from_string) (struct option_t* dst, const char* val,
161 char* errbuf, size_t errlen);
163 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
164 { DT_BOOL, bool_to_string, bool_from_string },
165 { DT_NUM, num_to_string, num_from_string },
166 { DT_STR, str_to_string, str_from_string },
167 { DT_PATH, str_to_string, path_from_string },
168 { DT_QUAD, quad_to_string, quad_from_string },
169 { DT_SORT, sort_to_string, sort_from_string },
170 { DT_RX, rx_to_string, rx_from_string },
171 { DT_MAGIC, magic_to_string, magic_from_string },
172 /* synonyms should be resolved already so we don't need this
173 * but must define it as DT_ is used for indexing */
174 { DT_SYN, NULL, NULL },
175 { DT_ADDR, addr_to_string, addr_from_string },
176 { DT_USER, user_to_string, user_from_string },
177 { DT_SYS, sys_to_string, NULL },
180 static void bool_to_string (char* dst, size_t dstlen,
181 struct option_t* option) {
182 snprintf (dst, dstlen, "%s=%s", option->option,
183 option (option->data) ? "yes" : "no");
186 static int bool_from_string (struct option_t* dst, const char* val,
187 char* errbuf, size_t errlen) {
192 if (ascii_strncasecmp (val, "yes", 3) == 0)
194 else if (ascii_strncasecmp (val, "no", 2) == 0)
200 set_option (dst->data);
202 unset_option (dst->data);
206 static void num_to_string (char* dst, size_t dstlen,
207 struct option_t* option) {
209 const char* fmt = (str_cmp (option->option, "umask") == 0) ?
211 snprintf (dst, dstlen, fmt, option->option,
212 *((short*) option->data));
215 static int num_from_string (struct option_t* dst, const char* val,
216 char* errbuf, size_t errlen) {
217 int num = 0, old = 0;
223 num = strtol (val, &t, 0);
225 if (!*val || *t || (short) num != num) {
227 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
233 /* just temporarily accept new val so that check_special for
234 * $history already has it when doing history's init() */
235 old = *((short*) dst->data);
236 *((short*) dst->data) = (short) num;
238 if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
239 *((short*) dst->data) = old;
246 static void str_to_string (char* dst, size_t dstlen,
247 struct option_t* option) {
248 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
249 NONULL (*((char**) option->data)));
252 static void user_to_string (char* dst, size_t dstlen,
253 struct option_t* option) {
254 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
255 NONULL (((char*) option->data)));
258 static void sys_to_string (char* dst, size_t dstlen,
259 struct option_t* option) {
260 char *val = NULL, *t = NULL;
263 /* get some $muttng_ values dynamically */
264 if (ascii_strcmp ("muttng_pwd", option->option) == 0) {
265 val = mem_malloc (_POSIX_PATH_MAX);
266 val = getcwd (val, _POSIX_PATH_MAX-1);
268 } else if (ascii_strcmp ("muttng_folder_path", option->option) == 0 &&
269 CurrentFolder && *CurrentFolder) {
271 } else if (ascii_strcmp ("muttng_folder_name", option->option) == 0 &&
272 CurrentFolder && *CurrentFolder) {
274 size_t Maildirlength = str_len (Maildir);
277 * if name starts with $folder, just strip it to keep hierarchy
278 * $folder=imap://host, path=imap://host/inbox/b -> inbox/b
280 if (Maildirlength > 0 && str_ncmp (CurrentFolder, Maildir,
281 Maildirlength) == 0 &&
282 str_len (CurrentFolder) > Maildirlength) {
283 val = CurrentFolder + Maildirlength;
284 if (Maildir[strlen(Maildir)-1]!='/')
286 /* if not $folder, just use everything after last / */
287 } else if ((t = strrchr (CurrentFolder, '/')) != NULL)
289 /* default: use as-is */
296 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (val));
301 static int path_from_string (struct option_t* dst, const char* val,
302 char* errbuf, size_t errlen) {
303 char path[_POSIX_PATH_MAX];
309 mem_free ((char**) dst->data);
314 strfcpy (path, val, sizeof(path));
315 mutt_expand_path (path, sizeof(path));
316 str_replace ((char **) dst->data, path);
320 static int str_from_string (struct option_t* dst, const char* val,
321 char* errbuf, size_t errlen) {
325 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
328 str_replace ((char**) dst->data, val);
332 static int user_from_string (struct option_t* dst, const char* val,
333 char* errbuf, size_t errlen) {
334 /* if dst == NULL, we may get here in case the user did unset it,
335 * see parse_set() where item is free()'d before coming here; so
336 * just silently ignore it */
339 if (str_len ((char*) dst->data) == 0)
340 dst->data = (unsigned long) str_dup (val);
342 char* s = (char*) dst->data;
343 str_replace (&s, val);
345 if (str_len (dst->init) == 0)
346 dst->init = str_dup ((char*) dst->data);
350 static void quad_to_string (char* dst, size_t dstlen,
351 struct option_t* option) {
352 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
353 snprintf (dst, dstlen, "%s=%s", option->option,
354 vals[quadoption (option->data)]);
357 static int quad_from_string (struct option_t* dst, const char* val,
358 char* errbuf, size_t errlen) {
363 if (ascii_strncasecmp (val, "yes", 3) == 0)
365 else if (ascii_strncasecmp (val, "no", 2) == 0)
367 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
369 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
375 set_quadoption (dst->data, flag);
379 static void sort_to_string (char* dst, size_t dstlen,
380 struct option_t* option) {
381 const struct mapping_t *map = get_sortmap (option);
382 const char *p = NULL;
385 snprintf (dst, sizeof(dst), "%s=unknown", option->option);
389 p = mutt_getnamebyvalue(*((short *)option->data) & SORT_MASK, map);
391 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
392 (*((short *) option->data) & SORT_REVERSE) ?
394 (*((short *) option->data) & SORT_LAST) ? "last-" :
398 static int sort_from_string (struct option_t* dst, const char* val,
399 char* errbuf, size_t errlen) {
400 const struct mapping_t *map = NULL;
401 if (!(map = get_sortmap (dst))) {
403 snprintf (errbuf, errlen, _("%s: Unknown type."),
407 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
412 static void rx_to_string (char* dst, size_t dstlen,
413 struct option_t* option) {
414 rx_t* p = (rx_t*) option->data;
415 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
416 NONULL (p->pattern));
419 static int rx_from_string (struct option_t* dst, const char* val,
420 char* errbuf, size_t errlen) {
423 int flags = 0, e = 0, not = 0;
429 if (option (OPTATTACHMSG) && !str_cmp (dst->option, "reply_regexp")) {
431 snprintf (errbuf, errlen,
432 "Operation not permitted when in attach-message mode.");
436 if (!((rx_t*) dst->data))
437 *((rx_t**) dst->data) = mem_calloc (1, sizeof(rx_t));
439 p = (rx_t*) dst->data;
441 /* something to do? */
442 if (!val || !*val || (p->pattern && str_cmp (p->pattern, val) == 0))
445 if (str_cmp (dst->option, "mask") != 0)
446 flags |= mutt_which_case (val);
449 if (str_cmp (dst->option, "mask") == 0 && *s == '!') {
454 rx = mem_malloc (sizeof(regex_t));
456 if ((e = REGCOMP (rx, s, flags)) != 0) {
457 regerror (e, rx, errbuf, errlen);
468 str_replace (&p->pattern, val);
472 if (str_cmp (dst->option, "reply_regexp") == 0)
473 mutt_adjust_all_subjects ();
478 static void magic_to_string (char* dst, size_t dstlen,
479 struct option_t* option) {
480 const char* s = NULL;
481 switch (option->data) {
482 case M_MBOX: s = "mbox"; break;
483 case M_MMDF: s = "MMDF"; break;
484 case M_MH: s = "MH"; break;
485 case M_MAILDIR: s = "Maildir"; break;
486 default: s = "unknown"; break;
488 snprintf (dst, dstlen, "%s=%s", option->option, s);
491 static int magic_from_string (struct option_t* dst, const char* val,
492 char* errbuf, size_t errlen) {
495 if (!dst || !val || !*val)
497 if (ascii_strncasecmp (val, "mbox", 4) == 0)
499 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
501 else if (ascii_strncasecmp (val, "mh", 2) == 0)
503 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
509 *((short*) dst->data) = flag;
514 static void addr_to_string (char* dst, size_t dstlen,
515 struct option_t* option) {
518 rfc822_write_address (s, sizeof(s), *((ADDRESS**) option->data), 0);
519 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
522 static int addr_from_string (struct option_t* dst, const char* val,
523 char* errbuf, size_t errlen) {
526 rfc822_free_address ((ADDRESS**) dst->data);
528 *((ADDRESS**) dst->data) = rfc822_parse_adrlist (NULL, val);
532 int mutt_option_value (const char* val, char* dst, size_t dstlen) {
533 struct option_t* option = NULL;
534 char* tmp = NULL, *t = NULL;
537 if (!(option = hash_find (ConfigOptions, val))) {
538 debug_print (1, ("var '%s' not found\n", val));
542 tmp = mem_malloc (dstlen+1);
543 FuncTable[DTYPE (option->type)].opt_to_string (tmp, dstlen, option);
545 /* as we get things of type $var=value and don't want to bloat the
546 * above "just" for expansion, we do the stripping here */
547 debug_print (1, ("orig == '%s'\n", tmp));
548 t = strchr (tmp, '=');
552 if (t[l-1] == '"' && *t == '"') {
557 memcpy (dst, t, l+1);
559 debug_print (1, ("stripped == '%s'\n", dst));
564 /* for synonym warning reports: adds synonym to end of list */
565 static void syn_add (struct option_t* n, struct option_t* o) {
566 syn_t* tmp = mem_malloc (sizeof(syn_t));
567 tmp->f = str_dup (CurRCFile);
571 list_push_back (&Synonyms, tmp);
574 /* for synonym warning reports: free single item (for list_del()) */
575 static void syn_del (void** p) {
576 mem_free(&(*(syn_t**) p)->f);
580 void toggle_quadoption (int opt)
583 int b = (opt % 4) * 2;
585 QuadOptions[n] ^= (1 << b);
588 void set_quadoption (int opt, int flag)
591 int b = (opt % 4) * 2;
593 QuadOptions[n] &= ~(0x3 << b);
594 QuadOptions[n] |= (flag & 0x3) << b;
597 int quadoption (int opt)
600 int b = (opt % 4) * 2;
602 return (QuadOptions[n] >> b) & 0x3;
605 int query_quadoption (int opt, const char *prompt)
607 int v = quadoption (opt);
615 v = mutt_yesorno (prompt, (v == M_ASKYES));
616 CLEARLINE (LINES - 1);
623 static void add_to_list (LIST ** list, const char *str)
625 LIST *t, *last = NULL;
627 /* don't add a NULL or empty string to the list */
628 if (!str || *str == '\0')
631 /* check to make sure the item is not already on this list */
632 for (last = *list; last; last = last->next) {
633 if (ascii_strcasecmp (str, last->data) == 0) {
634 /* already on the list, so just ignore it */
642 if (!*list || last) {
643 t = (LIST *) mem_calloc (1, sizeof(LIST));
644 t->data = str_dup (str);
654 static int add_to_rx_list (list2_t** list, const char *s, int flags,
663 if (!(rx = rx_compile (s, flags))) {
664 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
668 i = rx_lookup ((*list), rx->pattern);
672 list_push_back (list, rx);
676 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
677 const char *templ, BUFFER * err)
679 SPAM_LIST *t = NULL, *last = NULL;
684 if (!pat || !*pat || !templ)
687 if (!(rx = rx_compile (pat, REG_ICASE))) {
688 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
692 /* check to make sure the item is not already on this list */
693 for (last = *list; last; last = last->next) {
694 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
695 /* Already on the list. Formerly we just skipped this case, but
696 * now we're supporting removals, which means we're supporting
697 * re-adds conceptually. So we probably want this to imply a
698 * removal, then do an add. We can achieve the removal by freeing
699 * the template, and leaving t pointed at the current item.
702 mem_free(t->template);
709 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
710 * update. Otherwise we want to make a new one to link at the list's end.
713 t = mutt_new_spam_list ();
721 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
722 t->template = str_dup (templ);
724 /* Find highest match number in template string */
726 for (p = templ; *p;) {
731 while (*p && isdigit ((int) *p))
737 t->nmatch++; /* match 0 is always the whole expr */
742 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
744 SPAM_LIST *spam, *prev;
747 /* Being first is a special case. */
751 if (spam->rx && !str_cmp (spam->rx->pattern, pat)) {
754 mem_free(&spam->template);
760 for (spam = prev->next; spam;) {
761 if (!str_cmp (spam->rx->pattern, pat)) {
762 prev->next = spam->next;
764 mem_free(spam->template);
777 static void remove_from_list (LIST ** l, const char *str)
779 LIST *p, *last = NULL;
781 if (str_cmp ("*", str) == 0)
782 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
787 if (ascii_strcasecmp (str, p->data) == 0) {
790 last->next = p->next;
803 static int remove_from_rx_list (list2_t** l, const char *str)
807 if (str_cmp ("*", str) == 0) {
808 list_del (l, (list_del_t*) rx_free);
812 i = rx_lookup ((*l), str);
814 rx_t* r = list_pop_idx ((*l), i);
822 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
827 struct option_t* option = NULL;
829 memset (&token, 0, sizeof(token));
830 mutt_extract_token (tmp, s, 0);
832 /* is the item defined as a variable or a function? */
833 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL)
836 for (i = 0; !res && i < MENU_MAX; i++) {
837 struct binding_t *b = km_get_table (Menus[i].value);
842 for (j = 0; b[j].name; j++)
843 if (!ascii_strncasecmp (tmp->data, b[j].name, str_len (tmp->data))
844 && (str_len (b[j].name) == str_len (tmp->data))) {
850 /* check for feature_* */
851 if (!res && ascii_strncasecmp (tmp->data, "feature_", 8) == 0 &&
852 (j = str_len (tmp->data)) > 8) {
854 while (Features[i]) {
855 if (str_len (Features[i]) == j-8 &&
856 ascii_strncasecmp (Features[i], tmp->data+8, j-8) == 0) {
866 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
868 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
872 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
875 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
876 mutt_error ("Error: %s", err->data);
877 mem_free (&token.data);
880 mem_free (&token.data);
885 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
889 mutt_extract_token (buf, s, 0);
891 /* don't add "*" to the unignore list */
892 if (strcmp (buf->data, "*"))
893 add_to_list (&UnIgnore, buf->data);
895 remove_from_list (&Ignore, buf->data);
897 while (MoreArgs (s));
902 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
906 mutt_extract_token (buf, s, 0);
907 remove_from_list (&UnIgnore, buf->data);
908 add_to_list (&Ignore, buf->data);
910 while (MoreArgs (s));
915 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
919 mutt_extract_token (buf, s, 0);
920 add_to_list ((LIST **) data, buf->data);
922 while (MoreArgs (s));
927 static void _alternates_clean (void)
931 if (Context && Context->msgcount) {
932 for (i = 0; i < Context->msgcount; i++)
933 Context->hdrs[i]->recip_valid = 0;
937 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
940 _alternates_clean ();
942 mutt_extract_token (buf, s, 0);
943 remove_from_rx_list (&UnAlternates, buf->data);
945 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
948 while (MoreArgs (s));
953 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
956 _alternates_clean ();
958 mutt_extract_token (buf, s, 0);
959 remove_from_rx_list (&Alternates, buf->data);
961 if (str_cmp (buf->data, "*") &&
962 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
966 while (MoreArgs (s));
971 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
976 memset (&templ, 0, sizeof(templ));
978 /* Insist on at least one parameter */
981 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
983 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
987 /* Extract the first token, a regexp */
988 mutt_extract_token (buf, s, 0);
990 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
991 if (data == M_SPAM) {
992 /* If there's a second parameter, it's a template for the spam tag. */
994 mutt_extract_token (&templ, s, 0);
996 /* Add to the spam list. */
997 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
998 mem_free (&templ.data);
1001 mem_free (&templ.data);
1004 /* If not, try to remove from the nospam list. */
1006 remove_from_rx_list (&NoSpamList, buf->data);
1012 /* M_NOSPAM is for nospam commands. */
1013 else if (data == M_NOSPAM) {
1014 /* nospam only ever has one parameter. */
1016 /* "*" is a special case. */
1017 if (!str_cmp (buf->data, "*")) {
1018 mutt_free_spam_list (&SpamList);
1019 list_del (&NoSpamList, (list_del_t*) rx_free);
1023 /* If it's on the spam list, just remove it. */
1024 if (remove_from_spam_list (&SpamList, buf->data) != 0)
1027 /* Otherwise, add it to the nospam list. */
1028 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
1034 /* This should not happen. */
1035 strfcpy (err->data, "This is no good at all.", err->dsize);
1039 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
1043 mutt_extract_token (buf, s, 0);
1045 * Check for deletion of entire list
1047 if (str_cmp (buf->data, "*") == 0) {
1048 mutt_free_list ((LIST **) data);
1051 remove_from_list ((LIST **) data, buf->data);
1053 while (MoreArgs (s));
1058 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
1062 mutt_extract_token (buf, s, 0);
1063 remove_from_rx_list (&UnMailLists, buf->data);
1065 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1068 while (MoreArgs (s));
1073 /* always wise to do what someone else did before */
1074 static void _attachments_clean (void) {
1076 if (Context && Context->msgcount) {
1077 for (i = 0; i < Context->msgcount; i++)
1078 Context->hdrs[i]->attach_valid = 0;
1082 static int parse_attach_list (BUFFER *buf, BUFFER *s, LIST **ldata,
1085 LIST *listp, *lastp;
1090 /* Find the last item in the list that data points to. */
1092 debug_print (5, ("parse_attach_list: ldata = %08x, *ldata = %08x\n",
1093 (unsigned int)ldata, (unsigned int)*ldata));
1094 for (listp = *ldata; listp; listp = listp->next) {
1095 a = (ATTACH_MATCH *)listp->data;
1096 debug_print (5, ("parse_attach_list: skipping %s/%s\n", a->major, a->minor));
1101 mutt_extract_token (buf, s, 0);
1103 if (!buf->data || *buf->data == '\0')
1106 a = mem_malloc(sizeof(ATTACH_MATCH));
1108 /* some cheap hacks that I expect to remove */
1109 if (!str_casecmp(buf->data, "any"))
1110 a->major = str_dup("*/.*");
1111 else if (!str_casecmp(buf->data, "none"))
1112 a->major = str_dup("cheap_hack/this_should_never_match");
1114 a->major = str_dup(buf->data);
1116 if ((p = strchr(a->major, '/'))) {
1121 a->minor = "unknown";
1124 len = str_len (a->minor);
1125 tmpminor = mem_malloc(len+3);
1126 strcpy(&tmpminor[1], a->minor); /* __STRCPY_CHECKED__ */
1128 tmpminor[len+1] = '$';
1129 tmpminor[len+2] = '\0';
1131 a->major_int = mutt_check_mime_type(a->major);
1132 regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
1134 mem_free (&tmpminor);
1136 debug_print (5, ("parse_attach_list: added %s/%s [%d]\n",
1137 a->major, a->minor, a->major_int));
1139 listp = mem_malloc(sizeof(LIST));
1140 listp->data = (char *)a;
1143 lastp->next = listp;
1149 while (MoreArgs (s));
1151 _attachments_clean();
1155 static int parse_unattach_list (BUFFER *buf, BUFFER *s, LIST **ldata, BUFFER *err) {
1157 LIST *lp, *lastp, *newlp;
1163 mutt_extract_token (buf, s, 0);
1165 if (!str_casecmp(buf->data, "any"))
1166 tmp = str_dup("*/.*");
1167 else if (!str_casecmp(buf->data, "none"))
1168 tmp = str_dup("cheap_hack/this_should_never_match");
1170 tmp = str_dup(buf->data);
1172 if ((minor = strchr(tmp, '/'))) {
1178 major = mutt_check_mime_type(tmp);
1180 /* We must do our own walk here because remove_from_list() will only
1181 * remove the LIST->data, not anything pointed to by the LIST->data. */
1183 for(lp = *ldata; lp; ) {
1184 a = (ATTACH_MATCH *)lp->data;
1185 debug_print(5, ("parse_unattach_list: check %s/%s [%d] : %s/%s [%d]\n",
1186 a->major, a->minor, a->major_int, tmp, minor, major));
1187 if (a->major_int == major && !str_casecmp(minor, a->minor)) {
1188 debug_print(5, ("parse_unattach_list: removed %s/%s [%d]\n",
1189 a->major, a->minor, a->major_int));
1190 regfree(&a->minor_rx);
1191 mem_free(&a->major);
1193 /* Relink backward */
1195 lastp->next = lp->next;
1200 mem_free(&lp->data); /* same as a */
1210 while (MoreArgs (s));
1213 _attachments_clean();
1217 static int print_attach_list (LIST *lp, char op, const char *name) {
1219 printf("attachments %c%s %s/%s\n", op, name,
1220 ((ATTACH_MATCH *)lp->data)->major,
1221 ((ATTACH_MATCH *)lp->data)->minor);
1228 static int parse_attachments (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) {
1232 mutt_extract_token(buf, s, 0);
1233 if (!buf->data || *buf->data == '\0') {
1234 strfcpy(err->data, _("attachments: no disposition"), err->dsize);
1238 category = buf->data;
1244 printf("\nCurrent attachments settings:\n\n");
1245 print_attach_list(AttachAllow, '+', "A");
1246 print_attach_list(AttachExclude, '-', "A");
1247 print_attach_list(InlineAllow, '+', "I");
1248 print_attach_list(InlineExclude, '-', "I");
1249 set_option (OPTFORCEREDRAWINDEX);
1250 set_option (OPTFORCEREDRAWPAGER);
1251 mutt_any_key_to_continue (NULL);
1255 if (op != '+' && op != '-') {
1259 if (!str_ncasecmp(category, "attachment", strlen(category))) {
1261 listp = &AttachAllow;
1263 listp = &AttachExclude;
1265 else if (!str_ncasecmp(category, "inline", strlen(category))) {
1267 listp = &InlineAllow;
1269 listp = &InlineExclude;
1271 strfcpy(err->data, _("attachments: invalid disposition"), err->dsize);
1275 return parse_attach_list(buf, s, listp, err);
1278 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) {
1282 mutt_extract_token(buf, s, 0);
1283 if (!buf->data || *buf->data == '\0') {
1284 strfcpy(err->data, _("unattachments: no disposition"), err->dsize);
1290 if (op != '+' && op != '-') {
1294 if (!str_ncasecmp(p, "attachment", strlen(p))) {
1296 listp = &AttachAllow;
1298 listp = &AttachExclude;
1300 else if (!str_ncasecmp(p, "inline", strlen(p))) {
1302 listp = &InlineAllow;
1304 listp = &InlineExclude;
1307 strfcpy(err->data, _("unattachments: invalid disposition"), err->dsize);
1311 return parse_unattach_list(buf, s, listp, err);
1314 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
1318 mutt_extract_token (buf, s, 0);
1319 remove_from_rx_list (&SubscribedLists, buf->data);
1320 remove_from_rx_list (&MailLists, buf->data);
1322 if (str_cmp (buf->data, "*") &&
1323 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1326 while (MoreArgs (s));
1331 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1335 mutt_extract_token (buf, s, 0);
1336 remove_from_rx_list (&UnMailLists, buf->data);
1337 remove_from_rx_list (&UnSubscribedLists, buf->data);
1339 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1341 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1344 while (MoreArgs (s));
1349 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1353 mutt_extract_token (buf, s, 0);
1354 remove_from_rx_list (&SubscribedLists, buf->data);
1356 if (str_cmp (buf->data, "*") &&
1357 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1360 while (MoreArgs (s));
1365 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
1368 ALIAS *tmp, *last = NULL;
1371 mutt_extract_token (buf, s, 0);
1373 if (str_cmp ("*", buf->data) == 0) {
1374 if (CurrentMenu == MENU_ALIAS) {
1375 for (tmp = Aliases; tmp; tmp = tmp->next)
1377 set_option (OPTFORCEREDRAWINDEX);
1380 mutt_free_alias (&Aliases);
1384 for (tmp = Aliases; tmp; tmp = tmp->next) {
1385 if (str_casecmp (buf->data, tmp->name) == 0) {
1386 if (CurrentMenu == MENU_ALIAS) {
1388 set_option (OPTFORCEREDRAWINDEX);
1393 last->next = tmp->next;
1395 Aliases = tmp->next;
1397 mutt_free_alias (&tmp);
1403 while (MoreArgs (s));
1407 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
1410 ALIAS *tmp = Aliases;
1414 if (!MoreArgs (s)) {
1415 strfcpy (err->data, _("alias: no address"), err->dsize);
1419 mutt_extract_token (buf, s, 0);
1421 debug_print (2, ("first token is '%s'.\n", buf->data));
1423 /* check to see if an alias with this name already exists */
1424 for (; tmp; tmp = tmp->next) {
1425 if (!str_casecmp (tmp->name, buf->data))
1431 /* create a new alias */
1432 tmp = (ALIAS *) mem_calloc (1, sizeof(ALIAS));
1434 tmp->name = str_dup (buf->data);
1435 /* give the main addressbook code a chance */
1436 if (CurrentMenu == MENU_ALIAS)
1437 set_option (OPTMENUCALLER);
1440 /* override the previous value */
1441 rfc822_free_address (&tmp->addr);
1442 if (CurrentMenu == MENU_ALIAS)
1443 set_option (OPTFORCEREDRAWINDEX);
1446 mutt_extract_token (buf, s,
1447 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1448 debug_print (2, ("second token is '%s'.\n", buf->data));
1449 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1454 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1455 snprintf (err->data, err->dsize,
1456 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1460 if (DebugLevel >= 2) {
1463 /* A group is terminated with an empty address, so check a->mailbox */
1464 for (a = tmp->addr; a && a->mailbox; a = a->next) {
1466 debug_print (2, ("%s\n", a->mailbox));
1468 debug_print (2, ("group %s\n", a->mailbox));
1476 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
1479 LIST *tmp = UserHeader;
1484 mutt_extract_token (buf, s, 0);
1485 if (str_cmp ("*", buf->data) == 0)
1486 mutt_free_list (&UserHeader);
1491 l = str_len (buf->data);
1492 if (buf->data[l - 1] == ':')
1496 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1497 && tmp->data[l] == ':') {
1500 last->next = tmp->next;
1502 UserHeader = tmp->next;
1505 mutt_free_list (&ptr);
1514 while (MoreArgs (s));
1518 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
1525 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1526 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1527 strfcpy (err->data, _("invalid header field"), err->dsize);
1530 keylen = p - buf->data + 1;
1533 for (tmp = UserHeader;; tmp = tmp->next) {
1534 /* see if there is already a field by this name */
1535 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1536 /* replace the old value */
1537 mem_free (&tmp->data);
1538 tmp->data = buf->data;
1539 memset (buf, 0, sizeof(BUFFER));
1545 tmp->next = mutt_new_list ();
1549 tmp = mutt_new_list ();
1552 tmp->data = buf->data;
1553 memset (buf, 0, sizeof(BUFFER));
1558 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1559 char* errbuf, size_t errlen) {
1562 if (str_ncmp ("reverse-", s, 8) == 0) {
1564 flags = SORT_REVERSE;
1567 if (str_ncmp ("last-", s, 5) == 0) {
1572 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1574 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1578 *((short*) dst->data) = i | flags;
1582 /* if additional data more == 1, we want to resolve synonyms */
1583 static void mutt_set_default(const char *name, void* p, unsigned long more)
1585 char buf[LONG_STRING];
1586 struct option_t *ptr = p;
1588 if (DTYPE(ptr->type) == DT_SYN) {
1591 ptr = hash_find(ConfigOptions, (const char *)ptr->data);
1593 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_from_string)
1596 mutt_option_value(ptr->option, buf, sizeof(buf));
1597 if (str_len(ptr->init) == 0 && buf && *buf)
1598 ptr->init = str_dup(buf);
1601 static struct option_t* add_option (const char* name, const char* init,
1602 short type, short dodup) {
1603 struct option_t* option = mem_calloc (1, sizeof(struct option_t));
1605 debug_print (1, ("adding $%s\n", name));
1607 option->option = str_dup (name);
1608 option->type = type;
1610 option->init = dodup ? str_dup (init) : (char*) init;
1614 /* creates new option_t* of type DT_USER for $user_ var */
1615 static struct option_t* add_user_option (const char* name) {
1616 return (add_option (name, NULL, DT_USER, 1));
1619 /* free()'s option_t* */
1620 static void del_option (void* p) {
1621 struct option_t* ptr = (struct option_t*) p;
1622 char* s = (char*) ptr->data;
1623 debug_print (1, ("removing option '%s' from table\n", NONULL (ptr->option)));
1624 mem_free (&ptr->option);
1626 mem_free (&ptr->init);
1630 static int init_expand (char** dst, struct option_t* src) {
1636 if (DTYPE(src->type) == DT_STR ||
1637 DTYPE(src->type) == DT_PATH) {
1638 /* only expand for string as it's the only place where
1639 * we want to expand vars right now */
1640 if (src->init && *src->init) {
1641 memset (&token, 0, sizeof(BUFFER));
1642 memset (&in, 0, sizeof(BUFFER));
1643 len = str_len (src->init) + 2;
1644 in.data = mem_malloc (len+1);
1645 snprintf (in.data, len, "\"%s\"", src->init);
1648 mutt_extract_token (&token, &in, 0);
1649 if (token.data && *token.data)
1650 *dst = str_dup (token.data);
1652 *dst = str_dup ("");
1653 mem_free (&in.data);
1654 mem_free (&token.data);
1656 *dst = str_dup ("");
1658 /* for non-string: take value as is */
1659 *dst = str_dup (src->init);
1663 /* if additional data more == 1, we want to resolve synonyms */
1664 static void mutt_restore_default (const char* name, void* p,
1665 unsigned long more) {
1666 char errbuf[STRING];
1667 struct option_t* ptr = (struct option_t*) p;
1670 if (DTYPE (ptr->type) == DT_SYN) {
1673 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1677 if (FuncTable[DTYPE (ptr->type)].opt_from_string) {
1678 init_expand (&init, ptr);
1679 if (!FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, init, errbuf,
1681 if (!option (OPTNOCURSES))
1683 fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1684 "Please report this error: \"%s\"\n"),
1685 ptr->option, NONULL (init), errbuf);
1691 if (ptr->flags & R_INDEX)
1692 set_option (OPTFORCEREDRAWINDEX);
1693 if (ptr->flags & R_PAGER)
1694 set_option (OPTFORCEREDRAWPAGER);
1695 if (ptr->flags & R_RESORT_SUB)
1696 set_option (OPTSORTSUBTHREADS);
1697 if (ptr->flags & R_RESORT)
1698 set_option (OPTNEEDRESORT);
1699 if (ptr->flags & R_RESORT_INIT)
1700 set_option (OPTRESORTINIT);
1701 if (ptr->flags & R_TREE)
1702 set_option (OPTREDRAWTREE);
1705 /* check whether value for $dsn_return would be valid */
1706 static int check_dsn_return (const char* option, unsigned long p,
1707 char* errbuf, size_t errlen) {
1708 char* val = (char*) p;
1709 if (val && *val && str_ncmp (val, "hdrs", 4) != 0 &&
1710 str_ncmp (val, "full", 4) != 0) {
1712 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1718 /* check whether value for $dsn_notify would be valid */
1719 static int check_dsn_notify (const char* option, unsigned long p,
1720 char* errbuf, size_t errlen) {
1721 list2_t* list = NULL;
1723 char* val = (char*) p;
1727 list = list_from_str (val, ",");
1728 if (list_empty (list))
1731 for (i = 0; i < list->length; i++)
1732 if (str_ncmp (list->data[i], "never", 5) != 0 &&
1733 str_ncmp (list->data[i], "failure", 7) != 0 &&
1734 str_ncmp (list->data[i], "delay", 5) != 0 &&
1735 str_ncmp (list->data[i], "success", 7) != 0) {
1737 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1738 (char*) list->data[i], "dsn_notify");
1742 list_del (&list, (list_del_t*) _mem_free);
1746 static int check_num (const char* option, unsigned long p,
1747 char* errbuf, size_t errlen) {
1750 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1757 static int check_debug (const char* option, unsigned long p,
1758 char* errbuf, size_t errlen) {
1759 if ((int) p <= DEBUG_MAX_LEVEL &&
1760 (int) p >= DEBUG_MIN_LEVEL)
1764 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1769 static int check_history (const char* option, unsigned long p,
1770 char* errbuf, size_t errlen) {
1771 if (!check_num ("history", p, errbuf, errlen))
1773 mutt_init_history ();
1777 static int check_special (const char* name, unsigned long val,
1778 char* errbuf, size_t errlen) {
1781 for (i = 0; SpecialVars[i].name; i++) {
1782 if (str_cmp (SpecialVars[i].name, name) == 0) {
1783 return (SpecialVars[i].check (SpecialVars[i].name,
1784 val, errbuf, errlen));
1790 static const struct mapping_t* get_sortmap (struct option_t* option) {
1791 const struct mapping_t* map = NULL;
1793 switch (option->type & DT_SUBTYPE_MASK) {
1795 map = SortAliasMethods;
1797 case DT_SORT_BROWSER:
1798 map = SortBrowserMethods;
1801 if ((WithCrypto & APPLICATION_PGP))
1802 map = SortKeyMethods;
1805 map = SortAuxMethods;
1814 #define CHECK_PAGER \
1815 if ((CurrentMenu == MENU_PAGER) && \
1816 (!option || (option->flags & R_RESORT))) \
1818 snprintf (err->data, err->dsize, \
1819 _("Not available in this menu.")); \
1823 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1826 int query, unset, inv, reset, r = 0;
1827 struct option_t* option = NULL;
1829 while (MoreArgs (s)) {
1830 /* reset state variables */
1832 unset = data & M_SET_UNSET;
1833 inv = data & M_SET_INV;
1834 reset = data & M_SET_RESET;
1836 if (*s->dptr == '?') {
1840 else if (str_ncmp ("no", s->dptr, 2) == 0) {
1844 else if (str_ncmp ("inv", s->dptr, 3) == 0) {
1848 else if (*s->dptr == '&') {
1853 /* get the variable name */
1854 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1856 /* resolve synonyms */
1857 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1858 DTYPE (option->type == DT_SYN)) {
1859 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1860 syn_add (newopt, option);
1864 /* see if we need to add $user_ var */
1865 if (!option && ascii_strncmp ("user_", tmp->data, 5) == 0) {
1866 /* there's no option named like this yet so only add one
1867 * if the action isn't any of: reset, unset, query */
1868 if (!(reset || unset || query || *s->dptr != '=')) {
1869 debug_print (1, ("adding user option '%s'\n", tmp->data));
1870 option = add_user_option (tmp->data);
1871 hash_insert (ConfigOptions, option->option, option, 0);
1875 if (!option && !(reset && str_cmp ("all", tmp->data) == 0)) {
1876 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1882 if (query || unset || inv) {
1883 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1887 if (s && *s->dptr == '=') {
1888 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1892 if (!str_cmp ("all", tmp->data)) {
1893 if (CurrentMenu == MENU_PAGER) {
1894 snprintf (err->data, err->dsize, _("Not available in this menu."));
1897 hash_map (ConfigOptions, mutt_restore_default, 1);
1898 set_option (OPTFORCEREDRAWINDEX);
1899 set_option (OPTFORCEREDRAWPAGER);
1900 set_option (OPTSORTSUBTHREADS);
1901 set_option (OPTNEEDRESORT);
1902 set_option (OPTRESORTINIT);
1903 set_option (OPTREDRAWTREE);
1906 else if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1907 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1912 mutt_restore_default (NULL, option, 1);
1915 else if (DTYPE (option->type) == DT_BOOL) {
1916 /* XXX this currently ignores the function table
1917 * as we don't get invert and stuff into it */
1918 if (s && *s->dptr == '=') {
1919 if (unset || inv || query) {
1920 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1925 mutt_extract_token (tmp, s, 0);
1926 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1928 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1931 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1937 bool_to_string (err->data, err->dsize, option);
1943 unset_option (option->data);
1945 toggle_option (option->data);
1947 set_option (option->data);
1949 else if (DTYPE (option->type) == DT_STR ||
1950 DTYPE (option->type) == DT_PATH ||
1951 DTYPE (option->type) == DT_ADDR ||
1952 DTYPE (option->type) == DT_MAGIC ||
1953 DTYPE (option->type) == DT_NUM ||
1954 DTYPE (option->type) == DT_SORT ||
1955 DTYPE (option->type) == DT_RX ||
1956 DTYPE (option->type) == DT_USER ||
1957 DTYPE (option->type) == DT_SYS) {
1959 /* XXX maybe we need to get unset into handlers? */
1960 if (DTYPE (option->type) == DT_STR ||
1961 DTYPE (option->type) == DT_PATH ||
1962 DTYPE (option->type) == DT_ADDR ||
1963 DTYPE (option->type) == DT_USER ||
1964 DTYPE (option->type) == DT_SYS) {
1967 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1968 snprintf (err->data, err->dsize, _("$%s is read-only"),
1972 } else if (DTYPE (option->type) == DT_ADDR)
1973 rfc822_free_address ((ADDRESS **) option->data);
1974 else if (DTYPE (option->type) == DT_USER)
1975 /* to unset $user_ means remove */
1976 hash_delete (ConfigOptions, option->option,
1977 option, del_option);
1979 mem_free ((void *) option->data);
1984 if (query || *s->dptr != '=') {
1985 FuncTable[DTYPE (option->type)].opt_to_string
1986 (err->data, err->dsize, option);
1990 /* the $muttng_ variables are read-only */
1991 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1992 snprintf (err->data, err->dsize, _("$%s is read-only"),
1999 mutt_extract_token (tmp, s, 0);
2000 if (!FuncTable[DTYPE (option->type)].opt_from_string
2001 (option, tmp->data, err->data, err->dsize))
2005 else if (DTYPE (option->type) == DT_QUAD) {
2008 quad_to_string (err->data, err->dsize, option);
2012 if (*s->dptr == '=') {
2015 mutt_extract_token (tmp, s, 0);
2016 if (ascii_strcasecmp ("yes", tmp->data) == 0)
2017 set_quadoption (option->data, M_YES);
2018 else if (ascii_strcasecmp ("no", tmp->data) == 0)
2019 set_quadoption (option->data, M_NO);
2020 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
2021 set_quadoption (option->data, M_ASKYES);
2022 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
2023 set_quadoption (option->data, M_ASKNO);
2025 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
2026 tmp->data, option->option);
2033 toggle_quadoption (option->data);
2035 set_quadoption (option->data, M_NO);
2037 set_quadoption (option->data, M_YES);
2041 snprintf (err->data, err->dsize, _("%s: unknown type"),
2047 if (option->flags & R_INDEX)
2048 set_option (OPTFORCEREDRAWINDEX);
2049 if (option->flags & R_PAGER)
2050 set_option (OPTFORCEREDRAWPAGER);
2051 if (option->flags & R_RESORT_SUB)
2052 set_option (OPTSORTSUBTHREADS);
2053 if (option->flags & R_RESORT)
2054 set_option (OPTNEEDRESORT);
2055 if (option->flags & R_RESORT_INIT)
2056 set_option (OPTRESORTINIT);
2057 if (option->flags & R_TREE)
2058 set_option (OPTREDRAWTREE);
2065 /* reads the specified initialization file. returns -1 if errors were found
2066 so that we can pause to let the user know... */
2067 static int source_rc (const char *rcfile, BUFFER * err)
2070 int line = 0, rc = 0, conv = 0;
2072 char *linebuf = NULL;
2073 char *currentline = NULL;
2077 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
2079 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
2080 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
2084 memset (&token, 0, sizeof(token));
2085 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
2086 conv = ConfigCharset && (*ConfigCharset) && Charset;
2088 currentline = str_dup (linebuf);
2091 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
2094 currentline = linebuf;
2099 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
2100 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
2101 if (--rc < -MAXERRS) {
2103 mem_free (¤tline);
2112 mem_free (¤tline);
2114 mem_free (&token.data);
2115 mem_free (&linebuf);
2118 mutt_wait_filter (pid);
2120 /* the muttrc source keyword */
2121 snprintf (err->data, err->dsize,
2122 rc >= -MAXERRS ? _("source: errors in %s")
2123 : _("source: reading aborted due too many errors in %s"),
2132 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
2135 char path[_POSIX_PATH_MAX];
2139 if (mutt_extract_token (tmp, s, 0) != 0) {
2140 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
2144 strfcpy (path, tmp->data, sizeof(path));
2145 mutt_expand_path (path, sizeof(path));
2147 rc += source_rc (path, err);
2149 while (MoreArgs (s));
2151 return ((rc < 0) ? -1 : 0);
2154 /* line command to execute
2156 token scratch buffer to be used by parser. caller should free
2157 token->data when finished. the reason for this variable is
2158 to avoid having to allocate and deallocate a lot of memory
2159 if we are parsing many lines. the caller can pass in the
2160 memory to use, which avoids having to create new space for
2161 every call to this function.
2163 err where to write error messages */
2164 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
2169 memset (&expn, 0, sizeof(expn));
2170 expn.data = expn.dptr = line;
2171 expn.dsize = str_len (line);
2175 debug_print (1, ("expand '%s'\n", line));
2178 while (*expn.dptr) {
2179 if (*expn.dptr == '#')
2180 break; /* rest of line is a comment */
2181 if (*expn.dptr == ';') {
2185 mutt_extract_token (token, &expn, 0);
2186 for (i = 0; Commands[i].name; i++) {
2187 if (!str_cmp (token->data, Commands[i].name)) {
2188 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
2193 if (!Commands[i].name) {
2194 snprintf (err->data, err->dsize, _("%s: unknown command"),
2195 NONULL (token->data));
2202 mem_free (&expn.data);
2207 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
2208 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
2209 /* initial string that starts completion. No telling how much crap
2210 * the user has typed so far. Allocate LONG_STRING just to be sure! */
2211 char User_typed[LONG_STRING] = { 0 };
2213 int Num_matched = 0; /* Number of matches for completion */
2214 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
2215 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
2217 /* helper function for completion. Changes the dest buffer if
2218 necessary/possible to aid completion.
2219 dest == completion result gets here.
2220 src == candidate for completion.
2221 try == user entered data for completion.
2222 len == length of dest buffer.
2224 static void candidate (char *dest, char *try, const char *src, int len)
2228 if (strstr (src, try) == src) {
2229 Matches[Num_matched++] = src;
2231 strfcpy (dest, src, len);
2233 for (l = 0; src[l] && src[l] == dest[l]; l++);
2239 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
2243 int spaces; /* keep track of the number of leading spaces on the line */
2246 spaces = buffer - pt;
2248 pt = buffer + pos - spaces;
2249 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2252 if (pt == buffer) { /* complete cmd */
2253 /* first TAB. Collect all the matches */
2256 strfcpy (User_typed, pt, sizeof(User_typed));
2257 memset (Matches, 0, sizeof(Matches));
2258 memset (Completed, 0, sizeof(Completed));
2259 for (num = 0; Commands[num].name; num++)
2260 candidate (Completed, User_typed, Commands[num].name,
2262 Matches[Num_matched++] = User_typed;
2264 /* All matches are stored. Longest non-ambiguous string is ""
2265 * i.e. dont change 'buffer'. Fake successful return this time */
2266 if (User_typed[0] == 0)
2270 if (Completed[0] == 0 && User_typed[0])
2273 /* Num_matched will _always_ be atleast 1 since the initial
2274 * user-typed string is always stored */
2275 if (numtabs == 1 && Num_matched == 2)
2276 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2277 else if (numtabs > 1 && Num_matched > 2)
2278 /* cycle thru all the matches */
2279 snprintf (Completed, sizeof(Completed), "%s",
2280 Matches[(numtabs - 2) % Num_matched]);
2282 /* return the completed command */
2283 strncpy (buffer, Completed, len - spaces);
2285 else if (!str_ncmp (buffer, "set", 3)
2286 || !str_ncmp (buffer, "unset", 5)
2287 || !str_ncmp (buffer, "reset", 5)
2288 || !str_ncmp (buffer, "toggle", 6)) { /* complete variables */
2289 const char *prefixes[] = { "no", "inv", "?", "&", NULL };
2292 /* loop through all the possible prefixes (no, inv, ...) */
2293 if (!str_ncmp (buffer, "set", 3)) {
2294 for (num = 0; prefixes[num]; num++) {
2295 if (!str_ncmp (pt, prefixes[num], str_len (prefixes[num]))) {
2296 pt += str_len (prefixes[num]);
2302 /* first TAB. Collect all the matches */
2305 strfcpy (User_typed, pt, sizeof(User_typed));
2306 memset (Matches, 0, sizeof(Matches));
2307 memset (Completed, 0, sizeof(Completed));
2308 for (num = 0; MuttVars[num].option; num++)
2309 candidate (Completed, User_typed, MuttVars[num].option,
2311 Matches[Num_matched++] = User_typed;
2313 /* All matches are stored. Longest non-ambiguous string is ""
2314 * i.e. dont change 'buffer'. Fake successful return this time */
2315 if (User_typed[0] == 0)
2319 if (Completed[0] == 0 && User_typed[0])
2322 /* Num_matched will _always_ be atleast 1 since the initial
2323 * user-typed string is always stored */
2324 if (numtabs == 1 && Num_matched == 2)
2325 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2326 else if (numtabs > 1 && Num_matched > 2)
2327 /* cycle thru all the matches */
2328 snprintf (Completed, sizeof(Completed), "%s",
2329 Matches[(numtabs - 2) % Num_matched]);
2331 strncpy (pt, Completed, buffer + len - pt - spaces);
2333 else if (!str_ncmp (buffer, "exec", 4)) {
2334 struct binding_t *menu = km_get_table (CurrentMenu);
2336 if (!menu && CurrentMenu != MENU_PAGER)
2340 /* first TAB. Collect all the matches */
2343 strfcpy (User_typed, pt, sizeof(User_typed));
2344 memset (Matches, 0, sizeof(Matches));
2345 memset (Completed, 0, sizeof(Completed));
2346 for (num = 0; menu[num].name; num++)
2347 candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
2348 /* try the generic menu */
2349 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2351 for (num = 0; menu[num].name; num++)
2352 candidate (Completed, User_typed, menu[num].name,
2355 Matches[Num_matched++] = User_typed;
2357 /* All matches are stored. Longest non-ambiguous string is ""
2358 * i.e. dont change 'buffer'. Fake successful return this time */
2359 if (User_typed[0] == 0)
2363 if (Completed[0] == 0 && User_typed[0])
2366 /* Num_matched will _always_ be atleast 1 since the initial
2367 * user-typed string is always stored */
2368 if (numtabs == 1 && Num_matched == 2)
2369 snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
2370 else if (numtabs > 1 && Num_matched > 2)
2371 /* cycle thru all the matches */
2372 snprintf (Completed, sizeof(Completed), "%s",
2373 Matches[(numtabs - 2) % Num_matched]);
2375 strncpy (pt, Completed, buffer + len - pt - spaces);
2383 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2385 char var[STRING], *pt = buffer;
2387 struct option_t* option = NULL;
2393 spaces = buffer - pt;
2395 pt = buffer + pos - spaces;
2396 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2398 pt++; /* move past the space */
2399 if (*pt == '=') /* abort if no var before the '=' */
2402 if (str_ncmp (buffer, "set", 3) == 0) {
2403 strfcpy (var, pt, sizeof(var));
2404 /* ignore the trailing '=' when comparing */
2405 var[str_len (var) - 1] = 0;
2406 if (!(option = hash_find (ConfigOptions, var)))
2407 return 0; /* no such variable. */
2409 char tmp[LONG_STRING], tmp2[LONG_STRING];
2411 size_t dlen = buffer + len - pt - spaces;
2412 const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2416 if ((DTYPE (option->type) == DT_STR) ||
2417 (DTYPE (option->type) == DT_PATH) ||
2418 (DTYPE (option->type) == DT_RX)) {
2419 strfcpy (tmp, NONULL (*((char **) option->data)), sizeof(tmp));
2420 if (DTYPE (option->type) == DT_PATH)
2421 mutt_pretty_mailbox (tmp);
2423 else if (DTYPE (option->type) == DT_ADDR) {
2424 rfc822_write_address (tmp, sizeof(tmp),
2425 *((ADDRESS **) option->data), 0);
2427 else if (DTYPE (option->type) == DT_QUAD)
2428 strfcpy (tmp, vals[quadoption (option->data)], sizeof(tmp));
2429 else if (DTYPE (option->type) == DT_NUM)
2430 snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
2431 else if (DTYPE (option->type) == DT_SORT) {
2432 const struct mapping_t *map;
2435 switch (option->type & DT_SUBTYPE_MASK) {
2437 map = SortAliasMethods;
2439 case DT_SORT_BROWSER:
2440 map = SortBrowserMethods;
2443 if ((WithCrypto & APPLICATION_PGP))
2444 map = SortKeyMethods;
2452 p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2453 snprintf(tmp, sizeof(tmp), "%s%s%s",
2454 (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2455 (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2457 else if (DTYPE (option->type) == DT_MAGIC) {
2459 switch (DefaultMagic) {
2475 strfcpy (tmp, p, sizeof(tmp));
2477 else if (DTYPE (option->type) == DT_BOOL)
2478 strfcpy (tmp, option (option->data) ? "yes" : "no",
2483 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof(tmp2) - 2;) {
2484 if (*s == '\\' || *s == '"')
2490 strfcpy (tmp, pt, sizeof(tmp));
2491 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2499 /* Implement the -Q command line flag */
2500 int mutt_query_variables (LIST * queries)
2504 char errbuff[STRING];
2505 char command[STRING];
2509 memset (&err, 0, sizeof(err));
2510 memset (&token, 0, sizeof(token));
2513 err.dsize = sizeof(errbuff);
2515 for (p = queries; p; p = p->next) {
2516 snprintf (command, sizeof(command), "set ?%s\n", p->data);
2517 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2518 fprintf (stderr, "%s\n", err.data);
2519 mem_free (&token.data);
2522 printf ("%s\n", err.data);
2525 mem_free (&token.data);
2529 const char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2533 for (i = 0; map[i].name; i++)
2534 if (map[i].value == val)
2535 return (map[i].name);
2539 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2543 for (i = 0; map[i].name; i++)
2544 if (ascii_strcasecmp (map[i].name, name) == 0)
2545 return (map[i].value);
2549 static int mutt_execute_commands (LIST * p)
2552 char errstr[SHORT_STRING];
2554 memset (&err, 0, sizeof(err));
2556 err.dsize = sizeof(errstr);
2557 memset (&token, 0, sizeof(token));
2558 for (; p; p = p->next) {
2559 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2560 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2561 mem_free (&token.data);
2565 mem_free (&token.data);
2569 void mutt_init (int skip_sys_rc, LIST * commands)
2572 struct utsname utsname;
2574 char buffer[STRING], error[STRING];
2575 int i, default_rc = 0, need_pause = 0;
2578 memset (&err, 0, sizeof(err));
2580 err.dsize = sizeof(error);
2582 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2583 * to have some room for $user_ vars */
2584 ConfigOptions = hash_create (sizeof(MuttVars) * 3);
2585 for (i = 0; MuttVars[i].option; i++) {
2586 if (DTYPE (MuttVars[i].type) != DT_SYS)
2587 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2589 hash_insert (ConfigOptions, MuttVars[i].option,
2590 add_option (MuttVars[i].option, MuttVars[i].init,
2595 * XXX - use something even more difficult to predict?
2597 snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2598 "\033]9;%ld\a", (long) time (NULL));
2600 /* on one of the systems I use, getcwd() does not return the same prefix
2601 as is listed in the passwd file */
2602 if ((p = getenv ("HOME")))
2603 Homedir = str_dup (p);
2605 /* Get some information about the user */
2606 if ((pw = getpwuid (getuid ()))) {
2609 Username = str_dup (pw->pw_name);
2611 Homedir = str_dup (pw->pw_dir);
2613 Realname = str_dup (mutt_gecos_name (rnbuf, sizeof(rnbuf), pw));
2614 Shell = str_dup (pw->pw_shell);
2620 fputs (_("unable to determine home directory"), stderr);
2623 if ((p = getenv ("USER")))
2624 Username = str_dup (p);
2627 fputs (_("unable to determine username"), stderr);
2630 Shell = str_dup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2633 debug_start(Homedir);
2635 /* And about the host... */
2637 /* some systems report the FQDN instead of just the hostname */
2638 if ((p = strchr (utsname.nodename, '.'))) {
2639 Hostname = str_substrdup (utsname.nodename, p);
2641 strfcpy (buffer, p, sizeof(buffer)); /* save the domain for below */
2644 Hostname = str_dup (utsname.nodename);
2647 #define DOMAIN buffer
2648 if (!p && getdnsdomainname (buffer, sizeof(buffer)) == -1)
2649 Fqdn = str_dup ("@");
2652 if (*DOMAIN != '@') {
2653 Fqdn = mem_malloc (str_len (DOMAIN) + str_len (Hostname) + 2);
2654 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2657 Fqdn = str_dup (NONULL (Hostname));
2664 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2666 fgets (buffer, sizeof(buffer), f);
2670 while (*q && !isspace(*q))
2673 NewsServer = str_dup (p);
2677 if ((p = getenv ("NNTPSERVER")))
2678 NewsServer = str_dup (p);
2681 if ((p = getenv ("MAIL")))
2682 Spoolfile = str_dup (p);
2683 else if ((p = getenv ("MAILDIR")))
2684 Spoolfile = str_dup (p);
2687 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof(buffer));
2689 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof(buffer));
2691 Spoolfile = str_dup (buffer);
2694 if ((p = getenv ("MAILCAPS")))
2695 MailcapPath = str_dup (p);
2697 /* Default search path from RFC1524 */
2699 str_dup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2700 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2703 Tempdir = str_dup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2705 p = getenv ("VISUAL");
2707 p = getenv ("EDITOR");
2711 Editor = str_dup (p);
2712 Visual = str_dup (p);
2714 if ((p = getenv ("REPLYTO")) != NULL) {
2717 snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2719 memset (&buf, 0, sizeof(buf));
2720 buf.data = buf.dptr = buffer;
2721 buf.dsize = str_len (buffer);
2723 memset (&token, 0, sizeof(token));
2724 parse_my_hdr (&token, &buf, 0, &err);
2725 mem_free (&token.data);
2728 if ((p = getenv ("EMAIL")) != NULL)
2729 From = rfc822_parse_adrlist (NULL, p);
2731 mutt_set_langinfo_charset ();
2732 mutt_set_charset (Charset);
2735 /* Set standard defaults */
2736 hash_map (ConfigOptions, mutt_set_default, 0);
2737 hash_map (ConfigOptions, mutt_restore_default, 0);
2739 CurrentMenu = MENU_MAIN;
2742 #ifndef LOCALES_HACK
2743 /* Do we have a locale definition? */
2744 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2745 ((p = getenv ("LANG")) != NULL && p[0]) ||
2746 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2747 set_option (OPTLOCALES);
2751 /* Unset suspend by default if we're the session leader */
2752 if (getsid (0) == getpid ())
2753 unset_option (OPTSUSPEND);
2756 mutt_init_history ();
2765 * When changing the code which looks for a configuration file,
2766 * please also change the corresponding code in muttbug.sh.in.
2776 snprintf (buffer, sizeof(buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2778 if (access (buffer, F_OK) == -1)
2780 snprintf (buffer, sizeof(buffer), "%s/.muttngrc", NONULL (Homedir));
2781 if (access (buffer, F_OK) == -1)
2783 snprintf (buffer, sizeof(buffer), "%s/.muttng/muttngrc-%s",
2784 NONULL (Homedir), MUTT_VERSION);
2785 if (access (buffer, F_OK) == -1)
2787 snprintf (buffer, sizeof(buffer), "%s/.muttng/muttngrc",
2791 Muttrc = str_dup (buffer);
2794 strfcpy (buffer, Muttrc, sizeof(buffer));
2796 mutt_expand_path (buffer, sizeof(buffer));
2797 Muttrc = str_dup (buffer);
2799 mem_free (&AliasFile);
2800 AliasFile = str_dup (NONULL (Muttrc));
2802 /* Process the global rc file if it exists and the user hasn't explicity
2803 requested not to via "-n". */
2805 snprintf (buffer, sizeof(buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2807 if (access (buffer, F_OK) == -1)
2808 snprintf (buffer, sizeof(buffer), "%s/Muttngrc", SYSCONFDIR);
2809 if (access (buffer, F_OK) == -1)
2810 snprintf (buffer, sizeof(buffer), "%s/Muttngrc-%s", PKGDATADIR,
2812 if (access (buffer, F_OK) == -1)
2813 snprintf (buffer, sizeof(buffer), "%s/Muttngrc", PKGDATADIR);
2814 if (access (buffer, F_OK) != -1) {
2815 if (source_rc (buffer, &err) != 0) {
2816 fputs (err.data, stderr);
2817 fputc ('\n', stderr);
2823 /* Read the user's initialization file. */
2824 if (access (Muttrc, F_OK) != -1) {
2825 if (!option (OPTNOCURSES))
2827 if (source_rc (Muttrc, &err) != 0) {
2828 fputs (err.data, stderr);
2829 fputc ('\n', stderr);
2833 else if (!default_rc) {
2834 /* file specified by -F does not exist */
2835 snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2836 mutt_endwin (buffer);
2840 if (mutt_execute_commands (commands) != 0)
2843 /* warn about synonym variables */
2844 if (!list_empty(Synonyms)) {
2846 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2847 for (i = 0; i < Synonyms->length; i++) {
2848 struct option_t* newopt = NULL, *oldopt = NULL;
2849 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2850 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2851 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2852 oldopt ? NONULL (oldopt->option) : "",
2853 newopt ? NONULL (newopt->option) : "",
2854 NONULL(((syn_t*) Synonyms->data[i])->f),
2855 ((syn_t*) Synonyms->data[i])->l);
2857 fprintf (stderr, _("Warning: synonym variables are scheduled"
2858 " for removal.\n"));
2859 list_del (&Synonyms, syn_del);
2863 if (need_pause && !option (OPTNOCURSES)) {
2864 if (mutt_any_key_to_continue (NULL) == -1)
2869 set_option (OPTWEED); /* turn weeding on by default */
2873 int mutt_get_hook_type (const char *name)
2875 struct command_t *c;
2877 for (c = Commands; c->name; c++)
2878 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2883 /* compare two option_t*'s for sorting -t/-T output */
2884 static int opt_cmp (const void* a, const void* b) {
2885 return (str_cmp ((*(struct option_t**) a)->option,
2886 (*(struct option_t**) b)->option));
2889 /* callback for hash_map() to put all non-synonym vars into list */
2890 static void opt_sel_full (const char* key, void* data,
2891 unsigned long more) {
2892 list2_t** l = (list2_t**) more;
2893 struct option_t* option = (struct option_t*) data;
2895 if (DTYPE (option->type) == DT_SYN)
2897 list_push_back (l, option);
2900 /* callback for hash_map() to put all changed non-synonym vars into list */
2901 static void opt_sel_diff (const char* key, void* data,
2902 unsigned long more) {
2903 list2_t** l = (list2_t**) more;
2904 struct option_t* option = (struct option_t*) data;
2905 char buf[LONG_STRING];
2907 if (DTYPE (option->type) == DT_SYN)
2910 mutt_option_value (option->option, buf, sizeof(buf));
2911 if (str_cmp (buf, option->init) != 0)
2912 list_push_back (l, option);
2915 /* dump out the value of all the variables we have */
2916 int mutt_dump_variables (int full) {
2918 char outbuf[STRING];
2919 list2_t* tmp = NULL;
2920 struct option_t* option = NULL;
2922 /* get all non-synonyms into list... */
2923 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2924 (unsigned long) &tmp);
2926 if (!list_empty(tmp)) {
2927 /* ...and dump list sorted */
2928 qsort (tmp->data, tmp->length, sizeof(void*), opt_cmp);
2929 for (i = 0; i < tmp->length; i++) {
2930 option = (struct option_t*) tmp->data[i];
2931 FuncTable[DTYPE (option->type)].opt_to_string
2932 (outbuf, sizeof(outbuf), option);
2933 printf ("%s\n", outbuf);
2936 list_del (&tmp, NULL);